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:
1 parent
37c657987b
commit
521bfd356f
24 files changed
+1454
-1221
No files matched your search
@@ -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';
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
@@ -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 };
|
||||
}
|
||||
Reference in new issue
Block a user