84 lines
2.3 KiB
JavaScript
84 lines
2.3 KiB
JavaScript
import crypto from "node:crypto";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import Database from "better-sqlite3";
|
|
import { applySqlitePragmas, ensureDataLayout } from "./lib/data-dir.mjs";
|
|
|
|
const config = ensureDataLayout();
|
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
const backupDir = path.join(config.backupsDir, `neta-${timestamp}`);
|
|
const uploadsBackupDir = path.join(backupDir, "uploads");
|
|
const databaseBackupPath = path.join(backupDir, "neta.db");
|
|
|
|
fs.mkdirSync(backupDir, { recursive: true });
|
|
|
|
const sqlite = new Database(config.databasePath);
|
|
|
|
try {
|
|
applySqlitePragmas(sqlite);
|
|
await sqlite.backup(databaseBackupPath);
|
|
} finally {
|
|
sqlite.close();
|
|
}
|
|
|
|
copyDirectoryIfExists(config.uploadsDir, uploadsBackupDir);
|
|
|
|
const manifest = {
|
|
createdAt: new Date().toISOString(),
|
|
source: {
|
|
dataDir: config.dataDir,
|
|
databasePath: config.databasePath,
|
|
uploadsDir: config.uploadsDir,
|
|
},
|
|
files: collectFiles(backupDir).map((filePath) => ({
|
|
path: path.relative(backupDir, filePath).replace(/\\/g, "/"),
|
|
bytes: fs.statSync(filePath).size,
|
|
sha256: hashFile(filePath),
|
|
})),
|
|
};
|
|
|
|
fs.writeFileSync(path.join(backupDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
|
|
console.log(`Backup created at ${backupDir}`);
|
|
|
|
function copyDirectoryIfExists(sourceDir, targetDir) {
|
|
if (!fs.existsSync(sourceDir)) {
|
|
return;
|
|
}
|
|
|
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
|
|
for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
|
|
const sourcePath = path.join(sourceDir, entry.name);
|
|
const targetPath = path.join(targetDir, entry.name);
|
|
|
|
if (entry.isDirectory()) {
|
|
copyDirectoryIfExists(sourcePath, targetPath);
|
|
} else if (entry.isFile()) {
|
|
fs.copyFileSync(sourcePath, targetPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
function collectFiles(rootDir) {
|
|
const files = [];
|
|
|
|
for (const entry of fs.readdirSync(rootDir, { withFileTypes: true })) {
|
|
const entryPath = path.join(rootDir, entry.name);
|
|
|
|
if (entry.isDirectory()) {
|
|
files.push(...collectFiles(entryPath));
|
|
} else if (entry.isFile()) {
|
|
files.push(entryPath);
|
|
}
|
|
}
|
|
|
|
return files;
|
|
}
|
|
|
|
function hashFile(filePath) {
|
|
const hash = crypto.createHash("sha256");
|
|
hash.update(fs.readFileSync(filePath));
|
|
return hash.digest("hex");
|
|
}
|