diff --git a/app/api/auth/route.ts b/app/api/auth/route.ts index 4fdc364..837921f 100644 --- a/app/api/auth/route.ts +++ b/app/api/auth/route.ts @@ -3,13 +3,24 @@ import { authenticateLogin, createLoginResponse, getPublicAuthConfig, + ManagedAuthStorageError, validatePremiumAccess, } from '@/lib/server/auth'; export const runtime = 'edge'; export async function GET() { - return NextResponse.json(await getPublicAuthConfig()); + try { + return NextResponse.json(await getPublicAuthConfig()); + } catch (error) { + if (error instanceof ManagedAuthStorageError) { + return NextResponse.json( + { error: 'Managed authentication storage is unavailable' }, + { status: 503 }, + ); + } + throw error; + } } export async function POST(request: NextRequest) { @@ -32,7 +43,13 @@ export async function POST(request: NextRequest) { } return createLoginResponse(session, request); - } catch { + } catch (error) { + if (error instanceof ManagedAuthStorageError) { + return NextResponse.json( + { valid: false, message: 'Managed authentication storage is unavailable' }, + { status: 503 }, + ); + } return NextResponse.json({ valid: false, message: 'Invalid request' }, { status: 400 }); } } diff --git a/app/api/auth/session/route.ts b/app/api/auth/session/route.ts index 2463982..0ab58af 100644 --- a/app/api/auth/session/route.ts +++ b/app/api/auth/session/route.ts @@ -1,10 +1,24 @@ import { NextRequest } from 'next/server'; -import { createSessionStatusResponse, logoutResponse } from '@/lib/server/auth'; +import { + createSessionStatusResponse, + logoutResponse, + ManagedAuthStorageError, +} from '@/lib/server/auth'; export const runtime = 'edge'; export async function GET(request: NextRequest) { - return createSessionStatusResponse(request); + try { + return await createSessionStatusResponse(request); + } catch (error) { + if (error instanceof ManagedAuthStorageError) { + return Response.json( + { authenticated: false, error: 'Managed authentication storage is unavailable' }, + { status: 503 }, + ); + } + throw error; + } } export async function DELETE(request: NextRequest) { diff --git a/app/api/user/config/route.ts b/app/api/user/config/route.ts index 3195d41..e980c6a 100644 --- a/app/api/user/config/route.ts +++ b/app/api/user/config/route.ts @@ -5,7 +5,7 @@ * so they persist across browsers, devices, and PWA installs. */ -import { Redis } from '@upstash/redis'; +import { Redis } from '@upstash/redis/cloudflare'; import { NextRequest, NextResponse } from 'next/server'; import { authenticationRequiredResponse } from '@/lib/server/api-responses'; import { getServerSession } from '@/lib/server/auth'; diff --git a/app/api/user/sync/route.ts b/app/api/user/sync/route.ts index cd190d6..e52aee3 100644 --- a/app/api/user/sync/route.ts +++ b/app/api/user/sync/route.ts @@ -1,4 +1,4 @@ -import { Redis } from '@upstash/redis'; +import { Redis } from '@upstash/redis/cloudflare'; import { NextRequest, NextResponse } from 'next/server'; import { authenticationRequiredResponse } from '@/lib/server/api-responses'; import { getServerSession } from '@/lib/server/auth'; diff --git a/components/PasswordGate.tsx b/components/PasswordGate.tsx index d97d059..45fd718 100644 --- a/components/PasswordGate.tsx +++ b/components/PasswordGate.tsx @@ -207,6 +207,12 @@ export function PasswordGate({ }); const data = await response.json(); + if (response.status === 503) { + setError('认证存储暂时不可用,请检查 Upstash Redis 配置'); + setIsValidating(false); + return; + } + if (data.valid && data.session) { setSession(toAuthSession(data.session), data.persistSession ?? persistSession); setIsLocked(false); diff --git a/lib/server/auth.ts b/lib/server/auth.ts index 2a71490..4b36df1 100644 --- a/lib/server/auth.ts +++ b/lib/server/auth.ts @@ -1,4 +1,4 @@ -import { Redis } from '@upstash/redis'; +import { Redis } from '@upstash/redis/cloudflare'; import { NextRequest, NextResponse } from 'next/server'; import { getRuntimeFeatures } from '@/lib/server/runtime-features'; import { @@ -89,6 +89,13 @@ const effectiveAdminPassword = ADMIN_PASSWORD || ACCESS_PASSWORD; let cachedRedis: Redis | null | undefined; +export class ManagedAuthStorageError extends Error { + constructor(operation: 'read' | 'write', cause?: unknown) { + super(`Managed auth storage ${operation} failed`, { cause }); + this.name = 'ManagedAuthStorageError'; + } +} + function getRedisClient(): Redis | null { if (cachedRedis !== undefined) { return cachedRedis; @@ -99,7 +106,10 @@ function getRedisClient(): Redis | null { return cachedRedis; } - cachedRedis = Redis.fromEnv(); + cachedRedis = new Redis({ + url: process.env.UPSTASH_REDIS_REST_URL, + token: process.env.UPSTASH_REDIS_REST_TOKEN, + }); return cachedRedis; } @@ -140,8 +150,9 @@ async function readManagedAccounts(): Promise { const stored = await redis.get(MANAGED_ACCOUNTS_KEY); if (!Array.isArray(stored)) return []; return stored.filter(isStoredAccountRecord).map(normalizeStoredAccount); - } catch { - return []; + } catch (error) { + console.error('Managed auth Redis read failed:', error); + throw new ManagedAuthStorageError('read', error); } } @@ -151,7 +162,12 @@ async function saveManagedAccounts(accounts: StoredAccountRecord[]): Promise