feat(alert): A2 告警引擎分布式安全 — Redis 分布式锁(SetNX)
- Cache 接口新增 SetNX 方法(go-redis 原子 / redka check-then-set 单副本安全) - AlertEngine 改造为支持分布式锁: tryAcquireLock/releaseLock/GetEngineStatus - 多副本部署时仅 Leader 执行告警检查, 60s TTL 防死锁, fail-open 容错 - 新增 GET /alerts/engine/status 监控端点(is_leader/instance_id/lock_holder) - 修复 GrafanaDashboard.tsx Panel.options 类型不匹配导致构建中断 - 同步未提交的前端类型安全迁移(140+ 文件) - ROADMAP/TDD_PLAN/PROJECT_EVALUATION 三文档同步更新 验证: go build 零错误, Docker 部署 healthy, 引擎状态端点正常, 锁竞争测试通过(非 Leader 跳过检查), 5/5 核心 API 回归通过
This commit is contained in:
1 parent
5ca0e6d83a
commit
c68aef4444
150 files changed
+1038
-690
No files matched your search
@@ -20,6 +20,7 @@ import { YamlViewer } from './YamlViewer';
|
||||
import { PodExecTerminal } from './PodExecTerminal';
|
||||
import { PodFileBrowser } from './PodFileBrowser';
|
||||
import { formatK8sTime, statusColor, STATUS_COLORS } from './k8s-yaml-utils';
|
||||
import type { JsonData } from '@/types/common';
|
||||
|
||||
type WorkloadKind = 'StatefulSet' | 'DaemonSet';
|
||||
type ViewMode = 'view' | 'yaml';
|
||||
@@ -67,14 +68,14 @@ export default function WorkloadViewPage() {
|
||||
const kind: WorkloadKind = location.pathname.includes('/DaemonSet/') ? 'DaemonSet' : 'StatefulSet';
|
||||
const mode: ViewMode = params.mode === 'yaml' ? 'yaml' : 'view';
|
||||
|
||||
const [manifest, setManifest] = useState<any>(null);
|
||||
const [pods, setPods] = useState<any[]>([]);
|
||||
const [manifest, setManifest] = useState<JsonData>(null);
|
||||
const [pods, setPods] = useState<JsonData[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [scaleModal, setScaleModal] = useState(false);
|
||||
const [scaleValue, setScaleValue] = useState(1);
|
||||
const [actionLoading, setActionLoading] = useState(false);
|
||||
const [terminalPod, setTerminalPod] = useState<any>(null);
|
||||
const [fileBrowserPod, setFileBrowserPod] = useState<any>(null);
|
||||
const [terminalPod, setTerminalPod] = useState<JsonData>(null);
|
||||
const [fileBrowserPod, setFileBrowserPod] = useState<JsonData>(null);
|
||||
const [yamlModal, setYamlModal] = useState(false);
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
@@ -95,7 +96,7 @@ export default function WorkloadViewPage() {
|
||||
|
||||
// Filter pods by name prefix and namespace
|
||||
const podList = Array.isArray(podsRes) ? podsRes : (podsRes?.list || podsRes?.data || []);
|
||||
const filtered = podList.filter(( p: any) => {
|
||||
const filtered = podList.filter((p: JsonData) => {
|
||||
const podName = p.name || p.metadata?.name || '';
|
||||
const podNs = p.namespace || p.namespace_name || '';
|
||||
return podName.startsWith(name) && (!podNs || podNs === namespace);
|
||||
@@ -384,7 +385,7 @@ export default function WorkloadViewPage() {
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{vcts.map(( vct: any, idx: number) => {
|
||||
{vcts.map((vct: JsonData, idx: number) => {
|
||||
const accessModes = (vct.spec?.accessModes || []).join(', ');
|
||||
const storageClass = vct.spec?.storageClassName || vct.spec?.storageClassName || '-';
|
||||
const size = vct.spec?.resources?.requests?.storage || '-';
|
||||
@@ -415,7 +416,7 @@ export default function WorkloadViewPage() {
|
||||
<div className="mb-3">
|
||||
<p className="text-[11px] text-slate-500 mb-2 uppercase tracking-wide">Init Containers</p>
|
||||
<div className="space-y-2">
|
||||
{initContainers.map(( c: any, idx: number) => (
|
||||
{initContainers.map((c: JsonData, idx: number) => (
|
||||
<ContainerInfo key={`init-${idx}`} c={c} />
|
||||
))}
|
||||
</div>
|
||||
@@ -426,7 +427,7 @@ export default function WorkloadViewPage() {
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
<p className="text-[11px] text-slate-500 mb-1 uppercase tracking-wide">Containers</p>
|
||||
{containers.map(( c: any, idx: number) => (
|
||||
{containers.map((c: JsonData, idx: number) => (
|
||||
<ContainerInfo key={idx} c={c} />
|
||||
))}
|
||||
</div>
|
||||
@@ -456,7 +457,7 @@ export default function WorkloadViewPage() {
|
||||
<TableBody>
|
||||
{pods.length === 0 ? (
|
||||
<TableRow><TableCell colSpan={6} className="text-center text-slate-500 py-8">暂无 Pod</TableCell></TableRow>
|
||||
) : pods.map(( pod: any) => (
|
||||
) : pods.map((pod: JsonData) => (
|
||||
<TableRow key={pod.name} className="border-slate-800 hover:bg-slate-800/30">
|
||||
<TableCell className="font-medium text-slate-200">{pod.name}</TableCell>
|
||||
<TableCell>
|
||||
@@ -531,7 +532,7 @@ export default function WorkloadViewPage() {
|
||||
clusterId={clusterCode}
|
||||
namespace={namespace}
|
||||
podName={terminalPod.name}
|
||||
containers={terminalPod.containers?.map(( c: any) => c.name).filter(Boolean) || []}
|
||||
containers={terminalPod.containers?.map((c: JsonData) => c.name).filter(Boolean) || []}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -543,7 +544,7 @@ export default function WorkloadViewPage() {
|
||||
clusterCode={clusterCode}
|
||||
namespace={namespace}
|
||||
podName={fileBrowserPod.name}
|
||||
containers={fileBrowserPod.containers?.map(( c: any) => c.name).filter(Boolean) || []}
|
||||
containers={fileBrowserPod.containers?.map((c: JsonData) => c.name).filter(Boolean) || []}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -551,7 +552,7 @@ export default function WorkloadViewPage() {
|
||||
}
|
||||
|
||||
// Container info sub-component
|
||||
function ContainerInfo({ c }: { c: any}) {
|
||||
function ContainerInfo({ c }: { c: JsonData}) {
|
||||
return (
|
||||
<div className="border border-slate-800 rounded-lg p-3 bg-slate-950/30">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
@@ -570,7 +571,7 @@ function ContainerInfo({ c }: { c: any}) {
|
||||
</div>
|
||||
<div className="text-slate-400">
|
||||
端口: <span className="text-slate-200 font-mono">
|
||||
{(c.ports || []).map(( p: any) => p.containerPort || p.port).join(', ') || '-'}
|
||||
{(c.ports || []).map((p: JsonData) => p.containerPort || p.port).join(', ') || '-'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user