修改SSH链接属性字段

This commit is contained in:
dengqichen 2025-12-06 17:01:42 +08:00
parent 00628d2811
commit 5b6a06016e
5 changed files with 74 additions and 48 deletions

View File

@ -40,13 +40,13 @@ interface SSHTerminalContentProps {
onStatusChange?: (status: ConnectionStatus) => void; // 状态变化回调
}
type ConnectionStatus = 'initializing' | 'connecting' | 'connected' | 'disconnecting' | 'disconnected' | 'error';
type ConnectionStatus = 'connecting' | 'connected' | 'reconnecting' | 'disconnected' | 'error';
interface WebSocketMessage {
type: 'output' | 'error' | 'status';
data?: string;
message?: string;
status?: ConnectionStatus;
type: 'output' | 'input' | 'status' | 'error';
data: string;
timestamp?: number;
metadata?: Record<string, any> | null;
}
export const SSHTerminalContent: React.FC<SSHTerminalContentProps> = ({
@ -65,7 +65,7 @@ export const SSHTerminalContent: React.FC<SSHTerminalContentProps> = ({
const onStatusChangeRef = useRef(onStatusChange);
const isClosingRef = useRef<boolean>(false);
const [connectionStatus, setConnectionStatus] = useState<ConnectionStatus>('initializing');
const [connectionStatus, setConnectionStatus] = useState<ConnectionStatus>('connecting');
const [errorMessage, setErrorMessage] = useState<string>('');
const [fontSize, setFontSize] = useState<number>(14);
const [showSearch, setShowSearch] = useState<boolean>(false);
@ -158,7 +158,7 @@ export const SSHTerminalContent: React.FC<SSHTerminalContentProps> = ({
}
}
setConnectionStatus('initializing');
setConnectionStatus('connecting');
setErrorMessage('');
const terminal = new Terminal({
@ -275,27 +275,26 @@ export const SSHTerminalContent: React.FC<SSHTerminalContentProps> = ({
break;
case 'error':
console.error('❌ SSH错误:', msg.message);
terminalInstanceRef.current?.writeln(`\r\n\x1b[31m错误: ${msg.message}\x1b[0m\r\n`);
message.error(msg.message || '连接错误');
console.error('❌ SSH错误:', msg.data);
terminalInstanceRef.current?.writeln(`\r\n\x1b[31m错误: ${msg.data}\x1b[0m\r\n`);
message.error(msg.data || '连接错误');
break;
case 'status':
console.log('📊 状态变化:', msg.status);
if (msg.status) {
setConnectionStatus(msg.status);
if (msg.status === 'connected') {
// 显示审计警告
terminalInstanceRef.current?.writeln('\r\n\x1b[33m┌────────────────────────────────────────────────────────────┐\x1b[0m');
terminalInstanceRef.current?.writeln('\x1b[33m│ ⚠️ 安全提示本次SSH会话将被全程审计记录 │\x1b[0m');
terminalInstanceRef.current?.writeln('\x1b[33m│ • 所有操作命令、输入、输出都将被完整记录 │\x1b[0m');
terminalInstanceRef.current?.writeln('\x1b[33m│ • 审计日志用于安全审查、故障排查和合规要求 │\x1b[0m');
terminalInstanceRef.current?.writeln('\x1b[33m│ • 请规范操作,遵守企业信息安全管理制度 │\x1b[0m');
terminalInstanceRef.current?.writeln('\x1b[33m└────────────────────────────────────────────────────────────┘\x1b[0m\r\n');
setTimeout(() => {
fitAddonRef.current?.fit();
}, 100);
}
console.log('📊 状态变化:', msg.data);
setConnectionStatus(msg.data as ConnectionStatus);
if (msg.data === 'connected') {
// 显示审计警告
terminalInstanceRef.current?.writeln('\r\n\x1b[33m┌─────────────────────────────────────────────────────────────\x1b[0m');
terminalInstanceRef.current?.writeln('\x1b[33m│ ⚠️ 链宇技术有限公司 - 安全提示\x1b[0m');
terminalInstanceRef.current?.writeln('\x1b[33m│ 本次SSH会话将被全程审计记录\x1b[0m');
terminalInstanceRef.current?.writeln('\x1b[33m│ • 所有操作命令、输入、输出都将被完整记录\x1b[0m');
terminalInstanceRef.current?.writeln('\x1b[33m│ • 审计日志用于安全审查、故障排查和合规要求\x1b[0m');
terminalInstanceRef.current?.writeln('\x1b[33m│ • 请规范操作,遵守企业信息安全管理制度\x1b[0m');
terminalInstanceRef.current?.writeln('\x1b[33m└─────────────────────────────────────────────────────────────\x1b[0m\r\n');
setTimeout(() => {
fitAddonRef.current?.fit();
}, 100);
}
break;
}
@ -376,7 +375,6 @@ export const SSHTerminalContent: React.FC<SSHTerminalContentProps> = ({
const gracefulClose = useCallback(() => {
console.log('🚪 开始优雅关闭 SSH连接');
isClosingRef.current = true;
setConnectionStatus('disconnecting');
terminalInstanceRef.current?.writeln('\r\n\x1b[33m正在断开连接...\x1b[0m');
if (wsRef.current) {
@ -574,14 +572,12 @@ export const SSHTerminalContent: React.FC<SSHTerminalContentProps> = ({
const getStatusBadge = () => {
switch (connectionStatus) {
case 'initializing':
return <Badge variant="outline" className="bg-blue-100 text-blue-700"><Loader2 className="mr-1 h-3 w-3 animate-spin" /></Badge>;
case 'connecting':
return <Badge variant="outline" className="bg-yellow-100 text-yellow-700"><Loader2 className="mr-1 h-3 w-3 animate-spin" /></Badge>;
case 'connected':
return <Badge variant="outline" className="bg-emerald-100 text-emerald-700"><div className="mr-1 h-2 w-2 rounded-full bg-emerald-500 animate-pulse" /></Badge>;
case 'disconnecting':
return <Badge variant="outline" className="bg-orange-100 text-orange-700"><Loader2 className="mr-1 h-3 w-3 animate-spin" /></Badge>;
case 'reconnecting':
return <Badge variant="outline" className="bg-orange-100 text-orange-700"><Loader2 className="mr-1 h-3 w-3 animate-spin" /></Badge>;
case 'error':
return <Badge variant="outline" className="bg-red-100 text-red-700"><XCircle className="mr-1 h-3 w-3" /></Badge>;
case 'disconnected':
@ -721,17 +717,17 @@ export const SSHTerminalContent: React.FC<SSHTerminalContentProps> = ({
className="w-full h-full p-2 ssh-terminal-content"
/>
{connectionStatus === 'initializing' && (
<div className="absolute inset-0 flex flex-col items-center justify-center text-gray-400 bg-[#1e1e1e]">
{connectionStatus === 'connecting' && (
<div className="absolute inset-0 flex flex-col items-center justify-center text-yellow-400 bg-[#1e1e1e]">
<Loader2 className="h-12 w-12 animate-spin mb-4" />
<p className="text-lg">SSH终端...</p>
<p className="text-lg">SSH...</p>
</div>
)}
{connectionStatus === 'disconnecting' && (
{connectionStatus === 'reconnecting' && (
<div className="absolute inset-0 flex flex-col items-center justify-center text-orange-400 bg-[#1e1e1e]">
<Loader2 className="h-12 w-12 animate-spin mb-4" />
<p className="text-lg">...</p>
<p className="text-lg">...</p>
</div>
)}

View File

@ -10,7 +10,7 @@ export interface SSHWindow {
isMinimized: boolean;
position: { x: number; y: number };
size: { width: number; height: number };
connectionStatus?: 'initializing' | 'connecting' | 'connected' | 'disconnecting' | 'disconnected' | 'error';
connectionStatus?: 'connecting' | 'connected' | 'reconnecting' | 'disconnected' | 'error';
}
interface SSHWindowManagerProps {
@ -32,7 +32,7 @@ export const SSHWindowManager: React.FC<SSHWindowManagerProps> = ({ onOpenWindow
isMinimized: false,
position: { x: 100 + offset, y: 100 + offset },
size: { width: 1200, height: 700 },
connectionStatus: 'initializing',
connectionStatus: 'connecting',
};
setWindows(prev => [...prev, newWindow]);
@ -123,9 +123,8 @@ export const SSHWindowManager: React.FC<SSHWindowManagerProps> = ({ onOpenWindow
case 'disconnected':
return 'bg-red-600 hover:bg-red-700';
case 'connecting':
case 'initializing':
return 'bg-yellow-600 hover:bg-yellow-700';
case 'disconnecting':
case 'reconnecting':
return 'bg-orange-600 hover:bg-orange-700';
default:
return 'bg-blue-600 hover:bg-blue-700';

View File

@ -0,0 +1,29 @@
import React from 'react';
import { Navigate } from 'react-router-dom';
import { useSelector } from 'react-redux';
import type { RootState } from '@/store';
/**
*
*
* 1. ->
* 2. + -> Dashboard
* 3. + ->
*/
const RootRedirect: React.FC = () => {
const token = useSelector((state: RootState) => state.user.token);
const menus = useSelector((state: RootState) => state.user.menus);
const hasMenus = menus && menus.length > 0;
// 未登录,跳转到登录页
if (!token) {
return <Navigate to="/login" replace />;
}
// 已登录,根据菜单权限跳转
return hasMenus
? <Navigate to="/dashboard" replace />
: <Navigate to="/no-permission" replace />;
};
export default RootRedirect;

View File

@ -8,6 +8,7 @@ import store from '../store';
import ProtectedRoute from './ProtectedRoute';
import RouteLoading from '@/components/RouteLoading';
import ErrorFallback from '@/components/ErrorFallback';
import RootRedirect from './RootRedirect';
// 错误页面
const Forbidden = lazy(() => import('@/pages/Error/403'));
@ -97,7 +98,7 @@ export const createDynamicRouter = () => {
children: [
{
path: '',
element: hasMenus ? <Navigate to="/dashboard" replace /> : <Navigate to="/no-permission" replace />,
element: <RootRedirect />,
},
// 写死的测试路由:表单设计器测试页面
{
@ -116,9 +117,11 @@ export const createDynamicRouter = () => {
{
path: 'no-permission',
element: (
<Suspense fallback={<RouteLoading />}>
<NoPermission />
</Suspense>
<ProtectedRoute requiresAuth={true}>
<Suspense fallback={<RouteLoading />}>
<NoPermission />
</Suspense>
</ProtectedRoute>
),
},
// 403 无权限页面

View File

@ -2,6 +2,8 @@ import axios, {AxiosRequestConfig, AxiosResponse} from 'axios';
import {toast} from '@/components/ui/use-toast';
import { API_BASE_URL, isDev } from '@/config/env';
import { maintenanceDetector } from '@/services/maintenanceDetector';
import store from '../store';
import { logout } from '../store/userSlice';
export interface Response<T = any> {
code: number;
@ -100,11 +102,8 @@ const errorHandler = (error: any) => {
variant: 'destructive'
});
// 清除本地存储的所有用户相关信息
localStorage.removeItem('token');
localStorage.removeItem('userInfo');
localStorage.removeItem('menus');
localStorage.removeItem('tenantId');
// 使用统一的 logout action 清除所有用户数据
store.dispatch(logout());
// 延迟跳转,确保提示能显示出来
setTimeout(() => {