@xk2800/nextjs-template 0.8.0-beta.4 → 0.8.0-beta.6
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/components/dashboard/admin/feedbackTestForm.tsx +55 -0
- package/components/dashboard/admin/systemSettingsForm.tsx +28 -0
- package/components/dashboard/errorFeedback.tsx +65 -0
- package/components/dashboard/feedbackDialog.tsx +53 -11
- package/dist/activity/logger.js +12 -0
- package/dist/activity/logger.js.map +1 -1
- package/dist/activity/logger.mjs +3 -3
- package/dist/activity/queries.d.mts +1 -1
- package/dist/activity/queries.d.ts +1 -1
- package/dist/activity/queries.js +12 -0
- package/dist/activity/queries.js.map +1 -1
- package/dist/activity/queries.mjs +2 -2
- package/dist/admin/queries.js +12 -0
- package/dist/admin/queries.js.map +1 -1
- package/dist/admin/queries.mjs +2 -2
- package/dist/api/feedback.d.mts +15 -1
- package/dist/api/feedback.d.ts +15 -1
- package/dist/api/feedback.js +90 -3
- package/dist/api/feedback.js.map +1 -1
- package/dist/api/feedback.mjs +38 -9
- package/dist/api/feedback.mjs.map +1 -1
- package/dist/auth/helpers.js +13 -0
- package/dist/auth/helpers.js.map +1 -1
- package/dist/auth/helpers.mjs +5 -5
- package/dist/auth/index.js +13 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/index.mjs +5 -5
- package/dist/{chunk-IFZGEDMN.mjs → chunk-3MGEAGHU.mjs} +5 -4
- 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-G3B7NC5B.mjs → chunk-VQ5B4MA7.mjs} +4 -4
- package/dist/{chunk-G3B7NC5B.mjs.map → chunk-VQ5B4MA7.mjs.map} +1 -1
- package/dist/{chunk-LKPF3JCV.mjs → chunk-WZDAMCUX.mjs} +3 -3
- package/dist/{chunk-LKPF3JCV.mjs.map → chunk-WZDAMCUX.mjs.map} +1 -1
- package/dist/{chunk-GS33BWOL.mjs → chunk-XWVAJXAG.mjs} +6 -6
- package/dist/{chunk-GS33BWOL.mjs.map → chunk-XWVAJXAG.mjs.map} +1 -1
- package/dist/{chunk-YPKOQOTA.mjs → chunk-ZQCALTKB.mjs} +16 -4
- package/dist/chunk-ZQCALTKB.mjs.map +1 -0
- package/dist/db/index.d.mts +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +12 -0
- package/dist/db/index.js.map +1 -1
- package/dist/db/index.mjs +2 -2
- package/dist/db/schema.d.mts +1 -1
- package/dist/db/schema.d.ts +1 -1
- package/dist/db/schema.js +12 -0
- 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-CZnQXmhF.d.mts → schema-BNSBWNjK.d.mts} +95 -2
- package/dist/{schema-CZnQXmhF.d.ts → schema-BNSBWNjK.d.ts} +95 -2
- package/dist/sessions/queries.js +12 -0
- package/dist/sessions/queries.js.map +1 -1
- package/dist/sessions/queries.mjs +2 -2
- package/dist/settings/queries.d.mts +2 -2
- package/dist/settings/queries.d.ts +2 -2
- package/dist/settings/queries.js +13 -0
- package/dist/settings/queries.js.map +1 -1
- package/dist/settings/queries.mjs +3 -3
- package/dist/users/queries.js +12 -0
- package/dist/users/queries.js.map +1 -1
- package/dist/users/queries.mjs +2 -2
- package/package.json +9 -1
- package/dist/chunk-IFZGEDMN.mjs.map +0 -1
- package/dist/chunk-YPKOQOTA.mjs.map +0 -1
package/dist/api/feedback.js
CHANGED
|
@@ -20,6 +20,7 @@ var passkey = require('@better-auth/passkey');
|
|
|
20
20
|
var api = require('better-auth/api');
|
|
21
21
|
var uaParserJs = require('ua-parser-js');
|
|
22
22
|
var geoip = require('geoip-lite');
|
|
23
|
+
var crypto = require('crypto');
|
|
23
24
|
|
|
24
25
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
25
26
|
|
|
@@ -210,6 +211,7 @@ __export(schema_exports, {
|
|
|
210
211
|
activityLogs: () => activityLogs,
|
|
211
212
|
authThrottle: () => authThrottle,
|
|
212
213
|
deviceFingerprints: () => deviceFingerprints,
|
|
214
|
+
errorRefs: () => errorRefs,
|
|
213
215
|
passkeys: () => passkeys,
|
|
214
216
|
sessions: () => sessions,
|
|
215
217
|
systemSettings: () => systemSettings,
|
|
@@ -392,6 +394,9 @@ var systemSettings = pgCore.pgTable("system_settings", {
|
|
|
392
394
|
authEnableEmailPassword: pgCore.boolean("authEnableEmailPassword").notNull().default(true),
|
|
393
395
|
authEnableOneTap: pgCore.boolean("authEnableOneTap").notNull().default(false),
|
|
394
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),
|
|
395
400
|
updatedAt: pgCore.timestamp("updatedAt").notNull().defaultNow(),
|
|
396
401
|
// Who last changed a setting. This row doesn't "belong" to that admin the
|
|
397
402
|
// way sessions/activityLogs belong to their user, so deleting that admin's
|
|
@@ -400,6 +405,14 @@ var systemSettings = pgCore.pgTable("system_settings", {
|
|
|
400
405
|
}, (table) => [
|
|
401
406
|
pgCore.check("system_settings_singleton", drizzleOrm.sql`${table.id} = 'default'`)
|
|
402
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
|
+
]);
|
|
403
416
|
var envSchema = zod.z.object({
|
|
404
417
|
NODE_ENV: zod.z.enum(["development", "production", "test"]).default("development"),
|
|
405
418
|
PORT: zod.z.string().default("3000").transform(Number),
|
|
@@ -602,6 +615,7 @@ function fallbackSettings() {
|
|
|
602
615
|
authEnableEmailPassword: config.AUTH_ENABLE_EMAIL_PASSWORD,
|
|
603
616
|
authEnableOneTap: config.AUTH_ENABLE_ONE_TAP,
|
|
604
617
|
enableSessionRevocation: config.ENABLE_SESSION_REVOCATION,
|
|
618
|
+
feedbackAttachErrors: true,
|
|
605
619
|
updatedAt: /* @__PURE__ */ new Date(0),
|
|
606
620
|
updatedBy: null
|
|
607
621
|
};
|
|
@@ -1039,15 +1053,61 @@ var auth = new Proxy({}, {
|
|
|
1039
1053
|
return Reflect.getOwnPropertyDescriptor(getDefaultAuth(), prop);
|
|
1040
1054
|
}
|
|
1041
1055
|
});
|
|
1056
|
+
var REF_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
|
1057
|
+
var REF_PATTERN = /\(ref: ([2-9A-HJ-NP-Z]{6})\)/;
|
|
1058
|
+
var DETAIL_MAX_LENGTH = 8e3;
|
|
1059
|
+
var RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
1060
|
+
function describeError(error) {
|
|
1061
|
+
if (error instanceof Error) return error.stack || `${error.name}: ${error.message}`;
|
|
1062
|
+
try {
|
|
1063
|
+
return JSON.stringify(error, null, 2);
|
|
1064
|
+
} catch (e) {
|
|
1065
|
+
return String(error);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
function storeDetail(ref, message, error) {
|
|
1069
|
+
const save = async () => {
|
|
1070
|
+
try {
|
|
1071
|
+
await db.insert(errorRefs).values({
|
|
1072
|
+
ref,
|
|
1073
|
+
message,
|
|
1074
|
+
detail: describeError(error).slice(0, DETAIL_MAX_LENGTH)
|
|
1075
|
+
});
|
|
1076
|
+
await db.delete(errorRefs).where(drizzleOrm.lt(errorRefs.createdAt, new Date(Date.now() - RETENTION_MS)));
|
|
1077
|
+
} catch (storeError) {
|
|
1078
|
+
console.error(`[error ref ${ref}] could not store error detail`, storeError);
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
try {
|
|
1082
|
+
server.after(save);
|
|
1083
|
+
} catch (e) {
|
|
1084
|
+
void save();
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
async function lookupErrorDetail(text2) {
|
|
1088
|
+
var _a, _b;
|
|
1089
|
+
const ref = (_a = text2.match(REF_PATTERN)) == null ? void 0 : _a[1];
|
|
1090
|
+
if (!ref) return null;
|
|
1091
|
+
const [row] = await db.select({ detail: errorRefs.detail }).from(errorRefs).where(drizzleOrm.eq(errorRefs.ref, ref)).limit(1);
|
|
1092
|
+
return (_b = row == null ? void 0 : row.detail) != null ? _b : null;
|
|
1093
|
+
}
|
|
1094
|
+
function unexpectedErrorMessage(error, message = "Something went wrong. Please try again.") {
|
|
1095
|
+
const ref = Array.from(crypto.randomBytes(6), (b) => REF_ALPHABET[b % REF_ALPHABET.length]).join("");
|
|
1096
|
+
console.error(`[error ref ${ref}] ${message}`, error);
|
|
1097
|
+
storeDetail(ref, message, error);
|
|
1098
|
+
return `${message} (ref: ${ref})`;
|
|
1099
|
+
}
|
|
1042
1100
|
|
|
1043
1101
|
// lib/feedback-route.ts
|
|
1044
1102
|
var feedbackSchema = zod.z.object({
|
|
1045
1103
|
type: zod.z.enum(["bug", "feedback"]),
|
|
1046
1104
|
// Keep in sync with FEEDBACK_MAX_LENGTH in components/dashboard/feedbackDialog.tsx.
|
|
1047
1105
|
message: zod.z.string().trim().min(1).max(2e3),
|
|
1048
|
-
page: zod.z.string().max(300).optional()
|
|
1106
|
+
page: zod.z.string().max(300).optional(),
|
|
1107
|
+
// The error the user reported from (FeedbackDialog's errorDetails).
|
|
1108
|
+
error: zod.z.string().max(2e3).optional()
|
|
1049
1109
|
});
|
|
1050
|
-
var COOLDOWN_MS =
|
|
1110
|
+
var COOLDOWN_MS = 0;
|
|
1051
1111
|
var lastSentAt = /* @__PURE__ */ new Map();
|
|
1052
1112
|
async function POST(request) {
|
|
1053
1113
|
var _a;
|
|
@@ -1073,6 +1133,8 @@ async function POST(request) {
|
|
|
1073
1133
|
}
|
|
1074
1134
|
lastSentAt.set(session.user.id, now);
|
|
1075
1135
|
const { type, message, page } = parsed.data;
|
|
1136
|
+
const errorDetails = parsed.data.error && (await getSystemSettings()).feedbackAttachErrors ? parsed.data.error : void 0;
|
|
1137
|
+
const technicalDetail = errorDetails ? await lookupErrorDetail(errorDetails) : null;
|
|
1076
1138
|
const { user } = session;
|
|
1077
1139
|
const title = type === "bug" ? "\u{1F41E} Bug report" : "\u{1F4AC} Feedback";
|
|
1078
1140
|
const userAgent = request.headers.get("user-agent") || "unknown";
|
|
@@ -1093,6 +1155,8 @@ async function POST(request) {
|
|
|
1093
1155
|
fields: [
|
|
1094
1156
|
{ name: "User", value: `${user.name} (${user.email})`.slice(0, 1024) },
|
|
1095
1157
|
{ name: "Page", value: page || "unknown", inline: true },
|
|
1158
|
+
...errorDetails ? [{ name: "Error", value: errorDetails.slice(0, 1024) }] : [],
|
|
1159
|
+
...technicalDetail ? [{ name: "Technical details", value: "```\n" + technicalDetail.slice(0, 1e3) + "\n```" }] : [],
|
|
1096
1160
|
{ name: "User agent", value: userAgent.slice(0, 1024) }
|
|
1097
1161
|
],
|
|
1098
1162
|
timestamp: new Date(now).toISOString()
|
|
@@ -1114,7 +1178,11 @@ async function POST(request) {
|
|
|
1114
1178
|
|
|
1115
1179
|
---
|
|
1116
1180
|
User: ${user.name} <${user.email}>
|
|
1117
|
-
Page: ${page || "unknown"}
|
|
1181
|
+
Page: ${page || "unknown"}${errorDetails ? `
|
|
1182
|
+
Error: ${errorDetails}` : ""}${technicalDetail ? `
|
|
1183
|
+
|
|
1184
|
+
Technical details:
|
|
1185
|
+
${technicalDetail}` : ""}
|
|
1118
1186
|
User agent: ${userAgent}`
|
|
1119
1187
|
})).then(({ error }) => {
|
|
1120
1188
|
if (error) throw new Error(error.message);
|
|
@@ -1131,7 +1199,26 @@ User agent: ${userAgent}`
|
|
|
1131
1199
|
}
|
|
1132
1200
|
return server.NextResponse.json({ success: true });
|
|
1133
1201
|
}
|
|
1202
|
+
async function testErrorPOST(request) {
|
|
1203
|
+
if (process.env.NODE_ENV !== "development") {
|
|
1204
|
+
return server.NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
1205
|
+
}
|
|
1206
|
+
const session = await auth.api.getSession({ headers: await headers.headers() });
|
|
1207
|
+
if (!(session == null ? void 0 : session.user)) {
|
|
1208
|
+
return server.NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
1209
|
+
}
|
|
1210
|
+
const body = await request.json().catch(() => null);
|
|
1211
|
+
if (typeof (body == null ? void 0 : body.text) === "string" && body.text.trim() === "!error") {
|
|
1212
|
+
return server.NextResponse.json({ error: "Test error from the feedback test form" }, { status: 400 });
|
|
1213
|
+
}
|
|
1214
|
+
if (typeof (body == null ? void 0 : body.text) === "string" && body.text.trim() === "!crash") {
|
|
1215
|
+
const crash = new Error("Test crash: DB connection to postgres://admin:hunter2@db.internal failed");
|
|
1216
|
+
return server.NextResponse.json({ error: unexpectedErrorMessage(crash) }, { status: 500 });
|
|
1217
|
+
}
|
|
1218
|
+
return server.NextResponse.json({ success: true });
|
|
1219
|
+
}
|
|
1134
1220
|
|
|
1135
1221
|
exports.POST = POST;
|
|
1222
|
+
exports.testErrorPOST = testErrorPOST;
|
|
1136
1223
|
//# sourceMappingURL=feedback.js.map
|
|
1137
1224
|
//# sourceMappingURL=feedback.js.map
|