feat(q1): 前端类型安全治理完成 — any 83→40, P0 全部 done
- types/monitor.ts: 新增 6 个运行时记录类型(AlertEventRecord/AlertRuleRecord/ AgentHostRecord/CronJobRecord/CertExpiryRecord/ApprovalWorkflowStepRecord) - types/common.ts: 新增 OptionItem 通用下拉类型 - 组件级 any 清理 43 处: - AlertEventsTab/AlertRulesTab/AlertChannelDialog/alertShared (19处) - AuditOperationsTab normalizeLog + 筛选器 (8处) - CronJobManager 任务列表/编辑/操作 (6处) - AgentMonitor 主机列表/命令执行 (5处) - DashboardRecent 5 张卡片组件 (5处) - AlertManager 状态类型对齐 (联动修复) - Dashboard.tsx 调用端类型对齐 (联动修复) - AlertChannel 接口新增 test_status 字段 - ESLint any: 83→40 (<50 目标达成) - tsc -b / tsc --noEmit: 零错误 - npm run build: ✓ built in 17.11s - 9/9 核心 API 回归通过
This commit is contained in:
1 parent
0535f30c87
commit
bb2b91ebe0
15 files changed
+259
-93
No files matched your search
@@ -17,6 +17,7 @@ import {
|
||||
import { Plus, Trash2, Loader2 } from 'lucide-react';
|
||||
import { alertApi } from '@/api';
|
||||
import { CHANNEL_TYPES, getChannelTypeLabel } from './alertShared';
|
||||
import type { AlertChannel } from '@/types';
|
||||
|
||||
const defaultChannelForm = { name: '', type: 'dingtalk', config: '', enabled: true };
|
||||
|
||||
@@ -28,11 +29,11 @@ export function AlertChannelDialog({
|
||||
}: {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
notifyChannels: any[];
|
||||
notifyChannels: AlertChannel[];
|
||||
onChannelsChanged: () => void;
|
||||
}) {
|
||||
const _confirm = useConfirm();
|
||||
const [editingChannel, setEditingChannel] = useState<any>(null);
|
||||
const [editingChannel, setEditingChannel] = useState<AlertChannel | null>(null);
|
||||
const [channelSaving, setChannelSaving] = useState(false);
|
||||
const [channelTesting, setChannelTesting] = useState<number | null>(null);
|
||||
const [channelForm, setChannelForm] = useState({ ...defaultChannelForm });
|
||||
@@ -45,7 +46,7 @@ export function AlertChannelDialog({
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const openEditChannel = ( ch: any) => {
|
||||
const openEditChannel = ( ch: AlertChannel) => {
|
||||
setEditingChannel(ch);
|
||||
setChannelForm({
|
||||
name: ch.name || '',
|
||||
@@ -83,7 +84,7 @@ export function AlertChannelDialog({
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteChannel = async ( id: any) => {
|
||||
const handleDeleteChannel = async ( id: number) => {
|
||||
{ const ok = await _confirm({ title: '删除通知渠道', description: '确定删除此通知渠道?', confirmText: '删除', variant: 'destructive' }); if (!ok) return; }
|
||||
try {
|
||||
await alertApi.deleteNotifyChannel(id);
|
||||
@@ -94,7 +95,7 @@ export function AlertChannelDialog({
|
||||
}
|
||||
};
|
||||
|
||||
const handleToggleChannel = async ( ch: any) => {
|
||||
const handleToggleChannel = async ( ch: AlertChannel) => {
|
||||
try {
|
||||
await alertApi.updateNotifyChannel(ch.id, { enabled: !ch.enabled });
|
||||
onChannelsChanged();
|
||||
@@ -103,7 +104,7 @@ export function AlertChannelDialog({
|
||||
}
|
||||
};
|
||||
|
||||
const handleTestChannel = async ( id: any) => {
|
||||
const handleTestChannel = async ( id: number) => {
|
||||
setChannelTesting(id);
|
||||
try {
|
||||
await alertApi.testNotifyChannel(id);
|
||||
|
||||
@@ -14,13 +14,14 @@ import {
|
||||
} from '@/components/ui/table';
|
||||
import { Search, AlertTriangle, Bell, CheckCircle, Loader2 } from 'lucide-react';
|
||||
import { alertApi } from '@/api';
|
||||
import type { AlertEventRecord } from '@/types';
|
||||
|
||||
export function AlertEventsTab({
|
||||
alerts,
|
||||
loading,
|
||||
onReload,
|
||||
}: {
|
||||
alerts: any[];
|
||||
alerts: AlertEventRecord[];
|
||||
loading: boolean;
|
||||
onReload: () => void;
|
||||
}) {
|
||||
@@ -38,7 +39,7 @@ export function AlertEventsTab({
|
||||
return matchesSearch && matchesSeverity && matchesStatus;
|
||||
});
|
||||
|
||||
const getSeverityBadge = ( severity: any) => {
|
||||
const getSeverityBadge = ( severity: string) => {
|
||||
switch (severity) {
|
||||
case 'critical':
|
||||
return (
|
||||
@@ -66,7 +67,7 @@ export function AlertEventsTab({
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusBadge = ( status: any) => {
|
||||
const getStatusBadge = ( status: string) => {
|
||||
switch (status) {
|
||||
case 'firing':
|
||||
return (
|
||||
@@ -85,7 +86,7 @@ export function AlertEventsTab({
|
||||
}
|
||||
};
|
||||
|
||||
const handleAcknowledge = async ( id: any) => {
|
||||
const handleAcknowledge = async ( id: number) => {
|
||||
try {
|
||||
await alertApi.acknowledge(id);
|
||||
onReload();
|
||||
|
||||
@@ -18,16 +18,17 @@ import { AlertRulesTab } from './AlertRulesTab';
|
||||
import { AlertRuleDialog } from './AlertRuleDialog';
|
||||
import { AlertChannelDialog } from './AlertChannelDialog';
|
||||
import type { JsonData } from '@/types/common';
|
||||
import type { AlertEventRecord, AlertRuleRecord, AlertChannel } from '@/types';
|
||||
|
||||
export function AlertManager() {
|
||||
const [activeTab, setActiveTab] = useState('events');
|
||||
const [alerts, setAlerts] = useState<JsonData[]>([]);
|
||||
const [rules, setRules] = useState<JsonData[]>([]);
|
||||
const [alerts, setAlerts] = useState<AlertEventRecord[]>([]);
|
||||
const [rules, setRules] = useState<AlertRuleRecord[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [ruleDialogOpen, setRuleDialogOpen] = useState(false);
|
||||
const [editingRule, setEditingRule] = useState<JsonData>(null);
|
||||
const [editingRule, setEditingRule] = useState<AlertRuleRecord | null>(null);
|
||||
const [channelDialogOpen, setChannelDialogOpen] = useState(false);
|
||||
const [notifyChannels, setNotifyChannels] = useState<JsonData[]>([]);
|
||||
const [notifyChannels, setNotifyChannels] = useState<AlertChannel[]>([]);
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
@@ -56,7 +57,7 @@ export function AlertManager() {
|
||||
setRuleDialogOpen(true);
|
||||
};
|
||||
|
||||
const openEditRule = (rule: JsonData) => {
|
||||
const openEditRule = (rule: AlertRuleRecord) => {
|
||||
setEditingRule(rule);
|
||||
setRuleDialogOpen(true);
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@ import { Settings, Trash2 } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import { alertApi } from '@/api';
|
||||
import { renderRuleChannels } from './alertShared';
|
||||
import type { AlertChannel, AlertRuleRecord } from '@/types';
|
||||
|
||||
const COL_SPAN = 9; // 复选框 + 7 数据列 + 操作列 // Logan.Li
|
||||
|
||||
@@ -26,10 +27,10 @@ export function AlertRulesTab({
|
||||
onReload,
|
||||
onEditRule,
|
||||
}: {
|
||||
rules: any[];
|
||||
notifyChannels: any[];
|
||||
rules: AlertRuleRecord[];
|
||||
notifyChannels: AlertChannel[];
|
||||
onReload: () => void;
|
||||
onEditRule: ( rule: any) => void;
|
||||
onEditRule: ( rule: AlertRuleRecord) => void;
|
||||
}) {
|
||||
const confirm = useConfirm();
|
||||
const [selectedIds, setSelectedIds] = useState<number[]>([]);
|
||||
@@ -50,7 +51,7 @@ export function AlertRulesTab({
|
||||
);
|
||||
};
|
||||
|
||||
const handleDeleteRule = async ( id: any) => {
|
||||
const handleDeleteRule = async ( id: number) => {
|
||||
const ok = await confirm({
|
||||
title: '删除告警规则',
|
||||
description: '确定删除此告警规则?',
|
||||
@@ -89,7 +90,7 @@ export function AlertRulesTab({
|
||||
}
|
||||
};
|
||||
|
||||
const handleToggleRule = async ( id: any) => {
|
||||
const handleToggleRule = async ( id: number) => {
|
||||
try {
|
||||
await alertApi.toggleRule(id);
|
||||
onReload();
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Mail, Phone, MessageSquare, Bell } from 'lucide-react';
|
||||
import type { AlertChannel, AlertRuleRecord } from '@/types';
|
||||
|
||||
// 将 notify_channels (可能是数组/JSON字符串/逗号分隔) 解析为数组
|
||||
export function parseChannels( notifyChannels: any): string[] {
|
||||
export function parseChannels( notifyChannels: unknown): string[] {
|
||||
if (!notifyChannels) return [];
|
||||
if (Array.isArray(notifyChannels)) return notifyChannels;
|
||||
if (typeof notifyChannels === 'string') {
|
||||
@@ -30,7 +31,7 @@ export const CHANNEL_TYPE_LABELS: Record<string, string> = {
|
||||
|
||||
// 解析单个 notify_channels 引用值 — 返回 {id?, type, name, isLegacy}
|
||||
// 新格式存渠道 ID (string),旧格式存类型字符串 (email/dingtalk 等)
|
||||
export function resolveChannelRef(value: string, channels: any[]): { type: string; name: string; isLegacy: boolean } | null {
|
||||
export function resolveChannelRef(value: string, channels: AlertChannel[]): { type: string; name: string; isLegacy: boolean } | null {
|
||||
if (!value) return null;
|
||||
// 尝试按 ID 匹配已配置渠道
|
||||
const ch = channels.find((c) => String(c.id) === String(value));
|
||||
@@ -71,7 +72,7 @@ export function getChannelIcon(type: string) {
|
||||
}
|
||||
|
||||
// 渲染规则的通知渠道引用列表(兼容新格式=渠道ID 和 旧格式=类型字符串)
|
||||
export function renderRuleChannels( rule: any, notifyChannels: any[]) {
|
||||
export function renderRuleChannels( rule: AlertRuleRecord, notifyChannels: AlertChannel[]) {
|
||||
const refs = parseChannels(rule.notify_channels);
|
||||
if (refs.length === 0) return <span className="text-xs text-slate-600">--</span>;
|
||||
return (
|
||||
|
||||
Reference in new issue
Block a user