Skip to main content

codoseo_core/
plan.rs

1//! Plans and their limits. `None` means unlimited.
2
3use std::time::Duration;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum Plan {
10    Free,
11    Pro,
12    Agency,
13    SelfHosted,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
17#[serde(rename_all = "snake_case")]
18pub enum Schedule {
19    Weekly,
20    Daily,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
24#[serde(rename_all = "snake_case")]
25pub enum ManualAllowance {
26    PerWeek(u32),
27    PerDay(u32),
28    Unlimited,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
32pub struct PlanLimits {
33    pub max_sites: Option<u32>,
34    pub max_pages: Option<u32>,
35    pub max_duration: Option<Duration>,
36    /// The most frequent schedule allowed; `None` means no scheduled crawls.
37    pub fastest_schedule: Option<Schedule>,
38    pub manual_crawls: ManualAllowance,
39    pub history_days: Option<u32>,
40    pub api_calls_per_day: Option<u32>,
41    pub email_alerts_only: bool,
42}
43
44const HOUR: u64 = 3600;
45
46impl PlanLimits {
47    pub fn for_plan(plan: Plan) -> PlanLimits {
48        match plan {
49            Plan::Free => PlanLimits {
50                max_sites: Some(1),
51                max_pages: Some(500),
52                max_duration: Some(Duration::from_secs(600)),
53                fastest_schedule: Some(Schedule::Weekly),
54                manual_crawls: ManualAllowance::PerWeek(1),
55                history_days: Some(30),
56                api_calls_per_day: Some(100),
57                email_alerts_only: true,
58            },
59            Plan::Pro => PlanLimits {
60                max_sites: Some(5),
61                max_pages: Some(10_000),
62                max_duration: Some(Duration::from_secs(2 * HOUR)),
63                fastest_schedule: Some(Schedule::Daily),
64                manual_crawls: ManualAllowance::PerDay(1),
65                history_days: Some(365),
66                api_calls_per_day: Some(2_000),
67                email_alerts_only: false,
68            },
69            Plan::Agency => PlanLimits {
70                max_sites: Some(25),
71                max_pages: Some(50_000),
72                max_duration: Some(Duration::from_secs(6 * HOUR)),
73                fastest_schedule: Some(Schedule::Daily),
74                manual_crawls: ManualAllowance::Unlimited,
75                history_days: Some(365),
76                api_calls_per_day: Some(10_000),
77                email_alerts_only: false,
78            },
79            // No plan caps, but finite defaults so a calendar or faceted search
80            // can't crawl forever. Self-hosters can raise them in config.
81            Plan::SelfHosted => PlanLimits {
82                max_sites: None,
83                max_pages: Some(100_000),
84                max_duration: Some(Duration::from_secs(24 * HOUR)),
85                fastest_schedule: Some(Schedule::Daily),
86                manual_crawls: ManualAllowance::Unlimited,
87                history_days: Some(365),
88                api_calls_per_day: None,
89                email_alerts_only: false,
90            },
91        }
92    }
93
94    /// The no-signup audit: one 100-page crawl, nothing else.
95    pub fn quick_audit() -> PlanLimits {
96        PlanLimits {
97            max_sites: Some(0),
98            max_pages: Some(100),
99            max_duration: Some(Duration::from_secs(120)),
100            fastest_schedule: None,
101            manual_crawls: ManualAllowance::PerWeek(0),
102            history_days: Some(7),
103            api_calls_per_day: Some(0),
104            email_alerts_only: true,
105        }
106    }
107}