Spaces:
Sleeping
Sleeping
| import { Request, Response, NextFunction } from "express"; | |
| import jwt from "jsonwebtoken"; | |
| import { randomUUID } from "crypto"; | |
| import { eq } from "drizzle-orm"; | |
| import { db, usersTable } from "@workspace/db"; | |
| const JWT_SECRET = process.env.SESSION_SECRET || "raqim-secret-key-change-in-prod"; | |
| export interface AuthRequest extends Request { | |
| userId?: string; | |
| userRole?: string; | |
| userStatus?: string; | |
| } | |
| export async function requireAuth(req: AuthRequest, res: Response, next: NextFunction): Promise<void> { | |
| const authHeader = req.headers.authorization; | |
| if (!authHeader || !authHeader.startsWith("Bearer ")) { | |
| res.status(401).json({ error: "unauthorized", message: "No token provided" }); | |
| return; | |
| } | |
| const token = authHeader.slice(7); | |
| try { | |
| const payload = jwt.verify(token, JWT_SECRET) as { userId: string; role: string; status: string }; | |
| if (payload.status !== "active") { | |
| res.status(403).json({ error: "forbidden", message: "Account is not active" }); | |
| return; | |
| } | |
| // Verify the user ID from the token still exists in the DB. | |
| // This catches stale tokens issued before a DB wipe/re-seed. | |
| const row = await db | |
| .select({ id: usersTable.id }) | |
| .from(usersTable) | |
| .where(eq(usersTable.id, payload.userId)) | |
| .get(); | |
| if (!row) { | |
| console.error(`[auth] Token user ${payload.userId} not found in DB — rejecting as stale`); | |
| res.status(401).json({ error: "session_expired", message: "انتهت جلستك، يرجى تسجيل الدخول مجدداً" }); | |
| return; | |
| } | |
| req.userId = payload.userId; | |
| req.userRole = payload.role; | |
| req.userStatus = payload.status; | |
| next(); | |
| } catch { | |
| res.status(401).json({ error: "unauthorized", message: "Invalid or expired token" }); | |
| } | |
| } | |
| export async function requireAdmin(req: AuthRequest, res: Response, next: NextFunction): Promise<void> { | |
| let calledNext = false; | |
| await requireAuth(req, res, () => { calledNext = true; }); | |
| if (!calledNext) return; | |
| if (req.userRole !== "admin") { | |
| res.status(403).json({ error: "forbidden", message: "Admin access required" }); | |
| return; | |
| } | |
| next(); | |
| } | |
| export function generateAccessToken(userId: string, role: string, status: string): string { | |
| return jwt.sign({ userId, role, status }, JWT_SECRET, { expiresIn: "1h" }); | |
| } | |
| export function generateRefreshToken(userId: string): string { | |
| return jwt.sign({ userId, type: "refresh", jti: randomUUID() }, JWT_SECRET, { expiresIn: "30d" }); | |
| } | |