Files
pikasTech-HWLAB/internal/audit/index.mjs
T
2026-05-21 14:39:30 +00:00

146 lines
2.9 KiB
JavaScript

import { ENVIRONMENT_DEV } from "../protocol/index.mjs";
export const CLOUD_API_SERVICE_ID = "hwlab-cloud-api";
export const AUDIT_FIELD_NAMES = Object.freeze([
"requestId",
"actor",
"source",
"operation",
"target",
"result",
"timestamp"
]);
export function createAuditRecord(input = {}) {
const record = {
requestId: asString(input.requestId, "req_unassigned"),
actor: normalizeActor(input.actor),
source: normalizeSource(input.source, input.traceId),
operation: asString(input.operation, "cloud_api.request"),
target: normalizeTarget(input.target),
result: asString(input.result, "unknown"),
timestamp: asString(input.timestamp, new Date().toISOString())
};
assertAuditFields(record);
return record;
}
export function deriveActorFromMeta(meta = {}) {
return {
type: meta.actorType || "system",
id: meta.actorId || `system_${CLOUD_API_SERVICE_ID}`
};
}
export function assertAuditFields(record) {
if (!record || typeof record !== "object" || Array.isArray(record)) {
throw new Error("audit record must be an object");
}
for (const field of AUDIT_FIELD_NAMES) {
if (!Object.hasOwn(record, field)) {
throw new Error(`audit.${field} is required`);
}
}
if (Number.isNaN(Date.parse(record.timestamp))) {
throw new Error("audit.timestamp must be an RFC 3339 timestamp");
}
}
function normalizeActor(actor) {
if (!actor) {
return {
type: "system",
id: `system_${CLOUD_API_SERVICE_ID}`
};
}
if (typeof actor === "string") {
return {
type: "user",
id: actor
};
}
if (typeof actor === "object" && !Array.isArray(actor)) {
return {
type: asString(actor.type, "user"),
id: asString(actor.id, `system_${CLOUD_API_SERVICE_ID}`)
};
}
return {
type: "system",
id: `system_${CLOUD_API_SERVICE_ID}`
};
}
function normalizeSource(source, traceId) {
const base = {
serviceId: CLOUD_API_SERVICE_ID,
environment: ENVIRONMENT_DEV
};
if (traceId) {
base.traceId = traceId;
}
if (!source) {
return base;
}
if (typeof source === "string") {
return {
...base,
serviceId: source
};
}
if (typeof source === "object" && !Array.isArray(source)) {
return {
...base,
...source
};
}
return base;
}
function normalizeTarget(target) {
if (!target) {
return {
type: "cloud_api",
id: CLOUD_API_SERVICE_ID
};
}
if (typeof target === "string") {
return {
type: "resource",
id: target
};
}
if (typeof target === "object" && !Array.isArray(target)) {
return {
type: asString(target.type, "resource"),
id: asString(target.id, "target_unspecified")
};
}
return {
type: "cloud_api",
id: CLOUD_API_SERVICE_ID
};
}
function asString(value, fallback) {
if (value === undefined || value === null || value === "") {
return fallback;
}
return String(value);
}