feat: 工具配置增强、站点拨测与日志中心多项改进

- 工具配置:新增卸载 API/UI、Go 工具链、流水线构建自动安装依赖
- 修复 ListTools VerifyTool 递归栈溢出导致获取工具配置失败
- Node.js Alpine 安装改用 unofficial-builds musl 预编译包与 index.json 解析 LTS
- 站点拨测:详情页路由、告警自动恢复、并发拨测与默认可观测频率
- 日志中心:RBAC、查询构建器、ES/Loki 增强与审计 request/response 展示
- 发布配置:构建节点、流水线远程执行、Deploy RBAC 导航拆分
This commit is contained in:
Logan committed 2026-07-02 07:47:33 +08:00
1 parent cde7bbe540
commit 7e1c8f583c
84 files changed
+7729 -1507

No files matched your search

@@ -0,0 +1,121 @@
import { useMemo } from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Loader2, Save } from 'lucide-react';
import { matchPipelines, type PipelineMatchItem } from './matchPipeline';
interface JobRoleCreateDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
pipelines: PipelineMatchItem[];
saving: boolean;
name: string;
code: string;
pattern: string;
role: string;
description: string;
onNameChange: (v: string) => void;
onCodeChange: (v: string) => void;
onPatternChange: (v: string) => void;
onRoleChange: (v: string) => void;
onDescriptionChange: (v: string) => void;
onSubmit: () => void;
}
// 新建 Job 权限角色弹窗 // Logan.Li
export function JobRoleCreateDialog({
open,
onOpenChange,
pipelines,
saving,
name,
code,
pattern,
role,
description,
onNameChange,
onCodeChange,
onPatternChange,
onRoleChange,
onDescriptionChange,
onSubmit,
}: JobRoleCreateDialogProps) {
const preview = useMemo(() => matchPipelines(pattern, pipelines), [pattern, pipelines]);
const patternHint = useMemo(() => {
if (!pattern.trim()) return '输入正则,匹配流水线 code 或 Job 名称';
try {
// eslint-disable-next-line no-new
new RegExp(pattern);
return preview.length > 0 ? `命中 ${preview.length} 个发布任务` : '无匹配任务';
} catch {
return '正则语法错误';
}
}, [pattern, preview.length]);
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-md bg-[var(--dg-bg-primary)] border-[var(--dg-border)]">
<DialogHeader>
<DialogTitle>新建 Job 权限</DialogTitle>
<DialogDescription className="text-[var(--dg-text-muted)]">
按正则匹配流水线 code 或 Job 名称,分配 owner / editor / viewer 权限
</DialogDescription>
</DialogHeader>
<div className="space-y-3">
<div className="grid grid-cols-2 gap-2">
<div className="space-y-1">
<Label className="text-xs">名称</Label>
<Input value={name} onChange={(e) => onNameChange(e.target.value)} placeholder="后端发布" className="h-8 text-sm dg-input" />
</div>
<div className="space-y-1">
<Label className="text-xs">标识</Label>
<Input value={code} onChange={(e) => onCodeChange(e.target.value)} placeholder="backend-jobs" className="h-8 text-sm font-mono dg-input" />
</div>
</div>
<div className="space-y-1">
<Label className="text-xs">匹配正则</Label>
<Input
value={pattern}
onChange={(e) => onPatternChange(e.target.value)}
placeholder="^cex-.* 或 ^api-server$"
className="h-8 text-sm font-mono dg-input"
/>
<p className="text-[10px] text-[var(--dg-text-muted)]">{patternHint}</p>
</div>
<div className="space-y-1">
<Label className="text-xs">权限级别</Label>
<Select value={role} onValueChange={onRoleChange}>
<SelectTrigger className="h-8 text-sm dg-input"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="owner">拥有者 — 配置 + 执行 + 授权</SelectItem>
<SelectItem value="editor">编辑者 — 配置 + 执行</SelectItem>
<SelectItem value="viewer">查看者 — 只读</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-1">
<Label className="text-xs">说明</Label>
<Input value={description} onChange={(e) => onDescriptionChange(e.target.value)} placeholder="可选" className="h-8 text-sm dg-input" />
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => onOpenChange(false)}>取消</Button>
<Button onClick={onSubmit} disabled={saving}>
{saving ? <Loader2 className="h-4 w-4 animate-spin mr-1" /> : <Save className="h-4 w-4 mr-1" />}
创建
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}