/home/techb158/cosmic.abdallabala.com/src/operations
NameSizeModeActions
operationsService.js41640666editdlrm
Edit: /home/techb158/cosmic.abdallabala.com/src/operations/operationsService.js (4164B)
const fs = require("fs"); const path = require("path"); const crypto = require("crypto"); const { publicRuntimeSummary, validateRuntimeConfig } = require("../config/runtimeConfig.js"); function fileSha256(filePath) { const hash = crypto.createHash("sha256"); hash.update(fs.readFileSync(filePath)); return hash.digest("hex"); } function safeTimestamp() { return new Date().toISOString().replace(/[:.]/g, "-"); } class OperationsService { constructor(database, config) { this.database = database; this.config = config; } getHealth() { return { ok: true, service: this.config.appName, version: this.config.version, storage: "json-file", database: path.basename(this.config.databaseFile) }; } getReadiness() { const checks = []; const validation = validateRuntimeConfig(this.config); checks.push({ name: "runtime-config", ok: validation.ok, errors: validation.errors, warnings: validation.warnings }); try { const db = this.database.read(); checks.push({ name: "database-readable", ok: Boolean(db && db.tables), tableCount: db && db.tables ? Object.keys(db.tables).length : 0 }); } catch (error) { checks.push({ name: "database-readable", ok: false, error: error.message }); } try { fs.accessSync(path.dirname(this.config.databaseFile), fs.constants.R_OK | fs.constants.W_OK); checks.push({ name: "database-directory-writable", ok: true }); } catch (error) { checks.push({ name: "database-directory-writable", ok: false, error: error.message }); } try { if (!fs.existsSync(this.config.backupDir)) fs.mkdirSync(this.config.backupDir, { recursive: true }); fs.accessSync(this.config.backupDir, fs.constants.R_OK | fs.constants.W_OK); checks.push({ name: "backup-directory-writable", ok: true, directory: this.config.backupDir }); } catch (error) { checks.push({ name: "backup-directory-writable", ok: false, error: error.message }); } const ok = checks.every(check => check.ok); return { ok, service: this.config.appName, version: this.config.version, checkedAt: new Date().toISOString(), checks }; } getSecurityStatus() { return publicRuntimeSummary(this.config); } createBackup(actorUserId = "USER-SYSTEM") { if (!fs.existsSync(this.config.databaseFile)) throw new Error(`Database file not found: ${this.config.databaseFile}`); if (!fs.existsSync(this.config.backupDir)) fs.mkdirSync(this.config.backupDir, { recursive: true }); const backupFile = path.join(this.config.backupDir, `cosmic-db-${safeTimestamp()}.json`); fs.copyFileSync(this.config.databaseFile, backupFile); const stat = fs.statSync(backupFile); const manifest = { id: path.basename(backupFile, ".json"), actorUserId, sourceFile: this.config.databaseFile, backupFile, createdAt: new Date().toISOString(), sizeBytes: stat.size, sha256: fileSha256(backupFile) }; fs.writeFileSync(`${backupFile}.manifest.json`, JSON.stringify(manifest, null, 2) + "\n", "utf8"); this.pruneBackups(); return manifest; } listBackups() { if (!fs.existsSync(this.config.backupDir)) return []; return fs.readdirSync(this.config.backupDir) .filter(name => name.endsWith(".manifest.json")) .map(name => { try { return JSON.parse(fs.readFileSync(path.join(this.config.backupDir, name), "utf8")); } catch (_error) { return null; } }) .filter(Boolean) .sort((a, b) => String(b.createdAt).localeCompare(String(a.createdAt))); } pruneBackups() { const retention = Math.max(1, Number(this.config.backupRetention || 10)); const backups = this.listBackups(); backups.slice(retention).forEach(manifest => { [manifest.backupFile, `${manifest.backupFile}.manifest.json`].forEach(file => { try { if (file && fs.existsSync(file)) fs.unlinkSync(file); } catch (_error) { // Best-effort cleanup only. } }); }); } } module.exports = { OperationsService };