feat: 平台配置全面生效 — 配置策略接入实际逻辑
- query组: ExecuteQuery读取query_limit_max/query_timeout/query_dangerous_pattern - 查询超时通过context.WithTimeout生效,db.QueryContext/QueryRowContext - 危险模式检测拦截无WHERE的DELETE/UPDATE/DROP/TRUNCATE - LIMIT上限从settings读取,不再硬编码1000 - security组: Login读取login_max_attempts/login_lockout_minutes - 新增User字段: failed_login_attempts + locked_until - 登录失败达到阈值自动锁定,成功时清除计数 - JWT Token过期时间读取session_timeout_hours设置 - executor组: ExecuteTicket读取executor_timeout/executor_dry_run - 执行超时通过context.WithTimeout控制 - 开启dry_run时先EXPLAIN预检查SQL语法 - db.ExecContext替代db.Exec - cleanup组: SettingsCleanupPreview/Run从占位改为真实实现 - 预览统计4类数据(工单/查询历史/慢日志/审计日志)待清理数量 - 执行清理按retention_days配置删除过期数据 - ghost组: 13项gh-ost配置加入seedDefaultSettings - enabled/path/mode/osc_priority/env/cloud/trigger/chunk/load/lag等 - 已被ghost.go中getGhostConfig等函数读取 - 前端: PlatformConfigPage新增Ghost/OSC分组显示
This commit is contained in:
1 parent
8af868b3ca
commit
bc2092a980
11 files changed
+365
-45
No files matched your search
@@ -71,6 +71,7 @@ const envColor: Record<string, string> = {
|
||||
const statusLabel: Record<string, { text: string; online: boolean }> = {
|
||||
connected: { text: '在线', online: true },
|
||||
online: { text: '在线', online: true },
|
||||
active: { text: '在线', online: true },
|
||||
disconnected: { text: '离线', online: false },
|
||||
offline: { text: '离线', online: false },
|
||||
error: { text: '异常', online: false },
|
||||
@@ -146,6 +147,7 @@ export default function InstancePage() {
|
||||
const [batchDeleting, setBatchDeleting] = useState(false);
|
||||
const [showClone, setShowClone] = useState(false);
|
||||
const [cloneSource, setCloneSource] = useState<InstanceItem | null>(null);
|
||||
const [deleteConfirm, setDeleteConfirm] = useState<{msg: string; onConfirm: () => void} | null>(null);
|
||||
|
||||
const typeTabs = [
|
||||
{ key: 'all', label: '全部', icon: LayoutGrid },
|
||||
@@ -729,11 +731,11 @@ function InstanceDetail({ instance, onBack, config, canEdit, canDelete, onDelete
|
||||
}) {
|
||||
const [testing, setTesting] = useState(false);
|
||||
const [testResult, setTestResult] = useState<{ success: boolean; message: string; version?: string } | null>(null);
|
||||
const [metrics, setMetrics] = useState<{ connections: number; qps: number; latency: number; uptime: string } | null>(null);
|
||||
const [metrics, setMetrics] = useState<{ connections: number; qps: number; latency: number; uptime: string; cpu?: number; memory?: number; disk?: number; used_memory_human?: string; threads_running?: number; max_connections?: number; slow_queries?: number; data_size_mb?: number } | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
instanceApi.metrics(instance.id).then(res => {
|
||||
setMetrics({ connections: res.connections ?? 0, qps: res.qps ?? 0, latency: res.latency ?? 0, uptime: res.uptime ?? '-' });
|
||||
setMetrics({ connections: res.connections ?? 0, qps: res.qps ?? 0, latency: res.latency ?? 0, uptime: res.uptime ?? '-', cpu: res.cpu, memory: res.memory, disk: res.disk, used_memory_human: res.used_memory_human, threads_running: res.threads_running, max_connections: res.max_connections, slow_queries: res.slow_queries, data_size_mb: res.data_size_mb });
|
||||
}).catch(() => {});
|
||||
}, [instance.id]);
|
||||
|
||||
@@ -860,21 +862,35 @@ function InstanceDetail({ instance, onBack, config, canEdit, canDelete, onDelete
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{[
|
||||
{ label: 'CPU 使用率', value: instance.cpu ?? 0, color: (instance.cpu ?? 0) > 80 ? 'bg-red-400' : (instance.cpu ?? 0) > 60 ? 'bg-yellow-400' : 'bg-green-400' },
|
||||
{ label: '内存使用率', value: instance.memory ?? 0, color: (instance.memory ?? 0) > 85 ? 'bg-red-400' : (instance.memory ?? 0) > 70 ? 'bg-yellow-400' : 'bg-green-400' },
|
||||
{ label: '磁盘使用率', value: instance.disk ?? 0, color: (instance.disk ?? 0) > 80 ? 'bg-red-400' : (instance.disk ?? 0) > 60 ? 'bg-yellow-400' : 'bg-green-400' },
|
||||
].map(item => (
|
||||
<div key={item.label}>
|
||||
<div className="flex justify-between text-xs mb-1">
|
||||
<span className="text-[var(--dg-text-muted)]">{item.label}</span>
|
||||
<span className="font-medium">{item.value}%</span>
|
||||
</div>
|
||||
<div className="h-1.5 rounded-full bg-[var(--dg-bg-tertiary)] overflow-hidden">
|
||||
<div className={`h-full rounded-full transition-all duration-500 ${item.color}`} style={{ width: `${item.value}%` }} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{(() => {
|
||||
const isRedis = instance.db_type === 'redis';
|
||||
// Dynamic metric items based on db_type
|
||||
const items = isRedis ? [
|
||||
{ label: 'CPU 使用率', value: metrics?.cpu, text: undefined, color: (metrics?.cpu ?? 0) > 80 ? 'bg-red-400' : (metrics?.cpu ?? 0) > 60 ? 'bg-yellow-400' : 'bg-green-400' },
|
||||
{ label: '内存使用率', value: metrics?.memory, text: metrics?.used_memory_human, color: (metrics?.memory ?? 0) > 85 ? 'bg-red-400' : (metrics?.memory ?? 0) > 70 ? 'bg-yellow-400' : 'bg-green-400' },
|
||||
] : [
|
||||
{ label: '连接使用率', value: metrics?.cpu, text: undefined, color: (metrics?.cpu ?? 0) > 80 ? 'bg-red-400' : (metrics?.cpu ?? 0) > 60 ? 'bg-yellow-400' : 'bg-green-400' },
|
||||
{ label: 'Buffer Pool', value: metrics?.memory, text: undefined, color: (metrics?.memory ?? 0) > 85 ? 'bg-red-400' : (metrics?.memory ?? 0) > 70 ? 'bg-yellow-400' : 'bg-green-400' },
|
||||
{ label: '慢查询', value: undefined, text: metrics?.slow_queries != null ? String(metrics.slow_queries) + ' 条' : undefined, color: '' },
|
||||
];
|
||||
return items.filter(item => item.value != null || item.text != null).map(item => {
|
||||
const hasPercent = item.value != null && item.value >= 0;
|
||||
const pct = item.value ?? 0;
|
||||
return (
|
||||
<div key={item.label}>
|
||||
<div className="flex justify-between text-xs mb-1">
|
||||
<span className="text-[var(--dg-text-muted)]">{item.label}</span>
|
||||
<span className="font-medium">{hasPercent ? `${pct}%` : (item.text || 'N/A')}</span>
|
||||
</div>
|
||||
{hasPercent && (
|
||||
<div className="h-1.5 rounded-full bg-[var(--dg-bg-tertiary)] overflow-hidden">
|
||||
<div className={`h-full rounded-full transition-all duration-500 ${item.color}`} style={{ width: `${Math.max(pct, 1)}%` }} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
})()}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -890,9 +906,9 @@ function InstanceDetail({ instance, onBack, config, canEdit, canDelete, onDelete
|
||||
<CardContent className="p-5">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
|
||||
{[
|
||||
{ label: '连接数', value: instance.connections ?? metrics?.connections ?? 0, unit: '', icon: Users, color: 'text-blue-400', bg: 'bg-blue-400/10' },
|
||||
{ label: 'QPS', value: instance.qps ?? metrics?.qps ?? 0, unit: '/s', icon: Activity, color: 'text-green-400', bg: 'bg-green-400/10' },
|
||||
{ label: '延迟', value: instance.latency ?? metrics?.latency ?? 0, unit: 'ms', icon: Signal, color: 'text-orange-400', bg: 'bg-orange-400/10' },
|
||||
{ label: '连接数', value: metrics?.connections ?? 0, unit: '', icon: Users, color: 'text-blue-400', bg: 'bg-blue-400/10' },
|
||||
{ label: 'QPS', value: metrics?.qps ?? 0, unit: '/s', icon: Activity, color: 'text-green-400', bg: 'bg-green-400/10' },
|
||||
{ label: '延迟', value: metrics?.latency ?? 0, unit: 'ms', icon: Signal, color: 'text-orange-400', bg: 'bg-orange-400/10' },
|
||||
{ label: '运行时间', value: metrics?.uptime ?? '-', unit: '', icon: HardDrive, color: 'text-purple-400', bg: 'bg-purple-400/10' },
|
||||
].map(m => (
|
||||
<div key={m.label} className="p-3 rounded-lg bg-[var(--dg-bg-tertiary)] text-center">
|
||||
|
||||
@@ -80,11 +80,12 @@ function CleanupActions() {
|
||||
|
||||
/* ── 分组元信息 ── */
|
||||
const GROUP_META: Record<string, { label: string; icon: string; desc: string }> = {
|
||||
query: { label: '查询控制', icon: '🔍', desc: 'LIMIT 上限、超时、自动 LIMIT' },
|
||||
security: { label: '安全策略', icon: '🔒', desc: '登录安全、密码策略' },
|
||||
query: { label: '查询控制', icon: '🔍', desc: 'LIMIT 上限、超时、自动 LIMIT、危险检测' },
|
||||
security: { label: '安全策略', icon: '🔒', desc: '登录锁定、密码策略、会话超时' },
|
||||
data_masking: { label: '数据脱敏', icon: '🛡️', desc: '控制查询结果中的敏感字段脱敏' },
|
||||
cleanup: { label: '日志清理', icon: '🧹', desc: '工单、审计日志、查询历史保留天数' },
|
||||
executor: { label: '执行器', icon: '⚙️', desc: 'SQL 执行超时' },
|
||||
cleanup: { label: '日志清理', icon: '🧹', desc: '工单、审计日志、查询历史、慢日志保留天数' },
|
||||
executor: { label: '执行器', icon: '⚙️', desc: 'SQL 执行超时、预检查、备份' },
|
||||
ghost: { label: 'Ghost/OSC', icon: '👻', desc: 'gh-ost 在线 DDL 变更工具配置' },
|
||||
};
|
||||
|
||||
export default function PlatformConfigPage() {
|
||||
|
||||
Reference in new issue
Block a user