Maj export
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* autoExport.js — Job planifié à 3h00 du matin.
|
||||
* Génère un export complet (DB + assets) et le conserve dans DATA_DIR/exports/.
|
||||
* Résultat tracé dans job_logs (job_name = 'auto_export').
|
||||
*/
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import os from 'node:os';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import db from '../db/index.js';
|
||||
import { createZip } from '../utils/zip.js';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const dataDir = process.env.DATA_DIR
|
||||
? path.resolve(process.env.DATA_DIR)
|
||||
: path.resolve(__dirname, '../../../data');
|
||||
const exportsDir = path.join(dataDir, 'exports');
|
||||
const MAX_EXPORTS = 10;
|
||||
const JOB_NAME = 'auto_export';
|
||||
|
||||
function writeLog({ status, nbChanges = 0, details = null, errorMsg = null }) {
|
||||
try {
|
||||
db.prepare(`
|
||||
INSERT INTO job_logs (job_name, status, nb_changes, details, error_msg)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
`).run(JOB_NAME, status, nbChanges, details, errorMsg);
|
||||
} catch (e) {
|
||||
console.error('[autoExport] Impossible d\'écrire dans job_logs :', e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function purgeOldExports() {
|
||||
try {
|
||||
fs.mkdirSync(exportsDir, { recursive: true });
|
||||
const files = fs.readdirSync(exportsDir)
|
||||
.filter(f => f.endsWith('.zip'))
|
||||
.map(f => ({ f, mtime: fs.statSync(path.join(exportsDir, f)).mtimeMs }))
|
||||
.sort((a, b) => b.mtime - a.mtime);
|
||||
for (const { f } of files.slice(MAX_EXPORTS)) {
|
||||
fs.unlinkSync(path.join(exportsDir, f));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[autoExport] Erreur purge :', e.message);
|
||||
}
|
||||
}
|
||||
|
||||
export async function runAutoExport() {
|
||||
const tmpDb = path.join(os.tmpdir(), `cl-auto-export-${Date.now()}.db`);
|
||||
const startedAt = Date.now();
|
||||
console.log('[autoExport] Démarrage de l\'export automatique…');
|
||||
|
||||
try {
|
||||
await db.backup(tmpDb);
|
||||
const dbData = fs.readFileSync(tmpDb);
|
||||
|
||||
const now = new Date();
|
||||
const pad = n => String(n).padStart(2, '0');
|
||||
const ts = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`
|
||||
+ `_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
|
||||
const filename = `crowdlending-export-${ts}.zip`;
|
||||
|
||||
const entries = [];
|
||||
entries.push({
|
||||
name: 'manifest.json',
|
||||
data: JSON.stringify({
|
||||
version: '1.0', app: 'crowdlending',
|
||||
exported_at: now.toISOString(),
|
||||
type: 'full-export',
|
||||
source: 'auto_export_job',
|
||||
}, null, 2),
|
||||
});
|
||||
entries.push({ name: 'crowdlending.db', data: dbData });
|
||||
|
||||
let assetCount = 0;
|
||||
for (const subdir of ['logos', 'icons']) {
|
||||
const dir = path.join(dataDir, subdir);
|
||||
if (!fs.existsSync(dir)) continue;
|
||||
for (const f of fs.readdirSync(dir)) {
|
||||
const fpath = path.join(dir, f);
|
||||
if (fs.statSync(fpath).isFile()) {
|
||||
entries.push({ name: `${subdir}/${f}`, data: fs.readFileSync(fpath) });
|
||||
assetCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const zipBuf = createZip(entries);
|
||||
fs.mkdirSync(exportsDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(exportsDir, filename), zipBuf);
|
||||
purgeOldExports();
|
||||
|
||||
const elapsed = ((Date.now() - startedAt) / 1000).toFixed(1);
|
||||
const details = `Fichier : ${filename} | Taille : ${(zipBuf.length / 1024).toFixed(0)} Ko | Assets : ${assetCount} | Durée : ${elapsed}s`;
|
||||
writeLog({ status: 'ok', nbChanges: 1, details });
|
||||
console.log(`[autoExport] Export terminé en ${elapsed}s → ${filename}`);
|
||||
} catch (e) {
|
||||
console.error('[autoExport] Erreur :', e.message);
|
||||
writeLog({ status: 'error', nbChanges: 0, errorMsg: e.message });
|
||||
} finally {
|
||||
if (fs.existsSync(tmpDb)) fs.unlinkSync(tmpDb);
|
||||
}
|
||||
}
|
||||
|
||||
/** Planifie runAutoExport() tous les jours à 3h00 locale (sans dérive). */
|
||||
export function startAutoExportJob() {
|
||||
function msUntil3am() {
|
||||
const now = new Date();
|
||||
const next = new Date(now);
|
||||
next.setHours(3, 0, 0, 0);
|
||||
if (next <= now) next.setDate(next.getDate() + 1);
|
||||
return next.getTime() - now.getTime();
|
||||
}
|
||||
|
||||
function scheduleDailyRun() {
|
||||
const delay = msUntil3am();
|
||||
setTimeout(async () => {
|
||||
await runAutoExport();
|
||||
scheduleDailyRun();
|
||||
}, delay);
|
||||
}
|
||||
|
||||
scheduleDailyRun();
|
||||
console.log(`[autoExport] Job planifié — prochaine exécution dans ${Math.round(msUntil3am() / 60000)} min (3h00)`);
|
||||
}
|
||||
Reference in New Issue
Block a user