🪻 distributed transcription service thistle.dunkirk.sh
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: use consistent auth middleware

+273 -319
+273 -319
src/index.ts
··· 869 869 }, 870 870 "/api/auth/me": { 871 871 GET: (req) => { 872 - const sessionId = getSessionFromRequest(req); 873 - if (!sessionId) { 874 - return Response.json({ error: "Not authenticated" }, { status: 401 }); 875 - } 876 - const user = getUserBySession(sessionId); 877 - if (!user) { 878 - return Response.json({ error: "Invalid session" }, { status: 401 }); 879 - } 872 + try { 873 + const user = requireAuth(req); 880 874 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", 885 - ) 886 - .get(user.id); 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", 879 + ) 880 + .get(user.id); 887 881 888 - // Get notification preferences 889 - const prefs = db 890 - .query<{ email_notifications_enabled: number }, [number]>( 891 - "SELECT email_notifications_enabled FROM users WHERE id = ?", 892 - ) 893 - .get(user.id); 882 + // Get notification preferences 883 + const prefs = db 884 + .query<{ email_notifications_enabled: number }, [number]>( 885 + "SELECT email_notifications_enabled FROM users WHERE id = ?", 886 + ) 887 + .get(user.id); 894 888 895 - return Response.json({ 896 - email: user.email, 897 - name: user.name, 898 - avatar: user.avatar, 899 - created_at: user.created_at, 900 - role: user.role, 901 - has_subscription: !!subscription, 902 - email_verified: isEmailVerified(user.id), 903 - email_notifications_enabled: prefs?.email_notifications_enabled === 1, 904 - }); 889 + return Response.json({ 890 + email: user.email, 891 + name: user.name, 892 + avatar: user.avatar, 893 + created_at: user.created_at, 894 + role: user.role, 895 + has_subscription: !!subscription, 896 + email_verified: isEmailVerified(user.id), 897 + email_notifications_enabled: prefs?.email_notifications_enabled === 1, 898 + }); 899 + } catch (err) { 900 + return handleError(err); 901 + } 905 902 }, 906 903 }, 907 904 "/api/passkeys/register/options": { ··· 1091 1088 }, 1092 1089 "/api/sessions": { 1093 1090 GET: (req) => { 1094 - const sessionId = getSessionFromRequest(req); 1095 - if (!sessionId) { 1096 - return Response.json({ error: "Not authenticated" }, { status: 401 }); 1097 - } 1098 - const user = getUserBySession(sessionId); 1099 - if (!user) { 1100 - return Response.json({ error: "Invalid session" }, { status: 401 }); 1091 + try { 1092 + const sessionId = getSessionFromRequest(req); 1093 + if (!sessionId) { 1094 + return Response.json({ error: "Not authenticated" }, { status: 401 }); 1095 + } 1096 + const user = getUserBySession(sessionId); 1097 + if (!user) { 1098 + return Response.json({ error: "Invalid session" }, { status: 401 }); 1099 + } 1100 + const sessions = getUserSessionsForUser(user.id); 1101 + return Response.json({ 1102 + sessions: sessions.map((s) => ({ 1103 + id: s.id, 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, 1109 + })), 1110 + }); 1111 + } catch (err) { 1112 + return handleError(err); 1101 1113 } 1102 - const sessions = getUserSessionsForUser(user.id); 1103 - return Response.json({ 1104 - sessions: sessions.map((s) => ({ 1105 - id: s.id, 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, 1111 - })), 1112 - }); 1113 1114 }, 1114 1115 DELETE: async (req) => { 1115 - const currentSessionId = getSessionFromRequest(req); 1116 - if (!currentSessionId) { 1117 - return Response.json({ error: "Not authenticated" }, { status: 401 }); 1118 - } 1119 - const user = getUserBySession(currentSessionId); 1120 - if (!user) { 1121 - return Response.json({ error: "Invalid session" }, { status: 401 }); 1122 - } 1116 + try { 1117 + const currentSessionId = getSessionFromRequest(req); 1118 + if (!currentSessionId) { 1119 + return Response.json({ error: "Not authenticated" }, { status: 401 }); 1120 + } 1121 + const user = getUserBySession(currentSessionId); 1122 + if (!user) { 1123 + return Response.json({ error: "Invalid session" }, { status: 401 }); 1124 + } 1123 1125 1124 - const rateLimitError = enforceRateLimit(req, "delete-session", { 1125 - ip: { max: 20, windowSeconds: 60 * 60 }, 1126 - }); 1127 - if (rateLimitError) return rateLimitError; 1126 + const rateLimitError = enforceRateLimit(req, "delete-session", { 1127 + ip: { max: 20, windowSeconds: 60 * 60 }, 1128 + }); 1129 + if (rateLimitError) return rateLimitError; 1128 1130 1129 - const body = await req.json(); 1130 - const targetSessionId = body.sessionId; 1131 - if (!targetSessionId) { 1132 - return Response.json( 1133 - { error: "Session ID required" }, 1134 - { status: 400 }, 1135 - ); 1136 - } 1137 - // Prevent deleting current session 1138 - if (targetSessionId === currentSessionId) { 1139 - return Response.json( 1140 - { error: "Cannot kill current session. Use logout instead." }, 1141 - { status: 400 }, 1142 - ); 1143 - } 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" }, 1136 + { status: 400 }, 1137 + ); 1138 + } 1139 + // Prevent deleting current session 1140 + if (targetSessionId === currentSessionId) { 1141 + return Response.json( 1142 + { error: "Cannot kill current session. Use logout instead." }, 1143 + { status: 400 }, 1144 + ); 1145 + } 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 }); 1150 + } 1151 + deleteSession(targetSessionId); 1152 + return Response.json({ success: true }); 1153 + } catch (err) { 1154 + return handleError(err); 1148 1155 } 1149 - deleteSession(targetSessionId); 1150 - return Response.json({ success: true }); 1151 1156 }, 1152 1157 }, 1153 1158 "/api/user": { 1154 1159 DELETE: async (req) => { 1155 - const sessionId = getSessionFromRequest(req); 1156 - if (!sessionId) { 1157 - return Response.json({ error: "Not authenticated" }, { status: 401 }); 1158 - } 1159 - const user = getUserBySession(sessionId); 1160 - if (!user) { 1161 - return Response.json({ error: "Invalid session" }, { status: 401 }); 1162 - } 1160 + try { 1161 + const user = requireAuth(req); 1163 1162 1164 - // Rate limiting 1165 - const rateLimitError = enforceRateLimit(req, "delete-user", { 1166 - ip: { max: 3, windowSeconds: 60 * 60 }, 1167 - }); 1168 - if (rateLimitError) return rateLimitError; 1163 + // Rate limiting 1164 + const rateLimitError = enforceRateLimit(req, "delete-user", { 1165 + ip: { max: 3, windowSeconds: 60 * 60 }, 1166 + }); 1167 + if (rateLimitError) return rateLimitError; 1169 1168 1170 - await deleteUser(user.id); 1171 - return Response.json( 1172 - { success: true }, 1173 - { 1174 - headers: { 1175 - "Set-Cookie": 1176 - "session=; HttpOnly; Secure; Path=/; Max-Age=0; SameSite=Lax", 1169 + await deleteUser(user.id); 1170 + return Response.json( 1171 + { success: true }, 1172 + { 1173 + headers: { 1174 + "Set-Cookie": 1175 + "session=; HttpOnly; Secure; Path=/; Max-Age=0; SameSite=Lax", 1176 + }, 1177 1177 }, 1178 - }, 1179 - ); 1178 + ); 1179 + } catch (err) { 1180 + return handleError(err); 1181 + } 1180 1182 }, 1181 1183 }, 1182 1184 "/api/user/email": { 1183 1185 PUT: async (req) => { 1184 - const sessionId = getSessionFromRequest(req); 1185 - if (!sessionId) { 1186 - return Response.json({ error: "Not authenticated" }, { status: 401 }); 1187 - } 1188 - const user = getUserBySession(sessionId); 1189 - if (!user) { 1190 - return Response.json({ error: "Invalid session" }, { status: 401 }); 1191 - } 1186 + try { 1187 + const user = requireAuth(req); 1192 1188 1193 - // Rate limiting 1194 - const rateLimitError = enforceRateLimit(req, "update-email", { 1195 - ip: { max: 5, windowSeconds: 60 * 60 }, 1196 - }); 1197 - if (rateLimitError) return rateLimitError; 1189 + // Rate limiting 1190 + const rateLimitError = enforceRateLimit(req, "update-email", { 1191 + ip: { max: 5, windowSeconds: 60 * 60 }, 1192 + }); 1193 + if (rateLimitError) return rateLimitError; 1198 1194 1199 - const body = await req.json(); 1200 - const { email } = body; 1201 - if (!email) { 1202 - return Response.json({ error: "Email required" }, { status: 400 }); 1203 - } 1195 + const body = await req.json(); 1196 + const { email } = body; 1197 + if (!email) { 1198 + return Response.json({ error: "Email required" }, { status: 400 }); 1199 + } 1204 1200 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" }, 1210 - { status: 400 }, 1211 - ); 1212 - } 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" }, 1206 + { status: 400 }, 1207 + ); 1208 + } 1213 1209 1214 - try { 1215 - // Create email change token 1216 - const token = createEmailChangeToken(user.id, email); 1210 + try { 1211 + // Create email change token 1212 + const token = createEmailChangeToken(user.id, email); 1217 1213 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}`; 1221 1217 1222 - await sendEmail({ 1223 - to: user.email, 1224 - subject: "Verify your email change", 1225 - html: emailChangeTemplate({ 1226 - name: user.name, 1227 - currentEmail: user.email, 1228 - newEmail: email, 1229 - verifyLink: verifyUrl, 1230 - }), 1231 - }); 1218 + await sendEmail({ 1219 + to: user.email, 1220 + subject: "Verify your email change", 1221 + html: emailChangeTemplate({ 1222 + name: user.name, 1223 + currentEmail: user.email, 1224 + newEmail: email, 1225 + verifyLink: verifyUrl, 1226 + }), 1227 + }); 1232 1228 1233 - return Response.json({ 1234 - success: true, 1235 - message: `Verification email sent to ${user.email}`, 1236 - pendingEmail: email, 1237 - }); 1238 - } catch (error) { 1239 - console.error( 1240 - "[Email] Failed to send email change verification:", 1241 - error, 1242 - ); 1243 - return Response.json( 1244 - { error: "Failed to send verification email" }, 1245 - { status: 500 }, 1246 - ); 1229 + return Response.json({ 1230 + success: true, 1231 + message: `Verification email sent to ${user.email}`, 1232 + pendingEmail: email, 1233 + }); 1234 + } catch (error) { 1235 + console.error( 1236 + "[Email] Failed to send email change verification:", 1237 + error, 1238 + ); 1239 + return Response.json( 1240 + { error: "Failed to send verification email" }, 1241 + { status: 500 }, 1242 + ); 1243 + } 1244 + } catch (err) { 1245 + return handleError(err); 1247 1246 } 1248 1247 }, 1249 1248 }, ··· 1291 1290 }, 1292 1291 "/api/user/password": { 1293 1292 PUT: async (req) => { 1294 - const sessionId = getSessionFromRequest(req); 1295 - if (!sessionId) { 1296 - return Response.json({ error: "Not authenticated" }, { status: 401 }); 1297 - } 1298 - const user = getUserBySession(sessionId); 1299 - if (!user) { 1300 - return Response.json({ error: "Invalid session" }, { status: 401 }); 1301 - } 1293 + try { 1294 + const user = requireAuth(req); 1302 1295 1303 - // Rate limiting 1304 - const rateLimitError = enforceRateLimit(req, "update-password", { 1305 - ip: { max: 5, windowSeconds: 60 * 60 }, 1306 - }); 1307 - if (rateLimitError) return rateLimitError; 1296 + // Rate limiting 1297 + const rateLimitError = enforceRateLimit(req, "update-password", { 1298 + ip: { max: 5, windowSeconds: 60 * 60 }, 1299 + }); 1300 + if (rateLimitError) return rateLimitError; 1308 1301 1309 - const body = await req.json(); 1310 - const { password } = body; 1311 - if (!password) { 1312 - return Response.json({ error: "Password required" }, { status: 400 }); 1313 - } 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 }, 1319 - { status: 400 }, 1320 - ); 1321 - } 1322 - try { 1323 - await updateUserPassword(user.id, password); 1324 - return Response.json({ success: true }); 1325 - } catch { 1326 - return Response.json( 1327 - { error: "Failed to update password" }, 1328 - { status: 500 }, 1329 - ); 1302 + const body = await req.json(); 1303 + const { password } = body; 1304 + if (!password) { 1305 + return Response.json({ error: "Password required" }, { status: 400 }); 1306 + } 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 }, 1312 + { status: 400 }, 1313 + ); 1314 + } 1315 + try { 1316 + await updateUserPassword(user.id, password); 1317 + return Response.json({ success: true }); 1318 + } catch { 1319 + return Response.json( 1320 + { error: "Failed to update password" }, 1321 + { status: 500 }, 1322 + ); 1323 + } 1324 + } catch (err) { 1325 + return handleError(err); 1330 1326 } 1331 1327 }, 1332 1328 }, 1333 1329 "/api/user/name": { 1334 1330 PUT: async (req) => { 1335 - const sessionId = getSessionFromRequest(req); 1336 - if (!sessionId) { 1337 - return Response.json({ error: "Not authenticated" }, { status: 401 }); 1338 - } 1339 - const user = getUserBySession(sessionId); 1340 - if (!user) { 1341 - return Response.json({ error: "Invalid session" }, { status: 401 }); 1342 - } 1331 + try { 1332 + const user = requireAuth(req); 1343 1333 1344 - const rateLimitError = enforceRateLimit(req, "update-name", { 1345 - ip: { max: 10, windowSeconds: 5 * 60 }, 1346 - }); 1347 - if (rateLimitError) return rateLimitError; 1334 + const rateLimitError = enforceRateLimit(req, "update-name", { 1335 + ip: { max: 10, windowSeconds: 5 * 60 }, 1336 + }); 1337 + if (rateLimitError) return rateLimitError; 1348 1338 1349 - const body = await req.json(); 1350 - const { name } = body; 1351 - if (!name) { 1352 - return Response.json({ error: "Name required" }, { status: 400 }); 1353 - } 1354 - try { 1355 - updateUserName(user.id, name); 1356 - return Response.json({ success: true }); 1357 - } catch { 1358 - return Response.json( 1359 - { error: "Failed to update name" }, 1360 - { status: 500 }, 1361 - ); 1339 + const body = await req.json(); 1340 + const { name } = body; 1341 + if (!name) { 1342 + return Response.json({ error: "Name required" }, { status: 400 }); 1343 + } 1344 + try { 1345 + updateUserName(user.id, name); 1346 + return Response.json({ success: true }); 1347 + } catch { 1348 + return Response.json( 1349 + { error: "Failed to update name" }, 1350 + { status: 500 }, 1351 + ); 1352 + } 1353 + } catch (err) { 1354 + return handleError(err); 1362 1355 } 1363 1356 }, 1364 1357 }, 1365 1358 "/api/user/avatar": { 1366 1359 PUT: async (req) => { 1367 - const sessionId = getSessionFromRequest(req); 1368 - if (!sessionId) { 1369 - return Response.json({ error: "Not authenticated" }, { status: 401 }); 1370 - } 1371 - const user = getUserBySession(sessionId); 1372 - if (!user) { 1373 - return Response.json({ error: "Invalid session" }, { status: 401 }); 1374 - } 1360 + try { 1361 + const user = requireAuth(req); 1375 1362 1376 - const rateLimitError = enforceRateLimit(req, "update-avatar", { 1377 - ip: { max: 10, windowSeconds: 5 * 60 }, 1378 - }); 1379 - if (rateLimitError) return rateLimitError; 1363 + const rateLimitError = enforceRateLimit(req, "update-avatar", { 1364 + ip: { max: 10, windowSeconds: 5 * 60 }, 1365 + }); 1366 + if (rateLimitError) return rateLimitError; 1380 1367 1381 - const body = await req.json(); 1382 - const { avatar } = body; 1383 - if (!avatar) { 1384 - return Response.json({ error: "Avatar required" }, { status: 400 }); 1385 - } 1386 - try { 1387 - updateUserAvatar(user.id, avatar); 1388 - return Response.json({ success: true }); 1389 - } catch { 1390 - return Response.json( 1391 - { error: "Failed to update avatar" }, 1392 - { status: 500 }, 1393 - ); 1368 + const body = await req.json(); 1369 + const { avatar } = body; 1370 + if (!avatar) { 1371 + return Response.json({ error: "Avatar required" }, { status: 400 }); 1372 + } 1373 + try { 1374 + updateUserAvatar(user.id, avatar); 1375 + return Response.json({ success: true }); 1376 + } catch { 1377 + return Response.json( 1378 + { error: "Failed to update avatar" }, 1379 + { status: 500 }, 1380 + ); 1381 + } 1382 + } catch (err) { 1383 + return handleError(err); 1394 1384 } 1395 1385 }, 1396 1386 }, 1397 1387 "/api/user/notifications": { 1398 1388 PUT: async (req) => { 1399 - const sessionId = getSessionFromRequest(req); 1400 - if (!sessionId) { 1401 - return Response.json({ error: "Not authenticated" }, { status: 401 }); 1402 - } 1403 - const user = getUserBySession(sessionId); 1404 - if (!user) { 1405 - return Response.json({ error: "Invalid session" }, { status: 401 }); 1406 - } 1389 + try { 1390 + const user = requireAuth(req); 1407 1391 1408 - const rateLimitError = enforceRateLimit(req, "update-notifications", { 1409 - ip: { max: 10, windowSeconds: 5 * 60 }, 1410 - }); 1411 - if (rateLimitError) return rateLimitError; 1392 + const rateLimitError = enforceRateLimit(req, "update-notifications", { 1393 + ip: { max: 10, windowSeconds: 5 * 60 }, 1394 + }); 1395 + if (rateLimitError) return rateLimitError; 1412 1396 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" }, 1418 - { status: 400 }, 1419 - ); 1420 - } 1421 - try { 1422 - db.run( 1423 - "UPDATE users SET email_notifications_enabled = ? WHERE id = ?", 1424 - [email_notifications_enabled ? 1 : 0, user.id], 1425 - ); 1426 - return Response.json({ success: true }); 1427 - } catch { 1428 - return Response.json( 1429 - { error: "Failed to update notification settings" }, 1430 - { status: 500 }, 1431 - ); 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" }, 1402 + { status: 400 }, 1403 + ); 1404 + } 1405 + try { 1406 + db.run( 1407 + "UPDATE users SET email_notifications_enabled = ? WHERE id = ?", 1408 + [email_notifications_enabled ? 1 : 0, user.id], 1409 + ); 1410 + return Response.json({ success: true }); 1411 + } catch { 1412 + return Response.json( 1413 + { error: "Failed to update notification settings" }, 1414 + { status: 500 }, 1415 + ); 1416 + } 1417 + } catch (err) { 1418 + return handleError(err); 1432 1419 } 1433 1420 }, 1434 1421 }, 1435 1422 "/api/billing/checkout": { 1436 1423 POST: async (req) => { 1437 - const sessionId = getSessionFromRequest(req); 1438 - if (!sessionId) { 1439 - return Response.json({ error: "Not authenticated" }, { status: 401 }); 1440 - } 1441 - const user = getUserBySession(sessionId); 1442 - if (!user) { 1443 - return Response.json({ error: "Invalid session" }, { status: 401 }); 1444 - } 1424 + try { 1425 + const user = requireAuth(req); 1445 1426 1446 - try { 1447 1427 const { polar } = await import("./lib/polar"); 1448 1428 1449 1429 // Validated at startup ··· 1462 1442 }); 1463 1443 1464 1444 return Response.json({ url: checkout.url }); 1465 - } catch (error) { 1466 - console.error("Failed to create checkout:", error); 1467 - return Response.json( 1468 - { error: "Failed to create checkout session" }, 1469 - { status: 500 }, 1470 - ); 1445 + } catch (err) { 1446 + return handleError(err); 1471 1447 } 1472 1448 }, 1473 1449 }, 1474 1450 "/api/billing/subscription": { 1475 1451 GET: async (req) => { 1476 - const sessionId = getSessionFromRequest(req); 1477 - if (!sessionId) { 1478 - return Response.json({ error: "Not authenticated" }, { status: 401 }); 1479 - } 1480 - const user = getUserBySession(sessionId); 1481 - if (!user) { 1482 - return Response.json({ error: "Invalid session" }, { status: 401 }); 1483 - } 1452 + try { 1453 + const user = requireAuth(req); 1484 1454 1485 - try { 1486 1455 // Get subscription from database 1487 1456 const subscription = db 1488 1457 .query< ··· 1505 1474 } 1506 1475 1507 1476 return Response.json({ subscription }); 1508 - } catch (error) { 1509 - console.error("Failed to fetch subscription:", error); 1510 - return Response.json( 1511 - { error: "Failed to fetch subscription" }, 1512 - { status: 500 }, 1513 - ); 1477 + } catch (err) { 1478 + return handleError(err); 1514 1479 } 1515 1480 }, 1516 1481 }, 1517 1482 "/api/billing/portal": { 1518 1483 POST: async (req) => { 1519 - const sessionId = getSessionFromRequest(req); 1520 - if (!sessionId) { 1521 - return Response.json({ error: "Not authenticated" }, { status: 401 }); 1522 - } 1523 - const user = getUserBySession(sessionId); 1524 - if (!user) { 1525 - return Response.json({ error: "Invalid session" }, { status: 401 }); 1526 - } 1484 + try { 1485 + const user = requireAuth(req); 1527 1486 1528 - try { 1529 1487 const { polar } = await import("./lib/polar"); 1530 1488 1531 1489 // Get subscription to find customer ID ··· 1553 1511 }); 1554 1512 1555 1513 return Response.json({ url: session.customerPortalUrl }); 1556 - } catch (error) { 1557 - console.error("Failed to create portal session:", error); 1558 - return Response.json( 1559 - { error: "Failed to create portal session" }, 1560 - { status: 500 }, 1561 - ); 1514 + } catch (err) { 1515 + return handleError(err); 1562 1516 } 1563 1517 }, 1564 1518 },