78 lines
2.7 KiB
JavaScript
78 lines
2.7 KiB
JavaScript
import fs from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { buildRegistryManifests, normalizeDb, validateRegistry } from "../../src/lib/validation.js";
|
|
import { INITIAL_DB } from "../../src/model/seed.js";
|
|
import { evidenceDir, manifestsDir, registryPath } from "../config.js";
|
|
|
|
async function ensureDirs() {
|
|
await fs.mkdir(manifestsDir, { recursive: true });
|
|
await fs.mkdir(evidenceDir, { recursive: true });
|
|
}
|
|
|
|
export async function loadRegistry() {
|
|
await ensureDirs();
|
|
try {
|
|
const raw = await fs.readFile(registryPath, "utf-8");
|
|
return normalizeDb(JSON.parse(raw));
|
|
} catch {
|
|
return normalizeDb(INITIAL_DB);
|
|
}
|
|
}
|
|
|
|
export async function writeRegistry(input) {
|
|
await ensureDirs();
|
|
const normalized = normalizeDb(input);
|
|
const validation = validateRegistry(normalized);
|
|
if (!validation.isValid) {
|
|
const error = new Error("Registry validation failed.");
|
|
error.validation = validation;
|
|
throw error;
|
|
}
|
|
|
|
await fs.writeFile(registryPath, JSON.stringify(normalized, null, 2), "utf-8");
|
|
const manifests = buildRegistryManifests(normalized);
|
|
await fs.writeFile(path.join(manifestsDir, "evidence-manifest.json"), JSON.stringify(manifests.evidenceManifest, null, 2), "utf-8");
|
|
await fs.writeFile(path.join(manifestsDir, "auditor-pack-index.json"), JSON.stringify(manifests.auditorPackIndex, null, 2), "utf-8");
|
|
return { registry: normalized, validation, manifests };
|
|
}
|
|
|
|
export async function resetRegistry() {
|
|
return writeRegistry(INITIAL_DB);
|
|
}
|
|
|
|
export async function listEvidenceArtifacts() {
|
|
await ensureDirs();
|
|
const names = await fs.readdir(evidenceDir).catch(() => []);
|
|
const stats = await Promise.all(
|
|
names
|
|
.filter((name) => !name.startsWith("."))
|
|
.map(async (name) => {
|
|
const fullPath = path.join(evidenceDir, name);
|
|
const stat = await fs.stat(fullPath);
|
|
return {
|
|
filename: name,
|
|
size: stat.size,
|
|
modifiedAt: stat.mtime.toISOString(),
|
|
path: fullPath,
|
|
};
|
|
})
|
|
);
|
|
return stats.sort((a, b) => a.filename.localeCompare(b.filename));
|
|
}
|
|
|
|
export async function writeEvidenceArtifact({ filename, contentBase64 }) {
|
|
await ensureDirs();
|
|
if (!filename || typeof filename !== "string") throw new Error("filename is required.");
|
|
if (!contentBase64 || typeof contentBase64 !== "string") throw new Error("contentBase64 is required.");
|
|
|
|
const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
const fullPath = path.join(evidenceDir, safeName);
|
|
await fs.writeFile(fullPath, Buffer.from(contentBase64, "base64"));
|
|
const stat = await fs.stat(fullPath);
|
|
return {
|
|
filename: safeName,
|
|
size: stat.size,
|
|
modifiedAt: stat.mtime.toISOString(),
|
|
path: fullPath,
|
|
};
|
|
}
|