Skip to main content

rustauth_core/db/schema/
builder.rs

1use indexmap::IndexMap;
2
3use super::{
4    AuthSchemaOptions, DbField, DbFieldType, DbSchema, DbTable, ForeignKey, OnDelete,
5    RateLimitStorage, TableOptions,
6};
7
8/// Build RustAuth's core database schema metadata.
9pub fn auth_schema(options: AuthSchemaOptions) -> DbSchema {
10    let mut schema = DbSchema::default();
11    let user_table_name = table_name(&options.user, "users");
12    let id_field = options.id_policy.field();
13
14    schema.insert(
15        "user",
16        table(
17            &options.user,
18            "users",
19            Some(1),
20            [
21                ("id", options.id_policy.field()),
22                ("name", field(&options.user, "name", DbFieldType::String)),
23                (
24                    "email",
25                    field(&options.user, "email", DbFieldType::String).unique(),
26                ),
27                (
28                    "email_verified",
29                    field(&options.user, "email_verified", DbFieldType::Boolean).generated(),
30                ),
31                (
32                    "image",
33                    field(&options.user, "image", DbFieldType::String).optional(),
34                ),
35                (
36                    "created_at",
37                    field(&options.user, "created_at", DbFieldType::Timestamp).generated(),
38                ),
39                (
40                    "updated_at",
41                    field(&options.user, "updated_at", DbFieldType::Timestamp).generated(),
42                ),
43            ],
44        ),
45    );
46
47    if !options.has_secondary_storage || options.store_session_in_database {
48        schema.insert(
49            "session",
50            table(
51                &options.session,
52                "sessions",
53                Some(2),
54                [
55                    ("id", options.id_policy.field()),
56                    (
57                        "expires_at",
58                        field(&options.session, "expires_at", DbFieldType::Timestamp),
59                    ),
60                    (
61                        "token",
62                        field(&options.session, "token", DbFieldType::String).unique(),
63                    ),
64                    (
65                        "created_at",
66                        field(&options.session, "created_at", DbFieldType::Timestamp).generated(),
67                    ),
68                    (
69                        "updated_at",
70                        field(&options.session, "updated_at", DbFieldType::Timestamp).generated(),
71                    ),
72                    (
73                        "ip_address",
74                        field(&options.session, "ip_address", DbFieldType::String).optional(),
75                    ),
76                    (
77                        "user_agent",
78                        field(&options.session, "user_agent", DbFieldType::String).optional(),
79                    ),
80                    (
81                        "user_id",
82                        id_reference_field(&options.session, "user_id", &id_field)
83                            .indexed()
84                            .references(ForeignKey::new(
85                                user_table_name.clone(),
86                                "id",
87                                OnDelete::Cascade,
88                            )),
89                    ),
90                ],
91            ),
92        );
93    }
94
95    schema.insert(
96        "account",
97        table(
98            &options.account,
99            "accounts",
100            Some(3),
101            [
102                ("id", options.id_policy.field()),
103                (
104                    "account_id",
105                    field(&options.account, "account_id", DbFieldType::String),
106                ),
107                (
108                    "provider_id",
109                    field(&options.account, "provider_id", DbFieldType::String),
110                ),
111                (
112                    "user_id",
113                    id_reference_field(&options.account, "user_id", &id_field)
114                        .indexed()
115                        .references(ForeignKey::new(user_table_name, "id", OnDelete::Cascade)),
116                ),
117                (
118                    "access_token",
119                    field(&options.account, "access_token", DbFieldType::String)
120                        .optional()
121                        .hidden(),
122                ),
123                (
124                    "refresh_token",
125                    field(&options.account, "refresh_token", DbFieldType::String)
126                        .optional()
127                        .hidden(),
128                ),
129                (
130                    "id_token",
131                    field(&options.account, "id_token", DbFieldType::String)
132                        .optional()
133                        .hidden(),
134                ),
135                (
136                    "access_token_expires_at",
137                    field(
138                        &options.account,
139                        "access_token_expires_at",
140                        DbFieldType::Timestamp,
141                    )
142                    .optional()
143                    .hidden(),
144                ),
145                (
146                    "refresh_token_expires_at",
147                    field(
148                        &options.account,
149                        "refresh_token_expires_at",
150                        DbFieldType::Timestamp,
151                    )
152                    .optional()
153                    .hidden(),
154                ),
155                (
156                    "scope",
157                    field(&options.account, "scope", DbFieldType::String).optional(),
158                ),
159                (
160                    "password",
161                    field(&options.account, "password", DbFieldType::String)
162                        .optional()
163                        .hidden(),
164                ),
165                (
166                    "created_at",
167                    field(&options.account, "created_at", DbFieldType::Timestamp).generated(),
168                ),
169                (
170                    "updated_at",
171                    field(&options.account, "updated_at", DbFieldType::Timestamp).generated(),
172                ),
173            ],
174        ),
175    );
176
177    if !options.has_secondary_storage || options.store_verification_in_database {
178        schema.insert(
179            "verification",
180            table(
181                &options.verification,
182                "verifications",
183                Some(4),
184                [
185                    ("id", options.id_policy.field()),
186                    (
187                        "identifier",
188                        field(&options.verification, "identifier", DbFieldType::String).indexed(),
189                    ),
190                    (
191                        "value",
192                        field(&options.verification, "value", DbFieldType::String),
193                    ),
194                    (
195                        "expires_at",
196                        field(&options.verification, "expires_at", DbFieldType::Timestamp),
197                    ),
198                    (
199                        "created_at",
200                        field(&options.verification, "created_at", DbFieldType::Timestamp)
201                            .generated(),
202                    ),
203                    (
204                        "updated_at",
205                        field(&options.verification, "updated_at", DbFieldType::Timestamp)
206                            .generated(),
207                    ),
208                ],
209            ),
210        );
211    }
212
213    if options.rate_limit_storage == RateLimitStorage::Database {
214        schema.insert(
215            "rate_limit",
216            table(
217                &options.rate_limit,
218                "rate_limits",
219                None,
220                [
221                    (
222                        "key",
223                        field(&options.rate_limit, "key", DbFieldType::String).unique(),
224                    ),
225                    (
226                        "count",
227                        field(&options.rate_limit, "count", DbFieldType::Number),
228                    ),
229                    (
230                        "last_request",
231                        field(&options.rate_limit, "last_request", DbFieldType::Number),
232                    ),
233                ],
234            ),
235        );
236    }
237
238    schema
239}
240
241fn table<const N: usize>(
242    options: &TableOptions,
243    default_name: &str,
244    order: Option<u16>,
245    fields: [(&str, DbField); N],
246) -> DbTable {
247    let mut mapped_fields = fields
248        .into_iter()
249        .map(|(logical_name, field)| (logical_name.to_owned(), field))
250        .collect::<IndexMap<_, _>>();
251    mapped_fields.extend(options.additional_fields.clone());
252
253    DbTable {
254        name: table_name(options, default_name),
255        fields: mapped_fields,
256        order,
257    }
258}
259
260fn table_name(options: &TableOptions, default_name: &str) -> String {
261    options
262        .name
263        .clone()
264        .unwrap_or_else(|| default_name.to_owned())
265}
266
267fn field(options: &TableOptions, logical_name: &str, field_type: DbFieldType) -> DbField {
268    DbField::new(options.field_name(logical_name), field_type)
269}
270
271fn id_reference_field(options: &TableOptions, logical_name: &str, id_field: &DbField) -> DbField {
272    let mut field = field(options, logical_name, id_field.field_type.clone());
273    field.generated_id = id_field.generated_id;
274    field
275}