import { createHmac, timingSafeEqual } from 'node:crypto'; function signature(secret, type, parts) { return createHmac('sha256', secret).update([type, ...parts].join('\u001f')).digest('hex').slice(0, 24).toUpperCase(); } export function resolveDocumentVerificationSecret(env = process.env) { const configured = String(env.DOCUMENT_VERIFICATION_SECRET || ''); if (env.NODE_ENV === 'production' && configured.length < 32) { throw new Error('生产环境必须设置至少 32 个字符的 DOCUMENT_VERIFICATION_SECRET'); } return configured || String(env.SESSION_SECRET || '') || 'development-document-verification-secret'; } export function scoreReportCode(secret, registration, exam, results = []) { const scores = [...results].sort((a, b) => String(a.subjectId).localeCompare(String(b.subjectId))).map(item => `${item.subjectId}:${Number(item.score)}:${item.publishedAt || item.updatedAt || ''}`); return `SR-${signature(secret, 'score-report', [registration.id, registration.userId, exam.id, ...scores])}`; } export function admissionNoticeCode(secret, placement, exam) { return `AN-${signature(secret, 'admission-notice', [placement.id, placement.userId, placement.schoolId, exam.id, placement.payload?.categoryCode || '', placement.payload?.noticeNumber || '', placement.updatedAt || ''])}`; } export function safeCodeEqual(left, right) { const a = Buffer.from(String(left || '').toUpperCase()); const b = Buffer.from(String(right || '').toUpperCase()); return a.length === b.length && timingSafeEqual(a, b); }