@xk2800/nextjs-template 0.5.3

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 (199) hide show
  1. package/README.md +339 -0
  2. package/components/auth/authCard.tsx +92 -0
  3. package/components/auth/authShell.tsx +46 -0
  4. package/components/auth/deviceCheck.tsx +52 -0
  5. package/components/auth/emailPasswordLogin.tsx +136 -0
  6. package/components/auth/emailPasswordSignup.tsx +178 -0
  7. package/components/auth/forgotPasswordForm.tsx +66 -0
  8. package/components/auth/loginErrorRecovery.tsx +117 -0
  9. package/components/auth/logoutButtons.tsx +26 -0
  10. package/components/auth/oneTap.tsx +50 -0
  11. package/components/auth/passkeyLogin.tsx +50 -0
  12. package/components/auth/resetPasswordForm.tsx +112 -0
  13. package/components/auth/socialLogin.tsx +51 -0
  14. package/components/auth/twoFactorVerifyForm.tsx +100 -0
  15. package/components/dashboard/accountDetailsCard.tsx +58 -0
  16. package/components/dashboard/activityLogsCard.tsx +141 -0
  17. package/components/dashboard/activityLogsCardSkeleton.tsx +20 -0
  18. package/components/dashboard/activityLogsSection.tsx +16 -0
  19. package/components/dashboard/admin/activityLogsSection.tsx +13 -0
  20. package/components/dashboard/admin/adminActivityLogsSkeleton.tsx +34 -0
  21. package/components/dashboard/admin/adminActivityLogsTable.tsx +319 -0
  22. package/components/dashboard/admin/adminStatsGrid.tsx +30 -0
  23. package/components/dashboard/admin/adminStatsGridSkeleton.tsx +16 -0
  24. package/components/dashboard/admin/adminUserActivityLogCard.tsx +53 -0
  25. package/components/dashboard/admin/adminUserActivityLogTable.tsx +69 -0
  26. package/components/dashboard/admin/adminUserLoginInfoCard.tsx +50 -0
  27. package/components/dashboard/admin/adminUserLoginInfoTable.tsx +63 -0
  28. package/components/dashboard/admin/adminUserSessionsCard.tsx +225 -0
  29. package/components/dashboard/admin/adminUserStatusCard.tsx +57 -0
  30. package/components/dashboard/admin/adminUsersSkeleton.tsx +30 -0
  31. package/components/dashboard/admin/adminUsersTable.tsx +596 -0
  32. package/components/dashboard/admin/deviceThrottleCard.tsx +59 -0
  33. package/components/dashboard/admin/deviceThrottleTable.tsx +93 -0
  34. package/components/dashboard/admin/impersonationLogCard.tsx +73 -0
  35. package/components/dashboard/admin/newDeviceCard.tsx +35 -0
  36. package/components/dashboard/admin/newDeviceTable.tsx +71 -0
  37. package/components/dashboard/admin/revokeAllSessionsButton.tsx +77 -0
  38. package/components/dashboard/admin/sharedDeviceCard.tsx +42 -0
  39. package/components/dashboard/admin/sharedDeviceTable.tsx +75 -0
  40. package/components/dashboard/admin/systemSettingsForm.tsx +200 -0
  41. package/components/dashboard/admin/systemSettingsSection.tsx +9 -0
  42. package/components/dashboard/admin/systemSettingsSkeleton.tsx +29 -0
  43. package/components/dashboard/admin/usersSection.tsx +13 -0
  44. package/components/dashboard/adminSection.tsx +31 -0
  45. package/components/dashboard/appSidebar.tsx +170 -0
  46. package/components/dashboard/dashboardHeader.tsx +85 -0
  47. package/components/dashboard/heartbeat.tsx +22 -0
  48. package/components/dashboard/impersonationBanner.tsx +50 -0
  49. package/components/dashboard/passkeysCard.tsx +187 -0
  50. package/components/dashboard/profileCard.tsx +42 -0
  51. package/components/dashboard/sectionErrorFallback.tsx +18 -0
  52. package/components/dashboard/sessionsCard.tsx +194 -0
  53. package/components/dashboard/sessionsCardSkeleton.tsx +20 -0
  54. package/components/dashboard/sessionsSection.tsx +22 -0
  55. package/components/dashboard/twoFactorCard.tsx +344 -0
  56. package/components/dashboard/verifyEmailButton.tsx +32 -0
  57. package/components/email/email-template-2.tsx +44 -0
  58. package/components/email/email-template-new-device.tsx +80 -0
  59. package/components/email/email-template-passkey-change.tsx +81 -0
  60. package/components/email/email-template-reset-password.tsx +56 -0
  61. package/components/email/email-template-verify-email.tsx +55 -0
  62. package/components/email/email-template-welcome.tsx +53 -0
  63. package/components/email/email-template.tsx +33 -0
  64. package/components/email/send-email-button.tsx +61 -0
  65. package/components/providers/theme-provider.tsx +9 -0
  66. package/components/theme/theme-toggle.tsx +53 -0
  67. package/components/ui/alert-dialog.tsx +141 -0
  68. package/components/ui/avatar.tsx +53 -0
  69. package/components/ui/badge.tsx +46 -0
  70. package/components/ui/button.tsx +64 -0
  71. package/components/ui/card.tsx +92 -0
  72. package/components/ui/checkbox.tsx +32 -0
  73. package/components/ui/data-table.tsx +302 -0
  74. package/components/ui/dialog.tsx +143 -0
  75. package/components/ui/dropdown-menu.tsx +203 -0
  76. package/components/ui/input.tsx +21 -0
  77. package/components/ui/label.tsx +26 -0
  78. package/components/ui/password-input.tsx +38 -0
  79. package/components/ui/separator.tsx +28 -0
  80. package/components/ui/sheet.tsx +143 -0
  81. package/components/ui/sidebar.tsx +726 -0
  82. package/components/ui/skeleton.tsx +13 -0
  83. package/components/ui/switch.tsx +35 -0
  84. package/components/ui/table.tsx +116 -0
  85. package/components/ui/textarea.tsx +18 -0
  86. package/components/ui/tooltip.tsx +57 -0
  87. package/dist/activity/logger.d.mts +18 -0
  88. package/dist/activity/logger.d.ts +18 -0
  89. package/dist/activity/logger.js +289 -0
  90. package/dist/activity/logger.js.map +1 -0
  91. package/dist/activity/logger.mjs +7 -0
  92. package/dist/activity/logger.mjs.map +1 -0
  93. package/dist/activity/queries.d.mts +98 -0
  94. package/dist/activity/queries.d.ts +98 -0
  95. package/dist/activity/queries.js +372 -0
  96. package/dist/activity/queries.js.map +1 -0
  97. package/dist/activity/queries.mjs +124 -0
  98. package/dist/activity/queries.mjs.map +1 -0
  99. package/dist/admin/queries.d.mts +68 -0
  100. package/dist/admin/queries.d.ts +68 -0
  101. package/dist/admin/queries.js +450 -0
  102. package/dist/admin/queries.js.map +1 -0
  103. package/dist/admin/queries.mjs +143 -0
  104. package/dist/admin/queries.mjs.map +1 -0
  105. package/dist/auth/helpers.d.mts +111 -0
  106. package/dist/auth/helpers.d.ts +111 -0
  107. package/dist/auth/helpers.js +1023 -0
  108. package/dist/auth/helpers.js.map +1 -0
  109. package/dist/auth/helpers.mjs +67 -0
  110. package/dist/auth/helpers.mjs.map +1 -0
  111. package/dist/auth/index.d.mts +6589 -0
  112. package/dist/auth/index.d.ts +6589 -0
  113. package/dist/auth/index.js +968 -0
  114. package/dist/auth/index.js.map +1 -0
  115. package/dist/auth/index.mjs +10 -0
  116. package/dist/auth/index.mjs.map +1 -0
  117. package/dist/auth-client/index.d.mts +4864 -0
  118. package/dist/auth-client/index.d.ts +4864 -0
  119. package/dist/auth-client/index.js +55 -0
  120. package/dist/auth-client/index.js.map +1 -0
  121. package/dist/auth-client/index.mjs +49 -0
  122. package/dist/auth-client/index.mjs.map +1 -0
  123. package/dist/chunk-2FW7C3KS.mjs +556 -0
  124. package/dist/chunk-2FW7C3KS.mjs.map +1 -0
  125. package/dist/chunk-ENGYO4K7.mjs +189 -0
  126. package/dist/chunk-ENGYO4K7.mjs.map +1 -0
  127. package/dist/chunk-GTPTVNY2.mjs +40 -0
  128. package/dist/chunk-GTPTVNY2.mjs.map +1 -0
  129. package/dist/chunk-HYKUB3A2.mjs +30 -0
  130. package/dist/chunk-HYKUB3A2.mjs.map +1 -0
  131. package/dist/chunk-JIYHBWAU.mjs +79 -0
  132. package/dist/chunk-JIYHBWAU.mjs.map +1 -0
  133. package/dist/chunk-K6OJCSJS.mjs +41 -0
  134. package/dist/chunk-K6OJCSJS.mjs.map +1 -0
  135. package/dist/chunk-N6ECRCHY.mjs +61 -0
  136. package/dist/chunk-N6ECRCHY.mjs.map +1 -0
  137. package/dist/chunk-YN4436PK.mjs +33 -0
  138. package/dist/chunk-YN4436PK.mjs.map +1 -0
  139. package/dist/config/env.d.mts +15 -0
  140. package/dist/config/env.d.ts +15 -0
  141. package/dist/config/env.js +42 -0
  142. package/dist/config/env.js.map +1 -0
  143. package/dist/config/env.mjs +4 -0
  144. package/dist/config/env.mjs.map +1 -0
  145. package/dist/db/index.d.mts +10 -0
  146. package/dist/db/index.d.ts +10 -0
  147. package/dist/db/index.js +252 -0
  148. package/dist/db/index.js.map +1 -0
  149. package/dist/db/index.mjs +6 -0
  150. package/dist/db/index.mjs.map +1 -0
  151. package/dist/db/schema.d.mts +2 -0
  152. package/dist/db/schema.d.ts +2 -0
  153. package/dist/db/schema.js +186 -0
  154. package/dist/db/schema.js.map +1 -0
  155. package/dist/db/schema.mjs +4 -0
  156. package/dist/db/schema.mjs.map +1 -0
  157. package/dist/schema-CgkDv4wJ.d.mts +1758 -0
  158. package/dist/schema-CgkDv4wJ.d.ts +1758 -0
  159. package/dist/sessions/queries.d.mts +34 -0
  160. package/dist/sessions/queries.d.ts +34 -0
  161. package/dist/sessions/queries.js +268 -0
  162. package/dist/sessions/queries.js.map +1 -0
  163. package/dist/sessions/queries.mjs +23 -0
  164. package/dist/sessions/queries.mjs.map +1 -0
  165. package/dist/settings/queries.d.mts +20 -0
  166. package/dist/settings/queries.d.ts +20 -0
  167. package/dist/settings/queries.js +344 -0
  168. package/dist/settings/queries.js.map +1 -0
  169. package/dist/settings/queries.mjs +7 -0
  170. package/dist/settings/queries.mjs.map +1 -0
  171. package/dist/types/auth/loginSchema.d.mts +8 -0
  172. package/dist/types/auth/loginSchema.d.ts +8 -0
  173. package/dist/types/auth/loginSchema.js +18 -0
  174. package/dist/types/auth/loginSchema.js.map +1 -0
  175. package/dist/types/auth/loginSchema.mjs +16 -0
  176. package/dist/types/auth/loginSchema.mjs.map +1 -0
  177. package/dist/types/auth/signupSchema.d.mts +9 -0
  178. package/dist/types/auth/signupSchema.d.ts +9 -0
  179. package/dist/types/auth/signupSchema.js +21 -0
  180. package/dist/types/auth/signupSchema.js.map +1 -0
  181. package/dist/types/auth/signupSchema.mjs +19 -0
  182. package/dist/types/auth/signupSchema.mjs.map +1 -0
  183. package/dist/users/queries.d.mts +71 -0
  184. package/dist/users/queries.d.ts +71 -0
  185. package/dist/users/queries.js +293 -0
  186. package/dist/users/queries.js.map +1 -0
  187. package/dist/users/queries.mjs +47 -0
  188. package/dist/users/queries.mjs.map +1 -0
  189. package/lib/auth-client.ts +58 -0
  190. package/lib/cookie-names.ts +13 -0
  191. package/lib/device-fingerprint.ts +21 -0
  192. package/lib/formatters.ts +53 -0
  193. package/lib/recalculate-columns.ts +75 -0
  194. package/lib/request-info.ts +78 -0
  195. package/lib/utils.ts +6 -0
  196. package/package.json +301 -0
  197. package/styles/theme.css +124 -0
  198. package/types/auth/loginSchema.ts +11 -0
  199. package/types/auth/signupSchema.ts +14 -0
