@xk2800/nextjs-template 0.7.1-beta.1 → 0.7.1-beta.10

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 (131) hide show
  1. package/README.md +32 -30
  2. package/components/auth/deviceCheck.tsx +47 -6
  3. package/components/auth/oneTap.tsx +5 -5
  4. package/components/dashboard/admin/adminUserConsentCard.tsx +79 -0
  5. package/components/dashboard/admin/adminUserDevicesCard.tsx +80 -0
  6. package/components/dashboard/admin/broadcastActions.tsx +148 -0
  7. package/components/dashboard/admin/confirmSendDialog.tsx +44 -0
  8. package/components/dashboard/admin/marketingEmailForm.tsx +115 -0
  9. package/components/dashboard/admin/marketingSection.tsx +67 -0
  10. package/components/dashboard/admin/subscribersCard.tsx +62 -0
  11. package/components/dashboard/admin/systemSettingsForm.tsx +33 -0
  12. package/components/dashboard/admin/systemSettingsSection.tsx +8 -2
  13. package/components/dashboard/errorFeedback.tsx +2 -4
  14. package/components/dashboard/feedbackDialog.tsx +4 -4
  15. package/components/dashboard/marketingUpdatesCard.tsx +62 -0
  16. package/components/dashboard/marketingUpdatesSection.tsx +10 -0
  17. package/components/site/cookie-banner.tsx +109 -7
  18. package/components/ui/alert-dialog.tsx +1 -1
  19. package/components/ui/badge.tsx +1 -1
  20. package/components/ui/card.tsx +1 -1
  21. package/components/ui/data-table.tsx +4 -1
  22. package/components/ui/dialog.tsx +1 -1
  23. package/components/ui/dropdown-menu.tsx +2 -2
  24. package/components/ui/file-upload.tsx +120 -2
  25. package/components/ui/select.tsx +189 -0
  26. package/components/ui/switch.tsx +2 -2
  27. package/dist/activity/logger.js +36 -0
  28. package/dist/activity/logger.js.map +1 -1
  29. package/dist/activity/logger.mjs +4 -4
  30. package/dist/activity/queries.d.mts +1 -1
  31. package/dist/activity/queries.d.ts +1 -1
  32. package/dist/activity/queries.js +36 -0
  33. package/dist/activity/queries.js.map +1 -1
  34. package/dist/activity/queries.mjs +3 -3
  35. package/dist/admin/queries.d.mts +19 -1
  36. package/dist/admin/queries.d.ts +19 -1
  37. package/dist/admin/queries.js +64 -0
  38. package/dist/admin/queries.js.map +1 -1
  39. package/dist/admin/queries.mjs +26 -5
  40. package/dist/admin/queries.mjs.map +1 -1
  41. package/dist/api/feedback.js +53 -2
  42. package/dist/api/feedback.js.map +1 -1
  43. package/dist/api/feedback.mjs +9 -8
  44. package/dist/api/feedback.mjs.map +1 -1
  45. package/dist/api/marketing.d.mts +36 -0
  46. package/dist/api/marketing.d.ts +36 -0
  47. package/dist/api/marketing.js +1357 -0
  48. package/dist/api/marketing.js.map +1 -0
  49. package/dist/api/marketing.mjs +170 -0
  50. package/dist/api/marketing.mjs.map +1 -0
  51. package/dist/auth/helpers.d.mts +3 -3
  52. package/dist/auth/helpers.d.ts +3 -3
  53. package/dist/auth/helpers.js +51 -0
  54. package/dist/auth/helpers.js.map +1 -1
  55. package/dist/auth/helpers.mjs +9 -64
  56. package/dist/auth/helpers.mjs.map +1 -1
  57. package/dist/auth/index.js +51 -0
  58. package/dist/auth/index.js.map +1 -1
  59. package/dist/auth/index.mjs +8 -7
  60. package/dist/auth-client/index.d.mts +16 -16
  61. package/dist/auth-client/index.d.ts +16 -16
  62. package/dist/{chunk-KAEA35OC.mjs → chunk-6MDQRT6U.mjs} +4 -4
  63. package/dist/{chunk-KAEA35OC.mjs.map → chunk-6MDQRT6U.mjs.map} +1 -1
  64. package/dist/{chunk-N6ECRCHY.mjs → chunk-6OAHCEDJ.mjs} +4 -3
  65. package/dist/chunk-6OAHCEDJ.mjs.map +1 -0
  66. package/dist/chunk-6Z3WUSDJ.mjs +62 -0
  67. package/dist/chunk-6Z3WUSDJ.mjs.map +1 -0
  68. package/dist/{chunk-VQ5B4MA7.mjs → chunk-7F2POZM6.mjs} +4 -4
  69. package/dist/{chunk-VQ5B4MA7.mjs.map → chunk-7F2POZM6.mjs.map} +1 -1
  70. package/dist/{chunk-XWVAJXAG.mjs → chunk-7QVIOQ47.mjs} +23 -19
  71. package/dist/chunk-7QVIOQ47.mjs.map +1 -0
  72. package/dist/chunk-C6RGN6QA.mjs +60 -0
  73. package/dist/chunk-C6RGN6QA.mjs.map +1 -0
  74. package/dist/chunk-OVKVD2HH.mjs +25 -0
  75. package/dist/chunk-OVKVD2HH.mjs.map +1 -0
  76. package/dist/{chunk-3MGEAGHU.mjs → chunk-P2G23JMY.mjs} +11 -6
  77. package/dist/chunk-P2G23JMY.mjs.map +1 -0
  78. package/dist/{chunk-TMJYAOC2.mjs → chunk-TKWNEKK6.mjs} +8 -2
  79. package/dist/chunk-TKWNEKK6.mjs.map +1 -0
  80. package/dist/{chunk-ZQCALTKB.mjs → chunk-TR5XFBTD.mjs} +34 -4
  81. package/dist/chunk-TR5XFBTD.mjs.map +1 -0
  82. package/dist/{chunk-WZDAMCUX.mjs → chunk-ZJMFE5B2.mjs} +4 -4
  83. package/dist/{chunk-WZDAMCUX.mjs.map → chunk-ZJMFE5B2.mjs.map} +1 -1
  84. package/dist/config/env.d.mts +2 -0
  85. package/dist/config/env.d.ts +2 -0
  86. package/dist/config/env.js +6 -0
  87. package/dist/config/env.js.map +1 -1
  88. package/dist/config/env.mjs +1 -1
  89. package/dist/db/index.d.mts +1 -1
  90. package/dist/db/index.d.ts +1 -1
  91. package/dist/db/index.js +36 -0
  92. package/dist/db/index.js.map +1 -1
  93. package/dist/db/index.mjs +3 -3
  94. package/dist/db/schema.d.mts +1 -1
  95. package/dist/db/schema.d.ts +1 -1
  96. package/dist/db/schema.js +30 -0
  97. package/dist/db/schema.js.map +1 -1
  98. package/dist/db/schema.mjs +1 -1
  99. package/dist/errors.js +36 -0
  100. package/dist/errors.js.map +1 -1
  101. package/dist/errors.mjs +4 -4
  102. package/dist/marketing/index.d.mts +35 -0
  103. package/dist/marketing/index.d.ts +35 -0
  104. package/dist/marketing/index.js +125 -0
  105. package/dist/marketing/index.js.map +1 -0
  106. package/dist/marketing/index.mjs +6 -0
  107. package/dist/marketing/index.mjs.map +1 -0
  108. package/dist/{schema-UuqxoOzS.d.mts → schema-CN_Y6EXv.d.mts} +148 -2
  109. package/dist/{schema-UuqxoOzS.d.ts → schema-CN_Y6EXv.d.ts} +148 -2
  110. package/dist/sessions/queries.js +36 -0
  111. package/dist/sessions/queries.js.map +1 -1
  112. package/dist/sessions/queries.mjs +3 -3
  113. package/dist/settings/queries.d.mts +4 -3
  114. package/dist/settings/queries.d.ts +4 -3
  115. package/dist/settings/queries.js +42 -0
  116. package/dist/settings/queries.js.map +1 -1
  117. package/dist/settings/queries.mjs +4 -4
  118. package/dist/users/queries.js +36 -0
  119. package/dist/users/queries.js.map +1 -1
  120. package/dist/users/queries.mjs +3 -3
  121. package/lib/cookie-consent.ts +72 -20
  122. package/lib/cookie-names.ts +47 -0
  123. package/lib/device-fingerprint.ts +66 -10
  124. package/lib/recalculate-columns.ts +2 -0
  125. package/package.json +28 -3
  126. package/styles/theme.css +17 -3
  127. package/dist/chunk-3MGEAGHU.mjs.map +0 -1
  128. package/dist/chunk-N6ECRCHY.mjs.map +0 -1
  129. package/dist/chunk-TMJYAOC2.mjs.map +0 -1
  130. package/dist/chunk-XWVAJXAG.mjs.map +0 -1
  131. package/dist/chunk-ZQCALTKB.mjs.map +0 -1
