feat(web): complete audit log viewer with IP column and all event types

This commit is contained in:
SnapOtter
2026-06-13 16:42:10 +08:00
parent 3016571c2b
commit c6f9a29687
2 changed files with 33 additions and 4 deletions
+5
View File
@@ -12,6 +12,7 @@ export async function auditLogRoutes(app: FastifyInstance): Promise<void> {
page?: string;
limit?: string;
action?: string;
ip?: string;
from?: string;
to?: string;
};
@@ -30,6 +31,9 @@ export async function auditLogRoutes(app: FastifyInstance): Promise<void> {
if (request.query.action) {
conditions.push(eq(schema.auditLog.action, request.query.action));
}
if (request.query.ip) {
conditions.push(eq(schema.auditLog.ipAddress, request.query.ip));
}
if (request.query.from) {
const fromDate = new Date(request.query.from);
if (!Number.isNaN(fromDate.getTime())) {
@@ -68,6 +72,7 @@ export async function auditLogRoutes(app: FastifyInstance): Promise<void> {
targetId: e.targetId,
details: e.details ?? null,
ipAddress: e.ipAddress,
requestId: e.requestId ?? null,
createdAt: e.createdAt.toISOString(),
})),
total: countResult?.count ?? 0,
@@ -2571,26 +2571,39 @@ function RolesSection() {
const AUDIT_ACTIONS = [
"LOGIN_SUCCESS",
"LOGIN_FAILED",
"USER_CREATED",
"USER_UPDATED",
"USER_DELETED",
"LOGOUT",
"PASSWORD_CHANGED",
"PASSWORD_RESET",
"USER_CREATED",
"USER_DELETED",
"USER_UPDATED",
"FILE_UPLOADED",
"FILE_DELETED",
"API_KEY_CREATED",
"API_KEY_DELETED",
"ROLE_CREATED",
"ROLE_UPDATED",
"ROLE_DELETED",
"SETTINGS_UPDATED",
"OIDC_LOGIN_SUCCESS",
"OIDC_USER_CREATED",
"OIDC_USER_LINKED",
"OIDC_LOGIN_FAILED",
"TOOL_EXECUTED",
"BATCH_EXECUTED",
"PIPELINE_EXECUTED",
] as const;
interface AuditEntry {
id: string;
actorId: string | null;
actorUsername: string;
action: string;
targetType: string | null;
targetId: string | null;
details: Record<string, unknown> | null;
ipAddress: string | null;
requestId: string | null;
createdAt: string;
}
@@ -2692,6 +2705,11 @@ function AuditLogSection() {
</div>
<div className="flex items-center gap-2 mt-1">
<span className="text-sm text-foreground">{entry.actorUsername}</span>
{entry.ipAddress && (
<span className="font-mono text-xs text-muted-foreground tabular-nums">
{entry.ipAddress}
</span>
)}
{entry.targetType && (
<span className="text-xs text-muted-foreground">
{entry.targetType}
@@ -2720,6 +2738,9 @@ function AuditLogSection() {
<th className="text-start px-3 py-2 font-medium text-muted-foreground">
{t.settings.auditLog.tableHeaderUser}
</th>
<th className="text-start px-3 py-2 font-medium text-muted-foreground">
IP
</th>
<th className="text-start px-3 py-2 font-medium text-muted-foreground">
{t.settings.auditLog.tableHeaderAction}
</th>
@@ -2739,6 +2760,9 @@ function AuditLogSection() {
{formatRelativeTime(entry.createdAt)}
</td>
<td className="px-3 py-2 text-foreground">{entry.actorUsername}</td>
<td className="px-3 py-2 font-mono text-xs text-muted-foreground tabular-nums whitespace-nowrap">
{entry.ipAddress ?? "---"}
</td>
<td className="px-3 py-2">
<span className="font-mono text-xs bg-muted px-1.5 py-0.5 rounded">
{entry.action}
@@ -2752,7 +2776,7 @@ function AuditLogSection() {
</tr>
{expandedId === entry.id && entry.details && (
<tr className="border-b border-border last:border-0">
<td colSpan={4} className="px-3 py-2 bg-muted/10">
<td colSpan={5} className="px-3 py-2 bg-muted/10">
<pre className="text-xs text-muted-foreground whitespace-pre-wrap font-mono overflow-x-auto">
{JSON.stringify(entry.details, null, 2)}
</pre>