refactor: 清理SQL审计全栈死代码 (删除孤立组件+handler+model+路由+mock+API定义, 共-1278行)

SQL审计菜单此前已从侧边栏移除,但死代码残留(违反step5清理清单):
- 删除 frontend/components/audit/SQLAuditManager.tsx (629行, 无任何外部引用)
- 删除 backend/handlers/sql_audit.go (435行) + backend/models/sql_audit.go (64行)
- 移除 routes.go 中13条SQL审计路由 (API不再暴露, 返回404)
- 移除 database.go AutoMigrate 中2个SQL审计表
- 移除 api.ts sqlAuditApi 对象 (37行)
- 移除 mocks SQL审计mock数据 (90行) + barrel引用
This commit is contained in:
attacker committed 2026-06-16 03:05:14 +08:00
1 parent a6e8b5ab4f
commit a26f186e9b
8 files changed
+1 -1278

No files matched your search

@@ -1,629 +0,0 @@
import React, { useState, useEffect } from 'react';
import {
Search, Filter, Eye, Download, Shield, AlertTriangle, CheckCircle, XCircle,
Database, FileText, Clock, User, Server, Code, AlertOctagon, RotateCcw,
ChevronDown, ChevronUp, Play, Pause, Settings, Trash2, RefreshCw, History
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import {
Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter
} from '@/components/ui/dialog';
import {
Select, SelectContent, SelectItem, SelectTrigger, SelectValue
} from '@/components/ui/select';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Textarea } from '@/components/ui/textarea';
import { Switch } from '@/components/ui/switch';
import { Label } from '@/components/ui/label';
import { Separator } from '@/components/ui/separator';
import { toast } from 'sonner';
import { sqlAuditApi } from '@/services/api';
interface SQLAuditRecord {
id: string;
sqlContent: string;
sqlType: string;
databaseName: string;
tableName: string;
riskLevel: 'high' | 'medium' | 'low' | 'safe';
riskRules: string[];
status: 'pending' | 'approved' | 'rejected' | 'executed';
executor: string;
approver?: string;
executeTime?: string;
createdAt: string;
affectedRows?: number;
executionTime?: number;
sourceIP: string;
application: string;
ticketId?: string;
}
interface AuditRule {
id: string;
name: string;
description: string;
enabled: boolean;
severity: 'high' | 'medium' | 'low';
category: string;
}
const mapRecord = (r: any): SQLAuditRecord => ({
id: String(r.id),
sqlContent: r.sql_content || '',
sqlType: r.sql_type || '',
databaseName: r.database_name || '',
tableName: r.table_name || '',
riskLevel: (r.risk_level || 'safe') as SQLAuditRecord['riskLevel'],
riskRules: (() => { try { return JSON.parse(r.risk_rules || '[]'); } catch { return []; } })(),
status: (r.status || 'pending') as SQLAuditRecord['status'],
executor: r.executor || '',
approver: r.approver || '',
executeTime: r.execute_time || undefined,
createdAt: r.created_at ? new Date(r.created_at).toLocaleString('zh-CN') : '',
affectedRows: r.affected_rows || 0,
executionTime: r.execution_time || undefined,
sourceIP: r.source_ip || '',
application: r.application || '',
ticketId: r.ticket_id || '',
});
const mapRule = (r: any): AuditRule => ({
id: String(r.id),
name: r.name || '',
description: r.description || '',
enabled: r.enabled ?? true,
severity: (r.severity || 'medium') as AuditRule['severity'],
category: r.category || '',
});
const SQLAuditManager: React.FC = () => {
const [records, setRecords] = useState<SQLAuditRecord[]>([]);
const [rules, setRules] = useState<AuditRule[]>([]);
const [loading, setLoading] = useState(false);
const [searchQuery, setSearchQuery] = useState('');
const [riskFilter, setRiskFilter] = useState<string>('all');
const [statusFilter, setStatusFilter] = useState<string>('all');
const [selectedRecord, setSelectedRecord] = useState<SQLAuditRecord | null>(null);
const [detailOpen, setDetailOpen] = useState(false);
const [newAuditOpen, setNewAuditOpen] = useState(false);
const [rulesOpen, setRulesOpen] = useState(false);
const [sqlContent, setSqlContent] = useState('');
const [activeTab, setActiveTab] = useState('records');
useEffect(() => {
fetchRecords();
fetchRules();
}, []);
const fetchRecords = async () => {
setLoading(true);
try {
const res = await sqlAuditApi.getRecords();
setRecords((res.list || []).map(mapRecord));
} catch (error) {
toast.error('获取SQL审计记录失败');
} finally {
setLoading(false);
}
};
const fetchRules = async () => {
try {
const res = await sqlAuditApi.getRules();
setRules((res.list || []).map(mapRule));
} catch (error) {
toast.error('获取审计规则失败');
}
};
const handleAnalyzeSQL = async () => {
if (!sqlContent.trim()) {
toast.error('请输入SQL语句');
return;
}
try {
await sqlAuditApi.analyzeSQL({ sql: sqlContent });
toast.success('SQL分析完成');
setNewAuditOpen(false);
setSqlContent('');
fetchRecords();
} catch (error) {
toast.error('SQL分析失败');
}
};
const handleApprove = async (id: string) => {
try {
await sqlAuditApi.approve(id);
toast.success('SQL已审批通过');
fetchRecords();
} catch (error) {
toast.error('审批失败');
}
};
const handleReject = async (id: string) => {
try {
await sqlAuditApi.reject(id);
toast.success('SQL已驳回');
fetchRecords();
} catch (error) {
toast.error('驳回失败');
}
};
const handleExecute = async (id: string) => {
try {
await sqlAuditApi.execute(id);
toast.success('SQL执行成功');
fetchRecords();
} catch (error) {
toast.error('SQL执行失败');
}
};
const toggleRule = async (ruleId: string) => {
try {
await sqlAuditApi.toggleRule(ruleId);
fetchRules();
toast.success('规则状态已更新');
} catch (error) {
toast.error('更新规则失败');
}
};
const getRiskBadge = (level: string) => {
switch (level) {
case 'high':
return <Badge variant="destructive" className="gap-1"><AlertOctagon className="w-3 h-3" />高危</Badge>;
case 'medium':
return <Badge variant="default" className="bg-orange-500 gap-1"><AlertTriangle className="w-3 h-3" />中危</Badge>;
case 'low':
return <Badge variant="default" className="bg-yellow-500 gap-1"><AlertTriangle className="w-3 h-3" />低危</Badge>;
case 'safe':
return <Badge variant="default" className="bg-green-500 gap-1"><CheckCircle className="w-3 h-3" />安全</Badge>;
default:
return <Badge variant="secondary">未知</Badge>;
}
};
const getStatusBadge = (status: string) => {
switch (status) {
case 'pending':
return <Badge variant="outline" className="gap-1"><Clock className="w-3 h-3" />待审批</Badge>;
case 'approved':
return <Badge variant="default" className="bg-blue-500 gap-1"><CheckCircle className="w-3 h-3" />已批准</Badge>;
case 'rejected':
return <Badge variant="destructive" className="gap-1"><XCircle className="w-3 h-3" />已驳回</Badge>;
case 'executed':
return <Badge variant="default" className="bg-green-500 gap-1"><CheckCircle className="w-3 h-3" />已执行</Badge>;
default:
return <Badge variant="secondary">未知</Badge>;
}
};
const filteredRecords = records.filter(record => {
const matchSearch =
record.sqlContent.toLowerCase().includes(searchQuery.toLowerCase()) ||
record.databaseName.toLowerCase().includes(searchQuery.toLowerCase()) ||
record.executor.toLowerCase().includes(searchQuery.toLowerCase());
const matchRisk = riskFilter === 'all' || record.riskLevel === riskFilter;
const matchStatus = statusFilter === 'all' || record.status === statusFilter;
return matchSearch && matchRisk && matchStatus;
});
const stats = {
total: records.length,
highRisk: records.filter(r => r.riskLevel === 'high').length,
pending: records.filter(r => r.status === 'pending').length,
executed: records.filter(r => r.status === 'executed').length
};
return (
<div className="space-y-6">
{/* 统计卡片 */}
<div className="grid grid-cols-4 gap-4">
<Card>
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-muted-foreground">总审计数</p>
<p className="text-2xl font-bold">{stats.total}</p>
</div>
<div className="p-3 bg-blue-100 rounded-lg">
<Database className="w-5 h-5 text-blue-600" />
</div>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-muted-foreground">高危SQL</p>
<p className="text-2xl font-bold text-red-600">{stats.highRisk}</p>
</div>
<div className="p-3 bg-red-100 rounded-lg">
<AlertOctagon className="w-5 h-5 text-red-600" />
</div>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-muted-foreground">待审批</p>
<p className="text-2xl font-bold text-orange-600">{stats.pending}</p>
</div>
<div className="p-3 bg-orange-100 rounded-lg">
<Clock className="w-5 h-5 text-orange-600" />
</div>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-muted-foreground">已执行</p>
<p className="text-2xl font-bold text-green-600">{stats.executed}</p>
</div>
<div className="p-3 bg-green-100 rounded-lg">
<CheckCircle className="w-5 h-5 text-green-600" />
</div>
</div>
</CardContent>
</Card>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-2 lg:w-[400px]">
<TabsTrigger value="records">审计记录</TabsTrigger>
<TabsTrigger value="rules">审计规则</TabsTrigger>
</TabsList>
<TabsContent value="records" className="space-y-4">
{/* 操作栏 */}
<div className="flex flex-wrap items-center justify-between gap-4">
<div className="flex items-center gap-2 flex-1 min-w-[300px]">
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
<Input
placeholder="搜索SQL内容、数据库、执行人..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-10"
/>
</div>
<Select value={riskFilter} onValueChange={setRiskFilter}>
<SelectTrigger className="w-[120px]">
<SelectValue placeholder="风险等级" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">全部风险</SelectItem>
<SelectItem value="high">高危</SelectItem>
<SelectItem value="medium">中危</SelectItem>
<SelectItem value="low">低危</SelectItem>
<SelectItem value="safe">安全</SelectItem>
</SelectContent>
</Select>
<Select value={statusFilter} onValueChange={setStatusFilter}>
<SelectTrigger className="w-[120px]">
<SelectValue placeholder="状态" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">全部状态</SelectItem>
<SelectItem value="pending">待审批</SelectItem>
<SelectItem value="approved">已批准</SelectItem>
<SelectItem value="rejected">已驳回</SelectItem>
<SelectItem value="executed">已执行</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex items-center gap-2">
<Button variant="outline" onClick={fetchRecords}>
<RefreshCw className="w-4 h-4 mr-2" />
刷新
</Button>
<Button onClick={() => setNewAuditOpen(true)}>
<Shield className="w-4 h-4 mr-2" />
新建审计
</Button>
</div>
</div>
{/* 记录列表 */}
<Card>
<CardContent className="p-0">
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-muted/50">
<tr>
<th className="px-4 py-3 text-left text-sm font-medium">SQL内容</th>
<th className="px-4 py-3 text-left text-sm font-medium">数据库</th>
<th className="px-4 py-3 text-left text-sm font-medium">风险等级</th>
<th className="px-4 py-3 text-left text-sm font-medium">状态</th>
<th className="px-4 py-3 text-left text-sm font-medium">执行人</th>
<th className="px-4 py-3 text-left text-sm font-medium">创建时间</th>
<th className="px-4 py-3 text-left text-sm font-medium">操作</th>
</tr>
</thead>
<tbody className="divide-y">
{loading ? (
<tr>
<td colSpan={7} className="px-4 py-8 text-center text-muted-foreground">
加载中...
</td>
</tr>
) : filteredRecords.length === 0 ? (
<tr>
<td colSpan={7} className="px-4 py-8 text-center text-muted-foreground">
暂无SQL审计记录
</td>
</tr>
) : (
filteredRecords.map((record) => (
<tr key={record.id} className="hover:bg-muted/50">
<td className="px-4 py-3">
<div className="max-w-[300px] truncate font-mono text-sm" title={record.sqlContent}>
{record.sqlContent}
</div>
<div className="flex gap-1 mt-1">
{record.riskRules.map((rule, idx) => (
<Badge key={idx} variant="outline" className="text-xs">{rule}</Badge>
))}
</div>
</td>
<td className="px-4 py-3 text-sm">
<div>{record.databaseName}</div>
<div className="text-muted-foreground text-xs">{record.tableName}</div>
</td>
<td className="px-4 py-3">{getRiskBadge(record.riskLevel)}</td>
<td className="px-4 py-3">{getStatusBadge(record.status)}</td>
<td className="px-4 py-3 text-sm">{record.executor}</td>
<td className="px-4 py-3 text-sm text-muted-foreground">{record.createdAt}</td>
<td className="px-4 py-3">
<div className="flex items-center gap-1">
<Button
variant="ghost"
size="sm"
onClick={() => { setSelectedRecord(record); setDetailOpen(true); }}
>
<Eye className="w-4 h-4" />
</Button>
{record.status === 'pending' && (
<>
<Button
variant="ghost"
size="sm"
onClick={() => handleApprove(record.id)}
>
<CheckCircle className="w-4 h-4 text-green-600" />
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => handleReject(record.id)}
>
<XCircle className="w-4 h-4 text-red-600" />
</Button>
</>
)}
{record.status === 'approved' && (
<Button
variant="ghost"
size="sm"
onClick={() => handleExecute(record.id)}
>
<Play className="w-4 h-4 text-blue-600" />
</Button>
)}
</div>
</td>
</tr>
))
)}
</tbody>
</table>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="rules" className="space-y-4">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Shield className="w-5 h-5" />
SQL审计规则配置
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
{rules.map((rule) => (
<div key={rule.id} className="flex items-center justify-between p-4 border rounded-lg">
<div className="flex-1">
<div className="flex items-center gap-2">
<span className="font-medium">{rule.name}</span>
<Badge variant={rule.severity === 'high' ? 'destructive' : rule.severity === 'medium' ? 'default' : 'secondary'}>
{rule.severity === 'high' ? '高危' : rule.severity === 'medium' ? '中危' : '低危'}
</Badge>
<Badge variant="outline">{rule.category}</Badge>
</div>
<p className="text-sm text-muted-foreground mt-1">{rule.description}</p>
</div>
<div className="flex items-center gap-4">
<div className="flex items-center gap-2">
<Switch
checked={rule.enabled}
onCheckedChange={() => toggleRule(rule.id)}
/>
<Label className="text-sm">{rule.enabled ? '已启用' : '已禁用'}</Label>
</div>
</div>
</div>
))}
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
{/* 新建审计对话框 */}
<Dialog open={newAuditOpen} onOpenChange={setNewAuditOpen}>
<DialogContent className="max-w-3xl">
<DialogHeader>
<DialogTitle>新建SQL审计</DialogTitle>
<DialogDescription>输入SQL语句进行风险分析和审计</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div>
<Label>SQL语句</Label>
<Textarea
value={sqlContent}
onChange={(e) => setSqlContent(e.target.value)}
placeholder="请输入需要审计的SQL语句..."
className="min-h-[200px] font-mono text-sm"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<Label>目标数据库</Label>
<Select>
<SelectTrigger>
<SelectValue placeholder="选择数据库" />
</SelectTrigger>
<SelectContent>
<SelectItem value="db1">生产数据库</SelectItem>
<SelectItem value="db2">测试数据库</SelectItem>
<SelectItem value="db3">开发数据库</SelectItem>
</SelectContent>
</Select>
</div>
<div>
<Label>执行环境</Label>
<Select>
<SelectTrigger>
<SelectValue placeholder="选择环境" />
</SelectTrigger>
<SelectContent>
<SelectItem value="prod">生产环境</SelectItem>
<SelectItem value="staging">预发环境</SelectItem>
<SelectItem value="dev">开发环境</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setNewAuditOpen(false)}>取消</Button>
<Button onClick={handleAnalyzeSQL}>
<Shield className="w-4 h-4 mr-2" />
分析SQL
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* 详情对话框 */}
<Dialog open={detailOpen} onOpenChange={setDetailOpen}>
<DialogContent className="max-w-3xl">
<DialogHeader>
<DialogTitle>SQL审计详情</DialogTitle>
<DialogDescription className="sr-only">查看SQL审计记录详情</DialogDescription>
</DialogHeader>
{selectedRecord && (
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<Label className="text-muted-foreground">数据库</Label>
<p className="font-medium">{selectedRecord.databaseName}</p>
</div>
<div>
<Label className="text-muted-foreground">数据表</Label>
<p className="font-medium">{selectedRecord.tableName}</p>
</div>
<div>
<Label className="text-muted-foreground">SQL类型</Label>
<p className="font-medium">{selectedRecord.sqlType}</p>
</div>
<div>
<Label className="text-muted-foreground">风险等级</Label>
<p>{getRiskBadge(selectedRecord.riskLevel)}</p>
</div>
<div>
<Label className="text-muted-foreground">状态</Label>
<p>{getStatusBadge(selectedRecord.status)}</p>
</div>
<div>
<Label className="text-muted-foreground">执行人</Label>
<p className="font-medium">{selectedRecord.executor}</p>
</div>
</div>
<div>
<Label className="text-muted-foreground">SQL内容</Label>
<div className="p-3 bg-muted rounded-lg font-mono text-sm mt-1">
<pre>{selectedRecord.sqlContent}</pre>
</div>
</div>
{selectedRecord.riskRules.length > 0 && (
<div>
<Label className="text-muted-foreground">触发规则</Label>
<div className="flex gap-2 mt-1">
{selectedRecord.riskRules.map((rule, idx) => (
<Badge key={idx} variant="destructive">{rule}</Badge>
))}
</div>
</div>
)}
{selectedRecord.executionTime && (
<div className="grid grid-cols-2 gap-4">
<div>
<Label className="text-muted-foreground">执行时间</Label>
<p className="font-medium">{selectedRecord.executeTime}</p>
</div>
<div>
<Label className="text-muted-foreground">执行耗时</Label>
<p className="font-medium">{selectedRecord.executionTime}ms</p>
</div>
<div>
<Label className="text-muted-foreground">影响行数</Label>
<p className="font-medium">{selectedRecord.affectedRows}</p>
</div>
</div>
)}
</div>
)}
<DialogFooter>
<Button variant="outline" onClick={() => setDetailOpen(false)}>关闭</Button>
{selectedRecord?.status === 'pending' && (
<>
<Button variant="destructive" onClick={() => { handleReject(selectedRecord.id); setDetailOpen(false); }}>
<XCircle className="w-4 h-4 mr-2" />
驳回
</Button>
<Button onClick={() => { handleApprove(selectedRecord.id); setDetailOpen(false); }}>
<CheckCircle className="w-4 h-4 mr-2" />
批准
</Button>
</>
)}
{selectedRecord?.status === 'approved' && (
<Button onClick={() => { handleExecute(selectedRecord.id); setDetailOpen(false); }}>
<Play className="w-4 h-4 mr-2" />
执行SQL
</Button>
)}
</DialogFooter>
</DialogContent>
</Dialog>
</div>
);
};
export default SQLAuditManager;
+1 -3
View File
@@ -12,7 +12,7 @@ export * from './modules/monitor';
export * from './modules/misc';
// Aggregate mockData object (cross-module references)
import { sqlAuditRecords, sqlAuditRules, resourceModels, modelInstances } from './modules/misc';
import { resourceModels, modelInstances } from './modules/misc';
import { ansiblePlaybooks, ansibleJobs, ansibleInventories, cronJobsList, cronJobExecutions, scripts } from './modules/automation';
import { logMonitorEntries, logSources, traces, serviceMetrics, traceSpans, grafanaDashboards, datasources } from './modules/monitor';
import { alertCenterAlerts, alertCenterRules, notificationChannels, recentAlerts, alertEvents } from './modules/alerts';
@@ -25,8 +25,6 @@ import { pendingApprovals, currentUser, users } from './modules/system';
import { tickets, approvalFlows, approvalInstances } from './modules/approval';
export const mockData = {
sqlAuditRecords,
sqlAuditRules,
resourceModels,
modelInstances,
ansiblePlaybooks,
-90
View File
@@ -132,96 +132,6 @@ export const modelInstances: ModelInstance[] = [
},
];
// ============ SQL审计数据 ============
export const sqlAuditRecords = [
{
id: 'audit-001',
sqlContent: 'UPDATE users SET status = "inactive" WHERE last_login < "2024-01-01"',
sqlType: 'UPDATE',
databaseName: 'production_db',
tableName: 'users',
riskLevel: 'medium',
riskRules: ['批量更新', '无WHERE条件限制'],
status: 'pending',
executor: '张工程师',
createdAt: '2025-04-14 10:30:00',
sourceIP: '192.168.1.100',
application: 'user-management-service'
},
{
id: 'audit-002',
sqlContent: 'DELETE FROM logs WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY)',
sqlType: 'DELETE',
databaseName: 'production_db',
tableName: 'logs',
riskLevel: 'high',
riskRules: ['批量删除', '影响大量数据'],
status: 'approved',
executor: '李运维',
approver: '王经理',
executeTime: '2025-04-14 09:00:00',
createdAt: '2025-04-14 08:30:00',
affectedRows: 500000,
executionTime: 12500,
sourceIP: '192.168.1.101',
application: 'log-cleanup-service'
},
{
id: 'audit-003',
sqlContent: 'SELECT * FROM orders WHERE user_id = 12345',
sqlType: 'SELECT',
databaseName: 'production_db',
tableName: 'orders',
riskLevel: 'safe',
riskRules: [],
status: 'executed',
executor: '王开发',
executeTime: '2025-04-14 10:15:00',
createdAt: '2025-04-14 10:15:00',
affectedRows: 0,
executionTime: 45,
sourceIP: '192.168.1.102',
application: 'order-service'
},
{
id: 'audit-004',
sqlContent: 'ALTER TABLE users ADD COLUMN phone_verified BOOLEAN DEFAULT FALSE',
sqlType: 'DDL',
databaseName: 'production_db',
tableName: 'users',
riskLevel: 'high',
riskRules: ['DDL操作', '表结构变更'],
status: 'pending',
executor: '张工程师',
createdAt: '2025-04-14 11:00:00',
sourceIP: '192.168.1.100',
application: 'db-migration-tool'
},
{
id: 'audit-005',
sqlContent: 'DROP TABLE temp_backup_2024',
sqlType: 'DDL',
databaseName: 'production_db',
tableName: 'temp_backup_2024',
riskLevel: 'high',
riskRules: ['删除表', '高危操作'],
status: 'rejected',
executor: '李运维',
createdAt: '2025-04-14 09:30:00',
sourceIP: '192.168.1.101',
application: 'cleanup-script'
}
];
export const sqlAuditRules = [
{ id: 'rule-001', name: '禁止无WHERE条件的UPDATE', description: '检测UPDATE语句是否包含WHERE条件', enabled: true, severity: 'high', category: 'DML' },
{ id: 'rule-002', name: '禁止无WHERE条件的DELETE', description: '检测DELETE语句是否包含WHERE条件', enabled: true, severity: 'high', category: 'DML' },
{ id: 'rule-003', name: '限制批量更新影响行数', description: 'UPDATE/DELETE影响行数超过1000条需要审批', enabled: true, severity: 'medium', category: 'DML' },
{ id: 'rule-004', name: 'DDL操作需要审批', description: 'ALTER、DROP等DDL操作需要高级审批', enabled: true, severity: 'high', category: 'DDL' },
{ id: 'rule-005', name: '禁止SELECT *', description: '建议明确指定查询字段', enabled: false, severity: 'low', category: 'DQL' },
{ id: 'rule-006', name: '敏感表操作审计', description: '对用户表、订单表等敏感表的操作进行审计', enabled: true, severity: 'medium', category: 'AUDIT' }
];
// ============ Ansible数据 ============
// mockData aggregate moved to data.ts barrel (references cross-module exports)
-37
View File
@@ -527,43 +527,6 @@ export const auditApi = {
},
};
// ==================== SQL 审计 API ====================
export const sqlAuditApi = {
getRecords: async (params?: Record<string, string>) => {
return http.get<ListResponse>('/sql-audit/records', params);
},
getRules: async () => {
return http.get<ListResponse>('/sql-audit/rules');
},
analyzeSQL: async (data: { sql: string; database_name?: string; application?: string }) => {
return http.post('/sql-audit/analyze', data);
},
approve: async (id: string | number) => {
return http.post(`/sql-audit/records/${id}/approve`);
},
reject: async (id: string | number) => {
return http.post(`/sql-audit/records/${id}/reject`);
},
execute: async (id: string | number) => {
return http.post(`/sql-audit/records/${id}/execute`);
},
deleteRecord: async (id: string | number) => {
return http.delete(`/sql-audit/records/${id}`);
},
createRule: async (data: any) => {
return http.post('/sql-audit/rules', data);
},
updateRule: async (id: string | number, data: any) => {
return http.put(`/sql-audit/rules/${id}`, data);
},
deleteRule: async (id: string | number) => {
return http.delete(`/sql-audit/rules/${id}`);
},
toggleRule: async (ruleId: string | number) => {
return http.post(`/sql-audit/rules/${ruleId}/toggle`);
},
};
// ==================== Ansible 自动化 API ====================
export const ansibleApi = {
// Playbook 管理