import jwt from "jsonwebtoken"; import { NextRequest, NextResponse } from "next/server"; import { db } from "@/db"; import { users } from "@/db/schema"; import { eq } from "drizzle-orm"; const JWT_SECRET = process.env.JWT_SECRET!; /** * Create a JWT token for a user. */ export function createJwtToken(userId: string, role: string | null): string { const payload = { user_id: userId, role: role, exp: Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60, // 30 days }; return jwt.sign(payload, JWT_SECRET, { algorithm: "HS256" }); } /** * Verify and decode a JWT token. */ export function verifyJwtToken(token: string): { user_id: string; role: string | null } { try { const payload = jwt.verify(token, JWT_SECRET, { algorithms: ["HS256"] }) as { user_id: string; role: string | null; }; return payload; } catch (error) { throw new Error("Invalid or expired token"); } } /** * Extract user from Authorization header or query param (for SSE). */ export async function getCurrentUser(request: NextRequest) { let token: string | null = null; // Try Authorization header first const authHeader = request.headers.get("Authorization"); console.log("[getCurrentUser] Authorization header:", authHeader ? "Present" : "Missing"); if (authHeader && authHeader.startsWith("Bearer ")) { token = authHeader.substring(7); console.log("[getCurrentUser] Found token in Authorization header"); } // Fallback to query param for SSE connections if (!token) { const url = new URL(request.url); token = url.searchParams.get("token"); if (token) { console.log("[getCurrentUser] Found token in query params"); } } if (!token) { console.log("[getCurrentUser] No token found in header or query params"); return null; } try { const payload = verifyJwtToken(token); console.log("[getCurrentUser] Token verified, user_id:", payload.user_id); // Fetch full user from database // NOTE: sync_status columns excluded until Turso migration is applied const userRecords = await db .select({ id: users.id, githubId: users.githubId, username: users.username, avatarUrl: users.avatarUrl, role: users.role, githubAccessToken: users.githubAccessToken, createdAt: users.createdAt, updatedAt: users.updatedAt, }) .from(users) .where(eq(users.id, payload.user_id)) .limit(1); if (userRecords.length === 0) { console.log("[getCurrentUser] User not found in database for user_id:", payload.user_id); return null; } console.log("[getCurrentUser] User found:", userRecords[0].username); return userRecords[0]; } catch (error: any) { console.error("[getCurrentUser] Token verification failed:", error?.message); return null; } } /** * Helper to require authentication on a route. */ export async function requireAuth(request: NextRequest) { const user = await getCurrentUser(request); if (!user) { return { user: null, error: NextResponse.json( { error: "Unauthorized" }, { status: 401 } ), }; } return { user, error: null }; }