refactor(db): JSON columns are decoded through one declared schema per column

decodeJson/decodeJsonResult/encodeJson read and write a JSON-in-TEXT column against the schema and fallback declared for it in db/json-columns.ts, and log a row that does not decode. reservations.metadata, the OAuth scope and redirect lists, consents, API token scopes, MFA backup codes, journey entry tags and pros/cons and poll options go through it. Readers that used to throw on a broken row (a hotel update, the ICS export, the price mirror, the OAuth token and session reads, a consent, a journey entry, a poll) now take the tolerant fallback the other readers of the same column already used.
This commit is contained in:
Maurice
2026-10-10 12:13:56 +02:00
parent a926990e4a
commit 91deedae8b
26 changed files with 510 additions and 106 deletions
+3 -7
View File
@@ -2,6 +2,8 @@ import bcrypt from 'bcryptjs';
import crypto from 'crypto';
import { User } from '../../types';
import { utcSuffix } from '../../db/types';
import { USER_MFA_BACKUP_CODES } from '../../db/json-columns';
import { decodeJson } from '../../utils/json-column';
import { decrypt_api_key } from '../common/crypto/apiKeyCrypto';
/**
@@ -140,11 +142,5 @@ export function generateBackupCodes(count = MFA_BACKUP_CODE_COUNT): string[] {
}
export function parseBackupCodeHashes(raw: string | null | undefined): string[] {
if (!raw) return [];
try {
const parsed = JSON.parse(raw);
return Array.isArray(parsed) ? parsed.filter(v => typeof v === 'string') : [];
} catch {
return [];
}
return decodeJson(USER_MFA_BACKUP_CODES, raw).filter((v): v is string => typeof v === 'string');
}