krona-staff/src/domain/sync/cursor.ts
Ochenstarik fd1ec3d819 style: establish repository formatting baseline
Run the configured formatter across the repository before audit remediation so later security fixes produce reviewable, conflict-resistant diffs.
2026-09-26 14:22:25 +07:00

53 lines
1.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import crypto from 'node:crypto';
export class InvalidCursorError extends Error {
readonly code = 'INVALID_CURSOR';
constructor(message = 'Курсор синхронизации недействителен или повреждён') {
super(message);
this.name = 'InvalidCursorError';
}
}
const CURSOR_PREFIX = 'cur_';
const CURSOR_SALT = 'krona_staff_sync_cursor_salt_v1';
export function encodeCursor(tenantId: string, seq: bigint | number | string): string {
const seqStr = String(seq);
const hash = crypto
.createHash('sha256')
.update(`${tenantId}:${seqStr}:${CURSOR_SALT}`)
.digest('hex')
.slice(0, 12);
return `${CURSOR_PREFIX}${seqStr}_${hash}`;
}
export function decodeCursor(tenantId: string, cursor: string): bigint {
if (typeof cursor !== 'string' || !cursor) {
throw new InvalidCursorError('Курсор синхронизации не задан');
}
const match = cursor.match(/^cur_(\d+)_([0-9a-f]{12})$/);
if (!match) {
throw new InvalidCursorError('Неверный формат курсора синхронизации');
}
const seqStr = match[1]!;
const candidateHash = match[2]!;
const expectedHash = crypto
.createHash('sha256')
.update(`${tenantId}:${seqStr}:${CURSOR_SALT}`)
.digest('hex')
.slice(0, 12);
const candidateBuffer = Buffer.from(candidateHash, 'utf8');
const expectedBuffer = Buffer.from(expectedHash, 'utf8');
if (
candidateBuffer.length !== expectedBuffer.length ||
!crypto.timingSafeEqual(candidateBuffer, expectedBuffer)
) {
throw new InvalidCursorError(
'Подпись курсора синхронизации недействительна для данного арендатора',
);
}
return BigInt(seqStr);
}