/home/techb158/cosmic.abdallabala.com/src/operations
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 };