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:
1 parent
cac1414917
commit
a3cd0d3910
29 files changed
+2954
-180
No files matched your search
@@ -16,6 +16,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { kubernetesApi } from '@/services/api';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { useUserStore } from '@/store';
|
||||
|
||||
interface DeploymentDetailProps {
|
||||
clusterCode: string;
|
||||
@@ -69,10 +70,9 @@ export function DeploymentDetail({ clusterCode, namespace, deploymentName, onBac
|
||||
);
|
||||
setPods(depPods);
|
||||
|
||||
// Filter HPAs targeting this deployment
|
||||
// Filter HPAs targeting this deployment (flattened data)
|
||||
const depHpas = allHpas.filter((h: any) => {
|
||||
const ref = h.spec?.scaleTargetRef;
|
||||
return ref?.name === deploymentName && h.metadata?.namespace === namespace;
|
||||
return h.target_name === deploymentName && h.namespace === namespace;
|
||||
});
|
||||
setHpas(depHpas);
|
||||
} catch (e) {
|
||||
@@ -134,9 +134,9 @@ export function DeploymentDetail({ clusterCode, namespace, deploymentName, onBac
|
||||
|
||||
const handleHpaToggle = async (hpa: any) => {
|
||||
// Not direct toggle — delete HPA
|
||||
if (!confirm(`确定删除 HPA "${hpa.metadata?.name}"?`)) return;
|
||||
if (!confirm(`确定删除 HPA "${hpa.name}"?`)) return;
|
||||
try {
|
||||
await kubernetesApi.deleteHPA(clusterCode, namespace, hpa.metadata?.name);
|
||||
await kubernetesApi.deleteHPA(clusterCode, namespace, hpa.name);
|
||||
toast({ title: 'HPA 已删除' });
|
||||
loadData();
|
||||
} catch (e: any) {
|
||||
@@ -406,7 +406,7 @@ export function DeploymentDetail({ clusterCode, namespace, deploymentName, onBac
|
||||
onClick={async () => {
|
||||
setLogs('');
|
||||
// Stream mode
|
||||
const token = localStorage.getItem('token') || '';
|
||||
const token = useUserStore.getState().token || '';
|
||||
const url = `${window.location.protocol}//${window.location.host}/api/v1/kubernetes/${clusterCode}/namespace/${namespace}/pods/${logPod}/logs?tail=${logTail}&stream=true`;
|
||||
try {
|
||||
const resp = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
|
||||
@@ -471,18 +471,18 @@ export function DeploymentDetail({ clusterCode, namespace, deploymentName, onBac
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{hpas.map((hpa: any) => {
|
||||
const spec = hpa.spec || {};
|
||||
const status = hpa.status || {};
|
||||
const metrics = (spec.metrics || []).map((m: any) => {
|
||||
if (m.resource) return `${m.resource.name}: ${m.resource.target.averageUtilization}%`;
|
||||
return m.type;
|
||||
}).join(', ');
|
||||
const cpu = hpa.cpu_percent || 0;
|
||||
const mem = hpa.mem_percent || 0;
|
||||
const metricsParts = [];
|
||||
if (cpu) metricsParts.push(`CPU: ${cpu}%`);
|
||||
if (mem) metricsParts.push(`MEM: ${mem}%`);
|
||||
const metrics = metricsParts.join(', ') || '-';
|
||||
return (
|
||||
<TableRow key={hpa.metadata?.name} className="border-slate-800 hover:bg-slate-800/30">
|
||||
<TableCell className="font-medium text-slate-200">{hpa.metadata?.name}</TableCell>
|
||||
<TableCell className="text-slate-300">{spec.minReplicas} / {spec.maxReplicas}</TableCell>
|
||||
<TableCell className="text-sm text-slate-400">{metrics || '-'}</TableCell>
|
||||
<TableCell className="text-slate-300">{status.currentReplicas || 0} / {status.desiredReplicas || 0}</TableCell>
|
||||
<TableRow key={hpa.name} className="border-slate-800 hover:bg-slate-800/30">
|
||||
<TableCell className="font-medium text-slate-200">{hpa.name}</TableCell>
|
||||
<TableCell className="text-slate-300">{hpa.min_replicas} / {hpa.max_replicas}</TableCell>
|
||||
<TableCell className="text-sm text-slate-400">{metrics}</TableCell>
|
||||
<TableCell className="text-slate-300">{hpa.current_replicas || 0}</TableCell>
|
||||
<TableCell>
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 text-red-400 hover:text-red-300"
|
||||
onClick={() => handleHpaToggle(hpa)} title="删除 HPA">
|
||||
|
||||
Reference in new issue
Block a user