feat(admin): 完善审计访问上下文与后台布局
This commit is contained in:
@@ -7,6 +7,7 @@ export interface AuditEvent {
|
||||
eventLabel: string;
|
||||
actorId: string;
|
||||
actorName: string;
|
||||
actorAccount?: string;
|
||||
actorRole: string;
|
||||
actorRoleLabel: string;
|
||||
targetType: string;
|
||||
@@ -21,6 +22,19 @@ export interface AuditEvent {
|
||||
result: "SUCCESS" | "FAIL";
|
||||
resultLabel: string;
|
||||
timestamp: string;
|
||||
clientIp?: string;
|
||||
ipLocation?: string;
|
||||
ipCountry?: string;
|
||||
ipProvince?: string;
|
||||
ipCity?: string;
|
||||
ipIsp?: string;
|
||||
clientType?: string;
|
||||
clientVersion?: string;
|
||||
clientPlatform?: string;
|
||||
buildChannel?: string;
|
||||
buildCommit?: string;
|
||||
userAgent?: string;
|
||||
clientSourceLabel?: string;
|
||||
}
|
||||
|
||||
const entityTypeLabelMap: Record<string, string> = {
|
||||
@@ -619,6 +633,57 @@ const buildDiffText = (
|
||||
return lines;
|
||||
};
|
||||
|
||||
const fallbackIpLocation = (ip: string | null | undefined): string => {
|
||||
if (!ip) return "";
|
||||
if (ip === "127.0.0.1" || ip === "::1") return "本机访问";
|
||||
if (/^(10\.|192\.168\.|172\.(1[6-9]|2\d|3[0-1])\.)/.test(ip)) return "内网地址";
|
||||
return "";
|
||||
};
|
||||
|
||||
export const formatAuditIpLocation = (raw: {
|
||||
ip_country?: string | null;
|
||||
ip_province?: string | null;
|
||||
ip_city?: string | null;
|
||||
ip_isp?: string | null;
|
||||
ip_location?: string | null;
|
||||
client_ip?: string | null;
|
||||
clientIp?: string | null;
|
||||
}): string => {
|
||||
const parts = [
|
||||
raw.ip_country && raw.ip_country !== "中国" ? raw.ip_country : "",
|
||||
raw.ip_province,
|
||||
raw.ip_city,
|
||||
raw.ip_isp,
|
||||
].filter(Boolean);
|
||||
if (parts.length) return parts.join(" / ");
|
||||
return raw.ip_location || fallbackIpLocation(raw.client_ip || raw.clientIp) || "";
|
||||
};
|
||||
|
||||
const isAutomationUserAgent = (userAgent = ""): boolean =>
|
||||
/curl|wget|python-requests|httpie|go-http-client|node-fetch|axios|postman|insomnia|okhttp|java|powershell|libwww/i.test(userAgent);
|
||||
|
||||
const isBrowserUserAgent = (userAgent = ""): boolean =>
|
||||
/mozilla|chrome|safari|firefox|edg\//i.test(userAgent);
|
||||
|
||||
export const resolveAuditClientSourceLabel = (raw: {
|
||||
client_type?: string | null;
|
||||
client_platform?: string | null;
|
||||
user_agent?: string | null;
|
||||
clientType?: string | null;
|
||||
clientPlatform?: string | null;
|
||||
userAgent?: string | null;
|
||||
}): string => {
|
||||
const clientType = String(raw.client_type || raw.clientType || "").trim().toLowerCase();
|
||||
const platform = String(raw.client_platform || raw.clientPlatform || "").trim();
|
||||
const userAgent = String(raw.user_agent || raw.userAgent || "").trim();
|
||||
if (clientType === "web") return "网页端";
|
||||
if (clientType === "desktop") return platform ? `桌面端 / ${platform}` : "桌面端";
|
||||
if (clientType) return `未知客户端 / ${clientType}`;
|
||||
if (isAutomationUserAgent(userAgent)) return "脚本/命令行";
|
||||
if (isBrowserUserAgent(userAgent)) return "浏览器(未标识)";
|
||||
return "未知来源";
|
||||
};
|
||||
|
||||
export const normalizeAuditEvent = (raw: any, userMap: Record<string, string>): AuditEvent => {
|
||||
const dict = auditDict[raw.action] || buildReadableAuditDict(raw.action, raw.entity_type);
|
||||
let detailObj: any = {};
|
||||
@@ -668,7 +733,8 @@ export const normalizeAuditEvent = (raw: any, userMap: Record<string, string>):
|
||||
eventType: raw.action,
|
||||
eventLabel: dict.label,
|
||||
actorId: raw.operator_id,
|
||||
actorName: userMap[raw.operator_id] || raw.operator_id,
|
||||
actorName: raw.operator_name || userMap[raw.operator_id] || raw.operator_id,
|
||||
actorAccount: raw.operator_email || raw.operator_id,
|
||||
actorRole: raw.operator_role,
|
||||
actorRoleLabel: getDictLabel(roleDict, raw.operator_role),
|
||||
targetType: raw.entity_type || "",
|
||||
@@ -683,6 +749,19 @@ export const normalizeAuditEvent = (raw: any, userMap: Record<string, string>):
|
||||
result: detailObj.result || "SUCCESS",
|
||||
resultLabel: detailObj.result === "FAIL" ? TEXT.audit.resultFail : TEXT.audit.resultSuccess,
|
||||
timestamp: raw.created_at,
|
||||
clientIp: raw.client_ip || "",
|
||||
ipLocation: formatAuditIpLocation(raw),
|
||||
ipCountry: raw.ip_country || "",
|
||||
ipProvince: raw.ip_province || "",
|
||||
ipCity: raw.ip_city || "",
|
||||
ipIsp: raw.ip_isp || "",
|
||||
clientType: raw.client_type || "",
|
||||
clientVersion: raw.client_version || "",
|
||||
clientPlatform: raw.client_platform || "",
|
||||
buildChannel: raw.build_channel || "",
|
||||
buildCommit: raw.build_commit || "",
|
||||
userAgent: raw.user_agent || "",
|
||||
clientSourceLabel: resolveAuditClientSourceLabel(raw),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user