修改SSH链接属性字段
This commit is contained in:
parent
00628d2811
commit
5b6a06016e
@ -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,28 +275,27 @@ 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') {
|
||||
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│ ⚠️ 安全提示:本次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');
|
||||
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;
|
||||
}
|
||||
} catch (error) {
|
||||
@ -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>
|
||||
)}
|
||||
|
||||
|
||||
@ -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';
|
||||
|
||||
29
frontend/src/router/RootRedirect.tsx
Normal file
29
frontend/src/router/RootRedirect.tsx
Normal 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;
|
||||
@ -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: (
|
||||
<ProtectedRoute requiresAuth={true}>
|
||||
<Suspense fallback={<RouteLoading />}>
|
||||
<NoPermission />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
// 403 无权限页面
|
||||
|
||||
@ -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(() => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user