2026-05-14 05:38:18 +00:00
|
|
|
/**
|
|
|
|
|
* Audit logging helper — single inline call from route handlers.
|
|
|
|
|
*
|
|
|
|
|
* audit(repo, event, "user.login", { result: "ok" });
|
|
|
|
|
* audit(repo, event, "firmware.upload", { resource_id: rel.id, metadata: { version, channel } });
|
|
|
|
|
*
|
|
|
|
|
* Pulls actor + ip out of the h3 event context. Never throws — logging
|
|
|
|
|
* failure must not break the caller's request.
|
|
|
|
|
*/
|
2026-05-24 00:48:32 +00:00
|
|
|
import type { Repository } from "./db/repository.js";
|
2026-05-14 05:38:18 +00:00
|
|
|
import type { AuditActorType, AuditResult } from "./types.js";
|
|
|
|
|
|
|
|
|
|
interface AuditCtx {
|
|
|
|
|
context?: {
|
refactor: migrate all auto-increment PKs to UUIDv7 text IDs
Replace SERIAL/AUTOINCREMENT integer primary keys with UUIDv7 text
IDs across all 15 entity tables (users, api_keys, displays, cameras,
camera_streams, layouts, layout_cells, entities, kiosks, labels,
kiosk_gpio_bindings, event_log, kiosk_logs, audit_log,
camera_event_subscriptions). SetupState keeps id=1 INTEGER singleton.
Changes:
- types.ts: all id fields number->string, all FK fields number->string
- mappers.ts: n(r["id"])->s(r["id"]) for PKs, nn()->sn() for nullable FKs
- repository.ts: import uuidv7, generate IDs before INSERT, remove
RETURNING id, change all method signatures from number to string
- migrations-pg.ts: SERIAL->TEXT NOT NULL PRIMARY KEY, INTEGER FK->TEXT FK
- bundle.ts: all bundle interface IDs number->string
- pairing.ts, auth.ts: kioskId/userId types number->string
- coordinator-registry.ts: kioskId number->string
- audit.ts: actor_id number->string
- mqtt-bridge.ts: kioskId number->string in publish/subscribe
- All route handlers: Number(getRouterParam)->getRouterParam ?? ""
- admin-pages.tsx: template function params and Map types number->string
- kiosk/src/bundle.rs: flexible serde deserializer that accepts both
u32 (old) and String (new) IDs for backward compatibility
Fresh PG database -- no data migration needed, just schema changes.
SQLite migrations unchanged (dev-only, recreate DB for UUIDv7).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-26 05:11:45 +00:00
|
|
|
user?: { id?: string; username?: string };
|
2026-05-14 05:38:18 +00:00
|
|
|
apiKeyPrefix?: string;
|
|
|
|
|
session?: unknown;
|
|
|
|
|
};
|
|
|
|
|
req?: { headers: { get(name: string): string | null } };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface AuditInput {
|
|
|
|
|
resource_type?: string;
|
|
|
|
|
resource_id?: string | number;
|
|
|
|
|
metadata?: Record<string, unknown>;
|
|
|
|
|
result?: AuditResult;
|
|
|
|
|
/** Override actor (e.g. when system performs action on behalf of nobody). */
|
|
|
|
|
actor_type?: AuditActorType;
|
refactor: migrate all auto-increment PKs to UUIDv7 text IDs
Replace SERIAL/AUTOINCREMENT integer primary keys with UUIDv7 text
IDs across all 15 entity tables (users, api_keys, displays, cameras,
camera_streams, layouts, layout_cells, entities, kiosks, labels,
kiosk_gpio_bindings, event_log, kiosk_logs, audit_log,
camera_event_subscriptions). SetupState keeps id=1 INTEGER singleton.
Changes:
- types.ts: all id fields number->string, all FK fields number->string
- mappers.ts: n(r["id"])->s(r["id"]) for PKs, nn()->sn() for nullable FKs
- repository.ts: import uuidv7, generate IDs before INSERT, remove
RETURNING id, change all method signatures from number to string
- migrations-pg.ts: SERIAL->TEXT NOT NULL PRIMARY KEY, INTEGER FK->TEXT FK
- bundle.ts: all bundle interface IDs number->string
- pairing.ts, auth.ts: kioskId/userId types number->string
- coordinator-registry.ts: kioskId number->string
- audit.ts: actor_id number->string
- mqtt-bridge.ts: kioskId number->string in publish/subscribe
- All route handlers: Number(getRouterParam)->getRouterParam ?? ""
- admin-pages.tsx: template function params and Map types number->string
- kiosk/src/bundle.rs: flexible serde deserializer that accepts both
u32 (old) and String (new) IDs for backward compatibility
Fresh PG database -- no data migration needed, just schema changes.
SQLite migrations unchanged (dev-only, recreate DB for UUIDv7).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-26 05:11:45 +00:00
|
|
|
actor_id?: string | null;
|
2026-05-14 05:38:18 +00:00
|
|
|
actor_label?: string | null;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-23 00:07:44 +00:00
|
|
|
export async function audit(
|
2026-05-14 05:38:18 +00:00
|
|
|
repo: Repository,
|
|
|
|
|
event: AuditCtx | null,
|
|
|
|
|
action: string,
|
|
|
|
|
input: AuditInput = {},
|
2026-05-23 00:07:44 +00:00
|
|
|
): Promise<void> {
|
2026-05-14 05:38:18 +00:00
|
|
|
try {
|
|
|
|
|
const ctx = event?.context;
|
|
|
|
|
let actor_type: AuditActorType = input.actor_type ?? "system";
|
refactor: migrate all auto-increment PKs to UUIDv7 text IDs
Replace SERIAL/AUTOINCREMENT integer primary keys with UUIDv7 text
IDs across all 15 entity tables (users, api_keys, displays, cameras,
camera_streams, layouts, layout_cells, entities, kiosks, labels,
kiosk_gpio_bindings, event_log, kiosk_logs, audit_log,
camera_event_subscriptions). SetupState keeps id=1 INTEGER singleton.
Changes:
- types.ts: all id fields number->string, all FK fields number->string
- mappers.ts: n(r["id"])->s(r["id"]) for PKs, nn()->sn() for nullable FKs
- repository.ts: import uuidv7, generate IDs before INSERT, remove
RETURNING id, change all method signatures from number to string
- migrations-pg.ts: SERIAL->TEXT NOT NULL PRIMARY KEY, INTEGER FK->TEXT FK
- bundle.ts: all bundle interface IDs number->string
- pairing.ts, auth.ts: kioskId/userId types number->string
- coordinator-registry.ts: kioskId number->string
- audit.ts: actor_id number->string
- mqtt-bridge.ts: kioskId number->string in publish/subscribe
- All route handlers: Number(getRouterParam)->getRouterParam ?? ""
- admin-pages.tsx: template function params and Map types number->string
- kiosk/src/bundle.rs: flexible serde deserializer that accepts both
u32 (old) and String (new) IDs for backward compatibility
Fresh PG database -- no data migration needed, just schema changes.
SQLite migrations unchanged (dev-only, recreate DB for UUIDv7).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-26 05:11:45 +00:00
|
|
|
let actor_id: string | null = input.actor_id ?? null;
|
2026-05-14 05:38:18 +00:00
|
|
|
let actor_label: string | null = input.actor_label ?? null;
|
|
|
|
|
|
|
|
|
|
if (!input.actor_type && ctx) {
|
|
|
|
|
if (ctx.apiKeyPrefix) {
|
|
|
|
|
actor_type = "api_key";
|
|
|
|
|
actor_label = ctx.apiKeyPrefix;
|
|
|
|
|
} else if (ctx.user) {
|
|
|
|
|
actor_type = "user";
|
|
|
|
|
actor_id = ctx.user.id ?? null;
|
|
|
|
|
actor_label = ctx.user.username ?? null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const headers = event?.req?.headers;
|
|
|
|
|
const ip = headers?.get("x-real-ip")
|
|
|
|
|
?? headers?.get("x-forwarded-for")?.split(",")[0]?.trim()
|
|
|
|
|
?? null;
|
|
|
|
|
|
2026-05-23 00:07:44 +00:00
|
|
|
await repo.insertAudit({
|
2026-05-14 05:38:18 +00:00
|
|
|
actor_type,
|
|
|
|
|
actor_id,
|
|
|
|
|
actor_label,
|
|
|
|
|
action,
|
|
|
|
|
resource_type: input.resource_type ?? null,
|
|
|
|
|
resource_id: input.resource_id != null ? String(input.resource_id) : null,
|
|
|
|
|
ip,
|
|
|
|
|
metadata: input.metadata ?? {},
|
|
|
|
|
result: input.result ?? "ok",
|
|
|
|
|
});
|
|
|
|
|
} catch {
|
|
|
|
|
/* never throw from audit */
|
|
|
|
|
}
|
|
|
|
|
}
|