Skip to main content

systemprompt_users/
extension.rs

1//! `Extension` registration for the users domain: schema and required flag.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use systemprompt_extension::prelude::*;
7
8#[derive(Debug, Clone, Copy, Default)]
9pub struct UsersExtension;
10
11impl Extension for UsersExtension {
12    fn metadata(&self) -> ExtensionMetadata {
13        ExtensionMetadata {
14            id: "users",
15            name: "Users",
16            version: env!("CARGO_PKG_VERSION"),
17        }
18    }
19
20    fn is_required(&self) -> bool {
21        true
22    }
23
24    fn schemas(&self) -> Vec<SchemaDefinition> {
25        vec![
26            SchemaDefinition::new("users", include_str!("../schema/users.sql"))
27                .with_required_columns(vec![
28                    "id".into(),
29                    "name".into(),
30                    "email".into(),
31                    "created_at".into(),
32                ]),
33            SchemaDefinition::new("user_sessions", include_str!("../schema/user_sessions.sql"))
34                .with_required_columns(vec![
35                    "session_id".into(),
36                    "started_at".into(),
37                    "bot_type".into(),
38                ]),
39            SchemaDefinition::sql_only(include_str!("../schema/session_bot_type.sql")),
40            SchemaDefinition::new(
41                "session_traffic_rollup_hourly",
42                include_str!("../schema/session_traffic_rollup_hourly.sql"),
43            )
44            .with_required_columns(vec![
45                "bucket".into(),
46                "traffic_class".into(),
47                "sessions".into(),
48            ]),
49            SchemaDefinition::new("banned_ips", include_str!("../schema/banned_ips.sql"))
50                .with_required_columns(vec![
51                    "ip_address".into(),
52                    "reason".into(),
53                    "banned_at".into(),
54                ]),
55            SchemaDefinition::new("user_api_keys", include_str!("../schema/user_api_keys.sql"))
56                .with_required_columns(vec![
57                    "id".into(),
58                    "user_id".into(),
59                    "key_prefix".into(),
60                    "key_hash".into(),
61                    "model_allowlist".into(),
62                    "request_window_seconds".into(),
63                ]),
64            SchemaDefinition::new(
65                "user_api_key_scopes",
66                include_str!("../schema/user_api_key_scopes.sql"),
67            )
68            .with_required_columns(vec![
69                "key_id".into(),
70                "dimension".into(),
71                "value".into(),
72            ]),
73            SchemaDefinition::new(
74                "user_device_certs",
75                include_str!("../schema/user_device_certs.sql"),
76            )
77            .with_required_columns(vec![
78                "id".into(),
79                "user_id".into(),
80                "fingerprint".into(),
81                "label".into(),
82            ]),
83            SchemaDefinition::new(
84                "user_rate_limit_buckets",
85                include_str!("../schema/user_rate_limit_buckets.sql"),
86            )
87            .with_required_columns(vec![
88                "user_id".into(),
89                "scope".into(),
90                "window_start".into(),
91                "hits".into(),
92            ]),
93        ]
94        .into_iter()
95        .chain(std::iter::once(api_key_session_schema()))
96        .chain(federation_schemas())
97        .collect()
98    }
99
100    fn dependencies(&self) -> Vec<&'static str> {
101        vec!["events"]
102    }
103
104    fn migrations(&self) -> Vec<Migration> {
105        extension_migrations!()
106    }
107}
108
109register_extension!(UsersExtension);
110
111fn federation_schemas() -> [SchemaDefinition; 2] {
112    [
113        SchemaDefinition::new(
114            "federated_identities",
115            include_str!("../schema/federated_identities.sql"),
116        )
117        .with_required_columns(vec![
118            "issuer".into(),
119            "external_sub".into(),
120            "user_id".into(),
121        ]),
122        SchemaDefinition::new(
123            "sso_login_events",
124            include_str!("../schema/sso_login_events.sql"),
125        )
126        .with_required_columns(vec![
127            "provider_id".into(),
128            "outcome".into(),
129            "occurred_at".into(),
130        ]),
131    ]
132}
133
134fn api_key_session_schema() -> SchemaDefinition {
135    SchemaDefinition::new(
136        "api_key_sessions",
137        include_str!("../schema/api_key_sessions.sql"),
138    )
139    .with_required_columns(vec!["api_key_id".into(), "session_id".into()])
140}