@xk2800/nextjs-template 0.7.1-beta.0 → 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 (129) 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 +129 -7
  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.js +51 -0
  52. package/dist/auth/helpers.js.map +1 -1
  53. package/dist/auth/helpers.mjs +9 -64
  54. package/dist/auth/helpers.mjs.map +1 -1
  55. package/dist/auth/index.d.mts +2 -2
  56. package/dist/auth/index.d.ts +2 -2
  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/{chunk-KAEA35OC.mjs → chunk-6MDQRT6U.mjs} +4 -4
  61. package/dist/{chunk-KAEA35OC.mjs.map → chunk-6MDQRT6U.mjs.map} +1 -1
  62. package/dist/{chunk-N6ECRCHY.mjs → chunk-6OAHCEDJ.mjs} +4 -3
  63. package/dist/chunk-6OAHCEDJ.mjs.map +1 -0
  64. package/dist/chunk-6Z3WUSDJ.mjs +62 -0
  65. package/dist/chunk-6Z3WUSDJ.mjs.map +1 -0
  66. package/dist/{chunk-VQ5B4MA7.mjs → chunk-7F2POZM6.mjs} +4 -4
  67. package/dist/{chunk-VQ5B4MA7.mjs.map → chunk-7F2POZM6.mjs.map} +1 -1
  68. package/dist/{chunk-XWVAJXAG.mjs → chunk-7QVIOQ47.mjs} +23 -19
  69. package/dist/chunk-7QVIOQ47.mjs.map +1 -0
  70. package/dist/chunk-C6RGN6QA.mjs +60 -0
  71. package/dist/chunk-C6RGN6QA.mjs.map +1 -0
  72. package/dist/chunk-OVKVD2HH.mjs +25 -0
  73. package/dist/chunk-OVKVD2HH.mjs.map +1 -0
  74. package/dist/{chunk-3MGEAGHU.mjs → chunk-P2G23JMY.mjs} +11 -6
  75. package/dist/chunk-P2G23JMY.mjs.map +1 -0
  76. package/dist/{chunk-TMJYAOC2.mjs → chunk-TKWNEKK6.mjs} +8 -2
  77. package/dist/chunk-TKWNEKK6.mjs.map +1 -0
  78. package/dist/{chunk-ZQCALTKB.mjs → chunk-TR5XFBTD.mjs} +34 -4
  79. package/dist/chunk-TR5XFBTD.mjs.map +1 -0
  80. package/dist/{chunk-WZDAMCUX.mjs → chunk-ZJMFE5B2.mjs} +4 -4
  81. package/dist/{chunk-WZDAMCUX.mjs.map → chunk-ZJMFE5B2.mjs.map} +1 -1
  82. package/dist/config/env.d.mts +2 -0
  83. package/dist/config/env.d.ts +2 -0
  84. package/dist/config/env.js +6 -0
  85. package/dist/config/env.js.map +1 -1
  86. package/dist/config/env.mjs +1 -1
  87. package/dist/db/index.d.mts +1 -1
  88. package/dist/db/index.d.ts +1 -1
  89. package/dist/db/index.js +36 -0
  90. package/dist/db/index.js.map +1 -1
  91. package/dist/db/index.mjs +3 -3
  92. package/dist/db/schema.d.mts +1 -1
  93. package/dist/db/schema.d.ts +1 -1
  94. package/dist/db/schema.js +30 -0
  95. package/dist/db/schema.js.map +1 -1
  96. package/dist/db/schema.mjs +1 -1
  97. package/dist/errors.js +36 -0
  98. package/dist/errors.js.map +1 -1
  99. package/dist/errors.mjs +4 -4
  100. package/dist/marketing/index.d.mts +35 -0
  101. package/dist/marketing/index.d.ts +35 -0
  102. package/dist/marketing/index.js +125 -0
  103. package/dist/marketing/index.js.map +1 -0
  104. package/dist/marketing/index.mjs +6 -0
  105. package/dist/marketing/index.mjs.map +1 -0
  106. package/dist/{schema-BNSBWNjK.d.mts → schema-CN_Y6EXv.d.mts} +149 -3
  107. package/dist/{schema-BNSBWNjK.d.ts → schema-CN_Y6EXv.d.ts} +149 -3
  108. package/dist/sessions/queries.js +36 -0
  109. package/dist/sessions/queries.js.map +1 -1
  110. package/dist/sessions/queries.mjs +3 -3
  111. package/dist/settings/queries.d.mts +4 -3
  112. package/dist/settings/queries.d.ts +4 -3
  113. package/dist/settings/queries.js +42 -0
  114. package/dist/settings/queries.js.map +1 -1
  115. package/dist/settings/queries.mjs +4 -4
  116. package/dist/users/queries.js +36 -0
  117. package/dist/users/queries.js.map +1 -1
  118. package/dist/users/queries.mjs +3 -3
  119. package/lib/cookie-consent.ts +72 -20
  120. package/lib/cookie-names.ts +47 -0
  121. package/lib/device-fingerprint.ts +66 -10
  122. package/lib/recalculate-columns.ts +2 -0
  123. package/package.json +28 -3
  124. package/styles/theme.css +17 -3
  125. package/dist/chunk-3MGEAGHU.mjs.map +0 -1
  126. package/dist/chunk-N6ECRCHY.mjs.map +0 -1
  127. package/dist/chunk-TMJYAOC2.mjs.map +0 -1
  128. package/dist/chunk-XWVAJXAG.mjs.map +0 -1
  129. package/dist/chunk-ZQCALTKB.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../config/env-schema.ts","../../config/env.ts","../../lib/resend.ts","../../lib/marketing.ts"],"names":["z","error"],"mappings":"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","file":"index.js","sourcesContent":["// 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\"\nimport type { Resend } from \"resend\"\nimport { config } from \"@/config/env\"\n\n// From env, not hardcoded: scaffolded apps run this module from the package's\n// bundled auth, so a local edit here would never reach their auth emails.\nexport const EMAIL_FROM = config.EMAIL_FROM\n\n// Loaded on first send, so projects that never send email don't pay for it\n// at startup.\nlet resend: Resend | undefined\nexport async function getResend() {\n if (!resend) {\n const { Resend } = await import(\"resend\")\n resend = new Resend(config.RESEND_API_KEY)\n }\n return resend\n}\n\n// Broadcasts need a full access key, so they can use their own and leave\n// RESEND_API_KEY as a sending-only key.\nexport const BROADCAST_API_KEY = config.RESEND_BROADCAST_API_KEY || config.RESEND_API_KEY\n\nlet broadcastResend: Resend | undefined\nexport async function getBroadcastResend() {\n if (!broadcastResend) {\n const { Resend } = await import(\"resend\")\n broadcastResend = new Resend(BROADCAST_API_KEY)\n }\n return broadcastResend\n}\n","import \"server-only\"\n// Marketing email data helpers, shipped as `@xk2800/nextjs-template/marketing`.\n// Everything goes through Resend Broadcasts/Contacts with the broadcast key,\n// so it never touches the transactional sending quota.\nimport { config } from \"../config/env\"\nimport { getBroadcastResend, BROADCAST_API_KEY } from \"./resend\"\n\n// The segment users join from the dashboard toggle. Blank = toggle hidden.\nexport const MARKETING_SEGMENT_ID = config.RESEND_MARKETING_SEGMENT_ID\n\nexport interface MarketingSegment {\n id: string\n name: string\n}\n\nexport interface MarketingBroadcast {\n id: string\n name: string | null\n status: string\n created_at: string\n sent_at: string | null\n}\n\nexport interface MarketingContact {\n id: string\n email: string\n first_name?: string\n last_name?: string\n unsubscribed: boolean\n created_at: string\n}\n\n// Segments + the 10 latest broadcasts for the admin page. `error` is set\n// instead of throwing, so the page can explain a missing or sending-only key.\nexport async function getMarketingOverview(): Promise<{\n segments: MarketingSegment[]\n broadcasts: MarketingBroadcast[]\n error: string | null\n}> {\n if (!BROADCAST_API_KEY) {\n return { segments: [], broadcasts: [], error: \"Neither RESEND_BROADCAST_API_KEY nor RESEND_API_KEY is set.\" }\n }\n\n const resend = await getBroadcastResend()\n const [segRes, bcRes] = await Promise.all([\n resend.segments.list(),\n resend.broadcasts.list({ limit: 10 }),\n ])\n if (segRes.error) return { segments: [], broadcasts: [], error: segRes.error.message }\n return { segments: segRes.data.data, broadcasts: bcRes.data?.data ?? [], error: null }\n}\n\n// ponytail: first page only (Resend max 100); add cursor paging (`after`)\n// if a segment outgrows it.\nexport const SEGMENT_CONTACTS_LIMIT = 100\n\nexport async function listSegmentContacts(segmentId: string): Promise<{\n contacts: MarketingContact[]\n hasMore: boolean\n error: string | null\n}> {\n const { data, error } = await (await getBroadcastResend()).contacts.list({ segmentId, limit: SEGMENT_CONTACTS_LIMIT })\n if (error) return { contacts: [], hasMore: false, error: error.message }\n return { contacts: data.data, hasMore: data.has_more, error: null }\n}\n\n// Resend is the source of truth, so the toggle also reflects someone who\n// clicked Unsubscribe in an email or was edited in the Resend dashboard.\nexport async function isSubscribed(email: string): Promise<boolean> {\n const resend = await getBroadcastResend()\n const contact = await resend.contacts.get({ email })\n if (contact.error) {\n if (contact.error.name === \"not_found\") return false\n throw new Error(contact.error.message)\n }\n if (contact.data.unsubscribed) return false\n\n const segments = await resend.contacts.segments.list({ email })\n if (segments.error) throw new Error(segments.error.message)\n return segments.data.data.some((s) => s.id === MARKETING_SEGMENT_ID)\n}\n\nexport async function setSubscribed(email: string, name: string, subscribed: boolean) {\n const resend = await getBroadcastResend()\n const contact = await resend.contacts.get({ email })\n const exists = !contact.error\n if (contact.error && contact.error.name !== \"not_found\") throw new Error(contact.error.message)\n\n if (!subscribed) {\n // Same as the email's Unsubscribe link: opted out of every broadcast.\n if (!exists) return\n const { error } = await resend.contacts.update({ email, unsubscribed: true })\n if (error) throw new Error(error.message)\n return\n }\n\n const { error } = exists\n ? await resend.contacts.update({ email, unsubscribed: false })\n : await resend.contacts.create({ email, firstName: name || undefined, unsubscribed: false })\n if (error) throw new Error(error.message)\n\n const segments = await resend.contacts.segments.list({ email })\n if (segments.error) throw new Error(segments.error.message)\n if (!segments.data.data.some((s) => s.id === MARKETING_SEGMENT_ID)) {\n const added = await resend.contacts.segments.add({ email, segmentId: MARKETING_SEGMENT_ID })\n if (added.error) throw new Error(added.error.message)\n }\n}\n"]}
