47 lines
1.4 KiB
TypeScript
47 lines
1.4 KiB
TypeScript
import type { NextFunction, Request, Response } from 'express'
|
|
import { verifyToken } from '../auth.js'
|
|
import { getDb } from '../db.js'
|
|
import type { PublicUser, Role, UserRecord } from '../../shared/types.js'
|
|
|
|
export type AuthedRequest = Request & { user: PublicUser }
|
|
|
|
const findUserById = (users: UserRecord[], id: string): UserRecord | undefined =>
|
|
users.find((u) => u.id === id)
|
|
|
|
export const requireAuth = async (
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction,
|
|
): Promise<void> => {
|
|
const header = req.headers.authorization
|
|
const token = header?.startsWith('Bearer ') ? header.slice(7) : ''
|
|
const decoded = token ? verifyToken(token) : null
|
|
if (!decoded) {
|
|
res.status(401).json({ success: false, error: 'UNAUTHORIZED' })
|
|
return
|
|
}
|
|
|
|
const db = await getDb()
|
|
const record = findUserById(db.users, decoded.userId)
|
|
if (!record || record.status !== 'active') {
|
|
res.status(401).json({ success: false, error: 'UNAUTHORIZED' })
|
|
return
|
|
}
|
|
|
|
const { passwordHash: _pw, ...user } = record
|
|
;(req as AuthedRequest).user = user
|
|
next()
|
|
}
|
|
|
|
export const requireRole = (roles: Role[]) => {
|
|
return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
|
const user = (req as AuthedRequest).user
|
|
if (!user || !roles.includes(user.role)) {
|
|
res.status(403).json({ success: false, error: 'FORBIDDEN' })
|
|
return
|
|
}
|
|
next()
|
|
}
|
|
}
|
|
|