防伪+成绩单+录取通知书
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
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.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);
|
||||
}
|
||||
Reference in New Issue
Block a user