44 lines
1.5 KiB
TypeScript

import type { NextFunction, Request, Response } from 'express'
import { safeEqual, sha256 } from './signature'
import { AdminApiError, getAdminToken, type AdminAuditActor } from './admin-project-registry'
const clean = (value: unknown) => String(value || '').trim()
const parseBearerToken = (value: unknown) => {
const raw = clean(value)
if (!raw) return ''
const matched = raw.match(/^Bearer\s+(.+)$/i)
return clean(matched?.[1] || '')
}
export type AdminRequest = Request & {
adminActor?: AdminAuditActor
}
export const extractAdminActor = (req: Request): AdminAuditActor => ({
actorType: 'admin',
actorId: clean(req.header('x-yyd-admin-actor')) || 'admin',
requestId: clean(req.header('x-request-id')) || undefined,
ipAddress: clean(req.ip || req.socket.remoteAddress) || undefined,
})
export const authenticateAdmin = (req: AdminRequest, _res: Response, next: NextFunction) => {
try {
const expectedToken = getAdminToken()
if (!expectedToken) {
throw new AdminApiError(503, 'ADMIN_AUTH_NOT_CONFIGURED', '管理员鉴权未配置')
}
const actualToken = parseBearerToken(req.header('authorization'))
if (!actualToken) {
throw new AdminApiError(401, 'ADMIN_AUTH_REQUIRED', '缺少管理员 Bearer Token')
}
if (!safeEqual(sha256(actualToken), sha256(expectedToken))) {
throw new AdminApiError(401, 'ADMIN_TOKEN_INVALID', '管理员 Token 无效')
}
req.adminActor = extractAdminActor(req)
next()
} catch (error) {
next(error)
}
}