import { readFileSync, writeFileSync } from "node:fs"; import { deflateRawSync, inflateRawSync } from "node:zlib"; import type { BackupData, DeviceInventory } from "./types"; export const BACKUP_HEADER = new Uint8Array([0x98, 0x80, 0x01, 0x00]); export interface BackupStats { version: number; ruleCount: number; globalVariableCount: number; nodeCount: number; } export function unpackBackup(buffer: Uint8Array): BackupData { assertBackupHeader(buffer); const inflated = inflateRawSync(buffer.subarray(BACKUP_HEADER.length)); const json = JSON.parse(inflated.toString("utf8")) as BackupData; return normalizeBackup(json); } export function packBackup(data: BackupData): Uint8Array { const json = JSON.stringify(normalizeBackup(data)); const compressed = deflateRawSync(Buffer.from(json, "utf8")); const output = new Uint8Array(BACKUP_HEADER.length + compressed.length); output.set(BACKUP_HEADER, 0); output.set(compressed, BACKUP_HEADER.length); return output; } export function readBackupFile(path: string): BackupData { return unpackBackup(readFileSync(path)); } export function writeBackupFile(path: string, data: BackupData): void { writeFileSync(path, packBackup(data)); } export function readDeviceInventoryFile(path: string): DeviceInventory { return JSON.parse(readFileSync(path, "utf8")) as DeviceInventory; } export function backupStats(data: BackupData): BackupStats { return { version: data.version, ruleCount: data.rules.length, globalVariableCount: Object.keys(data.variables.global).length, nodeCount: data.rules.reduce((sum, rule) => sum + rule.nodes.length, 0), }; } function assertBackupHeader(buffer: Uint8Array): void { if (buffer.length < BACKUP_HEADER.length) { throw new Error("备份文件过短,缺少固定头"); } for (let index = 0; index < BACKUP_HEADER.length; index += 1) { if (buffer[index] !== BACKUP_HEADER[index]) { throw new Error("备份文件头不匹配,期望 0x98 0x80 0x01 0x00"); } } } function normalizeBackup(data: BackupData): BackupData { return { version: 2, rules: Array.isArray(data.rules) ? data.rules : [], variables: { global: data.variables?.global ?? {}, }, }; }