···
872
-
const sessionId = getSessionFromRequest(req);
874
-
return Response.json({ error: "Not authenticated" }, { status: 401 });
876
-
const user = getUserBySession(sessionId);
878
-
return Response.json({ error: "Invalid session" }, { status: 401 });
873
+
const user = requireAuth(req);
881
-
// Check subscription status
882
-
const subscription = db
883
-
.query<{ status: string }, [number]>(
884
-
"SELECT status FROM subscriptions WHERE user_id = ? AND status IN ('active', 'trialing', 'past_due') ORDER BY created_at DESC LIMIT 1",
875
+
// Check subscription status
876
+
const subscription = db
877
+
.query<{ status: string }, [number]>(
878
+
"SELECT status FROM subscriptions WHERE user_id = ? AND status IN ('active', 'trialing', 'past_due') ORDER BY created_at DESC LIMIT 1",
888
-
// Get notification preferences
890
-
.query<{ email_notifications_enabled: number }, [number]>(
891
-
"SELECT email_notifications_enabled FROM users WHERE id = ?",
882
+
// Get notification preferences
884
+
.query<{ email_notifications_enabled: number }, [number]>(
885
+
"SELECT email_notifications_enabled FROM users WHERE id = ?",
895
-
return Response.json({
898
-
avatar: user.avatar,
899
-
created_at: user.created_at,
901
-
has_subscription: !!subscription,
902
-
email_verified: isEmailVerified(user.id),
903
-
email_notifications_enabled: prefs?.email_notifications_enabled === 1,
889
+
return Response.json({
892
+
avatar: user.avatar,
893
+
created_at: user.created_at,
895
+
has_subscription: !!subscription,
896
+
email_verified: isEmailVerified(user.id),
897
+
email_notifications_enabled: prefs?.email_notifications_enabled === 1,
900
+
return handleError(err);
"/api/passkeys/register/options": {
···
1094
-
const sessionId = getSessionFromRequest(req);
1096
-
return Response.json({ error: "Not authenticated" }, { status: 401 });
1098
-
const user = getUserBySession(sessionId);
1100
-
return Response.json({ error: "Invalid session" }, { status: 401 });
1092
+
const sessionId = getSessionFromRequest(req);
1094
+
return Response.json({ error: "Not authenticated" }, { status: 401 });
1096
+
const user = getUserBySession(sessionId);
1098
+
return Response.json({ error: "Invalid session" }, { status: 401 });
1100
+
const sessions = getUserSessionsForUser(user.id);
1101
+
return Response.json({
1102
+
sessions: sessions.map((s) => ({
1104
+
ip_address: s.ip_address,
1105
+
user_agent: s.user_agent,
1106
+
created_at: s.created_at,
1107
+
expires_at: s.expires_at,
1108
+
is_current: s.id === sessionId,
1112
+
return handleError(err);
1102
-
const sessions = getUserSessionsForUser(user.id);
1103
-
return Response.json({
1104
-
sessions: sessions.map((s) => ({
1106
-
ip_address: s.ip_address,
1107
-
user_agent: s.user_agent,
1108
-
created_at: s.created_at,
1109
-
expires_at: s.expires_at,
1110
-
is_current: s.id === sessionId,
1115
-
const currentSessionId = getSessionFromRequest(req);
1116
-
if (!currentSessionId) {
1117
-
return Response.json({ error: "Not authenticated" }, { status: 401 });
1119
-
const user = getUserBySession(currentSessionId);
1121
-
return Response.json({ error: "Invalid session" }, { status: 401 });
1117
+
const currentSessionId = getSessionFromRequest(req);
1118
+
if (!currentSessionId) {
1119
+
return Response.json({ error: "Not authenticated" }, { status: 401 });
1121
+
const user = getUserBySession(currentSessionId);
1123
+
return Response.json({ error: "Invalid session" }, { status: 401 });
1124
-
const rateLimitError = enforceRateLimit(req, "delete-session", {
1125
-
ip: { max: 20, windowSeconds: 60 * 60 },
1127
-
if (rateLimitError) return rateLimitError;
1126
+
const rateLimitError = enforceRateLimit(req, "delete-session", {
1127
+
ip: { max: 20, windowSeconds: 60 * 60 },
1129
+
if (rateLimitError) return rateLimitError;
1129
-
const body = await req.json();
1130
-
const targetSessionId = body.sessionId;
1131
-
if (!targetSessionId) {
1132
-
return Response.json(
1133
-
{ error: "Session ID required" },
1137
-
// Prevent deleting current session
1138
-
if (targetSessionId === currentSessionId) {
1139
-
return Response.json(
1140
-
{ error: "Cannot kill current session. Use logout instead." },
1144
-
// Verify the session belongs to the user
1145
-
const targetSession = getSession(targetSessionId);
1146
-
if (!targetSession || targetSession.user_id !== user.id) {
1147
-
return Response.json({ error: "Session not found" }, { status: 404 });
1131
+
const body = await req.json();
1132
+
const targetSessionId = body.sessionId;
1133
+
if (!targetSessionId) {
1134
+
return Response.json(
1135
+
{ error: "Session ID required" },
1139
+
// Prevent deleting current session
1140
+
if (targetSessionId === currentSessionId) {
1141
+
return Response.json(
1142
+
{ error: "Cannot kill current session. Use logout instead." },
1146
+
// Verify the session belongs to the user
1147
+
const targetSession = getSession(targetSessionId);
1148
+
if (!targetSession || targetSession.user_id !== user.id) {
1149
+
return Response.json({ error: "Session not found" }, { status: 404 });
1151
+
deleteSession(targetSessionId);
1152
+
return Response.json({ success: true });
1154
+
return handleError(err);
1149
-
deleteSession(targetSessionId);
1150
-
return Response.json({ success: true });
1155
-
const sessionId = getSessionFromRequest(req);
1157
-
return Response.json({ error: "Not authenticated" }, { status: 401 });
1159
-
const user = getUserBySession(sessionId);
1161
-
return Response.json({ error: "Invalid session" }, { status: 401 });
1161
+
const user = requireAuth(req);
1165
-
const rateLimitError = enforceRateLimit(req, "delete-user", {
1166
-
ip: { max: 3, windowSeconds: 60 * 60 },
1168
-
if (rateLimitError) return rateLimitError;
1164
+
const rateLimitError = enforceRateLimit(req, "delete-user", {
1165
+
ip: { max: 3, windowSeconds: 60 * 60 },
1167
+
if (rateLimitError) return rateLimitError;
1170
-
await deleteUser(user.id);
1171
-
return Response.json(
1172
-
{ success: true },
1176
-
"session=; HttpOnly; Secure; Path=/; Max-Age=0; SameSite=Lax",
1169
+
await deleteUser(user.id);
1170
+
return Response.json(
1171
+
{ success: true },
1175
+
"session=; HttpOnly; Secure; Path=/; Max-Age=0; SameSite=Lax",
1180
+
return handleError(err);
1184
-
const sessionId = getSessionFromRequest(req);
1186
-
return Response.json({ error: "Not authenticated" }, { status: 401 });
1188
-
const user = getUserBySession(sessionId);
1190
-
return Response.json({ error: "Invalid session" }, { status: 401 });
1187
+
const user = requireAuth(req);
1194
-
const rateLimitError = enforceRateLimit(req, "update-email", {
1195
-
ip: { max: 5, windowSeconds: 60 * 60 },
1197
-
if (rateLimitError) return rateLimitError;
1190
+
const rateLimitError = enforceRateLimit(req, "update-email", {
1191
+
ip: { max: 5, windowSeconds: 60 * 60 },
1193
+
if (rateLimitError) return rateLimitError;
1199
-
const body = await req.json();
1200
-
const { email } = body;
1202
-
return Response.json({ error: "Email required" }, { status: 400 });
1195
+
const body = await req.json();
1196
+
const { email } = body;
1198
+
return Response.json({ error: "Email required" }, { status: 400 });
1205
-
// Check if email is already in use
1206
-
const existingUser = getUserByEmail(email);
1207
-
if (existingUser) {
1208
-
return Response.json(
1209
-
{ error: "Email already in use" },
1201
+
// Check if email is already in use
1202
+
const existingUser = getUserByEmail(email);
1203
+
if (existingUser) {
1204
+
return Response.json(
1205
+
{ error: "Email already in use" },
1215
-
// Create email change token
1216
-
const token = createEmailChangeToken(user.id, email);
1211
+
// Create email change token
1212
+
const token = createEmailChangeToken(user.id, email);
1218
-
// Send verification email to the CURRENT address
1219
-
const origin = process.env.ORIGIN || "http://localhost:3000";
1220
-
const verifyUrl = `${origin}/api/user/email/verify?token=${token}`;
1214
+
// Send verification email to the CURRENT address
1215
+
const origin = process.env.ORIGIN || "http://localhost:3000";
1216
+
const verifyUrl = `${origin}/api/user/email/verify?token=${token}`;
1224
-
subject: "Verify your email change",
1225
-
html: emailChangeTemplate({
1227
-
currentEmail: user.email,
1229
-
verifyLink: verifyUrl,
1220
+
subject: "Verify your email change",
1221
+
html: emailChangeTemplate({
1223
+
currentEmail: user.email,
1225
+
verifyLink: verifyUrl,
1233
-
return Response.json({
1235
-
message: `Verification email sent to ${user.email}`,
1236
-
pendingEmail: email,
1240
-
"[Email] Failed to send email change verification:",
1243
-
return Response.json(
1244
-
{ error: "Failed to send verification email" },
1229
+
return Response.json({
1231
+
message: `Verification email sent to ${user.email}`,
1232
+
pendingEmail: email,
1236
+
"[Email] Failed to send email change verification:",
1239
+
return Response.json(
1240
+
{ error: "Failed to send verification email" },
1245
+
return handleError(err);
···
1294
-
const sessionId = getSessionFromRequest(req);
1296
-
return Response.json({ error: "Not authenticated" }, { status: 401 });
1298
-
const user = getUserBySession(sessionId);
1300
-
return Response.json({ error: "Invalid session" }, { status: 401 });
1294
+
const user = requireAuth(req);
1304
-
const rateLimitError = enforceRateLimit(req, "update-password", {
1305
-
ip: { max: 5, windowSeconds: 60 * 60 },
1307
-
if (rateLimitError) return rateLimitError;
1297
+
const rateLimitError = enforceRateLimit(req, "update-password", {
1298
+
ip: { max: 5, windowSeconds: 60 * 60 },
1300
+
if (rateLimitError) return rateLimitError;
1309
-
const body = await req.json();
1310
-
const { password } = body;
1312
-
return Response.json({ error: "Password required" }, { status: 400 });
1314
-
// Validate password format (client-side hashed PBKDF2)
1315
-
const passwordValidation = validatePasswordHash(password);
1316
-
if (!passwordValidation.valid) {
1317
-
return Response.json(
1318
-
{ error: passwordValidation.error },
1323
-
await updateUserPassword(user.id, password);
1324
-
return Response.json({ success: true });
1326
-
return Response.json(
1327
-
{ error: "Failed to update password" },
1302
+
const body = await req.json();
1303
+
const { password } = body;
1305
+
return Response.json({ error: "Password required" }, { status: 400 });
1307
+
// Validate password format (client-side hashed PBKDF2)
1308
+
const passwordValidation = validatePasswordHash(password);
1309
+
if (!passwordValidation.valid) {
1310
+
return Response.json(
1311
+
{ error: passwordValidation.error },
1316
+
await updateUserPassword(user.id, password);
1317
+
return Response.json({ success: true });
1319
+
return Response.json(
1320
+
{ error: "Failed to update password" },
1325
+
return handleError(err);
1335
-
const sessionId = getSessionFromRequest(req);
1337
-
return Response.json({ error: "Not authenticated" }, { status: 401 });
1339
-
const user = getUserBySession(sessionId);
1341
-
return Response.json({ error: "Invalid session" }, { status: 401 });
1332
+
const user = requireAuth(req);
1344
-
const rateLimitError = enforceRateLimit(req, "update-name", {
1345
-
ip: { max: 10, windowSeconds: 5 * 60 },
1347
-
if (rateLimitError) return rateLimitError;
1334
+
const rateLimitError = enforceRateLimit(req, "update-name", {
1335
+
ip: { max: 10, windowSeconds: 5 * 60 },
1337
+
if (rateLimitError) return rateLimitError;
1349
-
const body = await req.json();
1350
-
const { name } = body;
1352
-
return Response.json({ error: "Name required" }, { status: 400 });
1355
-
updateUserName(user.id, name);
1356
-
return Response.json({ success: true });
1358
-
return Response.json(
1359
-
{ error: "Failed to update name" },
1339
+
const body = await req.json();
1340
+
const { name } = body;
1342
+
return Response.json({ error: "Name required" }, { status: 400 });
1345
+
updateUserName(user.id, name);
1346
+
return Response.json({ success: true });
1348
+
return Response.json(
1349
+
{ error: "Failed to update name" },
1354
+
return handleError(err);
1367
-
const sessionId = getSessionFromRequest(req);
1369
-
return Response.json({ error: "Not authenticated" }, { status: 401 });
1371
-
const user = getUserBySession(sessionId);
1373
-
return Response.json({ error: "Invalid session" }, { status: 401 });
1361
+
const user = requireAuth(req);
1376
-
const rateLimitError = enforceRateLimit(req, "update-avatar", {
1377
-
ip: { max: 10, windowSeconds: 5 * 60 },
1379
-
if (rateLimitError) return rateLimitError;
1363
+
const rateLimitError = enforceRateLimit(req, "update-avatar", {
1364
+
ip: { max: 10, windowSeconds: 5 * 60 },
1366
+
if (rateLimitError) return rateLimitError;
1381
-
const body = await req.json();
1382
-
const { avatar } = body;
1384
-
return Response.json({ error: "Avatar required" }, { status: 400 });
1387
-
updateUserAvatar(user.id, avatar);
1388
-
return Response.json({ success: true });
1390
-
return Response.json(
1391
-
{ error: "Failed to update avatar" },
1368
+
const body = await req.json();
1369
+
const { avatar } = body;
1371
+
return Response.json({ error: "Avatar required" }, { status: 400 });
1374
+
updateUserAvatar(user.id, avatar);
1375
+
return Response.json({ success: true });
1377
+
return Response.json(
1378
+
{ error: "Failed to update avatar" },
1383
+
return handleError(err);
"/api/user/notifications": {
1399
-
const sessionId = getSessionFromRequest(req);
1401
-
return Response.json({ error: "Not authenticated" }, { status: 401 });
1403
-
const user = getUserBySession(sessionId);
1405
-
return Response.json({ error: "Invalid session" }, { status: 401 });
1390
+
const user = requireAuth(req);
1408
-
const rateLimitError = enforceRateLimit(req, "update-notifications", {
1409
-
ip: { max: 10, windowSeconds: 5 * 60 },
1411
-
if (rateLimitError) return rateLimitError;
1392
+
const rateLimitError = enforceRateLimit(req, "update-notifications", {
1393
+
ip: { max: 10, windowSeconds: 5 * 60 },
1395
+
if (rateLimitError) return rateLimitError;
1413
-
const body = await req.json();
1414
-
const { email_notifications_enabled } = body;
1415
-
if (typeof email_notifications_enabled !== "boolean") {
1416
-
return Response.json(
1417
-
{ error: "email_notifications_enabled must be a boolean" },
1423
-
"UPDATE users SET email_notifications_enabled = ? WHERE id = ?",
1424
-
[email_notifications_enabled ? 1 : 0, user.id],
1426
-
return Response.json({ success: true });
1428
-
return Response.json(
1429
-
{ error: "Failed to update notification settings" },
1397
+
const body = await req.json();
1398
+
const { email_notifications_enabled } = body;
1399
+
if (typeof email_notifications_enabled !== "boolean") {
1400
+
return Response.json(
1401
+
{ error: "email_notifications_enabled must be a boolean" },
1407
+
"UPDATE users SET email_notifications_enabled = ? WHERE id = ?",
1408
+
[email_notifications_enabled ? 1 : 0, user.id],
1410
+
return Response.json({ success: true });
1412
+
return Response.json(
1413
+
{ error: "Failed to update notification settings" },
1418
+
return handleError(err);
"/api/billing/checkout": {
1437
-
const sessionId = getSessionFromRequest(req);
1439
-
return Response.json({ error: "Not authenticated" }, { status: 401 });
1441
-
const user = getUserBySession(sessionId);
1443
-
return Response.json({ error: "Invalid session" }, { status: 401 });
1425
+
const user = requireAuth(req);
const { polar } = await import("./lib/polar");
···
return Response.json({ url: checkout.url });
1466
-
console.error("Failed to create checkout:", error);
1467
-
return Response.json(
1468
-
{ error: "Failed to create checkout session" },
1446
+
return handleError(err);
"/api/billing/subscription": {
1476
-
const sessionId = getSessionFromRequest(req);
1478
-
return Response.json({ error: "Not authenticated" }, { status: 401 });
1480
-
const user = getUserBySession(sessionId);
1482
-
return Response.json({ error: "Invalid session" }, { status: 401 });
1453
+
const user = requireAuth(req);
// Get subscription from database
···
return Response.json({ subscription });
1509
-
console.error("Failed to fetch subscription:", error);
1510
-
return Response.json(
1511
-
{ error: "Failed to fetch subscription" },
1478
+
return handleError(err);
1519
-
const sessionId = getSessionFromRequest(req);
1521
-
return Response.json({ error: "Not authenticated" }, { status: 401 });
1523
-
const user = getUserBySession(sessionId);
1525
-
return Response.json({ error: "Invalid session" }, { status: 401 });
1485
+
const user = requireAuth(req);
const { polar } = await import("./lib/polar");
// Get subscription to find customer ID
···
return Response.json({ url: session.customerPortalUrl });
1557
-
console.error("Failed to create portal session:", error);
1558
-
return Response.json(
1559
-
{ error: "Failed to create portal session" },
1515
+
return handleError(err);