feat(ops-api/audit): annotate audit log IP with region lookup
Add an IPRegionResolver wrapping the ip2region xdb library and attach it to AuditService so each appended event records both the raw IP and a resolved region in a new ops.audit_logs.ip_region column. Loopback and private addresses short-circuit to local labels; missing xdb data or lookup errors degrade silently so auditing keeps working without it. The ops console audit view shows the region beneath the IP. Bundle the v4/v6 xdb data under internal/ops/app/ipregiondata so the resolver works out of the box, with config paths/env overrides for swapping in updated data sets. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -64,7 +64,13 @@
|
||||
<span v-else style="color: #cbd5e1">—</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'ip'">
|
||||
<span>{{ record.ip || "—" }}</span>
|
||||
<div v-if="record.ip">
|
||||
<span>{{ record.ip }}</span>
|
||||
<div v-if="record.ip_region" class="audit-ip-region">
|
||||
{{ formatIPRegion(record.ip_region) }}
|
||||
</div>
|
||||
</div>
|
||||
<span v-else style="color: #94a3b8">—</span>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
@@ -88,6 +94,7 @@ interface AuditRow {
|
||||
target_id: string | null;
|
||||
metadata: Record<string, unknown> | null;
|
||||
ip: string | null;
|
||||
ip_region: string | null;
|
||||
user_agent: string | null;
|
||||
request_id: string | null;
|
||||
created_at: string;
|
||||
@@ -112,7 +119,7 @@ const columns = [
|
||||
{ title: "动作", key: "action", width: 220 },
|
||||
{ title: "对象", key: "target", width: 200 },
|
||||
{ title: "元数据", key: "metadata" },
|
||||
{ title: "IP", key: "ip", width: 140 },
|
||||
{ title: "IP", key: "ip", width: 220 },
|
||||
];
|
||||
|
||||
const rows = ref<AuditRow[]>([]);
|
||||
@@ -183,6 +190,14 @@ function formatMetadata(meta: Record<string, unknown>): string {
|
||||
}
|
||||
}
|
||||
|
||||
function formatIPRegion(region: string): string {
|
||||
const parts = region
|
||||
.split("|")
|
||||
.map((part) => part.trim())
|
||||
.filter((part) => part && part !== "0");
|
||||
return parts.length > 0 ? parts.join(" / ") : region;
|
||||
}
|
||||
|
||||
function resolveActionColor(action: string): string {
|
||||
if (action.endsWith(".failure") || action.includes("disable")) return "red";
|
||||
if (action.endsWith(".success") || action.includes("create")) return "green";
|
||||
@@ -194,3 +209,12 @@ onMounted(() => {
|
||||
void reload();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.audit-ip-region {
|
||||
margin-top: 2px;
|
||||
color: #64748b;
|
||||
font-size: 12px;
|
||||
line-height: 1.35;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user