fix: fitler out the client side request

This commit is contained in:
Nathnael
2026-08-05 14:23:00 +00:00
parent 595c165820
commit 2133d6a574
3 changed files with 39 additions and 14 deletions

View File

@@ -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",

View File

@@ -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 "—";
}

View File

@@ -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 };
}