@@ -0,0 +1,344 @@
1
+ 'use strict';
2
+
3
+ require('server-only');
4
+ var serverless = require('@neondatabase/serverless');
5
+ var neonHttp = require('drizzle-orm/neon-http');
6
+ var nodePostgres = require('drizzle-orm/node-postgres');
7
+ var pg = require('pg');
8
+ var pgCore = require('drizzle-orm/pg-core');
9
+ var drizzleOrm = require('drizzle-orm');
10
+ var cuid2 = require('@paralleldrive/cuid2');
11
+ var zod = require('zod');
12
+
13
+ var __defProp = Object.defineProperty;
14
+ var __defProps = Object.defineProperties;
15
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
16
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
17
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
18
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
19
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
20
+ var __spreadValues = (a, b) => {
21
+ for (var prop in b || (b = {}))
22
+ if (__hasOwnProp.call(b, prop))
23
+ __defNormalProp(a, prop, b[prop]);
24
+ if (__getOwnPropSymbols)
25
+ for (var prop of __getOwnPropSymbols(b)) {
26
+ if (__propIsEnum.call(b, prop))
27
+ __defNormalProp(a, prop, b[prop]);
28
+ }
29
+ return a;
30
+ };
31
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
32
+ var __export = (target, all) => {
33
+ for (var name in all)
34
+ __defProp(target, name, { get: all[name], enumerable: true });
35
+ };
36
+
37
+ // server/db/schema.ts
38
+ var schema_exports = {};
39
+ __export(schema_exports, {
40
+ ActivityActionEnum: () => ActivityActionEnum,
41
+ RoleEnum: () => RoleEnum,
42
+ accounts: () => accounts,
43
+ activityLogs: () => activityLogs,
44
+ authThrottle: () => authThrottle,
45
+ deviceFingerprints: () => deviceFingerprints,
46
+ passkeys: () => passkeys,
47
+ sessions: () => sessions,
48
+ systemSettings: () => systemSettings,
49
+ twoFactors: () => twoFactors,
50
+ users: () => users,
51
+ verifications: () => verifications
52
+ });
53
+ var RoleEnum = pgCore.pgEnum("roles", ["user", "admin"]);
54
+ var ActivityActionEnum = pgCore.pgEnum("activity_actions", [
55
+ "login",
56
+ "logout",
57
+ "login_failed",
58
+ "password_changed",
59
+ "email_changed",
60
+ "profile_updated",
61
+ "session_revoked",
62
+ "user_deleted",
63
+ "user_banned",
64
+ "user_unbanned",
65
+ "role_changed",
66
+ "impersonation_started",
67
+ "impersonation_stopped",
68
+ "settings_changed",
69
+ "passkey_added",
70
+ "passkey_removed"
71
+ ]);
72
+ var users = pgCore.pgTable("user", {
73
+ id: pgCore.text("id").notNull().primaryKey().$defaultFn(() => cuid2.createId()),
74
+ name: pgCore.text("name").notNull(),
75
+ email: pgCore.text("email").notNull().unique(),
76
+ password: pgCore.text("password"),
77
+ emailVerified: pgCore.boolean("emailVerified").notNull().default(false),
78
+ image: pgCore.text("image"),
79
+ role: RoleEnum("roles").default("user").notNull(),
80
+ banned: pgCore.boolean("banned").default(false).notNull(),
81
+ bannedAt: pgCore.timestamp("bannedAt"),
82
+ bannedReason: pgCore.text("bannedReason"),
83
+ // Required by better-auth's admin plugin schema (auto-expiring bans). We
84
+ // never set this ourselves, so it stays null and bans behave as permanent,
85
+ // same as before the plugin was added.
86
+ banExpires: pgCore.timestamp("banExpires"),
87
+ lastLoginAt: pgCore.timestamp("lastLoginAt"),
88
+ lastActiveAt: pgCore.timestamp("lastActiveAt"),
89
+ // Set by better-auth's twoFactor plugin — true once the user has
90
+ // completed TOTP setup (see twoFactor pgTable below for the secret /
91
+ // backup codes themselves).
92
+ twoFactorEnabled: pgCore.boolean("twoFactorEnabled").notNull().default(false),
93
+ createdAt: pgCore.timestamp("createdAt").notNull().defaultNow(),
94
+ updatedAt: pgCore.timestamp("updatedAt").notNull().defaultNow()
95
+ });
96
+ var sessions = pgCore.pgTable("session", {
97
+ id: pgCore.text("id").notNull().primaryKey().$defaultFn(() => cuid2.createId()),
98
+ expiresAt: pgCore.timestamp("expiresAt").notNull(),
99
+ token: pgCore.text("token").notNull().unique(),
100
+ createdAt: pgCore.timestamp("createdAt").notNull().defaultNow(),
101
+ updatedAt: pgCore.timestamp("updatedAt").notNull().defaultNow(),
102
+ ipAddress: pgCore.text("ipAddress"),
103
+ userAgent: pgCore.text("userAgent"),
104
+ userId: pgCore.text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
105
+ // Set by better-auth's admin plugin while this session is an admin
106
+ // impersonating another user; holds the impersonating admin's user id.
107
+ impersonatedBy: pgCore.text("impersonatedBy")
108
+ });
109
+ var accounts = pgCore.pgTable("account", {
110
+ id: pgCore.text("id").notNull().primaryKey().$defaultFn(() => cuid2.createId()),
111
+ accountId: pgCore.text("accountId").notNull(),
112
+ providerId: pgCore.text("providerId").notNull(),
113
+ userId: pgCore.text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
114
+ accessToken: pgCore.text("accessToken"),
115
+ refreshToken: pgCore.text("refreshToken"),
116
+ idToken: pgCore.text("idToken"),
117
+ accessTokenExpiresAt: pgCore.timestamp("accessTokenExpiresAt"),
118
+ refreshTokenExpiresAt: pgCore.timestamp("refreshTokenExpiresAt"),
119
+ scope: pgCore.text("scope"),
120
+ password: pgCore.text("password"),
121
+ createdAt: pgCore.timestamp("createdAt").notNull().defaultNow(),
122
+ updatedAt: pgCore.timestamp("updatedAt").notNull().defaultNow()
123
+ });
124
+ var twoFactors = pgCore.pgTable("twoFactor", {
125
+ id: pgCore.text("id").notNull().primaryKey().$defaultFn(() => cuid2.createId()),
126
+ secret: pgCore.text("secret").notNull(),
127
+ backupCodes: pgCore.text("backupCodes").notNull(),
128
+ userId: pgCore.text("userId").notNull().references(() => users.id, { onDelete: "cascade" })
129
+ });
130
+ var passkeys = pgCore.pgTable("passkey", {
131
+ id: pgCore.text("id").notNull().primaryKey().$defaultFn(() => cuid2.createId()),
132
+ name: pgCore.text("name"),
133
+ publicKey: pgCore.text("publicKey").notNull(),
134
+ userId: pgCore.text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
135
+ credentialID: pgCore.text("credentialID").notNull().unique(),
136
+ counter: pgCore.integer("counter").notNull(),
137
+ deviceType: pgCore.text("deviceType").notNull(),
138
+ backedUp: pgCore.boolean("backedUp").notNull(),
139
+ transports: pgCore.text("transports"),
140
+ createdAt: pgCore.timestamp("createdAt"),
141
+ aaguid: pgCore.text("aaguid")
142
+ });
143
+ var verifications = pgCore.pgTable("verification", {
144
+ id: pgCore.text("id").notNull().primaryKey().$defaultFn(() => cuid2.createId()),
145
+ identifier: pgCore.text("identifier").notNull(),
146
+ value: pgCore.text("value").notNull(),
147
+ expiresAt: pgCore.timestamp("expiresAt").notNull(),
148
+ createdAt: pgCore.timestamp("createdAt").notNull().defaultNow(),
149
+ updatedAt: pgCore.timestamp("updatedAt").notNull().defaultNow()
150
+ });
151
+ var activityLogs = pgCore.pgTable("activity_log", {
152
+ id: pgCore.text("id").notNull().primaryKey().$defaultFn(() => cuid2.createId()),
153
+ userId: pgCore.text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
154
+ action: ActivityActionEnum("action").notNull(),
155
+ description: pgCore.text("description").notNull(),
156
+ ipAddress: pgCore.text("ipAddress"),
157
+ userAgent: pgCore.text("userAgent"),
158
+ metadata: pgCore.text("metadata"),
159
+ // Populated on 'login' events only (registration + sign-in) — parsed from
160
+ // userAgent/ipAddress at write time so the record stays stable even if
161
+ // the parsing library or geoip database changes later.
162
+ referrerUrl: pgCore.text("referrerUrl"),
163
+ os: pgCore.text("os"),
164
+ browser: pgCore.text("browser"),
165
+ deviceType: pgCore.text("deviceType"),
166
+ country: pgCore.text("country"),
167
+ city: pgCore.text("city"),
168
+ createdAt: pgCore.timestamp("createdAt").notNull().defaultNow()
169
+ });
170
+ var deviceFingerprints = pgCore.pgTable("device_fingerprint", {
171
+ id: pgCore.text("id").notNull().primaryKey().$defaultFn(() => cuid2.createId()),
172
+ userId: pgCore.text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
173
+ visitorId: pgCore.text("visitorId").notNull(),
174
+ // Context captured at first-seen time — for the admin panel's "New devices"
175
+ // card and the alert email. Not part of the new-vs-known decision (that's
176
+ // visitorId only), just so an admin can eyeball whether a sign-in looks like
177
+ // a hijack. Nullable: an old row predates these columns, or geoip missed.
178
+ ipAddress: pgCore.text("ipAddress"),
179
+ userAgent: pgCore.text("userAgent"),
180
+ country: pgCore.text("country"),
181
+ city: pgCore.text("city"),
182
+ createdAt: pgCore.timestamp("createdAt").notNull().defaultNow()
183
+ }, (table) => [
184
+ pgCore.unique("device_fingerprint_user_visitor").on(table.userId, table.visitorId)
185
+ ]);
186
+ var authThrottle = pgCore.pgTable("auth_throttle", {
187
+ fingerprint: pgCore.text("fingerprint").notNull().primaryKey(),
188
+ count: pgCore.integer("count").notNull().default(0),
189
+ windowStart: pgCore.timestamp("windowStart").notNull().defaultNow(),
190
+ // Blockable data points, overwritten on every attempt (last-write-wins) so
191
+ // the admin panel can show who to block — by IP or by fingerprint — with no
192
+ // join and no per-attempt history table. windowStart anchors the counter's
193
+ // window; lastAttemptAt is just "most recently seen".
194
+ lastAttemptAt: pgCore.timestamp("lastAttemptAt").notNull().defaultNow(),
195
+ ipAddress: pgCore.text("ipAddress"),
196
+ userAgent: pgCore.text("userAgent"),
197
+ lastEmail: pgCore.text("lastEmail"),
198
+ lastKind: pgCore.text("lastKind")
199
+ // 'signin' | 'signup'
200
+ });
201
+ var systemSettings = pgCore.pgTable("system_settings", {
202
+ id: pgCore.text("id").primaryKey().default("default"),
203
+ maintenanceMode: pgCore.boolean("maintenanceMode").notNull().default(false),
204
+ maintenanceMessage: pgCore.text("maintenanceMessage"),
205
+ authEnableGoogle: pgCore.boolean("authEnableGoogle").notNull().default(true),
206
+ authEnableEmailPassword: pgCore.boolean("authEnableEmailPassword").notNull().default(true),
207
+ authEnableOneTap: pgCore.boolean("authEnableOneTap").notNull().default(false),
208
+ enableSessionRevocation: pgCore.boolean("enableSessionRevocation").notNull().default(true),
209
+ updatedAt: pgCore.timestamp("updatedAt").notNull().defaultNow(),
210
+ // Who last changed a setting. This row doesn't "belong" to that admin the
211
+ // way sessions/activityLogs belong to their user, so deleting that admin's
212
+ // account should null this out rather than cascade-delete the singleton row.
213
+ updatedBy: pgCore.text("updatedBy").references(() => users.id, { onDelete: "set null" })
214
+ }, (table) => [
215
+ pgCore.check("system_settings_singleton", drizzleOrm.sql`${table.id} = 'default'`)
216
+ ]);
217
+ var envSchema = zod.z.object({
218
+ NODE_ENV: zod.z.enum(["development", "production", "test"]).default("development"),
219
+ PORT: zod.z.string().default("3000").transform(Number),
220
+ DATABASE_URL: zod.z.string().default(""),
221
+ RESEND_API_KEY: zod.z.string().default(""),
222
+ // Display name for this deployment — shown as the authenticator-app label
223
+ // (2FA issuer) and the passkey relying-party name. Consuming projects set
224
+ // this in their own .env; defaults to the template name.
225
+ APP_NAME: zod.z.string().default("Next.js Template"),
226
+ // Seed-only: the live value lives in the system_settings DB row (see
227
+ // lib/settings-queries.ts) once it's been seeded on first read, and is
228
+ // changed from the admin System Settings page, not by editing this var.
229
+ ENABLE_SESSION_REVOCATION: zod.z.string().default("true").transform((val) => val === "true"),
230
+ AUTH_ENABLE_GOOGLE: zod.z.string().default("true").transform((val) => val === "true"),
231
+ AUTH_ENABLE_EMAIL_PASSWORD: zod.z.string().default("true").transform((val) => val === "true"),
232
+ AUTH_ENABLE_ONE_TAP: zod.z.string().default("false").transform((val) => val === "true"),
233
+ DB_DRIVER: zod.z.enum(["pg", "neon"]).default("pg"),
234
+ // Only relevant when DB_DRIVER=pg — whether the target Postgres server has SSL
235
+ // enabled. Self-hosted Postgres (e.g. via Docker/Dokploy) typically does not.
236
+ DATABASE_SSL: zod.z.string().default("false").transform((val) => val === "true")
237
+ });
238
+
239
+ // config/env.ts
240
+ var results = envSchema.safeParse(process.env);
241
+ if (results.success && results.data.NODE_ENV === "development") {
242
+ console.log("Environment variables loaded:", Object.keys(results.data));
243
+ }
244
+ if (!results.success) {
245
+ console.error("Invalid environment variables:", results.error.format());
246
+ throw new Error("Invalid environment variables");
247
+ }
248
+ var config = results.data;
249
+
250
+ // server/db/index.ts
251
+ var db;
252
+ if (config.DB_DRIVER === "pg") {
253
+ const pool = new pg.Pool({
254
+ connectionString: config.DATABASE_URL,
255
+ ssl: config.DATABASE_SSL ? { rejectUnauthorized: false } : void 0
256
+ });
257
+ db = nodePostgres.drizzle(pool, {
258
+ schema: schema_exports,
259
+ logger: true
260
+ });
261
+ console.log("\u{1F7E1} Using PostgreSQL (pg) driver");
262
+ } else if (config.DB_DRIVER === "neon") {
263
+ const sql2 = serverless.neon(config.DATABASE_URL);
264
+ db = neonHttp.drizzle(sql2, { schema: schema_exports, logger: true });
265
+ console.log("\u{1F7E2} Using Neon (HTTP) driver");
266
+ } else {
267
+ console.log("Database connection not established, unknown DB_DRIVER:", config.DB_DRIVER);
268
+ }
269
+ var SETTINGS_ROW_ID = "default";
270
+ var CACHE_TTL_MS = 1e4;
271
+ var cached = null;
272
+ function fallbackSettings() {
273
+ return {
274
+ id: SETTINGS_ROW_ID,
275
+ maintenanceMode: false,
276
+ maintenanceMessage: null,
277
+ authEnableGoogle: config.AUTH_ENABLE_GOOGLE,
278
+ authEnableEmailPassword: config.AUTH_ENABLE_EMAIL_PASSWORD,
279
+ authEnableOneTap: config.AUTH_ENABLE_ONE_TAP,
280
+ enableSessionRevocation: config.ENABLE_SESSION_REVOCATION,
281
+ updatedAt: /* @__PURE__ */ new Date(0),
282
+ updatedBy: null
283
+ };
284
+ }
285
+ async function loadOrSeed() {
286
+ var _a;
287
+ const existing = await db.select().from(systemSettings).where(drizzleOrm.eq(systemSettings.id, SETTINGS_ROW_ID)).limit(1);
288
+ if (existing[0]) return existing[0];
289
+ const seeded = await db.insert(systemSettings).values({
290
+ id: SETTINGS_ROW_ID,
291
+ maintenanceMode: false,
292
+ authEnableGoogle: config.AUTH_ENABLE_GOOGLE,
293
+ authEnableEmailPassword: config.AUTH_ENABLE_EMAIL_PASSWORD,
294
+ authEnableOneTap: config.AUTH_ENABLE_ONE_TAP,
295
+ enableSessionRevocation: config.ENABLE_SESSION_REVOCATION
296
+ }).onConflictDoNothing().returning();
297
+ if (seeded[0]) return seeded[0];
298
+ const retry = await db.select().from(systemSettings).where(drizzleOrm.eq(systemSettings.id, SETTINGS_ROW_ID)).limit(1);
299
+ return (_a = retry[0]) != null ? _a : fallbackSettings();
300
+ }
301
+ async function getSystemSettings() {
302
+ const now = Date.now();
303
+ if (cached && cached.expiresAt > now) return cached.value;
304
+ try {
305
+ const value = await loadOrSeed();
306
+ cached = { value, expiresAt: now + CACHE_TTL_MS };
307
+ return value;
308
+ } catch (error) {
309
+ console.error("Failed to load system settings, failing open:", error);
310
+ return fallbackSettings();
311
+ }
312
+ }
313
+ function invalidateSystemSettingsCache() {
314
+ cached = null;
315
+ }
316
+ async function updateSystemSettings(patch, updatedBy) {
317
+ await loadOrSeed();
318
+ const [updated] = await db.update(systemSettings).set(__spreadProps(__spreadValues({}, patch), { updatedAt: /* @__PURE__ */ new Date(), updatedBy })).where(drizzleOrm.eq(systemSettings.id, SETTINGS_ROW_ID)).returning();
319
+ invalidateSystemSettingsCache();
320
+ return updated;
321
+ }
322
+ async function getEffectiveAuthFlags() {
323
+ const settings = await getSystemSettings();
324
+ return {
325
+ google: config.AUTH_ENABLE_GOOGLE && settings.authEnableGoogle,
326
+ emailPassword: config.AUTH_ENABLE_EMAIL_PASSWORD && settings.authEnableEmailPassword,
327
+ oneTap: config.AUTH_ENABLE_ONE_TAP && settings.authEnableOneTap
328
+ };
329
+ }
330
+ function getConfiguredAuthFlags() {
331
+ return {
332
+ google: config.AUTH_ENABLE_GOOGLE,
333
+ emailPassword: config.AUTH_ENABLE_EMAIL_PASSWORD,
334
+ oneTap: config.AUTH_ENABLE_ONE_TAP
335
+ };
336
+ }
337
+
338
+ exports.getConfiguredAuthFlags = getConfiguredAuthFlags;
339
+ exports.getEffectiveAuthFlags = getEffectiveAuthFlags;
340
+ exports.getSystemSettings = getSystemSettings;
341
+ exports.invalidateSystemSettingsCache = invalidateSystemSettingsCache;
342
+ exports.updateSystemSettings = updateSystemSettings;
343
+ //# sourceMappingURL=queries.js.map
344
+ //# sourceMappingURL=queries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../server/db/schema.ts","../../config/env-schema.ts","../../config/env.ts","../../server/db/index.ts","../../lib/settings-queries.ts"],"names":["pgEnum","pgTable","text","createId","boolean","timestamp","integer","unique","check","sql","z","Pool","drizzlePg","neon","drizzle","eq"],"mappings":"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","file":"queries.js","sourcesContent":["import { boolean, check, integer, pgEnum, pgTable, text, timestamp, unique } from \"drizzle-orm/pg-core\";\nimport { sql } from \"drizzle-orm\";\nimport { createId } from '@paralleldrive/cuid2'\n\nexport const RoleEnum = pgEnum('roles', ['user', 'admin'])\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});\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 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 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]);","// 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 // 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 // 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 { systemSettings } from \"../server/db/schema\"\nimport { config } from \"../config/env\"\nimport { eq } from \"drizzle-orm\"\n\nconst SETTINGS_ROW_ID = \"default\"\n// Short TTL: cheap reads (no DB round trip on every request), while an\n// admin's change is guaranteed live within this window even for a reader\n// that isn't the one who just saved (invalidateSystemSettingsCache() makes\n// it instant for that request's own process).\nconst CACHE_TTL_MS = 10_000\n\nexport type SystemSettings = typeof systemSettings.$inferSelect\n\nlet cached: { value: SystemSettings; expiresAt: number } | null = null\n\n// Fail-open shape if the DB is unreachable: auth flags fall back to their\n// config/env.ts defaults (unchanged behavior), maintenance mode is always\n// off — a DB hiccup must never lock out the whole site, including admins.\nfunction fallbackSettings(): SystemSettings {\n return {\n id: SETTINGS_ROW_ID,\n maintenanceMode: false,\n maintenanceMessage: null,\n authEnableGoogle: config.AUTH_ENABLE_GOOGLE,\n authEnableEmailPassword: config.AUTH_ENABLE_EMAIL_PASSWORD,\n authEnableOneTap: config.AUTH_ENABLE_ONE_TAP,\n enableSessionRevocation: config.ENABLE_SESSION_REVOCATION,\n updatedAt: new Date(0),\n updatedBy: null,\n }\n}\n\nasync function loadOrSeed(): Promise<SystemSettings> {\n const existing = await db\n .select()\n .from(systemSettings)\n .where(eq(systemSettings.id, SETTINGS_ROW_ID))\n .limit(1)\n if (existing[0]) return existing[0]\n\n // Lazy-seed on first read, from the *current* env config — not the\n // schema's hardcoded column defaults — so turning this feature on never\n // silently flips an already-deployed flag (e.g. a prod deploy that\n // already set AUTH_ENABLE_GOOGLE=false).\n const seeded = await db\n .insert(systemSettings)\n .values({\n id: SETTINGS_ROW_ID,\n maintenanceMode: false,\n authEnableGoogle: config.AUTH_ENABLE_GOOGLE,\n authEnableEmailPassword: config.AUTH_ENABLE_EMAIL_PASSWORD,\n authEnableOneTap: config.AUTH_ENABLE_ONE_TAP,\n enableSessionRevocation: config.ENABLE_SESSION_REVOCATION,\n })\n .onConflictDoNothing()\n .returning()\n\n if (seeded[0]) return seeded[0]\n\n // Lost the seed race to a concurrent first-reader.\n const retry = await db\n .select()\n .from(systemSettings)\n .where(eq(systemSettings.id, SETTINGS_ROW_ID))\n .limit(1)\n return retry[0] ?? fallbackSettings()\n}\n\nexport async function getSystemSettings(): Promise<SystemSettings> {\n const now = Date.now()\n if (cached && cached.expiresAt > now) return cached.value\n\n try {\n const value = await loadOrSeed()\n cached = { value, expiresAt: now + CACHE_TTL_MS }\n return value\n } catch (error) {\n console.error(\"Failed to load system settings, failing open:\", error)\n return fallbackSettings() // not cached — retry next request\n }\n}\n\nexport function invalidateSystemSettingsCache() {\n cached = null\n}\n\nexport type SystemSettingsPatch = Partial<Pick<SystemSettings,\n | 'maintenanceMode' | 'maintenanceMessage'\n | 'authEnableGoogle' | 'authEnableEmailPassword' | 'authEnableOneTap'\n | 'enableSessionRevocation'\n>>\n\nexport async function updateSystemSettings(\n patch: SystemSettingsPatch,\n updatedBy: string\n): Promise<SystemSettings> {\n await loadOrSeed() // ensure the row exists even if nobody's read it yet\n\n const [updated] = await db\n .update(systemSettings)\n .set({ ...patch, updatedAt: new Date(), updatedBy })\n .where(eq(systemSettings.id, SETTINGS_ROW_ID))\n .returning()\n\n invalidateSystemSettingsCache()\n return updated\n}\n\n// Effective auth-method visibility = deploy-time \"configured\" (env, decides\n// whether the provider is even registered with better-auth — see\n// server/auth.ts's createAuth()) AND runtime \"offered\" (this DB flag).\nexport async function getEffectiveAuthFlags() {\n const settings = await getSystemSettings()\n return {\n google: config.AUTH_ENABLE_GOOGLE && settings.authEnableGoogle,\n emailPassword: config.AUTH_ENABLE_EMAIL_PASSWORD && settings.authEnableEmailPassword,\n oneTap: config.AUTH_ENABLE_ONE_TAP && settings.authEnableOneTap,\n }\n}\n\n// The env-only half of the AND above. The System Settings page needs this\n// to explain *why* a toggle it controls isn't taking effect — flipping the\n// DB flag on can't do anything if the env var for that method is off, so\n// the UI should say so instead of looking like a silent no-op.\nexport function getConfiguredAuthFlags() {\n return {\n google: config.AUTH_ENABLE_GOOGLE,\n emailPassword: config.AUTH_ENABLE_EMAIL_PASSWORD,\n oneTap: config.AUTH_ENABLE_ONE_TAP,\n }\n}\n"]}
@@ -0,0 +1,7 @@
1
+ export { getConfiguredAuthFlags, getEffectiveAuthFlags, getSystemSettings, invalidateSystemSettingsCache, updateSystemSettings } from '../chunk-JIYHBWAU.mjs';
2
+ import '../chunk-HYKUB3A2.mjs';
3
+ import '../chunk-ENGYO4K7.mjs';
4
+ import '../chunk-GTPTVNY2.mjs';
5
+ import '../chunk-YN4436PK.mjs';
6
+ //# sourceMappingURL=queries.mjs.map
7
+ //# sourceMappingURL=queries.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"queries.mjs"}
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const LoginSchema: z.ZodObject<{
4
+ email: z.ZodString;
5
+ password: z.ZodString;
6
+ }, z.core.$strip>;
7
+
8
+ export { LoginSchema };
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const LoginSchema: z.ZodObject<{
4
+ email: z.ZodString;
5
+ password: z.ZodString;
6
+ }, z.core.$strip>;
7
+
8
+ export { LoginSchema };
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+
5
+ // types/auth/loginSchema.ts
6
+ var LoginSchema = zod.z.object({
7
+ email: zod.z.string().email(),
8
+ password: zod.z.string().min(6, {
9
+ message: "Password must be at least 6 characters long."
10
+ })
11
+ // name: z.string().min(1, {
12
+ // message: 'Name should be at least 1 character long.'
13
+ // })
14
+ });
15
+
16
+ exports.LoginSchema = LoginSchema;
17
+ //# sourceMappingURL=loginSchema.js.map
18
+ //# sourceMappingURL=loginSchema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../types/auth/loginSchema.ts"],"names":["z"],"mappings":";;;;;AAEO,IAAM,WAAA,GAAcA,MAAE,MAAA,CAAO;AAAA,EAClC,KAAA,EAAOA,KAAA,CAAE,MAAA,EAAO,CAAE,KAAA,EAAM;AAAA,EACxB,QAAA,EAAUA,KAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAA,EAAG;AAAA,IAC1B,OAAA,EAAS;AAAA,GACV;AAAA;AAAA;AAAA;AAIH,CAAC","file":"loginSchema.js","sourcesContent":["import { z } from \"zod\";\n\nexport const LoginSchema = z.object({\n email: z.string().email(),\n password: z.string().min(6, {\n message: 'Password must be at least 6 characters long.',\n }),\n // name: z.string().min(1, {\n // message: 'Name should be at least 1 character long.'\n // })\n})"]}
@@ -0,0 +1,16 @@
1
+ import '../../chunk-YN4436PK.mjs';
2
+ import { z } from 'zod';
3
+
4
+ var LoginSchema = z.object({
5
+ email: z.string().email(),
6
+ password: z.string().min(6, {
7
+ message: "Password must be at least 6 characters long."
8
+ })
9
+ // name: z.string().min(1, {
10
+ // message: 'Name should be at least 1 character long.'
11
+ // })
12
+ });
13
+
14
+ export { LoginSchema };
15
+ //# sourceMappingURL=loginSchema.mjs.map
16
+ //# sourceMappingURL=loginSchema.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../types/auth/loginSchema.ts"],"names":[],"mappings":";;;AAEO,IAAM,WAAA,GAAc,EAAE,MAAA,CAAO;AAAA,EAClC,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,KAAA,EAAM;AAAA,EACxB,QAAA,EAAU,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAA,EAAG;AAAA,IAC1B,OAAA,EAAS;AAAA,GACV;AAAA;AAAA;AAAA;AAIH,CAAC","file":"loginSchema.mjs","sourcesContent":["import { z } from \"zod\";\n\nexport const LoginSchema = z.object({\n email: z.string().email(),\n password: z.string().min(6, {\n message: 'Password must be at least 6 characters long.',\n }),\n // name: z.string().min(1, {\n // message: 'Name should be at least 1 character long.'\n // })\n})"]}
@@ -0,0 +1,9 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const SignupSchema: z.ZodObject<{
4
+ name: z.ZodString;
5
+ email: z.ZodString;
6
+ password: z.ZodString;
7
+ }, z.core.$strip>;
8
+
9
+ export { SignupSchema };
@@ -0,0 +1,9 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const SignupSchema: z.ZodObject<{
4
+ name: z.ZodString;
5
+ email: z.ZodString;
6
+ password: z.ZodString;
7
+ }, z.core.$strip>;
8
+
9
+ export { SignupSchema };
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+
5
+ // types/auth/signupSchema.ts
6
+ var SignupSchema = zod.z.object({
7
+ name: zod.z.string().min(1, {
8
+ message: "Name is required."
9
+ }),
10
+ email: zod.z.string().email(),
11
+ // Matches better-auth's default minPasswordLength (server/auth.ts doesn't
12
+ // override it) — was 6 here, which let the client accept passwords the
13
+ // server would then reject.
14
+ password: zod.z.string().min(8, {
15
+ message: "Password must be at least 8 characters long."
16
+ })
17
+ });
18
+
19
+ exports.SignupSchema = SignupSchema;
20
+ //# sourceMappingURL=signupSchema.js.map
21
+ //# sourceMappingURL=signupSchema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../types/auth/signupSchema.ts"],"names":["z"],"mappings":";;;;;AAEO,IAAM,YAAA,GAAeA,MAAE,MAAA,CAAO;AAAA,EACnC,IAAA,EAAMA,KAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAA,EAAG;AAAA,IACtB,OAAA,EAAS;AAAA,GACV,CAAA;AAAA,EACD,KAAA,EAAOA,KAAA,CAAE,MAAA,EAAO,CAAE,KAAA,EAAM;AAAA;AAAA;AAAA;AAAA,EAIxB,QAAA,EAAUA,KAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAA,EAAG;AAAA,IAC1B,OAAA,EAAS;AAAA,GACV;AACH,CAAC","file":"signupSchema.js","sourcesContent":["import { z } from \"zod\";\n\nexport const SignupSchema = z.object({\n name: z.string().min(1, {\n message: 'Name is required.',\n }),\n email: z.string().email(),\n // Matches better-auth's default minPasswordLength (server/auth.ts doesn't\n // override it) — was 6 here, which let the client accept passwords the\n // server would then reject.\n password: z.string().min(8, {\n message: 'Password must be at least 8 characters long.',\n }),\n})\n"]}
@@ -0,0 +1,19 @@
1
+ import '../../chunk-YN4436PK.mjs';
2
+ import { z } from 'zod';
3
+
4
+ var SignupSchema = z.object({
5
+ name: z.string().min(1, {
6
+ message: "Name is required."
7
+ }),
8
+ email: z.string().email(),
9
+ // Matches better-auth's default minPasswordLength (server/auth.ts doesn't
10
+ // override it) — was 6 here, which let the client accept passwords the
11
+ // server would then reject.
12
+ password: z.string().min(8, {
13
+ message: "Password must be at least 8 characters long."
14
+ })
15
+ });
16
+
17
+ export { SignupSchema };
18
+ //# sourceMappingURL=signupSchema.mjs.map
19
+ //# sourceMappingURL=signupSchema.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../types/auth/signupSchema.ts"],"names":[],"mappings":";;;AAEO,IAAM,YAAA,GAAe,EAAE,MAAA,CAAO;AAAA,EACnC,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAA,EAAG;AAAA,IACtB,OAAA,EAAS;AAAA,GACV,CAAA;AAAA,EACD,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,KAAA,EAAM;AAAA;AAAA;AAAA;AAAA,EAIxB,QAAA,EAAU,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAA,EAAG;AAAA,IAC1B,OAAA,EAAS;AAAA,GACV;AACH,CAAC","file":"signupSchema.mjs","sourcesContent":["import { z } from \"zod\";\n\nexport const SignupSchema = z.object({\n name: z.string().min(1, {\n message: 'Name is required.',\n }),\n email: z.string().email(),\n // Matches better-auth's default minPasswordLength (server/auth.ts doesn't\n // override it) — was 6 here, which let the client accept passwords the\n // server would then reject.\n password: z.string().min(8, {\n message: 'Password must be at least 8 characters long.',\n }),\n})\n"]}
@@ -0,0 +1,71 @@
1
+ declare function getUserById(id: string): Promise<{
2
+ id: string;
3
+ name: string;
4
+ email: string;
5
+ password: string | null;
6
+ emailVerified: boolean;
7
+ image: string | null;
8
+ role: "user" | "admin";
9
+ banned: boolean;
10
+ bannedAt: Date | null;
11
+ bannedReason: string | null;
12
+ banExpires: Date | null;
13
+ lastLoginAt: Date | null;
14
+ lastActiveAt: Date | null;
15
+ twoFactorEnabled: boolean;
16
+ createdAt: Date;
17
+ updatedAt: Date;
18
+ }>;
19
+ declare function getAdminEmails(): Promise<string[]>;
20
+ interface UserFilters {
21
+ search?: string;
22
+ page?: number;
23
+ limit?: number;
24
+ }
25
+ declare function getUsersFiltered({ search, page, limit }: UserFilters): Promise<{
26
+ users: {
27
+ id: string;
28
+ name: string;
29
+ email: string;
30
+ password: string | null;
31
+ emailVerified: boolean;
32
+ image: string | null;
33
+ role: "user" | "admin";
34
+ banned: boolean;
35
+ bannedAt: Date | null;
36
+ bannedReason: string | null;
37
+ banExpires: Date | null;
38
+ lastLoginAt: Date | null;
39
+ lastActiveAt: Date | null;
40
+ twoFactorEnabled: boolean;
41
+ createdAt: Date;
42
+ updatedAt: Date;
43
+ }[];
44
+ total: number;
45
+ page: number;
46
+ limit: number;
47
+ pages: number;
48
+ }>;
49
+ interface UserExportFilters {
50
+ search?: string;
51
+ }
52
+ declare function getUsersForExport({ search }: UserExportFilters): Promise<{
53
+ id: string;
54
+ name: string;
55
+ email: string;
56
+ password: string | null;
57
+ emailVerified: boolean;
58
+ image: string | null;
59
+ role: "user" | "admin";
60
+ banned: boolean;
61
+ bannedAt: Date | null;
62
+ bannedReason: string | null;
63
+ banExpires: Date | null;
64
+ lastLoginAt: Date | null;
65
+ lastActiveAt: Date | null;
66
+ twoFactorEnabled: boolean;
67
+ createdAt: Date;
68
+ updatedAt: Date;
69
+ }[]>;
70
+
71
+ export { type UserExportFilters, type UserFilters, getAdminEmails, getUserById, getUsersFiltered, getUsersForExport };