@banata-auth/convex 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +104 -0
- package/dist/auth-config.d.ts +22 -0
- package/dist/auth-config.d.ts.map +1 -0
- package/dist/auth-config.js +3 -0
- package/dist/auth-config.js.map +1 -0
- package/dist/auth.d.ts +462 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/component/adapter.d.ts +21 -0
- package/dist/component/adapter.d.ts.map +1 -0
- package/dist/component/adapter.js +3 -0
- package/dist/component/adapter.js.map +1 -0
- package/dist/component/schema.d.ts +1026 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/hooks.d.ts +25 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/http.d.ts +41 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +62 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9516 -0
- package/dist/index.js.map +1 -0
- package/dist/node.d.ts +389 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +9559 -0
- package/dist/node.js.map +1 -0
- package/dist/plugins/audit.d.ts +106 -0
- package/dist/plugins/audit.d.ts.map +1 -0
- package/dist/plugins/config.d.ts +83 -0
- package/dist/plugins/config.d.ts.map +1 -0
- package/dist/plugins/domains.d.ts +3 -0
- package/dist/plugins/domains.d.ts.map +1 -0
- package/dist/plugins/email-sender.d.ts +75 -0
- package/dist/plugins/email-sender.d.ts.map +1 -0
- package/dist/plugins/email-templates.d.ts +108 -0
- package/dist/plugins/email-templates.d.ts.map +1 -0
- package/dist/plugins/email.d.ts +82 -0
- package/dist/plugins/email.d.ts.map +1 -0
- package/dist/plugins/enterprise.d.ts +3 -0
- package/dist/plugins/enterprise.d.ts.map +1 -0
- package/dist/plugins/events.d.ts +40 -0
- package/dist/plugins/events.d.ts.map +1 -0
- package/dist/plugins/index.d.ts +18 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +9192 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/organization-rbac.d.ts +3 -0
- package/dist/plugins/organization-rbac.d.ts.map +1 -0
- package/dist/plugins/portal.d.ts +34 -0
- package/dist/plugins/portal.d.ts.map +1 -0
- package/dist/plugins/projects.d.ts +16 -0
- package/dist/plugins/projects.d.ts.map +1 -0
- package/dist/plugins/protection.d.ts +127 -0
- package/dist/plugins/protection.d.ts.map +1 -0
- package/dist/plugins/types.d.ts +508 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/user-management.d.ts +8 -0
- package/dist/plugins/user-management.d.ts.map +1 -0
- package/dist/plugins/vault.d.ts +68 -0
- package/dist/plugins/vault.d.ts.map +1 -0
- package/dist/plugins/webhook.d.ts +65 -0
- package/dist/plugins/webhook.d.ts.map +1 -0
- package/dist/triggers.d.ts +158 -0
- package/dist/triggers.d.ts.map +1 -0
- package/dist/triggers.js +36 -0
- package/dist/triggers.js.map +1 -0
- package/package.json +102 -0
- package/src/component/adapter.ts +21 -0
- package/src/component/convex.config.ts +15 -0
- package/src/component/schema.ts +916 -0
|
@@ -0,0 +1,916 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Banata Auth Convex component schema.
|
|
3
|
+
*
|
|
4
|
+
* This schema defines all tables managed by the Better Auth Convex component.
|
|
5
|
+
* It covers the core auth tables plus tables for all plugins:
|
|
6
|
+
* - Core: user, session, account, verification
|
|
7
|
+
* - Two-Factor: twoFactor
|
|
8
|
+
* - Passkeys: passkey
|
|
9
|
+
* - JWT/JWKS: jwks
|
|
10
|
+
* - Rate Limiting: rateLimit
|
|
11
|
+
* - Organizations: organization, member, invitation
|
|
12
|
+
* - SSO: ssoProvider
|
|
13
|
+
* - SCIM: scimProvider
|
|
14
|
+
* - API Key: apikey
|
|
15
|
+
* - Audit: auditEvent
|
|
16
|
+
* - Webhooks: webhookEndpoint, webhookDelivery
|
|
17
|
+
* - Vault: vaultSecret
|
|
18
|
+
* - Domains: domainVerification
|
|
19
|
+
*
|
|
20
|
+
* This file would normally be auto-generated by:
|
|
21
|
+
* npx auth generate --config ./auth.ts --output ./schema.ts
|
|
22
|
+
*
|
|
23
|
+
* We define it manually to ensure all fields needed by Banata Auth are present,
|
|
24
|
+
* including custom fields not in the default Better Auth schema.
|
|
25
|
+
*/
|
|
26
|
+
import { defineSchema, defineTable } from "convex/server";
|
|
27
|
+
import { v } from "convex/values";
|
|
28
|
+
|
|
29
|
+
const schema = defineSchema({
|
|
30
|
+
// ─── Project Tables ─────────────────────────────────────────────
|
|
31
|
+
// These are the top-level organizational units. A project represents
|
|
32
|
+
// a product/app (e.g., "Twitter Clone").
|
|
33
|
+
// All other tables are scoped to a project.
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Project table — top-level organizational unit.
|
|
37
|
+
* Each project is a fully isolated auth tenant with its own users,
|
|
38
|
+
* sessions, orgs, branding, templates, webhooks, API keys, etc.
|
|
39
|
+
*
|
|
40
|
+
* Example: A software firm uses Banata Auth to power auth for
|
|
41
|
+
* multiple client products — each product is a project.
|
|
42
|
+
*/
|
|
43
|
+
project: defineTable({
|
|
44
|
+
name: v.string(), // "Twitter Clone"
|
|
45
|
+
slug: v.string(), // "twitter-clone" (unique)
|
|
46
|
+
description: v.optional(v.string()),
|
|
47
|
+
logoUrl: v.optional(v.string()),
|
|
48
|
+
|
|
49
|
+
/** The dashboard user who created this project. */
|
|
50
|
+
ownerId: v.string(),
|
|
51
|
+
|
|
52
|
+
createdAt: v.float64(),
|
|
53
|
+
updatedAt: v.float64(),
|
|
54
|
+
})
|
|
55
|
+
.index("slug", ["slug"])
|
|
56
|
+
.index("ownerId", ["ownerId"])
|
|
57
|
+
.index("createdAt", ["createdAt"]),
|
|
58
|
+
|
|
59
|
+
// ─── Core Auth Tables ────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* User table - stores all user accounts.
|
|
63
|
+
* Extended with username, phone, admin, and 2FA fields.
|
|
64
|
+
* Scoped per project — each project has its own isolated user base.
|
|
65
|
+
*/
|
|
66
|
+
user: defineTable({
|
|
67
|
+
name: v.string(),
|
|
68
|
+
email: v.string(),
|
|
69
|
+
emailVerified: v.boolean(),
|
|
70
|
+
image: v.optional(v.string()),
|
|
71
|
+
|
|
72
|
+
// Project scoping — isolates users per project
|
|
73
|
+
projectId: v.optional(v.string()),
|
|
74
|
+
|
|
75
|
+
// Username plugin
|
|
76
|
+
username: v.optional(v.string()),
|
|
77
|
+
displayUsername: v.optional(v.string()),
|
|
78
|
+
|
|
79
|
+
// Phone plugin
|
|
80
|
+
phoneNumber: v.optional(v.string()),
|
|
81
|
+
phoneNumberVerified: v.optional(v.boolean()),
|
|
82
|
+
|
|
83
|
+
// Admin plugin
|
|
84
|
+
role: v.optional(v.string()),
|
|
85
|
+
banned: v.optional(v.boolean()),
|
|
86
|
+
banReason: v.optional(v.string()),
|
|
87
|
+
banExpires: v.optional(v.float64()),
|
|
88
|
+
|
|
89
|
+
// Two-factor plugin
|
|
90
|
+
twoFactorEnabled: v.optional(v.boolean()),
|
|
91
|
+
|
|
92
|
+
// Anonymous plugin
|
|
93
|
+
isAnonymous: v.optional(v.boolean()),
|
|
94
|
+
|
|
95
|
+
// Custom metadata
|
|
96
|
+
metadata: v.optional(v.any()),
|
|
97
|
+
|
|
98
|
+
// Timestamps
|
|
99
|
+
createdAt: v.float64(),
|
|
100
|
+
updatedAt: v.float64(),
|
|
101
|
+
})
|
|
102
|
+
.index("email", ["email"])
|
|
103
|
+
.index("username", ["username"])
|
|
104
|
+
.index("phoneNumber", ["phoneNumber"])
|
|
105
|
+
.index("role", ["role"])
|
|
106
|
+
.index("createdAt", ["createdAt"])
|
|
107
|
+
.index("projectId", ["projectId"])
|
|
108
|
+
.index("projectId_email", ["projectId", "email"]),
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Session table - stores active user sessions.
|
|
112
|
+
* Better Auth manages session lifecycle (create, refresh, revoke).
|
|
113
|
+
*/
|
|
114
|
+
session: defineTable({
|
|
115
|
+
userId: v.string(),
|
|
116
|
+
token: v.string(),
|
|
117
|
+
expiresAt: v.float64(),
|
|
118
|
+
|
|
119
|
+
// Project scoping
|
|
120
|
+
projectId: v.optional(v.string()),
|
|
121
|
+
|
|
122
|
+
// Session context
|
|
123
|
+
ipAddress: v.optional(v.string()),
|
|
124
|
+
userAgent: v.optional(v.string()),
|
|
125
|
+
|
|
126
|
+
// Organization context (organization plugin)
|
|
127
|
+
activeOrganizationId: v.optional(v.string()),
|
|
128
|
+
|
|
129
|
+
// Impersonation (admin plugin)
|
|
130
|
+
impersonatedBy: v.optional(v.string()),
|
|
131
|
+
|
|
132
|
+
createdAt: v.float64(),
|
|
133
|
+
updatedAt: v.float64(),
|
|
134
|
+
})
|
|
135
|
+
.index("token", ["token"])
|
|
136
|
+
.index("userId", ["userId"])
|
|
137
|
+
.index("projectId", ["projectId"]),
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Account table - stores provider credentials (OAuth, email/password, SAML, etc.)
|
|
141
|
+
* Each user can have multiple accounts (one per provider).
|
|
142
|
+
*/
|
|
143
|
+
account: defineTable({
|
|
144
|
+
userId: v.string(),
|
|
145
|
+
accountId: v.string(),
|
|
146
|
+
providerId: v.string(),
|
|
147
|
+
|
|
148
|
+
// Project scoping
|
|
149
|
+
projectId: v.optional(v.string()),
|
|
150
|
+
|
|
151
|
+
// OAuth tokens
|
|
152
|
+
accessToken: v.optional(v.string()),
|
|
153
|
+
refreshToken: v.optional(v.string()),
|
|
154
|
+
accessTokenExpiresAt: v.optional(v.float64()),
|
|
155
|
+
refreshTokenExpiresAt: v.optional(v.float64()),
|
|
156
|
+
scope: v.optional(v.string()),
|
|
157
|
+
idToken: v.optional(v.string()),
|
|
158
|
+
|
|
159
|
+
// Credential provider
|
|
160
|
+
password: v.optional(v.string()),
|
|
161
|
+
|
|
162
|
+
createdAt: v.float64(),
|
|
163
|
+
updatedAt: v.float64(),
|
|
164
|
+
})
|
|
165
|
+
.index("userId", ["userId"])
|
|
166
|
+
.index("accountId_providerId", ["accountId", "providerId"])
|
|
167
|
+
.index("projectId", ["projectId"]),
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Verification table - stores email verification tokens, password reset tokens,
|
|
171
|
+
* magic link tokens, and OTPs.
|
|
172
|
+
*/
|
|
173
|
+
verification: defineTable({
|
|
174
|
+
identifier: v.string(),
|
|
175
|
+
value: v.string(),
|
|
176
|
+
expiresAt: v.float64(),
|
|
177
|
+
|
|
178
|
+
// Project scoping
|
|
179
|
+
projectId: v.optional(v.string()),
|
|
180
|
+
|
|
181
|
+
createdAt: v.optional(v.float64()),
|
|
182
|
+
updatedAt: v.optional(v.float64()),
|
|
183
|
+
})
|
|
184
|
+
.index("identifier", ["identifier"])
|
|
185
|
+
.index("projectId", ["projectId"]),
|
|
186
|
+
|
|
187
|
+
// ─── Plugin Tables ──────────────────────────────────────────────
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Two-factor authentication table.
|
|
191
|
+
* Stores TOTP secrets and backup codes per user.
|
|
192
|
+
*/
|
|
193
|
+
twoFactor: defineTable({
|
|
194
|
+
userId: v.string(),
|
|
195
|
+
secret: v.string(),
|
|
196
|
+
backupCodes: v.string(),
|
|
197
|
+
|
|
198
|
+
// Project scoping
|
|
199
|
+
projectId: v.optional(v.string()),
|
|
200
|
+
|
|
201
|
+
createdAt: v.float64(),
|
|
202
|
+
updatedAt: v.float64(),
|
|
203
|
+
})
|
|
204
|
+
.index("userId", ["userId"])
|
|
205
|
+
.index("projectId", ["projectId"]),
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Passkey (WebAuthn) credentials.
|
|
209
|
+
* Stores registered passkeys for passwordless authentication.
|
|
210
|
+
*/
|
|
211
|
+
passkey: defineTable({
|
|
212
|
+
userId: v.string(),
|
|
213
|
+
name: v.optional(v.string()),
|
|
214
|
+
|
|
215
|
+
// Project scoping
|
|
216
|
+
projectId: v.optional(v.string()),
|
|
217
|
+
|
|
218
|
+
credentialID: v.string(),
|
|
219
|
+
publicKey: v.string(),
|
|
220
|
+
counter: v.float64(),
|
|
221
|
+
transports: v.optional(v.string()),
|
|
222
|
+
deviceType: v.optional(v.string()),
|
|
223
|
+
backedUp: v.optional(v.boolean()),
|
|
224
|
+
|
|
225
|
+
createdAt: v.float64(),
|
|
226
|
+
})
|
|
227
|
+
.index("credentialID", ["credentialID"])
|
|
228
|
+
.index("userId", ["userId"])
|
|
229
|
+
.index("projectId", ["projectId"]),
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* JWKS table - stores RS256 key pairs for JWT signing.
|
|
233
|
+
* Better Auth generates and rotates these automatically.
|
|
234
|
+
* Scoped per project — each project has its own signing keys.
|
|
235
|
+
*/
|
|
236
|
+
jwks: defineTable({
|
|
237
|
+
publicKey: v.string(),
|
|
238
|
+
privateKey: v.string(),
|
|
239
|
+
|
|
240
|
+
// Project scoping
|
|
241
|
+
projectId: v.optional(v.string()),
|
|
242
|
+
|
|
243
|
+
createdAt: v.float64(),
|
|
244
|
+
}).index("projectId", ["projectId"]),
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Rate limiting table.
|
|
248
|
+
* Tracks request counts per key (IP + endpoint hash).
|
|
249
|
+
*/
|
|
250
|
+
rateLimit: defineTable({
|
|
251
|
+
key: v.string(),
|
|
252
|
+
count: v.float64(),
|
|
253
|
+
lastRequest: v.float64(),
|
|
254
|
+
|
|
255
|
+
// Project scoping
|
|
256
|
+
projectId: v.optional(v.string()),
|
|
257
|
+
})
|
|
258
|
+
.index("key", ["key"])
|
|
259
|
+
.index("projectId", ["projectId"]),
|
|
260
|
+
|
|
261
|
+
// ─── Organization Tables ────────────────────────────────────────
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Organization table.
|
|
265
|
+
* Stores organization details and policies.
|
|
266
|
+
* Scoped per project — each project has its own orgs.
|
|
267
|
+
*/
|
|
268
|
+
organization: defineTable({
|
|
269
|
+
name: v.string(),
|
|
270
|
+
slug: v.string(),
|
|
271
|
+
logo: v.optional(v.string()),
|
|
272
|
+
|
|
273
|
+
// Project scoping
|
|
274
|
+
projectId: v.optional(v.string()),
|
|
275
|
+
|
|
276
|
+
metadata: v.optional(v.any()),
|
|
277
|
+
|
|
278
|
+
createdAt: v.float64(),
|
|
279
|
+
updatedAt: v.float64(),
|
|
280
|
+
})
|
|
281
|
+
.index("slug", ["slug"])
|
|
282
|
+
.index("createdAt", ["createdAt"])
|
|
283
|
+
.index("projectId", ["projectId"]),
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Organization member table.
|
|
287
|
+
* Links users to organizations with roles.
|
|
288
|
+
*/
|
|
289
|
+
member: defineTable({
|
|
290
|
+
organizationId: v.string(),
|
|
291
|
+
userId: v.string(),
|
|
292
|
+
role: v.string(),
|
|
293
|
+
|
|
294
|
+
// Project scoping
|
|
295
|
+
projectId: v.optional(v.string()),
|
|
296
|
+
|
|
297
|
+
createdAt: v.float64(),
|
|
298
|
+
updatedAt: v.float64(),
|
|
299
|
+
})
|
|
300
|
+
.index("organizationId", ["organizationId"])
|
|
301
|
+
.index("userId", ["userId"])
|
|
302
|
+
.index("organizationId_userId", ["organizationId", "userId"])
|
|
303
|
+
.index("projectId", ["projectId"]),
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Organization invitation table.
|
|
307
|
+
* Stores pending invitations to join an organization.
|
|
308
|
+
*/
|
|
309
|
+
invitation: defineTable({
|
|
310
|
+
organizationId: v.string(),
|
|
311
|
+
email: v.string(),
|
|
312
|
+
role: v.string(),
|
|
313
|
+
inviterId: v.string(),
|
|
314
|
+
|
|
315
|
+
// Project scoping
|
|
316
|
+
projectId: v.optional(v.string()),
|
|
317
|
+
|
|
318
|
+
status: v.string(),
|
|
319
|
+
expiresAt: v.float64(),
|
|
320
|
+
|
|
321
|
+
createdAt: v.float64(),
|
|
322
|
+
})
|
|
323
|
+
.index("organizationId", ["organizationId"])
|
|
324
|
+
.index("email", ["email"])
|
|
325
|
+
.index("status", ["status"])
|
|
326
|
+
.index("projectId", ["projectId"]),
|
|
327
|
+
|
|
328
|
+
// ─── SSO Tables ────────────────────────────────────────────────────
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* SSO Provider table - stores SAML/OIDC connection configurations.
|
|
332
|
+
* Created by the @better-auth/sso plugin.
|
|
333
|
+
*/
|
|
334
|
+
ssoProvider: defineTable({
|
|
335
|
+
organizationId: v.optional(v.string()),
|
|
336
|
+
providerId: v.string(),
|
|
337
|
+
issuer: v.string(),
|
|
338
|
+
domain: v.string(),
|
|
339
|
+
name: v.optional(v.string()),
|
|
340
|
+
domainVerified: v.optional(v.boolean()),
|
|
341
|
+
|
|
342
|
+
// Project scoping
|
|
343
|
+
projectId: v.optional(v.string()),
|
|
344
|
+
|
|
345
|
+
// OIDC config
|
|
346
|
+
oidcConfig: v.optional(v.any()),
|
|
347
|
+
|
|
348
|
+
// SAML config
|
|
349
|
+
samlConfig: v.optional(v.any()),
|
|
350
|
+
|
|
351
|
+
// Provider state
|
|
352
|
+
providerType: v.string(), // "saml" | "oidc"
|
|
353
|
+
active: v.optional(v.boolean()),
|
|
354
|
+
|
|
355
|
+
userId: v.optional(v.string()),
|
|
356
|
+
|
|
357
|
+
createdAt: v.float64(),
|
|
358
|
+
updatedAt: v.float64(),
|
|
359
|
+
})
|
|
360
|
+
.index("providerId", ["providerId"])
|
|
361
|
+
.index("domain", ["domain"])
|
|
362
|
+
.index("organizationId", ["organizationId"])
|
|
363
|
+
.index("projectId", ["projectId"]),
|
|
364
|
+
|
|
365
|
+
// ─── SCIM Tables ───────────────────────────────────────────────────
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* SCIM Provider table - stores SCIM directory connection configurations.
|
|
369
|
+
* Created by the @better-auth/scim plugin.
|
|
370
|
+
*/
|
|
371
|
+
scimProvider: defineTable({
|
|
372
|
+
organizationId: v.optional(v.string()),
|
|
373
|
+
providerId: v.string(),
|
|
374
|
+
scimToken: v.optional(v.string()),
|
|
375
|
+
name: v.optional(v.string()),
|
|
376
|
+
provider: v.optional(v.string()),
|
|
377
|
+
|
|
378
|
+
// Project scoping
|
|
379
|
+
projectId: v.optional(v.string()),
|
|
380
|
+
|
|
381
|
+
// SCIM endpoint URL (auto-generated)
|
|
382
|
+
endpointUrl: v.optional(v.string()),
|
|
383
|
+
|
|
384
|
+
// Bearer token for SCIM API (hashed)
|
|
385
|
+
tokenHash: v.optional(v.string()),
|
|
386
|
+
|
|
387
|
+
// Provider state
|
|
388
|
+
active: v.optional(v.boolean()),
|
|
389
|
+
lastSyncAt: v.optional(v.float64()),
|
|
390
|
+
lastSyncStatus: v.optional(v.string()),
|
|
391
|
+
|
|
392
|
+
userId: v.optional(v.string()),
|
|
393
|
+
|
|
394
|
+
createdAt: v.float64(),
|
|
395
|
+
updatedAt: v.float64(),
|
|
396
|
+
})
|
|
397
|
+
.index("providerId", ["providerId"])
|
|
398
|
+
.index("scimToken", ["scimToken"])
|
|
399
|
+
.index("organizationId", ["organizationId"])
|
|
400
|
+
.index("projectId", ["projectId"]),
|
|
401
|
+
|
|
402
|
+
// ─── API Key Tables ────────────────────────────────────────────────
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* API Key table - stores API keys for programmatic access.
|
|
406
|
+
* Created by the apiKey plugin from better-auth/plugins.
|
|
407
|
+
*/
|
|
408
|
+
apikey: defineTable({
|
|
409
|
+
name: v.optional(v.string()),
|
|
410
|
+
start: v.optional(v.string()),
|
|
411
|
+
prefix: v.optional(v.union(v.string(), v.null())),
|
|
412
|
+
key: v.string(),
|
|
413
|
+
|
|
414
|
+
userId: v.string(),
|
|
415
|
+
|
|
416
|
+
// Project scoping
|
|
417
|
+
projectId: v.optional(v.string()),
|
|
418
|
+
|
|
419
|
+
refillInterval: v.optional(v.union(v.string(), v.null())),
|
|
420
|
+
refillAmount: v.optional(v.union(v.float64(), v.null())),
|
|
421
|
+
lastRefillAt: v.optional(v.union(v.float64(), v.null())),
|
|
422
|
+
|
|
423
|
+
enabled: v.optional(v.boolean()),
|
|
424
|
+
rateLimitEnabled: v.optional(v.boolean()),
|
|
425
|
+
rateLimitTimeWindow: v.optional(v.float64()),
|
|
426
|
+
rateLimitMax: v.optional(v.float64()),
|
|
427
|
+
|
|
428
|
+
remaining: v.optional(v.union(v.float64(), v.null())),
|
|
429
|
+
|
|
430
|
+
requestCount: v.optional(v.float64()),
|
|
431
|
+
lastRequest: v.optional(v.union(v.float64(), v.null())),
|
|
432
|
+
|
|
433
|
+
expiresAt: v.optional(v.union(v.float64(), v.null())),
|
|
434
|
+
|
|
435
|
+
permissions: v.optional(v.string()),
|
|
436
|
+
metadata: v.optional(v.any()),
|
|
437
|
+
|
|
438
|
+
createdAt: v.float64(),
|
|
439
|
+
updatedAt: v.float64(),
|
|
440
|
+
})
|
|
441
|
+
.index("key", ["key"])
|
|
442
|
+
.index("userId", ["userId"])
|
|
443
|
+
.index("start", ["start"])
|
|
444
|
+
.index("projectId", ["projectId"]),
|
|
445
|
+
|
|
446
|
+
// ─── Audit Log Tables ──────────────────────────────────────────────
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Audit event table - stores all auditable auth events.
|
|
450
|
+
*/
|
|
451
|
+
auditEvent: defineTable({
|
|
452
|
+
action: v.string(),
|
|
453
|
+
version: v.optional(v.float64()),
|
|
454
|
+
|
|
455
|
+
// Project scoping
|
|
456
|
+
projectId: v.optional(v.string()),
|
|
457
|
+
|
|
458
|
+
// Actor info
|
|
459
|
+
actorType: v.string(), // "user" | "admin" | "system" | "api_key" | "scim"
|
|
460
|
+
actorId: v.string(),
|
|
461
|
+
actorName: v.optional(v.string()),
|
|
462
|
+
actorEmail: v.optional(v.string()),
|
|
463
|
+
actorMetadata: v.optional(v.any()),
|
|
464
|
+
|
|
465
|
+
// Target info (JSON stringified array)
|
|
466
|
+
targets: v.optional(v.string()),
|
|
467
|
+
|
|
468
|
+
// Context
|
|
469
|
+
organizationId: v.optional(v.string()),
|
|
470
|
+
ipAddress: v.optional(v.string()),
|
|
471
|
+
userAgent: v.optional(v.string()),
|
|
472
|
+
requestId: v.optional(v.string()),
|
|
473
|
+
|
|
474
|
+
// Changes (before/after JSON)
|
|
475
|
+
changes: v.optional(v.string()),
|
|
476
|
+
|
|
477
|
+
idempotencyKey: v.optional(v.string()),
|
|
478
|
+
metadata: v.optional(v.any()),
|
|
479
|
+
|
|
480
|
+
occurredAt: v.float64(),
|
|
481
|
+
createdAt: v.float64(),
|
|
482
|
+
})
|
|
483
|
+
.index("action", ["action"])
|
|
484
|
+
.index("actorId", ["actorId"])
|
|
485
|
+
.index("organizationId", ["organizationId"])
|
|
486
|
+
.index("occurredAt", ["occurredAt"])
|
|
487
|
+
.index("idempotencyKey", ["idempotencyKey"])
|
|
488
|
+
.index("projectId", ["projectId"])
|
|
489
|
+
.index("projectId_occurredAt", ["projectId", "occurredAt"]),
|
|
490
|
+
|
|
491
|
+
// ─── Webhook Tables ────────────────────────────────────────────────
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Webhook endpoint table - stores registered webhook URLs.
|
|
495
|
+
*/
|
|
496
|
+
webhookEndpoint: defineTable({
|
|
497
|
+
url: v.string(),
|
|
498
|
+
secret: v.string(),
|
|
499
|
+
|
|
500
|
+
// Project scoping
|
|
501
|
+
projectId: v.optional(v.string()),
|
|
502
|
+
|
|
503
|
+
eventTypes: v.optional(v.string()), // JSON stringified array
|
|
504
|
+
enabled: v.boolean(),
|
|
505
|
+
|
|
506
|
+
successCount: v.optional(v.float64()),
|
|
507
|
+
failureCount: v.optional(v.float64()),
|
|
508
|
+
consecutiveFailures: v.optional(v.float64()),
|
|
509
|
+
|
|
510
|
+
lastDeliveryAt: v.optional(v.float64()),
|
|
511
|
+
lastDeliveryStatus: v.optional(v.string()),
|
|
512
|
+
|
|
513
|
+
createdAt: v.float64(),
|
|
514
|
+
updatedAt: v.float64(),
|
|
515
|
+
})
|
|
516
|
+
.index("enabled", ["enabled"])
|
|
517
|
+
.index("projectId", ["projectId"]),
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Webhook delivery table - stores delivery attempts for audit trail.
|
|
521
|
+
*/
|
|
522
|
+
webhookDelivery: defineTable({
|
|
523
|
+
endpointId: v.string(),
|
|
524
|
+
eventType: v.string(),
|
|
525
|
+
|
|
526
|
+
// Project scoping
|
|
527
|
+
projectId: v.optional(v.string()),
|
|
528
|
+
|
|
529
|
+
payload: v.string(), // JSON stringified
|
|
530
|
+
|
|
531
|
+
attempt: v.float64(),
|
|
532
|
+
maxAttempts: v.float64(),
|
|
533
|
+
|
|
534
|
+
status: v.string(), // "pending" | "success" | "failed" | "retrying"
|
|
535
|
+
httpStatus: v.optional(v.float64()),
|
|
536
|
+
responseBody: v.optional(v.string()),
|
|
537
|
+
errorMessage: v.optional(v.string()),
|
|
538
|
+
|
|
539
|
+
nextRetryAt: v.optional(v.float64()),
|
|
540
|
+
|
|
541
|
+
deliveredAt: v.optional(v.float64()),
|
|
542
|
+
createdAt: v.float64(),
|
|
543
|
+
})
|
|
544
|
+
.index("endpointId", ["endpointId"])
|
|
545
|
+
.index("status", ["status"])
|
|
546
|
+
.index("nextRetryAt", ["nextRetryAt"])
|
|
547
|
+
.index("projectId", ["projectId"]),
|
|
548
|
+
|
|
549
|
+
// ─── Config Tables ────────────────────────────────────────────────
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Role definition table — stores custom RBAC roles defined via the dashboard.
|
|
553
|
+
* Better Auth has static code-defined roles; this table adds dynamic CRUD roles.
|
|
554
|
+
*/
|
|
555
|
+
roleDefinition: defineTable({
|
|
556
|
+
name: v.string(),
|
|
557
|
+
slug: v.string(),
|
|
558
|
+
description: v.optional(v.string()),
|
|
559
|
+
permissions: v.optional(v.string()), // JSON stringified array of permission slugs
|
|
560
|
+
isDefault: v.optional(v.boolean()),
|
|
561
|
+
|
|
562
|
+
// Project scoping
|
|
563
|
+
projectId: v.optional(v.string()),
|
|
564
|
+
|
|
565
|
+
createdAt: v.float64(),
|
|
566
|
+
updatedAt: v.float64(),
|
|
567
|
+
})
|
|
568
|
+
.index("slug", ["slug"])
|
|
569
|
+
.index("createdAt", ["createdAt"])
|
|
570
|
+
.index("projectId", ["projectId"]),
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Permission definition table — stores custom RBAC permissions defined via the dashboard.
|
|
574
|
+
*/
|
|
575
|
+
permissionDefinition: defineTable({
|
|
576
|
+
name: v.string(),
|
|
577
|
+
slug: v.string(),
|
|
578
|
+
description: v.optional(v.string()),
|
|
579
|
+
|
|
580
|
+
// Project scoping
|
|
581
|
+
projectId: v.optional(v.string()),
|
|
582
|
+
|
|
583
|
+
createdAt: v.float64(),
|
|
584
|
+
updatedAt: v.float64(),
|
|
585
|
+
})
|
|
586
|
+
.index("slug", ["slug"])
|
|
587
|
+
.index("createdAt", ["createdAt"])
|
|
588
|
+
.index("projectId", ["projectId"]),
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Branding config table — stores UI branding settings for the hosted auth UI.
|
|
592
|
+
* One row per project (keyed by projectId).
|
|
593
|
+
*/
|
|
594
|
+
brandingConfig: defineTable({
|
|
595
|
+
// Project scoping
|
|
596
|
+
projectId: v.optional(v.string()),
|
|
597
|
+
|
|
598
|
+
primaryColor: v.optional(v.string()),
|
|
599
|
+
bgColor: v.optional(v.string()),
|
|
600
|
+
borderRadius: v.optional(v.float64()),
|
|
601
|
+
darkMode: v.optional(v.boolean()),
|
|
602
|
+
customCss: v.optional(v.string()),
|
|
603
|
+
font: v.optional(v.string()),
|
|
604
|
+
logoUrl: v.optional(v.string()),
|
|
605
|
+
|
|
606
|
+
createdAt: v.float64(),
|
|
607
|
+
updatedAt: v.float64(),
|
|
608
|
+
}).index("projectId", ["projectId"]),
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Email template table — stores custom email templates as JSON block arrays.
|
|
612
|
+
* Templates are created/edited via the dashboard's visual email editor and
|
|
613
|
+
* can be sent programmatically via the SDK using the template slug.
|
|
614
|
+
* Scoped per project.
|
|
615
|
+
*/
|
|
616
|
+
emailTemplate: defineTable({
|
|
617
|
+
name: v.string(),
|
|
618
|
+
slug: v.string(),
|
|
619
|
+
subject: v.string(),
|
|
620
|
+
previewText: v.optional(v.string()),
|
|
621
|
+
category: v.string(), // "auth" | "marketing" | "transactional" | "onboarding" | "notification" | "custom"
|
|
622
|
+
description: v.optional(v.string()),
|
|
623
|
+
/** JSON-serialized EmailBlock[] array. */
|
|
624
|
+
blocksJson: v.string(),
|
|
625
|
+
/** JSON-serialized EmailTemplateVariable[] array. */
|
|
626
|
+
variablesJson: v.optional(v.string()),
|
|
627
|
+
/** Whether this is a built-in auth template (not deletable). */
|
|
628
|
+
builtIn: v.optional(v.boolean()),
|
|
629
|
+
/** The built-in email type this overrides (if any). */
|
|
630
|
+
builtInType: v.optional(v.string()),
|
|
631
|
+
|
|
632
|
+
// Project scoping
|
|
633
|
+
projectId: v.optional(v.string()),
|
|
634
|
+
|
|
635
|
+
createdAt: v.float64(),
|
|
636
|
+
updatedAt: v.float64(),
|
|
637
|
+
})
|
|
638
|
+
.index("slug", ["slug"])
|
|
639
|
+
.index("category", ["category"])
|
|
640
|
+
.index("builtInType", ["builtInType"])
|
|
641
|
+
.index("createdAt", ["createdAt"])
|
|
642
|
+
.index("projectId", ["projectId"])
|
|
643
|
+
.index("projectId_slug", ["projectId", "slug"]),
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Email config table — stores per-email-type enable/disable toggles.
|
|
647
|
+
* Each row represents a distinct email type (e.g. "welcome", "password-reset").
|
|
648
|
+
* Scoped per project.
|
|
649
|
+
*/
|
|
650
|
+
emailConfig: defineTable({
|
|
651
|
+
emailType: v.string(), // unique identifier, e.g. "welcome", "password-reset"
|
|
652
|
+
name: v.string(),
|
|
653
|
+
description: v.optional(v.string()),
|
|
654
|
+
enabled: v.boolean(),
|
|
655
|
+
category: v.string(), // "auth" | "org"
|
|
656
|
+
|
|
657
|
+
// Project scoping
|
|
658
|
+
projectId: v.optional(v.string()),
|
|
659
|
+
|
|
660
|
+
createdAt: v.float64(),
|
|
661
|
+
updatedAt: v.float64(),
|
|
662
|
+
})
|
|
663
|
+
.index("emailType", ["emailType"])
|
|
664
|
+
.index("category", ["category"])
|
|
665
|
+
.index("projectId", ["projectId"]),
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Dashboard config table — stores runtime overrides for the dashboard
|
|
669
|
+
* configuration (auth methods, social providers, features, sessions).
|
|
670
|
+
* One row per project.
|
|
671
|
+
*/
|
|
672
|
+
dashboardConfig: defineTable({
|
|
673
|
+
configJson: v.string(),
|
|
674
|
+
|
|
675
|
+
// Project scoping
|
|
676
|
+
projectId: v.optional(v.string()),
|
|
677
|
+
|
|
678
|
+
createdAt: v.float64(),
|
|
679
|
+
updatedAt: v.float64(),
|
|
680
|
+
}).index("projectId", ["projectId"]),
|
|
681
|
+
|
|
682
|
+
// ─── Domain Config Tables ─────────────────────────────────────────
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Domain config table — stores service domain settings (email, admin portal,
|
|
686
|
+
* auth API, authkit, custom). Managed via the dashboard.
|
|
687
|
+
*/
|
|
688
|
+
domainConfig: defineTable({
|
|
689
|
+
domainKey: v.string(), // unique key, e.g. "email", "admin-portal", "auth-api", "authkit", or UUID
|
|
690
|
+
title: v.string(),
|
|
691
|
+
description: v.optional(v.string()),
|
|
692
|
+
value: v.string(),
|
|
693
|
+
isDefault: v.optional(v.boolean()),
|
|
694
|
+
|
|
695
|
+
// Project scoping
|
|
696
|
+
projectId: v.optional(v.string()),
|
|
697
|
+
|
|
698
|
+
createdAt: v.float64(),
|
|
699
|
+
updatedAt: v.float64(),
|
|
700
|
+
})
|
|
701
|
+
.index("domainKey", ["domainKey"])
|
|
702
|
+
.index("createdAt", ["createdAt"])
|
|
703
|
+
.index("projectId", ["projectId"]),
|
|
704
|
+
|
|
705
|
+
// ─── Redirect Config Tables ───────────────────────────────────────
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Redirect config table — stores redirect URIs and endpoint settings.
|
|
709
|
+
* One row per project.
|
|
710
|
+
*/
|
|
711
|
+
redirectConfig: defineTable({
|
|
712
|
+
configJson: v.string(), // JSON-serialized RedirectsData
|
|
713
|
+
|
|
714
|
+
// Project scoping
|
|
715
|
+
projectId: v.optional(v.string()),
|
|
716
|
+
|
|
717
|
+
createdAt: v.float64(),
|
|
718
|
+
updatedAt: v.float64(),
|
|
719
|
+
}).index("projectId", ["projectId"]),
|
|
720
|
+
|
|
721
|
+
// ─── Actions Tables ───────────────────────────────────────────────
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Action table — stores automated action definitions triggered by events.
|
|
725
|
+
* Each action maps an event type to a webhook URL.
|
|
726
|
+
* Scoped per project.
|
|
727
|
+
*/
|
|
728
|
+
actionConfig: defineTable({
|
|
729
|
+
name: v.string(),
|
|
730
|
+
description: v.optional(v.string()),
|
|
731
|
+
triggerEvent: v.string(),
|
|
732
|
+
webhookUrl: v.string(),
|
|
733
|
+
|
|
734
|
+
// Project scoping
|
|
735
|
+
projectId: v.optional(v.string()),
|
|
736
|
+
|
|
737
|
+
createdAt: v.float64(),
|
|
738
|
+
updatedAt: v.float64(),
|
|
739
|
+
})
|
|
740
|
+
.index("triggerEvent", ["triggerEvent"])
|
|
741
|
+
.index("createdAt", ["createdAt"])
|
|
742
|
+
.index("projectId", ["projectId"]),
|
|
743
|
+
|
|
744
|
+
// ─── Radar / Bot Protection Tables ────────────────────────────────
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Radar config table — stores bot protection / radar settings.
|
|
748
|
+
* One row per project.
|
|
749
|
+
*/
|
|
750
|
+
radarConfig: defineTable({
|
|
751
|
+
configJson: v.string(), // JSON-serialized RadarConfig
|
|
752
|
+
|
|
753
|
+
// Project scoping
|
|
754
|
+
projectId: v.optional(v.string()),
|
|
755
|
+
|
|
756
|
+
createdAt: v.float64(),
|
|
757
|
+
updatedAt: v.float64(),
|
|
758
|
+
}).index("projectId", ["projectId"]),
|
|
759
|
+
|
|
760
|
+
// ─── Email Provider Tables ────────────────────────────────────────
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Email provider config table — stores email provider settings
|
|
764
|
+
* (which provider is active, API keys, etc.).
|
|
765
|
+
* One row per project.
|
|
766
|
+
*/
|
|
767
|
+
emailProviderConfig: defineTable({
|
|
768
|
+
configJson: v.string(), // JSON-serialized email provider config
|
|
769
|
+
|
|
770
|
+
// Project scoping
|
|
771
|
+
projectId: v.optional(v.string()),
|
|
772
|
+
|
|
773
|
+
createdAt: v.float64(),
|
|
774
|
+
updatedAt: v.float64(),
|
|
775
|
+
}).index("projectId", ["projectId"]),
|
|
776
|
+
|
|
777
|
+
// ─── Resource Type Tables ─────────────────────────────────────────
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Resource type table — stores fine-grained authorization resource types.
|
|
781
|
+
* Scoped per project.
|
|
782
|
+
*/
|
|
783
|
+
resourceType: defineTable({
|
|
784
|
+
name: v.string(),
|
|
785
|
+
slug: v.string(),
|
|
786
|
+
description: v.optional(v.string()),
|
|
787
|
+
|
|
788
|
+
// Project scoping
|
|
789
|
+
projectId: v.optional(v.string()),
|
|
790
|
+
|
|
791
|
+
createdAt: v.float64(),
|
|
792
|
+
updatedAt: v.float64(),
|
|
793
|
+
})
|
|
794
|
+
.index("slug", ["slug"])
|
|
795
|
+
.index("createdAt", ["createdAt"])
|
|
796
|
+
.index("projectId", ["projectId"]),
|
|
797
|
+
|
|
798
|
+
// ─── Addon Config Tables ──────────────────────────────────────────
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Addon config table — stores third-party integration enable/disable state.
|
|
802
|
+
* Singleton row with JSON-serialized config.
|
|
803
|
+
*/
|
|
804
|
+
addonConfig: defineTable({
|
|
805
|
+
configJson: v.string(), // JSON-serialized addon states
|
|
806
|
+
|
|
807
|
+
// Project scoping
|
|
808
|
+
projectId: v.optional(v.string()),
|
|
809
|
+
|
|
810
|
+
createdAt: v.float64(),
|
|
811
|
+
updatedAt: v.float64(),
|
|
812
|
+
}).index("projectId", ["projectId"]),
|
|
813
|
+
|
|
814
|
+
// ─── Project Config Tables ────────────────────────────────────────
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Project config table — stores per-project configuration overrides.
|
|
818
|
+
* One row per project.
|
|
819
|
+
*/
|
|
820
|
+
projectConfig: defineTable({
|
|
821
|
+
configJson: v.string(), // JSON-serialized project settings
|
|
822
|
+
|
|
823
|
+
// Project scoping
|
|
824
|
+
projectId: v.optional(v.string()),
|
|
825
|
+
|
|
826
|
+
createdAt: v.float64(),
|
|
827
|
+
updatedAt: v.float64(),
|
|
828
|
+
}).index("projectId", ["projectId"]),
|
|
829
|
+
|
|
830
|
+
// ─── Vault Tables ──────────────────────────────────────────────────
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* Vault secret table - stores encrypted secrets.
|
|
834
|
+
* Values are encrypted at rest using envelope encryption.
|
|
835
|
+
* Scoped per project.
|
|
836
|
+
*/
|
|
837
|
+
vaultSecret: defineTable({
|
|
838
|
+
name: v.string(),
|
|
839
|
+
encryptedValue: v.string(),
|
|
840
|
+
iv: v.string(), // Initialization vector
|
|
841
|
+
|
|
842
|
+
// Project scoping
|
|
843
|
+
projectId: v.optional(v.string()),
|
|
844
|
+
|
|
845
|
+
context: v.optional(v.string()),
|
|
846
|
+
organizationId: v.optional(v.string()),
|
|
847
|
+
|
|
848
|
+
version: v.float64(),
|
|
849
|
+
|
|
850
|
+
metadata: v.optional(v.any()),
|
|
851
|
+
|
|
852
|
+
createdAt: v.float64(),
|
|
853
|
+
updatedAt: v.float64(),
|
|
854
|
+
})
|
|
855
|
+
.index("name", ["name"])
|
|
856
|
+
.index("organizationId", ["organizationId"])
|
|
857
|
+
.index("name_context", ["name", "context"])
|
|
858
|
+
.index("projectId", ["projectId"]),
|
|
859
|
+
|
|
860
|
+
// ─── Portal Session Tables ─────────────────────────────────────────
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Portal session table - stores short-lived admin portal sessions.
|
|
864
|
+
* Each session grants an org admin access to a specific portal intent.
|
|
865
|
+
* Scoped per project.
|
|
866
|
+
*/
|
|
867
|
+
portalSession: defineTable({
|
|
868
|
+
organizationId: v.string(),
|
|
869
|
+
intent: v.string(), // "sso" | "dsync" | "domain_verification" | "audit_logs" | "log_streams" | "users"
|
|
870
|
+
returnUrl: v.optional(v.string()),
|
|
871
|
+
token: v.string(),
|
|
872
|
+
|
|
873
|
+
// Project scoping
|
|
874
|
+
projectId: v.optional(v.string()),
|
|
875
|
+
|
|
876
|
+
expiresAt: v.float64(),
|
|
877
|
+
createdAt: v.float64(),
|
|
878
|
+
})
|
|
879
|
+
.index("token", ["token"])
|
|
880
|
+
.index("organizationId", ["organizationId"])
|
|
881
|
+
.index("projectId", ["projectId"]),
|
|
882
|
+
|
|
883
|
+
// ─── Domain Verification Tables ────────────────────────────────────
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* Domain verification table - tracks domain ownership verification.
|
|
887
|
+
* Scoped per project.
|
|
888
|
+
*/
|
|
889
|
+
domainVerification: defineTable({
|
|
890
|
+
organizationId: v.string(),
|
|
891
|
+
domain: v.string(),
|
|
892
|
+
|
|
893
|
+
// Project scoping
|
|
894
|
+
projectId: v.optional(v.string()),
|
|
895
|
+
|
|
896
|
+
state: v.string(), // "pending" | "verified" | "failed" | "expired"
|
|
897
|
+
method: v.string(), // "dns_txt"
|
|
898
|
+
|
|
899
|
+
txtRecordName: v.string(),
|
|
900
|
+
txtRecordValue: v.string(),
|
|
901
|
+
|
|
902
|
+
verifiedAt: v.optional(v.float64()),
|
|
903
|
+
expiresAt: v.optional(v.float64()),
|
|
904
|
+
lastCheckedAt: v.optional(v.float64()),
|
|
905
|
+
checkCount: v.optional(v.float64()),
|
|
906
|
+
|
|
907
|
+
createdAt: v.float64(),
|
|
908
|
+
updatedAt: v.float64(),
|
|
909
|
+
})
|
|
910
|
+
.index("domain", ["domain"])
|
|
911
|
+
.index("organizationId", ["organizationId"])
|
|
912
|
+
.index("state", ["state"])
|
|
913
|
+
.index("projectId", ["projectId"]),
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
export default schema;
|