feat: add Network Backup section to Database Management
PC2 can now automatically pull and restore a fresh copy of PC1's database on a daily schedule. Config and API key are stored in local JSON files so they survive database restores. - New networkSyncConfigService: file-based config (network-backup-key.json, network-sync-config.json) that persists through DB restores - New networkSyncService: streams live pg_dump from source PC over HTTP and pipes into psql, then reconnects Prisma and applies missing migrations - 6 new endpoints: get/regenerate API key, serve backup stream (key-auth only), get/save sync config, trigger immediate sync - Hourly cron job that fires only when current hour matches configured syncHour - NetworkBackupManager component: shows this machine's key (show/copy/regen) and receiver config (enable toggle, hour picker 0-23, source URL + key, Save + Sync Now, last sync status) - README setup guide for both PCs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import crypto from "crypto";
|
||||
|
||||
const KEY_FILE = path.resolve(process.cwd(), "network-backup-key.json");
|
||||
const CONFIG_FILE = path.resolve(process.cwd(), "network-sync-config.json");
|
||||
|
||||
export interface NetworkSyncConfig {
|
||||
enabled: boolean;
|
||||
syncHour: number;
|
||||
sourceUrl: string;
|
||||
apiKey: string;
|
||||
lastSyncAt: string | null;
|
||||
lastSyncStatus: string | null;
|
||||
lastSyncError: string | null;
|
||||
}
|
||||
|
||||
export function getOrCreateApiKey(): string {
|
||||
if (fs.existsSync(KEY_FILE)) {
|
||||
try {
|
||||
const data = JSON.parse(fs.readFileSync(KEY_FILE, "utf8"));
|
||||
if (data.apiKey) return data.apiKey;
|
||||
} catch {}
|
||||
}
|
||||
const key = crypto.randomUUID();
|
||||
fs.writeFileSync(KEY_FILE, JSON.stringify({ apiKey: key }), "utf8");
|
||||
return key;
|
||||
}
|
||||
|
||||
export function regenerateApiKey(): string {
|
||||
const key = crypto.randomUUID();
|
||||
fs.writeFileSync(KEY_FILE, JSON.stringify({ apiKey: key }), "utf8");
|
||||
return key;
|
||||
}
|
||||
|
||||
export function readSyncConfig(): NetworkSyncConfig {
|
||||
if (fs.existsSync(CONFIG_FILE)) {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf8"));
|
||||
} catch {}
|
||||
}
|
||||
return {
|
||||
enabled: false,
|
||||
syncHour: 0,
|
||||
sourceUrl: "",
|
||||
apiKey: "",
|
||||
lastSyncAt: null,
|
||||
lastSyncStatus: null,
|
||||
lastSyncError: null,
|
||||
};
|
||||
}
|
||||
|
||||
export function writeSyncConfig(patch: Partial<NetworkSyncConfig>): NetworkSyncConfig {
|
||||
const current = readSyncConfig();
|
||||
const updated = { ...current, ...patch };
|
||||
fs.writeFileSync(CONFIG_FILE, JSON.stringify(updated, null, 2), "utf8");
|
||||
return updated;
|
||||
}
|
||||
Reference in New Issue
Block a user