import type { NextResponse } from "next/server"; import { SESSION_COOKIE_NAME, shouldUseSecureAuthCookie } from "@/lib/auth/config"; import type { AuthMode, AuthSession, AuthUser } from "@/lib/auth/session"; import type { PlatformOrganization, PlatformUserRecord } from "@/lib/types"; import { clearSessionCookieValues, setSessionCookieValue } from "@/lib/server/auth/session-cookie"; import { createSessionCookieValue } from "@/lib/auth/session"; import { getPlatformOrganization } from "@/lib/server/account-store"; export const LOCAL_SESSION_TTL_SECONDS = 24 * 60 * 60; const ipAttempts = new Map(); export function authUserFromPlatformRecord(user: PlatformUserRecord, organization?: PlatformOrganization | null): AuthUser { const role = user.role; const authorities = role === "super_admin" ? ["ROLE_SUPER_ADMIN", "SUPER_ADMIN"] : role === "organization_admin" ? ["ROLE_ORGANIZATION_ADMIN", "ORGANIZATION_ADMIN"] : ["ROLE_USER"]; return { id: user.id, subject: user.id, username: user.phone, phone: user.phone, displayName: user.displayName, clientId: "platform", organizationId: user.organizationId, organizationName: organization?.name, role, status: user.status, authorities, scope: [] }; } export async function createPlatformSession(user: PlatformUserRecord): Promise { const organization = user.organizationId ? await getPlatformOrganization(user.organizationId) : null; const now = Math.floor(Date.now() / 1000); const authMode: AuthMode = user.role === "user" ? "user" : "admin"; return { version: 1, authMode, user: authUserFromPlatformRecord(user, organization), issuedAt: now, expiresAt: now + LOCAL_SESSION_TTL_SECONDS, sessionVersion: user.sessionVersion }; } export async function setPlatformSessionCookie( response: NextResponse, requestUrl: string, session: AuthSession ) { const secret = process.env.ZHINIAN_AUTH_SESSION_SECRET || process.env.AUTH_SESSION_SECRET || process.env.NEXTAUTH_SECRET; if (!secret) throw new Error("ZHINIAN_AUTH_SESSION_SECRET 未配置。"); setSessionCookieValue( response, requestUrl, await createSessionCookieValue(session, secret), new Date(session.expiresAt * 1000) ); } export function clearPlatformSessionCookies(response: NextResponse, requestUrl: string) { clearSessionCookieValues(response, requestUrl); response.cookies.set(SESSION_COOKIE_NAME, "", { httpOnly: true, sameSite: "lax", secure: shouldUseSecureAuthCookie(requestUrl), path: "/", maxAge: 0 }); } export function clientIpFromRequest(request: Request): string { return request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || request.headers.get("x-real-ip")?.trim() || "unknown"; } export function checkIpLoginRateLimit(ip: string): void { const now = Date.now(); const current = ipAttempts.get(ip); if (!current || current.resetAt <= now) { ipAttempts.set(ip, { count: 1, resetAt: now + 15 * 60 * 1000 }); return; } if (current.count >= 30) { const error = new Error("请求过于频繁,请稍后再试。") as Error & { status: number }; error.status = 429; throw error; } current.count += 1; } export function clearIpLoginRateLimit(ip: string): void { ipAttempts.delete(ip); } export function resetLocalAuthRateLimitForTests(): void { ipAttempts.clear(); }