From 2133d6a574e48e817c63686a9827d386a70cf645 Mon Sep 17 00:00:00 2001 From: Nathnael Date: Wed, 5 Aug 2026 14:23:00 +0000 Subject: [PATCH] fix: fitler out the client side request --- .../src/modules/audit/audit.service.ts | 11 +++++++ .../src/pages/audit/AuditLogsPage.tsx | 29 +++++++++++-------- .../backoffice/src/services/audit.service.ts | 13 +++++++-- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/apps/edr-freight-api/src/modules/audit/audit.service.ts b/apps/edr-freight-api/src/modules/audit/audit.service.ts index 8bc792591..04ea4beed 100644 --- a/apps/edr-freight-api/src/modules/audit/audit.service.ts +++ b/apps/edr-freight-api/src/modules/audit/audit.service.ts @@ -3,6 +3,8 @@ import { InjectRepository } from "@nestjs/typeorm"; import { Repository } from "typeorm"; import { AuditLogCommand } from "@tria-plc/auditlog"; +import { CLIENT_APP_HEADER } from "../auth/login-audience.middleware"; + export interface AuditLogListResult { count: number; items: AuditLogCommand[]; @@ -38,6 +40,15 @@ export class AuditService { "(audit_log_commands.auditLogId IS NULL OR auditLog.status = :status)", { status: "Commit" }, ) + // Backoffice-only view: portal (customer-facing) writes carry the same + // request-header set by every axios call from that app — see + // login-audience.middleware.ts. Rows with no linked auditLog (child/ + // event commands with no request context) stay visible; they aren't + // attributable to any frontend, so they're not portal noise either. + .andWhere( + "(audit_log_commands.auditLogId IS NULL OR auditLog.requestHeader ->> :clientAppHeader = :clientApp)", + { clientAppHeader: CLIENT_APP_HEADER, clientApp: "backoffice" }, + ) .select([ "audit_log_commands.id", "audit_log_commands.createdAt", diff --git a/apps/edr-freight-web/backoffice/src/pages/audit/AuditLogsPage.tsx b/apps/edr-freight-web/backoffice/src/pages/audit/AuditLogsPage.tsx index d4451c2f2..ef3412a3a 100644 --- a/apps/edr-freight-web/backoffice/src/pages/audit/AuditLogsPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/audit/AuditLogsPage.tsx @@ -7,6 +7,7 @@ import type { AuditLogRow, AuditQueryMethod, AuditUser, + LocalizedText, } from "@/services/audit.service"; import { DataTable, @@ -44,18 +45,20 @@ function formatDateTime(iso: string): string { }); } -// The producer (@tria-plc/auditlog's ClientLoggerInterceptor) builds -// `name` from `${auditUser?.firstName} ${auditUser?.lastName}` — this app's -// user model only has a single `name` field, and unauthenticated/customer -// flows (e.g. Fayda verification) have no auditUser at all, so this literal -// "undefined undefined" ends up stored as-is. Filter it back out on render -// rather than showing raw garbage. +// See LocalizedText: `name`/`title` lifted from a raw audited entity can be +// a plain string or IAM's { am, en } — never render either directly. +// "undefined undefined" is the producer's own broken template when no user +// was attached at all (unauthenticated/customer flows, e.g. Fayda +// verification) — filtered out here rather than shown as raw garbage. +function localize(value: LocalizedText | null | undefined): string | undefined { + if (!value) return undefined; + if (typeof value === "object") return value.en ?? value.am ?? undefined; + if (/^undefined(\s+undefined)?$/.test(value.trim())) return undefined; + return value; +} + function formatUser(user: AuditUser | null | undefined): string { - const name = user?.name; - if (typeof name === "string" && /^undefined(\s+undefined)?$/.test(name.trim())) { - return "—"; - } - return name ?? user?.id ?? "—"; + return localize(user?.name) ?? user?.id ?? "—"; } function summarize(row: AuditLogRow): string { @@ -66,7 +69,9 @@ function summarize(row: AuditLogRow): string { .join(", ") + (row.changes.length > 2 ? `, +${row.changes.length - 2} more` : ""); } if (row.payload) { - return row.payload.name ?? row.payload.title ?? row.payload.id ?? "—"; + return ( + localize(row.payload.name) ?? localize(row.payload.title) ?? row.payload.id ?? "—" + ); } return "—"; } diff --git a/apps/edr-freight-web/backoffice/src/services/audit.service.ts b/apps/edr-freight-web/backoffice/src/services/audit.service.ts index 498d1c77c..ed0642d83 100644 --- a/apps/edr-freight-web/backoffice/src/services/audit.service.ts +++ b/apps/edr-freight-web/backoffice/src/services/audit.service.ts @@ -19,9 +19,18 @@ export interface AuditFieldChange { to: unknown; } +// IAM entities (users, orgs, positions, ...) name themselves bilingually — +// see edr-org.seeder.ts. Any `name`/`title` field lifted from a raw audited +// entity (auditLog.user, payload) can come back as either a plain string or +// this shape; both `name` fields below reflect that. +export type LocalizedText = string | { am?: string; en?: string }; + +// The vendored interceptor's own broken template produces a plain string +// ("undefined undefined") when no user was attached at all (unauthenticated/ +// customer flows) — that's the non-bilingual string case for `name` here. export interface AuditUser { id?: string; - name?: string; + name?: LocalizedText; organizationId?: string; organizationName?: string; [key: string]: unknown; @@ -34,7 +43,7 @@ export interface AuditLogRow { entityName: string; queryMethod: AuditQueryMethod; changes?: AuditFieldChange[] | null; - payload?: { name?: string; title?: string; id?: string } | null; + payload?: { name?: LocalizedText; title?: LocalizedText; id?: string } | null; auditLog?: { id?: string; user?: AuditUser | null }; }