@@ -0,0 +1,6 @@
1
+ export { MARKETING_SEGMENT_ID, SEGMENT_CONTACTS_LIMIT, getMarketingOverview, isSubscribed, listSegmentContacts, setSubscribed } from '../chunk-C6RGN6QA.mjs';
2
+ import '../chunk-OVKVD2HH.mjs';
3
+ import '../chunk-TKWNEKK6.mjs';
4
+ import '../chunk-3465SI5N.mjs';
5
+ //# sourceMappingURL=index.mjs.map
6
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.mjs"}
@@ -1142,7 +1142,7 @@ declare const activityLogs: drizzle_orm_pg_core.PgTableWithColumns<{
1142
1142
  tableName: "activity_log";
1143
1143
  dataType: "string";
1144
1144
  columnType: "PgEnumColumn";
1145
- data: "login_failed" | "login" | "logout" | "password_changed" | "email_changed" | "profile_updated" | "session_revoked" | "user_deleted" | "user_banned" | "user_unbanned" | "role_changed" | "impersonation_started" | "impersonation_stopped" | "settings_changed" | "passkey_added" | "passkey_removed";
1145
+ data: "login" | "logout" | "login_failed" | "password_changed" | "email_changed" | "profile_updated" | "session_revoked" | "user_deleted" | "user_banned" | "user_unbanned" | "role_changed" | "impersonation_started" | "impersonation_stopped" | "settings_changed" | "passkey_added" | "passkey_removed";
1146
1146
  driverParam: string;
1147
1147
  notNull: true;
1148
1148
  hasDefault: false;
@@ -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 };
@@ -1142,7 +1142,7 @@ declare const activityLogs: drizzle_orm_pg_core.PgTableWithColumns<{
1142
1142
  tableName: "activity_log";
1143
1143
  dataType: "string";
1144
1144
  columnType: "PgEnumColumn";
1145
- data: "login_failed" | "login" | "logout" | "password_changed" | "email_changed" | "profile_updated" | "session_revoked" | "user_deleted" | "user_banned" | "user_unbanned" | "role_changed" | "impersonation_started" | "impersonation_stopped" | "settings_changed" | "passkey_added" | "passkey_removed";
1145
+ data: "login" | "logout" | "login_failed" | "password_changed" | "email_changed" | "profile_updated" | "session_revoked" | "user_deleted" | "user_banned" | "user_unbanned" | "role_changed" | "impersonation_started" | "impersonation_stopped" | "settings_changed" | "passkey_added" | "passkey_removed";
1146
1146
  driverParam: string;
1147
1147
  notNull: true;
1148
1148
  hasDefault: false;
@@ -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>"),