feat(rdp): 新增 RDP 远程桌面/SMB/账单同步能力 + 清理编译产物

主要变更:
- RDP 远程桌面:新增 backend/pkg/rdp(RDP 引擎 + credssp/display/rle/trace)、
  backend/controller/rdp*(连接/凭证/文件/会话/实时)、frontend RDP 组件与 API
- Windows SMB 文件传输:backend/pkg/winsmb
- 云账单同步:backend/pkg/billingsync + billing_sync/aliyun_billing,前端账单详情
- Agent CLI Kubernetes 工具:kubernetes_action/get、pod_execute/logs
- 服务器导入助手:server_import_helpers、ImportCredentialSettings
- 国际化与组件重构(cloud/deploy/monitor/server/system 等多模块)
- 清理编译产物二进制:移除 backend/server、backend/agent/bin/aiops-agent-*
- .gitignore:新增 backend/agent/ 与 .cache/(避免 334MB go-build 缓存误入库)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
LoganandClaude committed 2026-07-04 01:50:36 +08:00
1 parent 777d9e4e71
commit 76ec9387f0
202 files changed
+16555 -2543

No files matched your search

@@ -10,8 +10,9 @@ import { FitAddon } from 'xterm-addon-fit';
import 'xterm/css/xterm.css';
import { Terminal, Maximize2, Minimize2, X, Loader2 } from 'lucide-react';
import { cn } from '@/lib/utils';
import { attachTerminalResize, mountXterm } from '@/lib/xterm-utils';
import { useUserStore } from '@/store';
import i18n from '@/i18n';
import { TerminalLoadingOverlay } from '@/components/common/TerminalLoadingOverlay';
interface KubectlTerminalProps {
open: boolean;
@@ -28,6 +29,7 @@ export function KubectlTerminal({ open, onOpenChange, clusterId, clusterName }:
const [connected, setConnected] = useState(false);
const [fullscreen, setFullscreen] = useState(false);
const [error, setError] = useState('');
const [hasOutput, setHasOutput] = useState(false);
// 重置全屏状态
useEffect(() => {
@@ -37,6 +39,7 @@ export function KubectlTerminal({ open, onOpenChange, clusterId, clusterName }:
// 连接终端
useEffect(() => {
if (!open || !clusterId) return;
setHasOutput(false);
const term = new XTerm({
cursorBlink: true,
@@ -79,40 +82,33 @@ export function KubectlTerminal({ open, onOpenChange, clusterId, clusterName }:
try {
const msg = JSON.parse(event.data);
if (msg.status === 0 && typeof msg.data === 'string') {
setHasOutput(true);
term.write(msg.data);
}
} catch {
if (typeof event.data === 'string') term.write(event.data);
if (typeof event.data === 'string') {
setHasOutput(true);
term.write(event.data);
}
}
};
ws.onopen = () => {
setConnected(true);
setError('');
if (containerRef.current) {
term.open(containerRef.current);
requestAnimationFrame(() => {
try {
fit.fit();
} catch { /* noop */ }
});
// 延迟 focus:确保 Dialog 渲染完成后再聚焦终端
setTimeout(() => {
try {
term.focus();
} catch { /* noop */ }
}, 150);
}
mountXterm(term, fit, () => containerRef.current, ws);
};
ws.onclose = () => {
setConnected(false);
term.write(i18n.t('kubernetes.kubectlterminal.sf4ee00d1'));
setHasOutput(true);
term.write('\r\n\x1b[31m[连接已断开]\x1b[0m\r\n');
};
ws.onerror = () => {
setError(i18n.t('aiops.wsConnectFailed'));
term.write(i18n.t('kubernetes.podexecterminal.sc8107726'));
setError('WebSocket 连接失败');
setHasOutput(true);
term.write('\r\n\x1b[31m[连接发生错误]\x1b[0m\r\n');
};
return () => {
@@ -128,35 +124,24 @@ export function KubectlTerminal({ open, onOpenChange, clusterId, clusterName }:
};
}, [open, clusterId]);
// ResizeObserver 监听容器变化自动 fit
// ResizeObserver 监听容器变化自动 fit,并同步远端 PTY 尺寸。 // Logan.Li
useEffect(() => {
if (!open) return;
const container = containerRef.current;
if (!container) return;
let resizeTimer: ReturnType<typeof setTimeout>;
const ro = new ResizeObserver(() => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
if (fitRef.current && termRef.current && wsRef.current) {
try {
fitRef.current.fit();
termRef.current.focus();
const { cols, rows } = termRef.current;
if (wsRef.current.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify({ status: 1, cols, rows }));
}
} catch { /* noop */ }
}
}, 60);
});
ro.observe(container);
return () => {
clearTimeout(resizeTimer);
ro.disconnect();
let cleanup: (() => void) | undefined;
let retry = 0;
const attach = () => {
const container = containerRef.current;
const fit = fitRef.current;
const term = termRef.current;
if (!container || !fit || !term) {
if (retry++ < 30) requestAnimationFrame(attach);
return;
}
cleanup = attachTerminalResize(container, fit, term, wsRef.current);
};
}, [open]);
attach();
return () => cleanup?.();
}, [open, fullscreen]);
return (
<Dialog open={open} onOpenChange={onOpenChange}>
@@ -202,11 +187,10 @@ export function KubectlTerminal({ open, onOpenChange, clusterId, clusterName }:
</div>
{/* 终端容器 */}
<div
ref={containerRef}
className="bg-[#0f172a]"
style={{ height: fullscreen ? 'calc(100vh - 49px)' : '480px' }}
/>
<div className="relative bg-[#0f172a]" style={{ height: fullscreen ? 'calc(100vh - 49px)' : '480px' }}>
<div ref={containerRef} className="xterm-terminal-host h-full" />
<TerminalLoadingOverlay show={!hasOutput && !error} label="正在连接集群..." />
</div>
</DialogContent>
</Dialog>
);