feat: PTY kubectl终端 + 侧边栏对齐修复 + 构建缓存清理

**kubectl WebShell 修复:**
- 引入 github.com/creack/pty 库实现真正的伪终端
- 替换 bash --noediting + TERM=dumb 方案为 PTY-backed bash
- 支持 xterm-256color、窗口自适应、readline 交互
- 修复键盘输入无响应问题

**侧边栏优化:**
- 独立菜单项高度从 py-1.5 改为 h-8,与父菜单对齐
- 独立菜单图标从 h-3 w-3 改为 h-3.5 w-3.5,统一 14px

**构建优化:**
- run.sh docker up 增加 docker image prune -f 清理悬空镜像
- 每次部署自动清理 builder cache + dangling images

**认证修复(历史):**
- Token 存储:Zustand + localStorage 双写,http.ts 双源 fallback
- YamlViewer/DeploymentDetail token 来源统一为 Zustand store
This commit is contained in:
attacker committed 2026-06-19 11:22:45 +00:00
1 parent cac1414917
commit a3cd0d3910
29 files changed
+2954 -180

No files matched your search

+71 -1
View File
@@ -219,7 +219,7 @@ export const kubernetesApi = {
// === HPA 管理 (Logan.Li) ===
getHPAs: async (clusterCode: string, namespace?: string) => {
if (namespace) return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/hpas`);
return http.get(`/kubernetes-clusters/${clusterCode}/hpas`); // fallback old route
return http.get(`/kubernetes/${clusterCode}/hpas`); // AllNS route
},
createHPA: async (clusterCode: string, data: { name: string; namespace: string; targetKind: string; targetName: string; minReplicas: number; maxReplicas: number; cpuPercent: number; memPercent: number }) => {
return http.post(`/kubernetes/${clusterCode}/namespace/${data.namespace}/hpas`, data);
@@ -270,6 +270,76 @@ export const kubernetesApi = {
getNamespaces: async (clusterCode: string) => {
return http.get(`/kubernetes/${clusterCode}/namespaces`);
},
// === All-namespace listing (Logan.Li) ===
getAllDeployments: async (clusterCode: string) => {
return http.get(`/kubernetes/${clusterCode}/deployments`);
},
getAllPods: async (clusterCode: string) => {
return http.get(`/kubernetes/${clusterCode}/pods`);
},
// === PVC (存储) ===
getPVCs: async (clusterCode: string, namespace: string) => {
return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/pvcs`);
},
deletePVC: async (clusterCode: string, namespace: string, name: string) => {
return http.delete(`/kubernetes/${clusterCode}/namespace/${namespace}/pvcs/${name}`);
},
// === StatefulSet / DaemonSet ===
getStatefulSets: async (clusterCode: string, namespace: string) => {
return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/statefulsets`);
},
getDaemonSets: async (clusterCode: string, namespace: string) => {
return http.get(`/kubernetes/${clusterCode}/namespace/${namespace}/daemonsets`);
},
// === YAML ===
getResourceYaml: async (clusterCode: string, namespace: string, resource: string, name: string) => {
return http.getRaw(`/kubernetes/${clusterCode}/namespace/${namespace}/${resource}/${name}/yaml`);
},
updateResourceYaml: async (clusterCode: string, namespace: string, resource: string, name: string, yaml: string) => {
return http.putRaw(`/kubernetes/${clusterCode}/namespace/${namespace}/${resource}/${name}/yaml`, yaml, 'application/yaml');
},
// === Export CSV ===
exportAsCSV: (filename: string, data: any[]) => {
if (data.length === 0) return;
const headers = Object.keys(data[0]);
const csv = [headers.join(','), ...data.map((row: any) => headers.map(h => JSON.stringify(row[h] ?? '')).join(','))].join('\n');
const blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8;' });
const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = filename; a.click();
},
exportAsJSON: (filename: string, data: any[]) => {
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = filename; a.click();
},
// === 🆕 Pod Metrics (kubectl top pods) ===
getPodMetrics: async (clusterCode: string, namespace?: string) => {
const params = namespace ? { namespace } : undefined;
return http.get(`/kubernetes/${clusterCode}/metrics/pods`, params);
},
// === 🆕 Events ===
getEvents: async (clusterCode: string, namespace?: string, type?: string) => {
const params: any = {};
if (namespace) params.namespace = namespace;
if (type) params.type = type;
return http.get(`/kubernetes/${clusterCode}/events`, params);
},
// === 🆕 Capacity Analysis ===
getCapacity: async (clusterCode: string, namespace?: string) => {
const params = namespace ? { namespace } : undefined;
return http.get(`/kubernetes/${clusterCode}/capacity`, params);
},
// === 🆕 YAML Apply ===
applyYAML: async (clusterCode: string, yaml: string) => {
return http.post(`/kubernetes/${clusterCode}/yaml/apply`, { yaml });
},
};
// ==================== 告警 API ====================