@@ -1467,6 +1467,25 @@ declare const deviceFingerprints: drizzle_orm_pg_core.PgTableWithColumns<{
1467
1467
  identity: undefined;
1468
1468
  generated: undefined;
1469
1469
  }, {}, {}>;
1470
+ components: drizzle_orm_pg_core.PgColumn<{
1471
+ name: "components";
1472
+ tableName: "device_fingerprint";
1473
+ dataType: "json";
1474
+ columnType: "PgJsonb";
1475
+ data: Record<string, unknown>;
1476
+ driverParam: unknown;
1477
+ notNull: false;
1478
+ hasDefault: false;
1479
+ isPrimaryKey: false;
1480
+ isAutoincrement: false;
1481
+ hasRuntimeDefault: false;
1482
+ enumValues: undefined;
1483
+ baseColumn: never;
1484
+ identity: undefined;
1485
+ generated: undefined;
1486
+ }, {}, {
1487
+ $type: Record<string, unknown>;
1488
+ }>;
1470
1489
  createdAt: drizzle_orm_pg_core.PgColumn<{
1471
1490
  name: "createdAt";
1472
1491
  tableName: "device_fingerprint";
@@ -1487,6 +1506,115 @@ declare const deviceFingerprints: drizzle_orm_pg_core.PgTableWithColumns<{
1487
1506
  };
1488
1507
  dialect: "pg";
1489
1508
  }>;
