Files
system_update/server/db/client.ts
T
gilles 47fe952240 feat(settings): backup/restore de la base de données (amelioration #4)
- service dbBackup : createBackup (VACUUM INTO → archive .db cohérente),
  validateSqlite (header + integrity_check + schéma), prepareRestore
  (sauvegarde de sécurité auto + dépôt <db>.incoming)
- swap hors-ligne au démarrage (db/client.ts) : aucune corruption d'une base
  ouverte ; restauration appliquée au redémarrage
- routes GET /system/db/info|backup, POST /system/db/restore
- lib api : dbInfo / dbBackup (download navigateur) / dbRestore (upload)
- SettingsModal : onglet « Base de données » (taille, télécharger, restaurer
  avec confirmation Popup), icônes database/upload, styles DS variables only

Testé end-to-end : backup 184 Ko valide, restore + safety .bak + swap au boot,
fichier invalide rejeté. tsc 0 erreur · 91 tests · build OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 06:13:03 +02:00

28 lines
961 B
TypeScript

// server/db/client.ts
import Database from "better-sqlite3";
import { drizzle } from "drizzle-orm/better-sqlite3";
import { mkdirSync, existsSync, rmSync, renameSync } from "node:fs";
import { dirname } from "node:path";
import { env } from "../env.js";
import * as schema from "./schema.js";
mkdirSync(dirname(env.dbPath), { recursive: true });
// Restauration en attente : un fichier `<db>.incoming` déposé par /system/db/restore
// est appliqué au démarrage (swap hors-ligne = aucune corruption d'une base ouverte).
const incoming = `${env.dbPath}.incoming`;
if (existsSync(incoming)) {
for (const ext of ["", "-wal", "-shm"]) {
const p = `${env.dbPath}${ext}`;
if (existsSync(p)) rmSync(p, { force: true });
}
renameSync(incoming, env.dbPath);
}
const sqlite = new Database(env.dbPath);
sqlite.pragma("journal_mode = WAL");
sqlite.pragma("foreign_keys = ON");
export const db = drizzle(sqlite, { schema });
export { schema, sqlite };