WFDASH-WEBAPP/src/lib/audit-fetch.ts
Dave 7555196166 feat(audit): implement audit trail for delivery notes and orders
- Added an audit trail feature that logs every create, update, and delete action on delivery notes and orders, capturing detailed changes per field.
- Introduced the `AuditTimeline` component to display the history of changes for delivery notes and orders, enhancing traceability.
- Updated the `page.tsx` files for delivery notes and orders to fetch and display audit logs, ensuring users can view the history of modifications.
- Enhanced the `package.json` with a new migration script for setting up the audit log functionality.

This feature improves accountability and transparency in the management of delivery notes and orders.
2026-09-30 01:48:44 +02:00

41 lines
1.6 KiB
TypeScript

/**
* Client-side: tag every same-origin `/api/` fetch with the logged-in
* user's PB token (as `x-wfdash-auth`) so the server can attribute the
* PocketBase writes it makes on their behalf in the audit trail.
*
* Many dashboard calls don't send `Authorization`, so without this the
* server only knows "someone" clicked. The token is verified by PB in
* the `audit_log` hook — it's attribution only, not route auth. The
* header name matches `AUDIT_AUTH_REQUEST_HEADER` in
* src/lib/server/audit-context.ts.
*/
const AUDIT_AUTH_HEADER = "x-wfdash-auth";
const INSTALLED = Symbol.for("wfdash.auditFetchInstalled");
function isSameOriginApi(input: RequestInfo | URL): boolean {
try {
const raw = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
const url = new URL(raw, window.location.origin);
return url.origin === window.location.origin && url.pathname.startsWith("/api/");
} catch {
return false;
}
}
export function installAuditFetch(getToken: () => string): void {
if (typeof window === "undefined") return;
const w = window as unknown as Record<symbol, boolean>;
if (w[INSTALLED]) return;
w[INSTALLED] = true;
const original = window.fetch.bind(window);
window.fetch = (input: RequestInfo | URL, init?: RequestInit) => {
const token = getToken();
if (!token || !isSameOriginApi(input)) return original(input, init);
const headers = new Headers(
init?.headers ?? (input instanceof Request ? input.headers : undefined),
);
if (!headers.has(AUDIT_AUTH_HEADER)) headers.set(AUDIT_AUTH_HEADER, token);
return original(input, { ...init, headers });
};
}