import crypto from "node:crypto"; import { headers } from "next/headers"; import { NextRequest, NextResponse } from "next/server"; import { auth } from "@/lib/auth/server"; import { db, apiKeys, users, eq } from "@epicure/db"; import { applyRateLimit } from "@/lib/rate-limit"; export async function requireSession() { const session = await auth.api.getSession({ headers: await headers() }); if (!session) { return { session: null, response: NextResponse.json({ error: "Unauthorized" }, { status: 401 }) }; } return { session, response: null }; } export async function requireAdmin() { const { session, response } = await requireSession(); if (response) return { session: null, response }; if (session!.user.role !== "admin") { return { session: null, response: NextResponse.json({ error: "Forbidden" }, { status: 403 }) }; } return { session, response: null }; } type SessionLike = { user: { id: string; email: string; name: string; tier: string; role?: string; image?: string | null; }; }; type RateLimitOpts = { limit: number; windowSeconds: number }; export async function requireSessionOrApiKey( req: NextRequest, opts?: { rateLimit?: RateLimitOpts } ): Promise<{ session: SessionLike; response: null } | { session: null; response: NextResponse }> { // 1. Try Bearer API key const authHeader = req.headers.get("authorization"); if (authHeader?.startsWith("Bearer ")) { const rawKey = authHeader.slice(7).trim(); if (rawKey.startsWith("ek_")) { const keyHash = crypto.createHash("sha256").update(rawKey).digest("hex"); const [keyRow] = await db .select({ id: apiKeys.id, userId: apiKeys.userId }) .from(apiKeys) .where(eq(apiKeys.keyHash, keyHash)) .limit(1); if (keyRow) { // Update lastUsedAt asynchronously — don't block response void db .update(apiKeys) .set({ lastUsedAt: new Date() }) .where(eq(apiKeys.id, keyRow.id)); const [user] = await db .select({ id: users.id, email: users.email, name: users.name, tier: users.tier, role: users.role, }) .from(users) .where(eq(users.id, keyRow.userId)) .limit(1); if (user) { // Apply rate limit when requested (API key path only) if (opts?.rateLimit) { const { limit, windowSeconds } = opts.rateLimit; const rateLimitResponse = await applyRateLimit( `rl:api:${user.id}`, limit, windowSeconds ); if (rateLimitResponse) { return { session: null, response: rateLimitResponse }; } } return { session: { user: { ...user, image: null } }, response: null, }; } } return { session: null, response: NextResponse.json({ error: "Unauthorized" }, { status: 401 }), }; } } // 2. Fall back to session cookie return requireSession(); }