88 lines
2.8 KiB
JavaScript
88 lines
2.8 KiB
JavaScript
import { mkdirSync, readFileSync, renameSync, writeFileSync, existsSync } from 'fs';
|
|
import { dirname } from 'path';
|
|
import { PlatformError } from '../../kernel/errors.mjs';
|
|
|
|
const SCHEMA_VERSION = 1;
|
|
|
|
function normalizeState(input = {}) {
|
|
const modules = {};
|
|
const source = input && typeof input === 'object' && !Array.isArray(input) ? input.modules : null;
|
|
if (source && typeof source === 'object' && !Array.isArray(source)) {
|
|
for (const [id, record] of Object.entries(source)) {
|
|
if (!/^vendoo\.[a-z][a-z0-9-]*$/.test(id)) continue;
|
|
if (!record || typeof record !== 'object' || Array.isArray(record)) continue;
|
|
modules[id] = {
|
|
enabled: record.enabled !== false,
|
|
source: String(record.source || 'admin'),
|
|
updatedAt: String(record.updatedAt || new Date(0).toISOString()),
|
|
};
|
|
}
|
|
}
|
|
return { schemaVersion: SCHEMA_VERSION, modules };
|
|
}
|
|
|
|
export class ModuleStateStore {
|
|
#path;
|
|
#state;
|
|
|
|
constructor(pathname) {
|
|
this.#path = pathname;
|
|
this.#state = this.#read();
|
|
}
|
|
|
|
#read() {
|
|
try {
|
|
if (!existsSync(this.#path)) return normalizeState();
|
|
const parsed = JSON.parse(readFileSync(this.#path, 'utf8'));
|
|
if (Number(parsed?.schemaVersion || 0) !== SCHEMA_VERSION) {
|
|
throw new Error(`Nicht unterstützte Modulstatus-Version: ${parsed?.schemaVersion}`);
|
|
}
|
|
return normalizeState(parsed);
|
|
} catch (error) {
|
|
throw new PlatformError('Der persistente Modulstatus konnte nicht gelesen werden.', {
|
|
code: 'MODULE_STATE_READ_FAILED',
|
|
cause: error,
|
|
details: { path: this.#path },
|
|
});
|
|
}
|
|
}
|
|
|
|
#persist() {
|
|
mkdirSync(dirname(this.#path), { recursive: true });
|
|
const temporary = `${this.#path}.${process.pid}.tmp`;
|
|
writeFileSync(temporary, `${JSON.stringify(this.#state, null, 2)}\n`, { mode: 0o600 });
|
|
renameSync(temporary, this.#path);
|
|
}
|
|
|
|
has(id) { return Boolean(this.#state.modules[id]); }
|
|
|
|
isEnabled(id, fallback = true) {
|
|
const record = this.#state.modules[id];
|
|
return record ? record.enabled !== false : Boolean(fallback);
|
|
}
|
|
|
|
setEnabled(id, enabled, { source = 'admin' } = {}) {
|
|
if (!/^vendoo\.[a-z][a-z0-9-]*$/.test(String(id || ''))) {
|
|
throw new PlatformError('Ungültige Modul-ID für den Modulstatus.', { code: 'MODULE_ID_INVALID', status: 400, expose: true });
|
|
}
|
|
this.#state.modules[id] = {
|
|
enabled: Boolean(enabled),
|
|
source: String(source || 'admin').slice(0, 80),
|
|
updatedAt: new Date().toISOString(),
|
|
};
|
|
this.#persist();
|
|
return { id, ...this.#state.modules[id] };
|
|
}
|
|
|
|
remove(id) {
|
|
if (!this.#state.modules[id]) return false;
|
|
delete this.#state.modules[id];
|
|
this.#persist();
|
|
return true;
|
|
}
|
|
|
|
snapshot() {
|
|
return JSON.parse(JSON.stringify(this.#state));
|
|
}
|
|
}
|