refactor(frontend): P3 — K8s 组件拆分、AlertCenter Query、permission utils

- 拆分 DeploymentView / DeploymentDetail 至子目录(<700 行红线)
- AlertCenter 接入 useAlerts / useAgentAlertRules / useAlertMutations
- 新增 utils/permission.ts,usePermission 复用纯函数
- useAlerts 增强分页统计与 Agent 规则 hooks
This commit is contained in:
Logan.Li committed 2026-06-26 10:51:15 +08:00
1 parent 37c657987b
commit 521bfd356f
24 files changed
+1454 -1221

No files matched your search

+7 -1
View File
@@ -8,6 +8,12 @@ export { useServers, useServerMutations, type ServerRecord } from './useServers'
export { useCredentials, useCredentialDetail, useCredentialMutations, type CredentialRecord } from './useCredentials';
export { usePromDatasources, usePromDatasourceMutations, type PromDatasource } from './usePromDatasources';
export { usePipelines, usePipelineRuns, usePipelineMutations } from './usePipelines';
export { useAlertRules, useAlerts, useAlertMutations } from './useAlerts';
export {
useAlertRules,
useAgentAlertRules,
useAlerts,
useAlertMutations,
type AlertListResult,
} from './useAlerts';
export { useAuditLogs } from './useAuditLogs';
export { useSessionCommands, type SessionCommand } from './useSessionCommands';
+1
View File
@@ -13,6 +13,7 @@ export const queryKeys = {
pipelineRuns: (code: string) => ['pipeline-runs', code] as const,
alerts: (params?: Record<string, unknown>) => ['alerts', params] as const,
alertRules: () => ['alert-rules'] as const,
agentAlertRules: () => ['agent-alert-rules'] as const,
auditLogs: (params?: Record<string, unknown>) => ['audit-logs', params] as const,
sessionCommands: (params?: Record<string, unknown>) => ['session-commands', params] as const,
k8sClusters: () => ['k8s-clusters'] as const,
+97 -7
View File
@@ -1,9 +1,32 @@
// 告警规则 / 告警事件 — TanStack Query // Logan.Li
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { alertApi, alertCenterApi } from '@/api';
import { alertApi, alertCenterApi, agentApi } from '@/api';
import { queryKeys } from './queryKeys';
import { unwrapList } from './unwrap';
import { unwrapList, unwrapTotal } from './unwrap';
import { mapAlert, type Alert, type AlertRule } from '@/components/monitor/alert-center/alert-center-helpers';
type AlertListParams = {
page?: number;
page_size?: number;
status?: string;
severity?: string;
};
export type AlertListResult = {
list: Alert[];
total: number;
stats: { firing: number; critical: number; warning: number; resolved: number };
};
function buildAlertStats(list: Alert[]) {
return {
firing: list.filter((a) => a.status === 'firing').length,
critical: list.filter((a) => a.severity === 'critical' && a.status === 'firing').length,
warning: list.filter((a) => a.severity === 'warning' && a.status === 'firing').length,
resolved: list.filter((a) => a.status === 'resolved').length,
};
}
export function useAlertRules() {
return useQuery({
@@ -16,20 +39,55 @@ export function useAlertRules() {
});
}
export function useAlerts(params: { page?: number; page_size?: number; status?: string; severity?: string } = {}) {
/** Agent 主机告警规则(告警中心「规则」Tab) // Logan.Li */
export function useAgentAlertRules() {
return useQuery({
queryKey: queryKeys.agentAlertRules(),
queryFn: async () => {
const res = await agentApi.listRules();
const raw = unwrapList<Record<string, unknown>>(res);
return raw.map((r) => ({
id: r.id as number,
name: String(r.name || ''),
description: `${r.metric} ${r.operator} ${r.threshold}%`,
enabled: Boolean(r.enabled),
severity: 'warning' as const,
type: String(r.metric || ''),
hostname: String(r.hostname || '全部主机'),
threshold: Number(r.threshold || 0),
})) as AlertRule[];
},
staleTime: 30_000,
});
}
export function useAlerts(params: AlertListParams = {}) {
return useQuery({
queryKey: queryKeys.alerts(params),
queryFn: async () => {
const resp = await alertCenterApi.getAlerts(params);
return unwrapList<Record<string, unknown>>(resp);
queryFn: async (): Promise<AlertListResult> => {
const resp = await alertCenterApi.getAlerts({
page: params.page,
page_size: params.page_size,
severity: params.severity !== 'all' ? params.severity : undefined,
status: params.status !== 'all' ? params.status : undefined,
});
const raw = unwrapList<Record<string, unknown>>(resp);
const list = raw.map((item) => mapAlert(item as Parameters<typeof mapAlert>[0]));
return {
list,
total: unwrapTotal(resp),
stats: buildAlertStats(list),
};
},
staleTime: 15_000,
refetchInterval: 30_000,
});
}
export function useAlertMutations() {
const qc = useQueryClient();
const invalidateRules = () => qc.invalidateQueries({ queryKey: queryKeys.alertRules() });
const invalidateAgentRules = () => qc.invalidateQueries({ queryKey: queryKeys.agentAlertRules() });
const invalidateAlerts = () => qc.invalidateQueries({ queryKey: ['alerts'] });
const toggleRuleMutation = useMutation({
@@ -42,10 +100,42 @@ export function useAlertMutations() {
onSuccess: invalidateRules,
});
const toggleAgentRuleMutation = useMutation({
mutationFn: ({ id, enabled }: { id: number; enabled: boolean }) =>
agentApi.updateRule(id, { enabled }),
onSuccess: invalidateAgentRules,
});
const deleteAgentRuleMutation = useMutation({
mutationFn: (id: number) => agentApi.deleteRule(id),
onSuccess: invalidateAgentRules,
});
const ackMutation = useMutation({
mutationFn: (id: number | string) => alertCenterApi.acknowledge(id),
onSuccess: invalidateAlerts,
});
return { toggleRuleMutation, deleteRuleMutation, ackMutation, invalidateRules, invalidateAlerts };
const resolveMutation = useMutation({
mutationFn: (id: number | string) => alertCenterApi.resolve(id),
onSuccess: invalidateAlerts,
});
const quickSetupMutation = useMutation({
mutationFn: (scope: string) => alertCenterApi.quickSetup(scope),
onSuccess: invalidateAgentRules,
});
return {
toggleRuleMutation,
deleteRuleMutation,
toggleAgentRuleMutation,
deleteAgentRuleMutation,
ackMutation,
resolveMutation,
quickSetupMutation,
invalidateRules,
invalidateAgentRules,
invalidateAlerts,
};
}
+10 -35
View File
@@ -17,45 +17,20 @@
* </HasPermission>
*/
import { useUserStore } from '@/store';
// 权限通配符 — admin 用户拥有
const WILDCARD = '*';
import {
checkPermission,
checkAnyPermission,
checkAllPermissions,
isWildcardAdmin,
} from '@/utils/permission';
export function usePermission() {
const permissions = useUserStore((s) => s.permissions);
const isAdmin = isWildcardAdmin(permissions);
// admin 通配符短路
const isAdmin = permissions?.includes(WILDCARD) ?? false;
/**
* 检查是否拥有指定权限
* @param code 权限码,如 'server:create'
* @returns true=有权限;permissions 未加载时默认 true
*/
const hasPermission = (code: string): boolean => {
// 权限未加载(登录后未调用 setPermissions)→ 默认放行
if (permissions === null || permissions === undefined) return true;
if (isAdmin) return true;
return permissions.includes(code);
};
/**
* 检查是否拥有任意一个权限(OR 逻辑)
*/
const hasAnyPermission = (codes: string[]): boolean => {
if (permissions === null || permissions === undefined) return true;
if (isAdmin) return true;
return codes.some((c) => permissions.includes(c));
};
/**
* 检查是否拥有全部权限(AND 逻辑)
*/
const hasAllPermissions = (codes: string[]): boolean => {
if (permissions === null || permissions === undefined) return true;
if (isAdmin) return true;
return codes.every((c) => permissions.includes(c));
};
const hasPermission = (code: string) => checkPermission(permissions, code);
const hasAnyPermission = (codes: string[]) => checkAnyPermission(permissions, codes);
const hasAllPermissions = (codes: string[]) => checkAllPermissions(permissions, codes);
return { hasPermission, hasAnyPermission, hasAllPermissions, isAdmin };
}