1509
+ declare const consentLog: drizzle_orm_pg_core.PgTableWithColumns<{
1510
+ name: "consent_log";
1511
+ schema: undefined;
1512
+ columns: {
1513
+ id: drizzle_orm_pg_core.PgColumn<{
1514
+ name: "id";
1515
+ tableName: "consent_log";
1516
+ dataType: "string";
1517
+ columnType: "PgText";
1518
+ data: string;
1519
+ driverParam: string;
1520
+ notNull: true;
1521
+ hasDefault: true;
1522
+ isPrimaryKey: true;
1523
+ isAutoincrement: false;
1524
+ hasRuntimeDefault: true;
1525
+ enumValues: [string, ...string[]];
1526
+ baseColumn: never;
1527
+ identity: undefined;
1528
+ generated: undefined;
1529
+ }, {}, {}>;
1530
+ userId: drizzle_orm_pg_core.PgColumn<{
1531
+ name: "userId";
1532
+ tableName: "consent_log";
1533
+ dataType: "string";
1534
+ columnType: "PgText";
1535
+ data: string;
1536
+ driverParam: string;
1537
+ notNull: true;
1538
+ hasDefault: false;
1539
+ isPrimaryKey: false;
1540
+ isAutoincrement: false;
1541
+ hasRuntimeDefault: false;
1542
+ enumValues: [string, ...string[]];
1543
+ baseColumn: never;
1544
+ identity: undefined;
1545
+ generated: undefined;
1546
+ }, {}, {}>;
1547
+ choice: drizzle_orm_pg_core.PgColumn<{
1548
+ name: "choice";
1549
+ tableName: "consent_log";
1550
+ dataType: "string";
1551
+ columnType: "PgText";
1552
+ data: string;
1553
+ driverParam: string;
1554
+ notNull: true;
1555
+ hasDefault: false;
1556
+ isPrimaryKey: false;
1557
+ isAutoincrement: false;
1558
+ hasRuntimeDefault: false;
1559
+ enumValues: [string, ...string[]];
1560
+ baseColumn: never;
1561
+ identity: undefined;
1562
+ generated: undefined;
1563
+ }, {}, {}>;
1564
+ version: drizzle_orm_pg_core.PgColumn<{
1565
+ name: "version";
1566
+ tableName: "consent_log";
1567
+ dataType: "number";
1568
+ columnType: "PgInteger";
1569
+ data: number;
1570
+ driverParam: string | number;
1571
+ notNull: true;
1572
+ hasDefault: false;
1573
+ isPrimaryKey: false;
1574
+ isAutoincrement: false;
1575
+ hasRuntimeDefault: false;
1576
+ enumValues: undefined;
1577
+ baseColumn: never;
1578
+ identity: undefined;
1579
+ generated: undefined;
1580
+ }, {}, {}>;
1581
+ previousId: drizzle_orm_pg_core.PgColumn<{
1582
+ name: "previousId";
1583
+ tableName: "consent_log";
1584
+ dataType: "string";
1585
+ columnType: "PgText";
1586
+ data: string;
1587
+ driverParam: string;
1588
+ notNull: false;
1589
+ hasDefault: false;
1590
+ isPrimaryKey: false;
1591
+ isAutoincrement: false;
1592
+ hasRuntimeDefault: false;
1593
+ enumValues: [string, ...string[]];
1594
+ baseColumn: never;
1595
+ identity: undefined;
1596
+ generated: undefined;
1597
+ }, {}, {}>;
1598
+ createdAt: drizzle_orm_pg_core.PgColumn<{
1599
+ name: "createdAt";
1600
+ tableName: "consent_log";
1601
+ dataType: "date";
1602
+ columnType: "PgTimestamp";
1603
+ data: Date;
1604
+ driverParam: string;
1605
+ notNull: true;
1606
+ hasDefault: true;
1607
+ isPrimaryKey: false;
1608
+ isAutoincrement: false;
1609
+ hasRuntimeDefault: false;
1610
+ enumValues: undefined;
1611
+ baseColumn: never;
1612
+ identity: undefined;
1613
+ generated: undefined;
1614
+ }, {}, {}>;
1615
+ };
1616
+ dialect: "pg";
1617
+ }>;
1490
1618
  declare const authThrottle: drizzle_orm_pg_core.PgTableWithColumns<{
1491
1619
  name: "auth_throttle";
1492
1620
  schema: undefined;
@@ -1813,6 +1941,23 @@ declare const systemSettings: drizzle_orm_pg_core.PgTableWithColumns<{
1813
1941
  identity: undefined;
1814
1942
  generated: undefined;
1815
1943
  }, {}, {}>;
1944
+ consentRequired: drizzle_orm_pg_core.PgColumn<{
1945
+ name: "consentRequired";
1946
+ tableName: "system_settings";
1947
+ dataType: "boolean";
1948
+ columnType: "PgBoolean";
1949
+ data: boolean;
1950
+ driverParam: boolean;
1951
+ notNull: false;
1952
+ hasDefault: false;
1953
+ isPrimaryKey: false;
1954
+ isAutoincrement: false;
1955
+ hasRuntimeDefault: false;
1956
+ enumValues: undefined;
1957
+ baseColumn: never;
1958
+ identity: undefined;
1959
+ generated: undefined;
1960
+ }, {}, {}>;
1816
1961
  updatedAt: drizzle_orm_pg_core.PgColumn<{
1817
1962
  name: "updatedAt";
1818
1963
  tableName: "system_settings";
@@ -1931,6 +2076,7 @@ declare const schema_RoleEnum: typeof RoleEnum;
1931
2076
  declare const schema_accounts: typeof accounts;
1932
2077
  declare const schema_activityLogs: typeof activityLogs;
1933
2078
  declare const schema_authThrottle: typeof authThrottle;
2079
+ declare const schema_consentLog: typeof consentLog;
1934
2080
  declare const schema_deviceFingerprints: typeof deviceFingerprints;
1935
2081
  declare const schema_errorRefs: typeof errorRefs;
1936
2082
  declare const schema_passkeys: typeof passkeys;
@@ -1940,7 +2086,7 @@ declare const schema_twoFactors: typeof twoFactors;
1940
2086
  declare const schema_users: typeof users;
1941
2087
  declare const schema_verifications: typeof verifications;
1942
2088
  declare namespace schema {
1943
- export { schema_ActivityActionEnum as ActivityActionEnum, schema_RoleEnum as RoleEnum, schema_accounts as accounts, schema_activityLogs as activityLogs, schema_authThrottle as authThrottle, schema_deviceFingerprints as deviceFingerprints, schema_errorRefs as errorRefs, schema_passkeys as passkeys, schema_sessions as sessions, schema_systemSettings as systemSettings, schema_twoFactors as twoFactors, schema_users as users, schema_verifications as verifications };
2089
+ export { schema_ActivityActionEnum as ActivityActionEnum, schema_RoleEnum as RoleEnum, schema_accounts as accounts, schema_activityLogs as activityLogs, schema_authThrottle as authThrottle, schema_consentLog as consentLog, schema_deviceFingerprints as deviceFingerprints, schema_errorRefs as errorRefs, schema_passkeys as passkeys, schema_sessions as sessions, schema_systemSettings as systemSettings, schema_twoFactors as twoFactors, schema_users as users, schema_verifications as verifications };
1944
2090
  }
1945
2091
 
1946
- export { ActivityActionEnum as A, RoleEnum as R, schema as a, accounts as b, activityLogs as c, authThrottle as d, deviceFingerprints as e, errorRefs as f, sessions as g, passkeys as p, systemSettings as s, twoFactors as t, users as u, verifications as v };
2092
+ export { ActivityActionEnum as A, RoleEnum as R, schema as a, accounts as b, activityLogs as c, authThrottle as d, consentLog as e, deviceFingerprints as f, errorRefs as g, sessions as h, passkeys as p, systemSettings as s, twoFactors as t, users as u, verifications as v };
@@ -24,6 +24,7 @@ __export(schema_exports, {
24
24
  accounts: () => accounts,
25
25
  activityLogs: () => activityLogs,
26
26
  authThrottle: () => authThrottle,
27
+ consentLog: () => consentLog,
27
28
  deviceFingerprints: () => deviceFingerprints,
28
29
  errorRefs: () => errorRefs,
29
30
  passkeys: () => passkeys,
@@ -178,10 +179,32 @@ var deviceFingerprints = pgCore.pgTable("device_fingerprint", {
178
179
  userAgent: pgCore.text("userAgent"),
179
180
  country: pgCore.text("country"),
180
181
  city: pgCore.text("city"),
182
+ // Latest FingerprintJS signals for this device (lib/device-fingerprint.ts
183
+ // getDeviceSignals(): accessibility signals dropped, large values hashed),
184
+ // shown on the admin user page. Null for rows from before this column.
185
+ components: pgCore.jsonb("components").$type(),
181
186
  createdAt: pgCore.timestamp("createdAt").notNull().defaultNow()
182
187
  }, (table) => [
183
188
  pgCore.unique("device_fingerprint_user_visitor").on(table.userId, table.visitorId)
184
189
  ]);
190
+ var consentLog = pgCore.pgTable("consent_log", {
191
+ id: pgCore.text("id").notNull().primaryKey().$defaultFn(() => cuid2.createId()),
192
+ userId: pgCore.text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
193
+ // The cookie_consent value: "all", "necessary" or a comma list of purposes.
194
+ choice: pgCore.text("choice").notNull(),
195
+ // CONSENT_VERSION (lib/cookie-names.ts) at the time — which banner/policy
196
+ // wording the user agreed to.
197
+ version: pgCore.integer("version").notNull(),
198
+ // id of the user's record this one follows ("" for their first). Unique per
199
+ // user, so concurrent posts from several tabs that all saw the same latest
200
+ // record can't each insert a duplicate — one wins, the rest conflict. Works
201
+ // without transactions (neon-http has none). Null on rows from before it.
202
+ previousId: pgCore.text("previousId"),
203
+ createdAt: pgCore.timestamp("createdAt").notNull().defaultNow()
204
+ }, (table) => [
205
+ pgCore.index("consent_log_user_idx").on(table.userId, table.createdAt),
206
+ pgCore.unique("consent_log_chain").on(table.userId, table.previousId)
207
+ ]);
185
208
  var authThrottle = pgCore.pgTable("auth_throttle", {
186
209
  fingerprint: pgCore.text("fingerprint").notNull().primaryKey(),
187
210
  count: pgCore.integer("count").notNull().default(0),
@@ -211,6 +234,13 @@ var systemSettings = pgCore.pgTable("system_settings", {
211
234
  // Attach the error a user hit to feedback they send from it (see
212
235
  // lib/feedback-route.ts). Off = only what the user typed is sent.
213
236
  feedbackAttachErrors: pgCore.boolean("feedbackAttachErrors").notNull().default(true),
237
+ // GDPR/ePrivacy consent mode. On: cookie banner, FingerprintJS and One Tap
238
+ // only after "Accept all", device record erased on withdrawal. Off (no EU/UK
239
+ // users): no banner, treated as consented. Null = follow
240
+ // NEXT_PUBLIC_COOKIE_BANNER (nullable so migrating never flips a deployed
241
+ // banner on or off). proxy.ts mirrors the effective value into the
242
+ // consent_required cookie for the client (lib/cookie-consent.ts).
243
+ consentRequired: pgCore.boolean("consentRequired"),
214
244
  updatedAt: pgCore.timestamp("updatedAt").notNull().defaultNow(),
215
245
  // Who last changed a setting. This row doesn't "belong" to that admin the
216
246
  // way sessions/activityLogs belong to their user, so deleting that admin's
@@ -232,6 +262,12 @@ var envSchema = zod.z.object({
232
262
  PORT: zod.z.string().default("3000").transform(Number),
233
263
  DATABASE_URL: zod.z.string().default(""),
234
264
  RESEND_API_KEY: zod.z.string().default(""),
265
+ // Full access key for marketing broadcasts (admin Marketing email page).
266
+ // Blank = use RESEND_API_KEY.
267
+ RESEND_BROADCAST_API_KEY: zod.z.string().default(""),
268
+ // Resend segment users join from the dashboard "Email updates" toggle.
269
+ // Blank = toggle hidden.
270
+ RESEND_MARKETING_SEGMENT_ID: zod.z.string().default(""),
235
271
  // Sender for every transactional email. Must be on a domain verified in
236
272
  // your Resend account.
237
273
  EMAIL_FROM: zod.z.string().default("Acme <onboarding@xavierkhew.xyz>"),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../server/db/schema.ts","../../lib/maintenance.ts","../../config/env-schema.ts","../../config/env.ts","../../server/db/index.ts","../../lib/session-queries.ts"],"names":["pgEnum","pgTable","text","createId","boolean","timestamp","integer","unique","check","sql","index","z","Pool","drizzlePg","neon","drizzle","eq","desc"],"mappings":"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","file":"queries.js","sourcesContent":["import { boolean, check, index, integer, pgEnum, pgTable, text, timestamp, unique } from \"drizzle-orm/pg-core\";\nimport { sql } from \"drizzle-orm\";\nimport { createId } from '@paralleldrive/cuid2'\nimport { DEFAULT_MAINTENANCE_EXEMPT_PATHS } from \"../../lib/maintenance\";\n\n// 'tester' is a regular user account flagged for testing; apps can use it to\n// keep test accounts apart from real users. It has no admin access.\nexport const RoleEnum = pgEnum('roles', ['user', 'admin', 'tester'])\n\nexport const ActivityActionEnum = pgEnum('activity_actions', [\n 'login',\n 'logout',\n 'login_failed',\n 'password_changed',\n 'email_changed',\n 'profile_updated',\n 'session_revoked',\n 'user_deleted',\n 'user_banned',\n 'user_unbanned',\n 'role_changed',\n 'impersonation_started',\n 'impersonation_stopped',\n 'settings_changed',\n 'passkey_added',\n 'passkey_removed',\n])\n\nexport const users = pgTable(\"user\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n name: text('name').notNull(),\n email: text('email').notNull().unique(),\n password: text('password'),\n emailVerified: boolean(\"emailVerified\").notNull().default(false),\n image: text(\"image\"),\n role: RoleEnum('roles').default('user').notNull(),\n banned: boolean('banned').default(false).notNull(),\n bannedAt: timestamp('bannedAt'),\n bannedReason: text('bannedReason'),\n // Required by better-auth's admin plugin schema (auto-expiring bans). We\n // never set this ourselves, so it stays null and bans behave as permanent,\n // same as before the plugin was added.\n banExpires: timestamp('banExpires'),\n lastLoginAt: timestamp('lastLoginAt'),\n lastActiveAt: timestamp('lastActiveAt'),\n // Set by better-auth's twoFactor plugin — true once the user has\n // completed TOTP setup (see twoFactor pgTable below for the secret /\n // backup codes themselves).\n twoFactorEnabled: boolean('twoFactorEnabled').notNull().default(false),\n createdAt: timestamp('createdAt').notNull().defaultNow(),\n updatedAt: timestamp('updatedAt').notNull().defaultNow(),\n});\n\nexport const sessions = pgTable(\"session\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n expiresAt: timestamp(\"expiresAt\").notNull(),\n token: text(\"token\").notNull().unique(),\n createdAt: timestamp(\"createdAt\").notNull().defaultNow(),\n updatedAt: timestamp(\"updatedAt\").notNull().defaultNow(),\n ipAddress: text(\"ipAddress\"),\n userAgent: text(\"userAgent\"),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n // Set by better-auth's admin plugin while this session is an admin\n // impersonating another user; holds the impersonating admin's user id.\n impersonatedBy: text(\"impersonatedBy\"),\n});\n\nexport const accounts = pgTable(\"account\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n accountId: text(\"accountId\").notNull(),\n providerId: text(\"providerId\").notNull(),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n accessToken: text(\"accessToken\"),\n refreshToken: text(\"refreshToken\"),\n idToken: text(\"idToken\"),\n accessTokenExpiresAt: timestamp(\"accessTokenExpiresAt\"),\n refreshTokenExpiresAt: timestamp(\"refreshTokenExpiresAt\"),\n scope: text(\"scope\"),\n password: text(\"password\"),\n createdAt: timestamp(\"createdAt\").notNull().defaultNow(),\n updatedAt: timestamp(\"updatedAt\").notNull().defaultNow(),\n});\n\n// Better-auth's twoFactor plugin. One row per user (created on first\n// `enable`); secret and backupCodes are its own encrypted-at-rest strings,\n// field names/types must match its expectations exactly (drizzleAdapter in\n// server/auth.ts maps this table to the plugin's \"twoFactor\" model as-is).\nexport const twoFactors = pgTable(\"twoFactor\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n secret: text(\"secret\").notNull(),\n backupCodes: text(\"backupCodes\").notNull(),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n // Added in better-auth 1.7 (TOTP lockout). Defaults match the plugin's.\n verified: boolean(\"verified\").default(true),\n failedVerificationCount: integer(\"failedVerificationCount\").default(0),\n lockedUntil: timestamp(\"lockedUntil\"),\n});\n\n// Better-auth's passkey plugin — one row per registered WebAuthn credential.\nexport const passkeys = pgTable(\"passkey\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n name: text(\"name\"),\n publicKey: text(\"publicKey\").notNull(),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n credentialID: text(\"credentialID\").notNull().unique(),\n counter: integer(\"counter\").notNull(),\n deviceType: text(\"deviceType\").notNull(),\n backedUp: boolean(\"backedUp\").notNull(),\n transports: text(\"transports\"),\n createdAt: timestamp(\"createdAt\"),\n aaguid: text(\"aaguid\"),\n});\n\nexport const verifications = pgTable(\"verification\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n identifier: text(\"identifier\").notNull(),\n value: text(\"value\").notNull(),\n expiresAt: timestamp(\"expiresAt\").notNull(),\n createdAt: timestamp(\"createdAt\").notNull().defaultNow(),\n updatedAt: timestamp(\"updatedAt\").notNull().defaultNow(),\n});\n\nexport const activityLogs = pgTable(\"activity_log\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n action: ActivityActionEnum('action').notNull(),\n description: text('description').notNull(),\n ipAddress: text('ipAddress'),\n userAgent: text('userAgent'),\n metadata: text('metadata'),\n // Populated on 'login' events only (registration + sign-in) — parsed from\n // userAgent/ipAddress at write time so the record stays stable even if\n // the parsing library or geoip database changes later.\n referrerUrl: text('referrerUrl'),\n os: text('os'),\n browser: text('browser'),\n deviceType: text('deviceType'),\n country: text('country'),\n city: text('city'),\n createdAt: timestamp('createdAt').notNull().defaultNow(),\n});\n\n// One row per (user, device fingerprint) pair. Its only job is to answer\n// \"has this account signed in from this device before?\" — no matching row ⇒\n// send a new-device alert (see app/api/device-check/route.ts). visitorId is\n// FingerprintJS's identifier, computed client-side in\n// components/auth/deviceCheck.tsx. UA+IP are deliberately NOT the signal here:\n// they churn constantly on mobile and would alert on every other login.\nexport const deviceFingerprints = pgTable(\"device_fingerprint\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n visitorId: text(\"visitorId\").notNull(),\n // Context captured at first-seen time — for the admin panel's \"New devices\"\n // card and the alert email. Not part of the new-vs-known decision (that's\n // visitorId only), just so an admin can eyeball whether a sign-in looks like\n // a hijack. Nullable: an old row predates these columns, or geoip missed.\n ipAddress: text(\"ipAddress\"),\n userAgent: text(\"userAgent\"),\n country: text(\"country\"),\n city: text(\"city\"),\n createdAt: timestamp(\"createdAt\").notNull().defaultNow(),\n}, (table) => [\n unique(\"device_fingerprint_user_visitor\").on(table.userId, table.visitorId),\n]);\n\n// Per-device abuse throttle for /sign-in/email and /sign-up/email — one\n// fixed-window counter row per FingerprintJS visitorId (sent by the auth\n// forms as the x-device-fingerprint header; see lib/auth-throttle.ts).\n// Keyed by device, not IP: IP is shared behind corporate NAT / CGNAT / VPN,\n// so an IP counter throttles coworkers together while missing an abuser who\n// rotates IPs. Rows whose window has elapsed are dead weight but harmless\n// (reset on the next attempt) — prune on a cron if the table ever grows.\nexport const authThrottle = pgTable(\"auth_throttle\", {\n fingerprint: text(\"fingerprint\").notNull().primaryKey(),\n count: integer(\"count\").notNull().default(0),\n windowStart: timestamp(\"windowStart\").notNull().defaultNow(),\n // Blockable data points, overwritten on every attempt (last-write-wins) so\n // the admin panel can show who to block — by IP or by fingerprint — with no\n // join and no per-attempt history table. windowStart anchors the counter's\n // window; lastAttemptAt is just \"most recently seen\".\n lastAttemptAt: timestamp(\"lastAttemptAt\").notNull().defaultNow(),\n ipAddress: text(\"ipAddress\"),\n userAgent: text(\"userAgent\"),\n lastEmail: text(\"lastEmail\"),\n lastKind: text(\"lastKind\"), // 'signin' | 'signup'\n});\n\n// Singleton row (id is always 'default') holding admin-editable settings for\n// config that used to be env-var-only. See lib/settings-queries.ts for the\n// cached read path and lazy-seed-from-env behavior.\nexport const systemSettings = pgTable(\"system_settings\", {\n id: text(\"id\").primaryKey().default(\"default\"),\n maintenanceMode: boolean(\"maintenanceMode\").notNull().default(false),\n maintenanceMessage: text(\"maintenanceMessage\"),\n // Path prefixes that stay reachable while maintenance mode is on (see\n // lib/maintenance.ts — a few auth paths are always open on top of these).\n maintenanceExemptPaths: text(\"maintenanceExemptPaths\").array().notNull().default(DEFAULT_MAINTENANCE_EXEMPT_PATHS),\n authEnableGoogle: boolean(\"authEnableGoogle\").notNull().default(true),\n authEnableEmailPassword: boolean(\"authEnableEmailPassword\").notNull().default(true),\n authEnableOneTap: boolean(\"authEnableOneTap\").notNull().default(false),\n enableSessionRevocation: boolean(\"enableSessionRevocation\").notNull().default(true),\n // Attach the error a user hit to feedback they send from it (see\n // lib/feedback-route.ts). Off = only what the user typed is sent.\n feedbackAttachErrors: boolean(\"feedbackAttachErrors\").notNull().default(true),\n updatedAt: timestamp(\"updatedAt\").notNull().defaultNow(),\n // Who last changed a setting. This row doesn't \"belong\" to that admin the\n // way sessions/activityLogs belong to their user, so deleting that admin's\n // account should null this out rather than cascade-delete the singleton row.\n updatedBy: text(\"updatedBy\").references(() => users.id, { onDelete: \"set null\" }),\n}, (table) => [\n check(\"system_settings_singleton\", sql`${table.id} = 'default'`),\n]);\n// Technical detail of unexpected errors, keyed by the short ref the user sees\n// (\"... (ref: K7Q2PX)\", see lib/errors.ts). Never sent to the browser: the\n// feedback route looks a ref up and attaches the detail to the report it\n// sends to Discord/email. Rows older than 30 days are pruned on insert.\nexport const errorRefs = pgTable(\"error_refs\", {\n ref: text(\"ref\").primaryKey(),\n message: text(\"message\").notNull(),\n detail: text(\"detail\").notNull(),\n createdAt: timestamp(\"createdAt\").notNull().defaultNow(),\n}, (table) => [\n index(\"error_refs_created_at_idx\").on(table.createdAt),\n]);\n","// Pure path logic for maintenance mode — no DB / server-only imports, so it\n// can run in proxy.ts and in the admin form alike.\n\n// Default for the admin-editable exempt list: the public marketing pages.\nexport const DEFAULT_MAINTENANCE_EXEMPT_PATHS = [\n \"/\",\n \"/features\",\n \"/changelog\",\n \"/changelog-template\",\n \"/privacy\",\n \"/terms\",\n]\n\n// Always reachable, whatever the admin saves — otherwise an admin could\n// lock themselves out of the sign-in flow needed to turn maintenance off.\nexport const ALWAYS_OPEN_PATHS = [\n \"/maintenance\",\n \"/login\", // includes /login/2fa\n \"/forgot-password\",\n \"/reset-password\",\n \"/api/auth\",\n \"/api/settings/public\", // login page reads the One Tap flag from here\n]\n\n// `/foo` matches `/foo` and `/foo/...` but not `/foobar`; `/` matches only `/`.\nfunction matches(pathname: string, prefix: string) {\n const p = prefix.length > 1 ? prefix.replace(/\\/+$/, \"\") : prefix\n return pathname === p || (p !== \"/\" && pathname.startsWith(p + \"/\"))\n}\n\nexport function isMaintenanceExempt(pathname: string, exemptPaths: string[]) {\n return [...ALWAYS_OPEN_PATHS, ...exemptPaths].some((p) => matches(pathname, p))\n}\n","// No \"server-only\" guard here — this schema is also imported by scripts that\n// run outside Next.js's bundler (e.g. scripts/doctor.ts), where that guard\n// throws unconditionally. config/env.ts re-exports the parsed, guarded result\n// for app code; import this file directly only from non-Next scripts.\nimport { z } from 'zod';\n\nexport const envSchema = z.object({\n NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n PORT: z.string().default('3000').transform(Number),\n DATABASE_URL: z.string().default(''),\n RESEND_API_KEY: z.string().default(''),\n // Sender for every transactional email. Must be on a domain verified in\n // your Resend account.\n EMAIL_FROM: z.string().default('Acme <onboarding@xavierkhew.xyz>'),\n // Display name for this deployment — shown as the authenticator-app label\n // (2FA issuer) and the passkey relying-party name. Consuming projects set\n // this in their own .env; defaults to the template name.\n APP_NAME: z.string().default('Next.js Template'),\n // Where the dashboard feedback form delivers (app/api/feedback). Set either\n // or both; with neither, the Feedback button is hidden. FEEDBACK_EMAIL takes\n // a comma-separated list and sends through Resend (needs RESEND_API_KEY).\n FEEDBACK_DISCORD_WEBHOOK_URL: z.string().default(''),\n FEEDBACK_EMAIL: z.string().default(''),\n // Seed-only: the live value lives in the system_settings DB row (see\n // lib/settings-queries.ts) once it's been seeded on first read, and is\n // changed from the admin System Settings page, not by editing this var.\n ENABLE_SESSION_REVOCATION: z.string().default('true').transform(val => val === 'true'),\n AUTH_ENABLE_GOOGLE: z.string().default('true').transform(val => val === 'true'),\n AUTH_ENABLE_EMAIL_PASSWORD: z.string().default('true').transform(val => val === 'true'),\n AUTH_ENABLE_ONE_TAP: z.string().default('false').transform(val => val === 'true'),\n DB_DRIVER: z.enum(['pg', 'neon']).default('pg'),\n // Only relevant when DB_DRIVER=pg — whether the target Postgres server has SSL\n // enabled. Self-hosted Postgres (e.g. via Docker/Dokploy) typically does not.\n DATABASE_SSL: z.string().default('false').transform(val => val === 'true'),\n});\n","import \"server-only\";\nimport { envSchema } from './env-schema';\n\nconst results = envSchema.safeParse(process.env);\n\nif (results.success && (results.data.NODE_ENV === 'development')) {\n console.log('Environment variables loaded:', Object.keys(results.data));\n}\n\nif (!results.success) {\n console.error('Invalid environment variables:', results.error.format());\n throw new Error('Invalid environment variables');\n}\n\nexport const config = results.data;\n","import 'server-only';\n\n// neon-http driver for production\nimport { neon } from '@neondatabase/serverless';\nimport { drizzle } from 'drizzle-orm/neon-http';\n\n// postgres driver for local development\nimport { drizzle as drizzlePg } from 'drizzle-orm/node-postgres';\nimport { Pool } from 'pg';\n\nimport * as schema from './schema';\nimport { config } from '../../config/env';\n\n// Import the query builder\nimport { PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { NeonHttpDatabase } from 'drizzle-orm/neon-http';\n\ntype DrizzleNeon = NeonHttpDatabase<typeof schema>;\ntype DrizzlePg = PostgresJsDatabase<typeof schema>;\n\nlet db: DrizzleNeon | DrizzlePg\n\n\nif (config.DB_DRIVER === 'pg') {\n const pool = new Pool({\n connectionString: config.DATABASE_URL!,\n ssl: config.DATABASE_SSL ? { rejectUnauthorized: false } : undefined,\n });\n db = drizzlePg(pool, {\n schema,\n logger: true,\n });\n\n console.log('🟡 Using PostgreSQL (pg) driver');\n\n} else if (config.DB_DRIVER === 'neon') {\n const sql = neon(config.DATABASE_URL!);\n db = drizzle(sql, { schema, logger: true });\n\n console.log('🟢 Using Neon (HTTP) driver');\n\n} else {\n console.log('Database connection not established, unknown DB_DRIVER:', config.DB_DRIVER);\n}\n// Note: In production, you might want to disable logging or use a more sophisticated logger.\n\nexport { db }","import \"server-only\"\nimport { db } from \"../server/db\"\nimport { sessions } from \"../server/db/schema\"\nimport { eq, desc } from \"drizzle-orm\"\n\n/**\n * Get all active sessions for a user\n */\nexport async function getUserSessions(userId: string) {\n return await db\n .select()\n .from(sessions)\n .where(eq(sessions.userId, userId))\n .orderBy(desc(sessions.createdAt))\n}\n\n/**\n * Get current session info by token\n */\nexport async function getSessionByToken(token: string) {\n const result = await db\n .select()\n .from(sessions)\n .where(eq(sessions.token, token))\n .limit(1)\n\n return result[0] || null\n}\n\n/**\n * Count active sessions for user\n */\nexport async function countUserSessions(userId: string): Promise<number> {\n const userSessions = await getUserSessions(userId)\n\n // Filter out expired sessions\n const now = new Date()\n return userSessions.filter(s => s.expiresAt > now).length\n}\n"]}
1
+ {"version":3,"sources":["../../server/db/schema.ts","../../lib/maintenance.ts","../../config/env-schema.ts","../../config/env.ts","../../server/db/index.ts","../../lib/session-queries.ts"],"names":["pgEnum","pgTable","text","createId","boolean","timestamp","integer","jsonb","unique","index","check","sql","z","Pool","drizzlePg","neon","drizzle","eq","desc"],"mappings":"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","file":"queries.js","sourcesContent":["import { boolean, check, index, integer, jsonb, pgEnum, pgTable, text, timestamp, unique } from \"drizzle-orm/pg-core\";\nimport { sql } from \"drizzle-orm\";\nimport { createId } from '@paralleldrive/cuid2'\nimport { DEFAULT_MAINTENANCE_EXEMPT_PATHS } from \"../../lib/maintenance\";\n\n// 'tester' is a regular user account flagged for testing; apps can use it to\n// keep test accounts apart from real users. It has no admin access.\nexport const RoleEnum = pgEnum('roles', ['user', 'admin', 'tester'])\n\nexport const ActivityActionEnum = pgEnum('activity_actions', [\n 'login',\n 'logout',\n 'login_failed',\n 'password_changed',\n 'email_changed',\n 'profile_updated',\n 'session_revoked',\n 'user_deleted',\n 'user_banned',\n 'user_unbanned',\n 'role_changed',\n 'impersonation_started',\n 'impersonation_stopped',\n 'settings_changed',\n 'passkey_added',\n 'passkey_removed',\n])\n\nexport const users = pgTable(\"user\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n name: text('name').notNull(),\n email: text('email').notNull().unique(),\n password: text('password'),\n emailVerified: boolean(\"emailVerified\").notNull().default(false),\n image: text(\"image\"),\n role: RoleEnum('roles').default('user').notNull(),\n banned: boolean('banned').default(false).notNull(),\n bannedAt: timestamp('bannedAt'),\n bannedReason: text('bannedReason'),\n // Required by better-auth's admin plugin schema (auto-expiring bans). We\n // never set this ourselves, so it stays null and bans behave as permanent,\n // same as before the plugin was added.\n banExpires: timestamp('banExpires'),\n lastLoginAt: timestamp('lastLoginAt'),\n lastActiveAt: timestamp('lastActiveAt'),\n // Set by better-auth's twoFactor plugin — true once the user has\n // completed TOTP setup (see twoFactor pgTable below for the secret /\n // backup codes themselves).\n twoFactorEnabled: boolean('twoFactorEnabled').notNull().default(false),\n createdAt: timestamp('createdAt').notNull().defaultNow(),\n updatedAt: timestamp('updatedAt').notNull().defaultNow(),\n});\n\nexport const sessions = pgTable(\"session\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n expiresAt: timestamp(\"expiresAt\").notNull(),\n token: text(\"token\").notNull().unique(),\n createdAt: timestamp(\"createdAt\").notNull().defaultNow(),\n updatedAt: timestamp(\"updatedAt\").notNull().defaultNow(),\n ipAddress: text(\"ipAddress\"),\n userAgent: text(\"userAgent\"),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n // Set by better-auth's admin plugin while this session is an admin\n // impersonating another user; holds the impersonating admin's user id.\n impersonatedBy: text(\"impersonatedBy\"),\n});\n\nexport const accounts = pgTable(\"account\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n accountId: text(\"accountId\").notNull(),\n providerId: text(\"providerId\").notNull(),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n accessToken: text(\"accessToken\"),\n refreshToken: text(\"refreshToken\"),\n idToken: text(\"idToken\"),\n accessTokenExpiresAt: timestamp(\"accessTokenExpiresAt\"),\n refreshTokenExpiresAt: timestamp(\"refreshTokenExpiresAt\"),\n scope: text(\"scope\"),\n password: text(\"password\"),\n createdAt: timestamp(\"createdAt\").notNull().defaultNow(),\n updatedAt: timestamp(\"updatedAt\").notNull().defaultNow(),\n});\n\n// Better-auth's twoFactor plugin. One row per user (created on first\n// `enable`); secret and backupCodes are its own encrypted-at-rest strings,\n// field names/types must match its expectations exactly (drizzleAdapter in\n// server/auth.ts maps this table to the plugin's \"twoFactor\" model as-is).\nexport const twoFactors = pgTable(\"twoFactor\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n secret: text(\"secret\").notNull(),\n backupCodes: text(\"backupCodes\").notNull(),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n // Added in better-auth 1.7 (TOTP lockout). Defaults match the plugin's.\n verified: boolean(\"verified\").default(true),\n failedVerificationCount: integer(\"failedVerificationCount\").default(0),\n lockedUntil: timestamp(\"lockedUntil\"),\n});\n\n// Better-auth's passkey plugin — one row per registered WebAuthn credential.\nexport const passkeys = pgTable(\"passkey\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n name: text(\"name\"),\n publicKey: text(\"publicKey\").notNull(),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n credentialID: text(\"credentialID\").notNull().unique(),\n counter: integer(\"counter\").notNull(),\n deviceType: text(\"deviceType\").notNull(),\n backedUp: boolean(\"backedUp\").notNull(),\n transports: text(\"transports\"),\n createdAt: timestamp(\"createdAt\"),\n aaguid: text(\"aaguid\"),\n});\n\nexport const verifications = pgTable(\"verification\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n identifier: text(\"identifier\").notNull(),\n value: text(\"value\").notNull(),\n expiresAt: timestamp(\"expiresAt\").notNull(),\n createdAt: timestamp(\"createdAt\").notNull().defaultNow(),\n updatedAt: timestamp(\"updatedAt\").notNull().defaultNow(),\n});\n\nexport const activityLogs = pgTable(\"activity_log\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n action: ActivityActionEnum('action').notNull(),\n description: text('description').notNull(),\n ipAddress: text('ipAddress'),\n userAgent: text('userAgent'),\n metadata: text('metadata'),\n // Populated on 'login' events only (registration + sign-in) — parsed from\n // userAgent/ipAddress at write time so the record stays stable even if\n // the parsing library or geoip database changes later.\n referrerUrl: text('referrerUrl'),\n os: text('os'),\n browser: text('browser'),\n deviceType: text('deviceType'),\n country: text('country'),\n city: text('city'),\n createdAt: timestamp('createdAt').notNull().defaultNow(),\n});\n\n// One row per (user, device fingerprint) pair. Its only job is to answer\n// \"has this account signed in from this device before?\" — no matching row ⇒\n// send a new-device alert (see app/api/device-check/route.ts). visitorId is\n// FingerprintJS's identifier, computed client-side in\n// components/auth/deviceCheck.tsx. UA+IP are deliberately NOT the signal here:\n// they churn constantly on mobile and would alert on every other login.\nexport const deviceFingerprints = pgTable(\"device_fingerprint\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n visitorId: text(\"visitorId\").notNull(),\n // Context captured at first-seen time — for the admin panel's \"New devices\"\n // card and the alert email. Not part of the new-vs-known decision (that's\n // visitorId only), just so an admin can eyeball whether a sign-in looks like\n // a hijack. Nullable: an old row predates these columns, or geoip missed.\n ipAddress: text(\"ipAddress\"),\n userAgent: text(\"userAgent\"),\n country: text(\"country\"),\n city: text(\"city\"),\n // Latest FingerprintJS signals for this device (lib/device-fingerprint.ts\n // getDeviceSignals(): accessibility signals dropped, large values hashed),\n // shown on the admin user page. Null for rows from before this column.\n components: jsonb(\"components\").$type<Record<string, unknown>>(),\n createdAt: timestamp(\"createdAt\").notNull().defaultNow(),\n}, (table) => [\n unique(\"device_fingerprint_user_visitor\").on(table.userId, table.visitorId),\n]);\n\n// Proof of cookie consent (GDPR Art. 7(1): the controller must be able to\n// demonstrate consent). One row per choice a signed-in user makes, written by\n// app/api/consent from the banner (components/site/cookie-banner.tsx).\n// Deliberately minimal — no IP/UA. Deleted with the account.\nexport const consentLog = pgTable(\"consent_log\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n // The cookie_consent value: \"all\", \"necessary\" or a comma list of purposes.\n choice: text(\"choice\").notNull(),\n // CONSENT_VERSION (lib/cookie-names.ts) at the time — which banner/policy\n // wording the user agreed to.\n version: integer(\"version\").notNull(),\n // id of the user's record this one follows (\"\" for their first). Unique per\n // user, so concurrent posts from several tabs that all saw the same latest\n // record can't each insert a duplicate — one wins, the rest conflict. Works\n // without transactions (neon-http has none). Null on rows from before it.\n previousId: text(\"previousId\"),\n createdAt: timestamp(\"createdAt\").notNull().defaultNow(),\n}, (table) => [\n index(\"consent_log_user_idx\").on(table.userId, table.createdAt),\n unique(\"consent_log_chain\").on(table.userId, table.previousId),\n]);\n\n// Per-device abuse throttle for /sign-in/email and /sign-up/email — one\n// fixed-window counter row per FingerprintJS visitorId (sent by the auth\n// forms as the x-device-fingerprint header; see lib/auth-throttle.ts).\n// Keyed by device, not IP: IP is shared behind corporate NAT / CGNAT / VPN,\n// so an IP counter throttles coworkers together while missing an abuser who\n// rotates IPs. Rows untouched for 30 days are deleted (RETENTION_MS in\n// lib/auth-throttle-limits.ts, swept by lib/auth-throttle.ts).\nexport const authThrottle = pgTable(\"auth_throttle\", {\n fingerprint: text(\"fingerprint\").notNull().primaryKey(),\n count: integer(\"count\").notNull().default(0),\n windowStart: timestamp(\"windowStart\").notNull().defaultNow(),\n // Blockable data points, overwritten on every attempt (last-write-wins) so\n // the admin panel can show who to block — by IP or by fingerprint — with no\n // join and no per-attempt history table. windowStart anchors the counter's\n // window; lastAttemptAt is just \"most recently seen\".\n lastAttemptAt: timestamp(\"lastAttemptAt\").notNull().defaultNow(),\n ipAddress: text(\"ipAddress\"),\n userAgent: text(\"userAgent\"),\n lastEmail: text(\"lastEmail\"),\n lastKind: text(\"lastKind\"), // 'signin' | 'signup'\n});\n\n// Singleton row (id is always 'default') holding admin-editable settings for\n// config that used to be env-var-only. See lib/settings-queries.ts for the\n// cached read path and lazy-seed-from-env behavior.\nexport const systemSettings = pgTable(\"system_settings\", {\n id: text(\"id\").primaryKey().default(\"default\"),\n maintenanceMode: boolean(\"maintenanceMode\").notNull().default(false),\n maintenanceMessage: text(\"maintenanceMessage\"),\n // Path prefixes that stay reachable while maintenance mode is on (see\n // lib/maintenance.ts — a few auth paths are always open on top of these).\n maintenanceExemptPaths: text(\"maintenanceExemptPaths\").array().notNull().default(DEFAULT_MAINTENANCE_EXEMPT_PATHS),\n authEnableGoogle: boolean(\"authEnableGoogle\").notNull().default(true),\n authEnableEmailPassword: boolean(\"authEnableEmailPassword\").notNull().default(true),\n authEnableOneTap: boolean(\"authEnableOneTap\").notNull().default(false),\n enableSessionRevocation: boolean(\"enableSessionRevocation\").notNull().default(true),\n // Attach the error a user hit to feedback they send from it (see\n // lib/feedback-route.ts). Off = only what the user typed is sent.\n feedbackAttachErrors: boolean(\"feedbackAttachErrors\").notNull().default(true),\n // GDPR/ePrivacy consent mode. On: cookie banner, FingerprintJS and One Tap\n // only after \"Accept all\", device record erased on withdrawal. Off (no EU/UK\n // users): no banner, treated as consented. Null = follow\n // NEXT_PUBLIC_COOKIE_BANNER (nullable so migrating never flips a deployed\n // banner on or off). proxy.ts mirrors the effective value into the\n // consent_required cookie for the client (lib/cookie-consent.ts).\n consentRequired: boolean(\"consentRequired\"),\n updatedAt: timestamp(\"updatedAt\").notNull().defaultNow(),\n // Who last changed a setting. This row doesn't \"belong\" to that admin the\n // way sessions/activityLogs belong to their user, so deleting that admin's\n // account should null this out rather than cascade-delete the singleton row.\n updatedBy: text(\"updatedBy\").references(() => users.id, { onDelete: \"set null\" }),\n}, (table) => [\n check(\"system_settings_singleton\", sql`${table.id} = 'default'`),\n]);\n// Technical detail of unexpected errors, keyed by the short ref the user sees\n// (\"... (ref: K7Q2PX)\", see lib/errors.ts). Never sent to the browser: the\n// feedback route looks a ref up and attaches the detail to the report it\n// sends to Discord/email. Rows older than 30 days are pruned on insert.\nexport const errorRefs = pgTable(\"error_refs\", {\n ref: text(\"ref\").primaryKey(),\n message: text(\"message\").notNull(),\n detail: text(\"detail\").notNull(),\n createdAt: timestamp(\"createdAt\").notNull().defaultNow(),\n}, (table) => [\n index(\"error_refs_created_at_idx\").on(table.createdAt),\n]);\n","// Pure path logic for maintenance mode — no DB / server-only imports, so it\n// can run in proxy.ts and in the admin form alike.\n\n// Default for the admin-editable exempt list: the public marketing pages.\nexport const DEFAULT_MAINTENANCE_EXEMPT_PATHS = [\n \"/\",\n \"/features\",\n \"/changelog\",\n \"/changelog-template\",\n \"/privacy\",\n \"/terms\",\n]\n\n// Always reachable, whatever the admin saves — otherwise an admin could\n// lock themselves out of the sign-in flow needed to turn maintenance off.\nexport const ALWAYS_OPEN_PATHS = [\n \"/maintenance\",\n \"/login\", // includes /login/2fa\n \"/forgot-password\",\n \"/reset-password\",\n \"/api/auth\",\n \"/api/settings/public\", // login page reads the One Tap flag from here\n]\n\n// `/foo` matches `/foo` and `/foo/...` but not `/foobar`; `/` matches only `/`.\nfunction matches(pathname: string, prefix: string) {\n const p = prefix.length > 1 ? prefix.replace(/\\/+$/, \"\") : prefix\n return pathname === p || (p !== \"/\" && pathname.startsWith(p + \"/\"))\n}\n\nexport function isMaintenanceExempt(pathname: string, exemptPaths: string[]) {\n return [...ALWAYS_OPEN_PATHS, ...exemptPaths].some((p) => matches(pathname, p))\n}\n","// No \"server-only\" guard here — this schema is also imported by scripts that\n// run outside Next.js's bundler (e.g. scripts/doctor.ts), where that guard\n// throws unconditionally. config/env.ts re-exports the parsed, guarded result\n// for app code; import this file directly only from non-Next scripts.\nimport { z } from 'zod';\n\nexport const envSchema = z.object({\n NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n PORT: z.string().default('3000').transform(Number),\n DATABASE_URL: z.string().default(''),\n RESEND_API_KEY: z.string().default(''),\n // Full access key for marketing broadcasts (admin Marketing email page).\n // Blank = use RESEND_API_KEY.\n RESEND_BROADCAST_API_KEY: z.string().default(''),\n // Resend segment users join from the dashboard \"Email updates\" toggle.\n // Blank = toggle hidden.\n RESEND_MARKETING_SEGMENT_ID: z.string().default(''),\n // Sender for every transactional email. Must be on a domain verified in\n // your Resend account.\n EMAIL_FROM: z.string().default('Acme <onboarding@xavierkhew.xyz>'),\n // Display name for this deployment — shown as the authenticator-app label\n // (2FA issuer) and the passkey relying-party name. Consuming projects set\n // this in their own .env; defaults to the template name.\n APP_NAME: z.string().default('Next.js Template'),\n // Where the dashboard feedback form delivers (app/api/feedback). Set either\n // or both; with neither, the Feedback button is hidden. FEEDBACK_EMAIL takes\n // a comma-separated list and sends through Resend (needs RESEND_API_KEY).\n FEEDBACK_DISCORD_WEBHOOK_URL: z.string().default(''),\n FEEDBACK_EMAIL: z.string().default(''),\n // Seed-only: the live value lives in the system_settings DB row (see\n // lib/settings-queries.ts) once it's been seeded on first read, and is\n // changed from the admin System Settings page, not by editing this var.\n ENABLE_SESSION_REVOCATION: z.string().default('true').transform(val => val === 'true'),\n AUTH_ENABLE_GOOGLE: z.string().default('true').transform(val => val === 'true'),\n AUTH_ENABLE_EMAIL_PASSWORD: z.string().default('true').transform(val => val === 'true'),\n AUTH_ENABLE_ONE_TAP: z.string().default('false').transform(val => val === 'true'),\n DB_DRIVER: z.enum(['pg', 'neon']).default('pg'),\n // Only relevant when DB_DRIVER=pg — whether the target Postgres server has SSL\n // enabled. Self-hosted Postgres (e.g. via Docker/Dokploy) typically does not.\n DATABASE_SSL: z.string().default('false').transform(val => val === 'true'),\n});\n","import \"server-only\";\nimport { envSchema } from './env-schema';\n\nconst results = envSchema.safeParse(process.env);\n\nif (results.success && (results.data.NODE_ENV === 'development')) {\n console.log('Environment variables loaded:', Object.keys(results.data));\n}\n\nif (!results.success) {\n console.error('Invalid environment variables:', results.error.format());\n throw new Error('Invalid environment variables');\n}\n\nexport const config = results.data;\n","import 'server-only';\n\n// neon-http driver for production\nimport { neon } from '@neondatabase/serverless';\nimport { drizzle } from 'drizzle-orm/neon-http';\n\n// postgres driver for local development\nimport { drizzle as drizzlePg } from 'drizzle-orm/node-postgres';\nimport { Pool } from 'pg';\n\nimport * as schema from './schema';\nimport { config } from '../../config/env';\n\n// Import the query builder\nimport { PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { NeonHttpDatabase } from 'drizzle-orm/neon-http';\n\ntype DrizzleNeon = NeonHttpDatabase<typeof schema>;\ntype DrizzlePg = PostgresJsDatabase<typeof schema>;\n\nlet db: DrizzleNeon | DrizzlePg\n\n\nif (config.DB_DRIVER === 'pg') {\n const pool = new Pool({\n connectionString: config.DATABASE_URL!,\n ssl: config.DATABASE_SSL ? { rejectUnauthorized: false } : undefined,\n });\n db = drizzlePg(pool, {\n schema,\n logger: true,\n });\n\n console.log('🟡 Using PostgreSQL (pg) driver');\n\n} else if (config.DB_DRIVER === 'neon') {\n const sql = neon(config.DATABASE_URL!);\n db = drizzle(sql, { schema, logger: true });\n\n console.log('🟢 Using Neon (HTTP) driver');\n\n} else {\n console.log('Database connection not established, unknown DB_DRIVER:', config.DB_DRIVER);\n}\n// Note: In production, you might want to disable logging or use a more sophisticated logger.\n\nexport { db }","import \"server-only\"\nimport { db } from \"../server/db\"\nimport { sessions } from \"../server/db/schema\"\nimport { eq, desc } from \"drizzle-orm\"\n\n/**\n * Get all active sessions for a user\n */\nexport async function getUserSessions(userId: string) {\n return await db\n .select()\n .from(sessions)\n .where(eq(sessions.userId, userId))\n .orderBy(desc(sessions.createdAt))\n}\n\n/**\n * Get current session info by token\n */\nexport async function getSessionByToken(token: string) {\n const result = await db\n .select()\n .from(sessions)\n .where(eq(sessions.token, token))\n .limit(1)\n\n return result[0] || null\n}\n\n/**\n * Count active sessions for user\n */\nexport async function countUserSessions(userId: string): Promise<number> {\n const userSessions = await getUserSessions(userId)\n\n // Filter out expired sessions\n const now = new Date()\n return userSessions.filter(s => s.expiresAt > now).length\n}\n"]}
@@ -1,6 +1,6 @@
1
- import { db } from '../chunk-WZDAMCUX.mjs';
2
- import { sessions } from '../chunk-ZQCALTKB.mjs';
3
- import '../chunk-TMJYAOC2.mjs';
1
+ import { db } from '../chunk-ZJMFE5B2.mjs';
2
+ import { sessions } from '../chunk-TR5XFBTD.mjs';
3
+ import '../chunk-TKWNEKK6.mjs';
4
4
  import '../chunk-3465SI5N.mjs';
5
5
  import 'server-only';
6
6
  import { eq, desc } from 'drizzle-orm';
@@ -1,21 +1,22 @@
1
- import { s as systemSettings } from '../schema-UuqxoOzS.mjs';
1
+ import { s as systemSettings } from '../schema-CN_Y6EXv.mjs';
2
2
  import 'drizzle-orm';
3
3
  import 'drizzle-orm/pg-core';
4
4
 
5
5
  type SystemSettings = typeof systemSettings.$inferSelect;
6
6
  declare function getSystemSettings(): Promise<SystemSettings>;
7
7
  declare function invalidateSystemSettingsCache(): void;
8
- type SystemSettingsPatch = Partial<Pick<SystemSettings, 'maintenanceMode' | 'maintenanceMessage' | 'maintenanceExemptPaths' | 'authEnableGoogle' | 'authEnableEmailPassword' | 'authEnableOneTap' | 'enableSessionRevocation' | 'feedbackAttachErrors'>>;
8
+ type SystemSettingsPatch = Partial<Pick<SystemSettings, 'maintenanceMode' | 'maintenanceMessage' | 'maintenanceExemptPaths' | 'authEnableGoogle' | 'authEnableEmailPassword' | 'authEnableOneTap' | 'enableSessionRevocation' | 'feedbackAttachErrors' | 'consentRequired'>>;
9
9
  declare function updateSystemSettings(patch: SystemSettingsPatch, updatedBy: string): Promise<SystemSettings>;
10
10
  declare function getEffectiveAuthFlags(): Promise<{
11
11
  google: boolean;
12
12
  emailPassword: boolean;
13
13
  oneTap: boolean;
14
14
  }>;
15
+ declare function isConsentRequired(settings: Pick<SystemSettings, 'consentRequired'>): boolean;
15
16
  declare function getConfiguredAuthFlags(): {
16
17
  google: boolean;
17
18
  emailPassword: boolean;
18
19
  oneTap: boolean;
19
20
  };
20
21
 
21
- export { type SystemSettings, type SystemSettingsPatch, getConfiguredAuthFlags, getEffectiveAuthFlags, getSystemSettings, invalidateSystemSettingsCache, updateSystemSettings };
22
+ export { type SystemSettings, type SystemSettingsPatch, getConfiguredAuthFlags, getEffectiveAuthFlags, getSystemSettings, invalidateSystemSettingsCache, isConsentRequired, updateSystemSettings };
@@ -1,21 +1,22 @@
1
- import { s as systemSettings } from '../schema-UuqxoOzS.js';
1
+ import { s as systemSettings } from '../schema-CN_Y6EXv.js';
2
2
  import 'drizzle-orm';
3
3
  import 'drizzle-orm/pg-core';
4
4
 
5
5
  type SystemSettings = typeof systemSettings.$inferSelect;
6
6
  declare function getSystemSettings(): Promise<SystemSettings>;
7
7
  declare function invalidateSystemSettingsCache(): void;
8
- type SystemSettingsPatch = Partial<Pick<SystemSettings, 'maintenanceMode' | 'maintenanceMessage' | 'maintenanceExemptPaths' | 'authEnableGoogle' | 'authEnableEmailPassword' | 'authEnableOneTap' | 'enableSessionRevocation' | 'feedbackAttachErrors'>>;
8
+ type SystemSettingsPatch = Partial<Pick<SystemSettings, 'maintenanceMode' | 'maintenanceMessage' | 'maintenanceExemptPaths' | 'authEnableGoogle' | 'authEnableEmailPassword' | 'authEnableOneTap' | 'enableSessionRevocation' | 'feedbackAttachErrors' | 'consentRequired'>>;
9
9
  declare function updateSystemSettings(patch: SystemSettingsPatch, updatedBy: string): Promise<SystemSettings>;
10
10
  declare function getEffectiveAuthFlags(): Promise<{
11
11
  google: boolean;
12
12
  emailPassword: boolean;
13
13
  oneTap: boolean;
14
14
  }>;
15
+ declare function isConsentRequired(settings: Pick<SystemSettings, 'consentRequired'>): boolean;
15
16
  declare function getConfiguredAuthFlags(): {
16
17
  google: boolean;
17
18
  emailPassword: boolean;
18
19
  oneTap: boolean;
19
20
  };
20
21
 
21
- export { type SystemSettings, type SystemSettingsPatch, getConfiguredAuthFlags, getEffectiveAuthFlags, getSystemSettings, invalidateSystemSettingsCache, updateSystemSettings };
22
+ export { type SystemSettings, type SystemSettingsPatch, getConfiguredAuthFlags, getEffectiveAuthFlags, getSystemSettings, invalidateSystemSettingsCache, isConsentRequired, updateSystemSettings };
@@ -42,6 +42,7 @@ __export(schema_exports, {
42
42
  accounts: () => accounts,
43
43
  activityLogs: () => activityLogs,
44
44
  authThrottle: () => authThrottle,
45
+ consentLog: () => consentLog,
45
46
  deviceFingerprints: () => deviceFingerprints,
46
47
  errorRefs: () => errorRefs,
47
48
  passkeys: () => passkeys,
@@ -196,10 +197,32 @@ var deviceFingerprints = pgCore.pgTable("device_fingerprint", {
196
197
  userAgent: pgCore.text("userAgent"),
197
198
  country: pgCore.text("country"),
198
199
  city: pgCore.text("city"),
200
+ // Latest FingerprintJS signals for this device (lib/device-fingerprint.ts
201
+ // getDeviceSignals(): accessibility signals dropped, large values hashed),
202
+ // shown on the admin user page. Null for rows from before this column.
203
+ components: pgCore.jsonb("components").$type(),
199
204
  createdAt: pgCore.timestamp("createdAt").notNull().defaultNow()
200
205
  }, (table) => [
201
206
  pgCore.unique("device_fingerprint_user_visitor").on(table.userId, table.visitorId)
202
207
  ]);
208
+ var consentLog = pgCore.pgTable("consent_log", {
209
+ id: pgCore.text("id").notNull().primaryKey().$defaultFn(() => cuid2.createId()),
210
+ userId: pgCore.text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
211
+ // The cookie_consent value: "all", "necessary" or a comma list of purposes.
212
+ choice: pgCore.text("choice").notNull(),
213
+ // CONSENT_VERSION (lib/cookie-names.ts) at the time — which banner/policy
214
+ // wording the user agreed to.
215
+ version: pgCore.integer("version").notNull(),
216
+ // id of the user's record this one follows ("" for their first). Unique per
217
+ // user, so concurrent posts from several tabs that all saw the same latest
218
+ // record can't each insert a duplicate — one wins, the rest conflict. Works
219
+ // without transactions (neon-http has none). Null on rows from before it.
220
+ previousId: pgCore.text("previousId"),
221
+ createdAt: pgCore.timestamp("createdAt").notNull().defaultNow()
222
+ }, (table) => [
223
+ pgCore.index("consent_log_user_idx").on(table.userId, table.createdAt),
224
+ pgCore.unique("consent_log_chain").on(table.userId, table.previousId)
225
+ ]);
203
226
  var authThrottle = pgCore.pgTable("auth_throttle", {
204
227
  fingerprint: pgCore.text("fingerprint").notNull().primaryKey(),
205
228
  count: pgCore.integer("count").notNull().default(0),
@@ -229,6 +252,13 @@ var systemSettings = pgCore.pgTable("system_settings", {
229
252
  // Attach the error a user hit to feedback they send from it (see
230
253
  // lib/feedback-route.ts). Off = only what the user typed is sent.
231
254
  feedbackAttachErrors: pgCore.boolean("feedbackAttachErrors").notNull().default(true),
255
+ // GDPR/ePrivacy consent mode. On: cookie banner, FingerprintJS and One Tap
256
+ // only after "Accept all", device record erased on withdrawal. Off (no EU/UK
257
+ // users): no banner, treated as consented. Null = follow
258
+ // NEXT_PUBLIC_COOKIE_BANNER (nullable so migrating never flips a deployed
259
+ // banner on or off). proxy.ts mirrors the effective value into the
260
+ // consent_required cookie for the client (lib/cookie-consent.ts).
261
+ consentRequired: pgCore.boolean("consentRequired"),
232
262
  updatedAt: pgCore.timestamp("updatedAt").notNull().defaultNow(),
233
263
  // Who last changed a setting. This row doesn't "belong" to that admin the
234
264
  // way sessions/activityLogs belong to their user, so deleting that admin's
@@ -250,6 +280,12 @@ var envSchema = zod.z.object({
250
280
  PORT: zod.z.string().default("3000").transform(Number),
251
281
  DATABASE_URL: zod.z.string().default(""),
252
282
  RESEND_API_KEY: zod.z.string().default(""),
283
+ // Full access key for marketing broadcasts (admin Marketing email page).
284
+ // Blank = use RESEND_API_KEY.
285
+ RESEND_BROADCAST_API_KEY: zod.z.string().default(""),
286
+ // Resend segment users join from the dashboard "Email updates" toggle.
287
+ // Blank = toggle hidden.
288
+ RESEND_MARKETING_SEGMENT_ID: zod.z.string().default(""),
253
289
  // Sender for every transactional email. Must be on a domain verified in
254
290
  // your Resend account.
255
291
  EMAIL_FROM: zod.z.string().default("Acme <onboarding@xavierkhew.xyz>"),
@@ -319,6 +355,7 @@ function fallbackSettings() {
319
355
  authEnableOneTap: config.AUTH_ENABLE_ONE_TAP,
320
356
  enableSessionRevocation: config.ENABLE_SESSION_REVOCATION,
321
357
  feedbackAttachErrors: true,
358
+ consentRequired: null,
322
359
  updatedAt: /* @__PURE__ */ new Date(0),
323
360
  updatedBy: null
324
361
  };
@@ -368,6 +405,10 @@ async function getEffectiveAuthFlags() {
368
405
  oneTap: config.AUTH_ENABLE_ONE_TAP && settings.authEnableOneTap
369
406
  };
370
407
  }
408
+ function isConsentRequired(settings) {
409
+ var _a;
410
+ return (_a = settings.consentRequired) != null ? _a : process.env.NEXT_PUBLIC_COOKIE_BANNER !== "false";
411
+ }
371
412
  function getConfiguredAuthFlags() {
372
413
  return {
373
414
  google: config.AUTH_ENABLE_GOOGLE,
@@ -380,6 +421,7 @@ exports.getConfiguredAuthFlags = getConfiguredAuthFlags;
380
421
  exports.getEffectiveAuthFlags = getEffectiveAuthFlags;
381
422
  exports.getSystemSettings = getSystemSettings;
382
423
  exports.invalidateSystemSettingsCache = invalidateSystemSettingsCache;
424
+ exports.isConsentRequired = isConsentRequired;
383
425
  exports.updateSystemSettings = updateSystemSettings;
384
426
  //# sourceMappingURL=queries.js.map
385
427
  //# sourceMappingURL=queries.js.map