Files
miot_x/src/backup.ts

71 lines
2.2 KiB
TypeScript

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 ?? {},
},
};
}