@xk2800/nextjs-template 0.6.1-beta.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -321
- package/components/dashboard/admin/adminUsersTable.tsx +36 -2
- package/components/dashboard/admin/feedbackTestForm.tsx +55 -0
- package/components/dashboard/admin/systemSettingsForm.tsx +28 -0
- package/components/dashboard/appSidebar.tsx +4 -4
- package/components/dashboard/deleteAccountCard.tsx +137 -0
- package/components/dashboard/errorFeedback.tsx +65 -0
- package/components/dashboard/feedbackDialog.tsx +160 -0
- package/components/dashboard/installAppCard.tsx +187 -0
- package/components/site/cookie-banner.tsx +57 -0
- package/components/ui/button.tsx +1 -1
- package/components/ui/file-upload.tsx +178 -0
- package/components/ui/input.tsx +1 -1
- package/components/ui/separator.tsx +1 -1
- package/components/ui/sheet.tsx +1 -1
- package/components/ui/sidebar.tsx +8 -8
- package/components/ui/skeleton.tsx +1 -1
- package/components/ui/tooltip.tsx +1 -1
- package/dist/activity/logger.js +21 -1
- package/dist/activity/logger.js.map +1 -1
- package/dist/activity/logger.mjs +4 -4
- package/dist/activity/queries.d.mts +1 -1
- package/dist/activity/queries.d.ts +1 -1
- package/dist/activity/queries.js +21 -1
- package/dist/activity/queries.js.map +1 -1
- package/dist/activity/queries.mjs +3 -3
- package/dist/admin/queries.js +21 -1
- package/dist/admin/queries.js.map +1 -1
- package/dist/admin/queries.mjs +3 -3
- package/dist/api/feedback.d.mts +23 -0
- package/dist/api/feedback.d.ts +23 -0
- package/dist/api/feedback.js +1224 -0
- package/dist/api/feedback.js.map +1 -0
- package/dist/api/feedback.mjs +136 -0
- package/dist/api/feedback.mjs.map +1 -0
- package/dist/auth/helpers.d.mts +3 -3
- package/dist/auth/helpers.d.ts +3 -3
- package/dist/auth/helpers.js +45 -4
- package/dist/auth/helpers.js.map +1 -1
- package/dist/auth/helpers.mjs +7 -7
- package/dist/auth/helpers.mjs.map +1 -1
- package/dist/auth/index.d.mts +8 -2
- package/dist/auth/index.d.ts +8 -2
- package/dist/auth/index.js +44 -3
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/index.mjs +6 -6
- package/dist/auth-client/index.d.mts +16 -16
- package/dist/auth-client/index.d.ts +16 -16
- package/dist/{chunk-IF3JZRSZ.mjs → chunk-3MGEAGHU.mjs} +6 -5
- package/dist/chunk-3MGEAGHU.mjs.map +1 -0
- package/dist/chunk-KAEA35OC.mjs +76 -0
- package/dist/chunk-KAEA35OC.mjs.map +1 -0
- package/dist/{chunk-GTPTVNY2.mjs → chunk-TMJYAOC2.mjs} +10 -2
- package/dist/chunk-TMJYAOC2.mjs.map +1 -0
- package/dist/{chunk-OHAUKE2Q.mjs → chunk-VQ5B4MA7.mjs} +4 -4
- package/dist/{chunk-OHAUKE2Q.mjs.map → chunk-VQ5B4MA7.mjs.map} +1 -1
- package/dist/{chunk-5PYDAD4P.mjs → chunk-WZDAMCUX.mjs} +4 -4
- package/dist/{chunk-5PYDAD4P.mjs.map → chunk-WZDAMCUX.mjs.map} +1 -1
- package/dist/{chunk-V72CFFU2.mjs → chunk-XWVAJXAG.mjs} +31 -11
- package/dist/chunk-XWVAJXAG.mjs.map +1 -0
- package/dist/{chunk-4B7MJOLH.mjs → chunk-ZQCALTKB.mjs} +17 -5
- package/dist/chunk-ZQCALTKB.mjs.map +1 -0
- package/dist/config/env.d.mts +3 -0
- package/dist/config/env.d.ts +3 -0
- package/dist/config/env.js +8 -0
- package/dist/config/env.js.map +1 -1
- package/dist/config/env.mjs +1 -1
- package/dist/db/index.d.mts +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +21 -1
- package/dist/db/index.js.map +1 -1
- package/dist/db/index.mjs +3 -3
- package/dist/db/schema.d.mts +1 -1
- package/dist/db/schema.d.ts +1 -1
- package/dist/db/schema.js +13 -1
- package/dist/db/schema.js.map +1 -1
- package/dist/db/schema.mjs +1 -1
- package/dist/errors.d.mts +29 -0
- package/dist/errors.d.ts +29 -0
- package/dist/errors.js +365 -0
- package/dist/errors.js.map +1 -0
- package/dist/errors.mjs +7 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/{schema-BghJ8N4X.d.mts → schema-BNSBWNjK.d.mts} +99 -6
- package/dist/{schema-BghJ8N4X.d.ts → schema-BNSBWNjK.d.ts} +99 -6
- package/dist/sessions/queries.js +21 -1
- package/dist/sessions/queries.js.map +1 -1
- package/dist/sessions/queries.mjs +3 -3
- package/dist/settings/queries.d.mts +2 -2
- package/dist/settings/queries.d.ts +2 -2
- package/dist/settings/queries.js +22 -1
- package/dist/settings/queries.js.map +1 -1
- package/dist/settings/queries.mjs +4 -4
- package/dist/users/queries.d.mts +3 -3
- package/dist/users/queries.d.ts +3 -3
- package/dist/users/queries.js +21 -1
- package/dist/users/queries.js.map +1 -1
- package/dist/users/queries.mjs +3 -3
- package/hooks/use-mobile.ts +19 -0
- package/lib/device-fingerprint.ts +2 -3
- package/package.json +35 -32
- package/dist/chunk-4B7MJOLH.mjs.map +0 -1
- package/dist/chunk-GTPTVNY2.mjs.map +0 -1
- package/dist/chunk-IF3JZRSZ.mjs.map +0 -1
- package/dist/chunk-V72CFFU2.mjs.map +0 -1
package/dist/auth/helpers.js
CHANGED
|
@@ -211,6 +211,7 @@ __export(schema_exports, {
|
|
|
211
211
|
activityLogs: () => activityLogs,
|
|
212
212
|
authThrottle: () => authThrottle,
|
|
213
213
|
deviceFingerprints: () => deviceFingerprints,
|
|
214
|
+
errorRefs: () => errorRefs,
|
|
214
215
|
passkeys: () => passkeys,
|
|
215
216
|
sessions: () => sessions,
|
|
216
217
|
systemSettings: () => systemSettings,
|
|
@@ -230,7 +231,7 @@ var DEFAULT_MAINTENANCE_EXEMPT_PATHS = [
|
|
|
230
231
|
];
|
|
231
232
|
|
|
232
233
|
// server/db/schema.ts
|
|
233
|
-
var RoleEnum = pgCore.pgEnum("roles", ["user", "admin"]);
|
|
234
|
+
var RoleEnum = pgCore.pgEnum("roles", ["user", "admin", "tester"]);
|
|
234
235
|
var ActivityActionEnum = pgCore.pgEnum("activity_actions", [
|
|
235
236
|
"login",
|
|
236
237
|
"logout",
|
|
@@ -393,6 +394,9 @@ var systemSettings = pgCore.pgTable("system_settings", {
|
|
|
393
394
|
authEnableEmailPassword: pgCore.boolean("authEnableEmailPassword").notNull().default(true),
|
|
394
395
|
authEnableOneTap: pgCore.boolean("authEnableOneTap").notNull().default(false),
|
|
395
396
|
enableSessionRevocation: pgCore.boolean("enableSessionRevocation").notNull().default(true),
|
|
397
|
+
// Attach the error a user hit to feedback they send from it (see
|
|
398
|
+
// lib/feedback-route.ts). Off = only what the user typed is sent.
|
|
399
|
+
feedbackAttachErrors: pgCore.boolean("feedbackAttachErrors").notNull().default(true),
|
|
396
400
|
updatedAt: pgCore.timestamp("updatedAt").notNull().defaultNow(),
|
|
397
401
|
// Who last changed a setting. This row doesn't "belong" to that admin the
|
|
398
402
|
// way sessions/activityLogs belong to their user, so deleting that admin's
|
|
@@ -401,15 +405,31 @@ var systemSettings = pgCore.pgTable("system_settings", {
|
|
|
401
405
|
}, (table) => [
|
|
402
406
|
pgCore.check("system_settings_singleton", drizzleOrm.sql`${table.id} = 'default'`)
|
|
403
407
|
]);
|
|
408
|
+
var errorRefs = pgCore.pgTable("error_refs", {
|
|
409
|
+
ref: pgCore.text("ref").primaryKey(),
|
|
410
|
+
message: pgCore.text("message").notNull(),
|
|
411
|
+
detail: pgCore.text("detail").notNull(),
|
|
412
|
+
createdAt: pgCore.timestamp("createdAt").notNull().defaultNow()
|
|
413
|
+
}, (table) => [
|
|
414
|
+
pgCore.index("error_refs_created_at_idx").on(table.createdAt)
|
|
415
|
+
]);
|
|
404
416
|
var envSchema = zod.z.object({
|
|
405
417
|
NODE_ENV: zod.z.enum(["development", "production", "test"]).default("development"),
|
|
406
418
|
PORT: zod.z.string().default("3000").transform(Number),
|
|
407
419
|
DATABASE_URL: zod.z.string().default(""),
|
|
408
420
|
RESEND_API_KEY: zod.z.string().default(""),
|
|
421
|
+
// Sender for every transactional email. Must be on a domain verified in
|
|
422
|
+
// your Resend account.
|
|
423
|
+
EMAIL_FROM: zod.z.string().default("Acme <onboarding@xavierkhew.xyz>"),
|
|
409
424
|
// Display name for this deployment — shown as the authenticator-app label
|
|
410
425
|
// (2FA issuer) and the passkey relying-party name. Consuming projects set
|
|
411
426
|
// this in their own .env; defaults to the template name.
|
|
412
427
|
APP_NAME: zod.z.string().default("Next.js Template"),
|
|
428
|
+
// Where the dashboard feedback form delivers (app/api/feedback). Set either
|
|
429
|
+
// or both; with neither, the Feedback button is hidden. FEEDBACK_EMAIL takes
|
|
430
|
+
// a comma-separated list and sends through Resend (needs RESEND_API_KEY).
|
|
431
|
+
FEEDBACK_DISCORD_WEBHOOK_URL: zod.z.string().default(""),
|
|
432
|
+
FEEDBACK_EMAIL: zod.z.string().default(""),
|
|
413
433
|
// Seed-only: the live value lives in the system_settings DB row (see
|
|
414
434
|
// lib/settings-queries.ts) once it's been seeded on first read, and is
|
|
415
435
|
// changed from the admin System Settings page, not by editing this var.
|
|
@@ -595,6 +615,7 @@ function fallbackSettings() {
|
|
|
595
615
|
authEnableEmailPassword: config.AUTH_ENABLE_EMAIL_PASSWORD,
|
|
596
616
|
authEnableOneTap: config.AUTH_ENABLE_ONE_TAP,
|
|
597
617
|
enableSessionRevocation: config.ENABLE_SESSION_REVOCATION,
|
|
618
|
+
feedbackAttachErrors: true,
|
|
598
619
|
updatedAt: /* @__PURE__ */ new Date(0),
|
|
599
620
|
updatedBy: null
|
|
600
621
|
};
|
|
@@ -635,7 +656,7 @@ async function getEffectiveAuthFlags() {
|
|
|
635
656
|
oneTap: config.AUTH_ENABLE_ONE_TAP && settings.authEnableOneTap
|
|
636
657
|
};
|
|
637
658
|
}
|
|
638
|
-
var EMAIL_FROM =
|
|
659
|
+
var EMAIL_FROM = config.EMAIL_FROM;
|
|
639
660
|
var resend;
|
|
640
661
|
async function getResend() {
|
|
641
662
|
if (!resend) {
|
|
@@ -787,7 +808,13 @@ function createAuth(overrides = {}) {
|
|
|
787
808
|
input: false
|
|
788
809
|
// Prevent users from setting their own role
|
|
789
810
|
}
|
|
790
|
-
}
|
|
811
|
+
},
|
|
812
|
+
// Self-service "Delete account" (dashboard Settings → Danger zone).
|
|
813
|
+
// Credential users confirm with their password; OAuth-only users have
|
|
814
|
+
// no password, so better-auth instead requires a session younger than
|
|
815
|
+
// `freshAge` (default 1 day). Every user-owned table cascades on delete.
|
|
816
|
+
// Impersonation and last-admin guards live in hooks.before below.
|
|
817
|
+
deleteUser: { enabled: true }
|
|
791
818
|
},
|
|
792
819
|
// Advanced options
|
|
793
820
|
advanced: {
|
|
@@ -891,6 +918,20 @@ function createAuth(overrides = {}) {
|
|
|
891
918
|
}
|
|
892
919
|
return;
|
|
893
920
|
}
|
|
921
|
+
if (ctx.path === "/delete-user") {
|
|
922
|
+
const current2 = await api.getSessionFromCtx(ctx, { disableCookieCache: true });
|
|
923
|
+
if (!current2) return;
|
|
924
|
+
if (current2.session.impersonatedBy) {
|
|
925
|
+
throw new api.APIError("FORBIDDEN", { message: "Stop impersonating before deleting an account." });
|
|
926
|
+
}
|
|
927
|
+
if (current2.user.role === "admin") {
|
|
928
|
+
const [{ count }] = await db.select({ count: drizzleOrm.sql`count(*)` }).from(users).where(drizzleOrm.eq(users.role, "admin"));
|
|
929
|
+
if (Number(count) <= 1) {
|
|
930
|
+
throw new api.APIError("FORBIDDEN", { message: "You're the only admin. Promote another user to admin first." });
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
894
935
|
if (ctx.path !== "/admin/stop-impersonating") return;
|
|
895
936
|
const current = await api.getSessionFromCtx(ctx, { disableCookieCache: true });
|
|
896
937
|
if (current == null ? void 0 : current.session.impersonatedBy) {
|
|
@@ -1038,7 +1079,7 @@ function hasRole(userRole, requiredRole) {
|
|
|
1038
1079
|
if (requiredRole === "admin") {
|
|
1039
1080
|
return userRole === "admin";
|
|
1040
1081
|
}
|
|
1041
|
-
return userRole === "user" || userRole === "admin";
|
|
1082
|
+
return userRole === "user" || userRole === "admin" || userRole === "tester";
|
|
1042
1083
|
}
|
|
1043
1084
|
async function requireRole(requiredRole) {
|
|
1044
1085
|
const session = await requireAuth();
|