@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.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +104 -0
  3. package/dist/auth-config.d.ts +22 -0
  4. package/dist/auth-config.d.ts.map +1 -0
  5. package/dist/auth-config.js +3 -0
  6. package/dist/auth-config.js.map +1 -0
  7. package/dist/auth.d.ts +462 -0
  8. package/dist/auth.d.ts.map +1 -0
  9. package/dist/component/adapter.d.ts +21 -0
  10. package/dist/component/adapter.d.ts.map +1 -0
  11. package/dist/component/adapter.js +3 -0
  12. package/dist/component/adapter.js.map +1 -0
  13. package/dist/component/schema.d.ts +1026 -0
  14. package/dist/component/schema.d.ts.map +1 -0
  15. package/dist/hooks.d.ts +25 -0
  16. package/dist/hooks.d.ts.map +1 -0
  17. package/dist/http.d.ts +41 -0
  18. package/dist/http.d.ts.map +1 -0
  19. package/dist/http.js +62 -0
  20. package/dist/http.js.map +1 -0
  21. package/dist/index.d.ts +9 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +9516 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/node.d.ts +389 -0
  26. package/dist/node.d.ts.map +1 -0
  27. package/dist/node.js +9559 -0
  28. package/dist/node.js.map +1 -0
  29. package/dist/plugins/audit.d.ts +106 -0
  30. package/dist/plugins/audit.d.ts.map +1 -0
  31. package/dist/plugins/config.d.ts +83 -0
  32. package/dist/plugins/config.d.ts.map +1 -0
  33. package/dist/plugins/domains.d.ts +3 -0
  34. package/dist/plugins/domains.d.ts.map +1 -0
  35. package/dist/plugins/email-sender.d.ts +75 -0
  36. package/dist/plugins/email-sender.d.ts.map +1 -0
  37. package/dist/plugins/email-templates.d.ts +108 -0
  38. package/dist/plugins/email-templates.d.ts.map +1 -0
  39. package/dist/plugins/email.d.ts +82 -0
  40. package/dist/plugins/email.d.ts.map +1 -0
  41. package/dist/plugins/enterprise.d.ts +3 -0
  42. package/dist/plugins/enterprise.d.ts.map +1 -0
  43. package/dist/plugins/events.d.ts +40 -0
  44. package/dist/plugins/events.d.ts.map +1 -0
  45. package/dist/plugins/index.d.ts +18 -0
  46. package/dist/plugins/index.d.ts.map +1 -0
  47. package/dist/plugins/index.js +9192 -0
  48. package/dist/plugins/index.js.map +1 -0
  49. package/dist/plugins/organization-rbac.d.ts +3 -0
  50. package/dist/plugins/organization-rbac.d.ts.map +1 -0
  51. package/dist/plugins/portal.d.ts +34 -0
  52. package/dist/plugins/portal.d.ts.map +1 -0
  53. package/dist/plugins/projects.d.ts +16 -0
  54. package/dist/plugins/projects.d.ts.map +1 -0
  55. package/dist/plugins/protection.d.ts +127 -0
  56. package/dist/plugins/protection.d.ts.map +1 -0
  57. package/dist/plugins/types.d.ts +508 -0
  58. package/dist/plugins/types.d.ts.map +1 -0
  59. package/dist/plugins/user-management.d.ts +8 -0
  60. package/dist/plugins/user-management.d.ts.map +1 -0
  61. package/dist/plugins/vault.d.ts +68 -0
  62. package/dist/plugins/vault.d.ts.map +1 -0
  63. package/dist/plugins/webhook.d.ts +65 -0
  64. package/dist/plugins/webhook.d.ts.map +1 -0
  65. package/dist/triggers.d.ts +158 -0
  66. package/dist/triggers.d.ts.map +1 -0
  67. package/dist/triggers.js +36 -0
  68. package/dist/triggers.js.map +1 -0
  69. package/package.json +102 -0
  70. package/src/component/adapter.ts +21 -0
  71. package/src/component/convex.config.ts +15 -0
  72. 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;