1use indexmap::IndexMap;
2
3use super::{
4 AuthSchemaOptions, DbField, DbFieldType, DbSchema, DbTable, ForeignKey, OnDelete,
5 RateLimitStorage, TableOptions,
6};
7
8pub 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}