73 lines
2.0 KiB
JavaScript
Executable File
73 lines
2.0 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const root = path.resolve(__dirname, "..");
|
|
const contractPath = path.join(
|
|
root,
|
|
"adk-rust/crates/detmir-portal/src/contracts/openapi.json",
|
|
);
|
|
|
|
const requiredPublicPaths = [
|
|
"/api/contracts",
|
|
"/api/contracts/openapi.json",
|
|
"/api/contracts/typescript.d.ts",
|
|
"/api/reports",
|
|
"/api/executive",
|
|
"/api/workforce",
|
|
"/api/security",
|
|
"/api/forensics",
|
|
"/api/ueba",
|
|
"/api/pfsense",
|
|
"/api/incidents",
|
|
"/api/cases",
|
|
"/api/readiness/latest",
|
|
"/api/readiness/bundle",
|
|
"/api/readiness/verify",
|
|
];
|
|
|
|
function fail(message, details = []) {
|
|
console.error(message);
|
|
for (const detail of details) {
|
|
console.error(`- ${detail}`);
|
|
}
|
|
process.exit(1);
|
|
}
|
|
|
|
let contract;
|
|
try {
|
|
contract = JSON.parse(fs.readFileSync(contractPath, "utf8"));
|
|
} catch (error) {
|
|
fail(`failed to read OpenAPI contract: ${contractPath}`, [error.message]);
|
|
}
|
|
|
|
if (!contract || typeof contract !== "object" || !contract.paths || typeof contract.paths !== "object") {
|
|
fail("OpenAPI contract has no object 'paths' section.");
|
|
}
|
|
|
|
const contractPaths = Object.keys(contract.paths);
|
|
const forbiddenPaths = contractPaths.filter((contractPathName) =>
|
|
/dioxus|prototype-mirror|mirror/i.test(contractPathName),
|
|
);
|
|
if (forbiddenPaths.length > 0) {
|
|
fail("OpenAPI contract contains legacy/prototype paths.", forbiddenPaths);
|
|
}
|
|
|
|
const effectivePublicPaths = new Set();
|
|
for (const contractPathName of contractPaths) {
|
|
effectivePublicPaths.add(contractPathName);
|
|
if (contractPathName.startsWith("/") && !contractPathName.startsWith("/api/")) {
|
|
effectivePublicPaths.add(`/api${contractPathName}`);
|
|
}
|
|
}
|
|
|
|
const missingPaths = requiredPublicPaths.filter((requiredPath) => !effectivePublicPaths.has(requiredPath));
|
|
if (missingPaths.length > 0) {
|
|
fail("OpenAPI contract is missing required public API paths.", missingPaths);
|
|
}
|
|
|
|
console.log("portal contract sync guard: OK");
|