Spaces:
Sleeping
Sleeping
File size: 3,536 Bytes
8e23875 c0b84a6 8e23875 c0b84a6 86c1aaf c0b84a6 8e23875 6bb9678 c0b84a6 6bb9678 c0b84a6 86c1aaf c0b84a6 6bb9678 c0b84a6 8e23875 c0b84a6 6bb9678 8e23875 6bb9678 8e23875 e9b2cbc 8e23875 86c1aaf 8e23875 6bb9678 8e23875 6bb9678 8e23875 6bb9678 8e23875 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | 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 };
}
|