修改SSH链接属性字段
This commit is contained in:
parent
00628d2811
commit
5b6a06016e
@ -40,13 +40,13 @@ interface SSHTerminalContentProps {
|
|||||||
onStatusChange?: (status: ConnectionStatus) => void; // 状态变化回调
|
onStatusChange?: (status: ConnectionStatus) => void; // 状态变化回调
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConnectionStatus = 'initializing' | 'connecting' | 'connected' | 'disconnecting' | 'disconnected' | 'error';
|
type ConnectionStatus = 'connecting' | 'connected' | 'reconnecting' | 'disconnected' | 'error';
|
||||||
|
|
||||||
interface WebSocketMessage {
|
interface WebSocketMessage {
|
||||||
type: 'output' | 'error' | 'status';
|
type: 'output' | 'input' | 'status' | 'error';
|
||||||
data?: string;
|
data: string;
|
||||||
message?: string;
|
timestamp?: number;
|
||||||
status?: ConnectionStatus;
|
metadata?: Record<string, any> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SSHTerminalContent: React.FC<SSHTerminalContentProps> = ({
|
export const SSHTerminalContent: React.FC<SSHTerminalContentProps> = ({
|
||||||
@ -65,7 +65,7 @@ export const SSHTerminalContent: React.FC<SSHTerminalContentProps> = ({
|
|||||||
const onStatusChangeRef = useRef(onStatusChange);
|
const onStatusChangeRef = useRef(onStatusChange);
|
||||||
const isClosingRef = useRef<boolean>(false);
|
const isClosingRef = useRef<boolean>(false);
|
||||||
|
|
||||||
const [connectionStatus, setConnectionStatus] = useState<ConnectionStatus>('initializing');
|
const [connectionStatus, setConnectionStatus] = useState<ConnectionStatus>('connecting');
|
||||||
const [errorMessage, setErrorMessage] = useState<string>('');
|
const [errorMessage, setErrorMessage] = useState<string>('');
|
||||||
const [fontSize, setFontSize] = useState<number>(14);
|
const [fontSize, setFontSize] = useState<number>(14);
|
||||||
const [showSearch, setShowSearch] = useState<boolean>(false);
|
const [showSearch, setShowSearch] = useState<boolean>(false);
|
||||||
@ -158,7 +158,7 @@ export const SSHTerminalContent: React.FC<SSHTerminalContentProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setConnectionStatus('initializing');
|
setConnectionStatus('connecting');
|
||||||
setErrorMessage('');
|
setErrorMessage('');
|
||||||
|
|
||||||
const terminal = new Terminal({
|
const terminal = new Terminal({
|
||||||
@ -275,27 +275,26 @@ export const SSHTerminalContent: React.FC<SSHTerminalContentProps> = ({
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'error':
|
case 'error':
|
||||||
console.error('❌ SSH错误:', msg.message);
|
console.error('❌ SSH错误:', msg.data);
|
||||||
terminalInstanceRef.current?.writeln(`\r\n\x1b[31m错误: ${msg.message}\x1b[0m\r\n`);
|
terminalInstanceRef.current?.writeln(`\r\n\x1b[31m错误: ${msg.data}\x1b[0m\r\n`);
|
||||||
message.error(msg.message || '连接错误');
|
message.error(msg.data || '连接错误');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'status':
|
case 'status':
|
||||||
console.log('📊 状态变化:', msg.status);
|
console.log('📊 状态变化:', msg.data);
|
||||||
if (msg.status) {
|
setConnectionStatus(msg.data as ConnectionStatus);
|
||||||
setConnectionStatus(msg.status);
|
if (msg.data === 'connected') {
|
||||||
if (msg.status === 'connected') {
|
// 显示审计警告
|
||||||
// 显示审计警告
|
terminalInstanceRef.current?.writeln('\r\n\x1b[33m┌─────────────────────────────────────────────────────────────\x1b[0m');
|
||||||
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│ 本次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');
|
||||||
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('\x1b[33m└─────────────────────────────────────────────────────────────\x1b[0m\r\n');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
fitAddonRef.current?.fit();
|
fitAddonRef.current?.fit();
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -376,7 +375,6 @@ export const SSHTerminalContent: React.FC<SSHTerminalContentProps> = ({
|
|||||||
const gracefulClose = useCallback(() => {
|
const gracefulClose = useCallback(() => {
|
||||||
console.log('🚪 开始优雅关闭 SSH连接');
|
console.log('🚪 开始优雅关闭 SSH连接');
|
||||||
isClosingRef.current = true;
|
isClosingRef.current = true;
|
||||||
setConnectionStatus('disconnecting');
|
|
||||||
terminalInstanceRef.current?.writeln('\r\n\x1b[33m正在断开连接...\x1b[0m');
|
terminalInstanceRef.current?.writeln('\r\n\x1b[33m正在断开连接...\x1b[0m');
|
||||||
|
|
||||||
if (wsRef.current) {
|
if (wsRef.current) {
|
||||||
@ -574,14 +572,12 @@ export const SSHTerminalContent: React.FC<SSHTerminalContentProps> = ({
|
|||||||
|
|
||||||
const getStatusBadge = () => {
|
const getStatusBadge = () => {
|
||||||
switch (connectionStatus) {
|
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':
|
case 'connecting':
|
||||||
return <Badge variant="outline" className="bg-yellow-100 text-yellow-700"><Loader2 className="mr-1 h-3 w-3 animate-spin" />连接中</Badge>;
|
return <Badge variant="outline" className="bg-yellow-100 text-yellow-700"><Loader2 className="mr-1 h-3 w-3 animate-spin" />连接中</Badge>;
|
||||||
case 'connected':
|
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>;
|
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':
|
case 'reconnecting':
|
||||||
return <Badge variant="outline" className="bg-orange-100 text-orange-700"><Loader2 className="mr-1 h-3 w-3 animate-spin" />断开中</Badge>;
|
return <Badge variant="outline" className="bg-orange-100 text-orange-700"><Loader2 className="mr-1 h-3 w-3 animate-spin" />重连中</Badge>;
|
||||||
case 'error':
|
case 'error':
|
||||||
return <Badge variant="outline" className="bg-red-100 text-red-700"><XCircle className="mr-1 h-3 w-3" />连接失败</Badge>;
|
return <Badge variant="outline" className="bg-red-100 text-red-700"><XCircle className="mr-1 h-3 w-3" />连接失败</Badge>;
|
||||||
case 'disconnected':
|
case 'disconnected':
|
||||||
@ -721,17 +717,17 @@ export const SSHTerminalContent: React.FC<SSHTerminalContentProps> = ({
|
|||||||
className="w-full h-full p-2 ssh-terminal-content"
|
className="w-full h-full p-2 ssh-terminal-content"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{connectionStatus === 'initializing' && (
|
{connectionStatus === 'connecting' && (
|
||||||
<div className="absolute inset-0 flex flex-col items-center justify-center text-gray-400 bg-[#1e1e1e]">
|
<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" />
|
<Loader2 className="h-12 w-12 animate-spin mb-4" />
|
||||||
<p className="text-lg">初始化SSH终端...</p>
|
<p className="text-lg">正在连接SSH...</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{connectionStatus === 'disconnecting' && (
|
{connectionStatus === 'reconnecting' && (
|
||||||
<div className="absolute inset-0 flex flex-col items-center justify-center text-orange-400 bg-[#1e1e1e]">
|
<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" />
|
<Loader2 className="h-12 w-12 animate-spin mb-4" />
|
||||||
<p className="text-lg">正在断开连接...</p>
|
<p className="text-lg">连接中断,正在重连...</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export interface SSHWindow {
|
|||||||
isMinimized: boolean;
|
isMinimized: boolean;
|
||||||
position: { x: number; y: number };
|
position: { x: number; y: number };
|
||||||
size: { width: number; height: number };
|
size: { width: number; height: number };
|
||||||
connectionStatus?: 'initializing' | 'connecting' | 'connected' | 'disconnecting' | 'disconnected' | 'error';
|
connectionStatus?: 'connecting' | 'connected' | 'reconnecting' | 'disconnected' | 'error';
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SSHWindowManagerProps {
|
interface SSHWindowManagerProps {
|
||||||
@ -32,7 +32,7 @@ export const SSHWindowManager: React.FC<SSHWindowManagerProps> = ({ onOpenWindow
|
|||||||
isMinimized: false,
|
isMinimized: false,
|
||||||
position: { x: 100 + offset, y: 100 + offset },
|
position: { x: 100 + offset, y: 100 + offset },
|
||||||
size: { width: 1200, height: 700 },
|
size: { width: 1200, height: 700 },
|
||||||
connectionStatus: 'initializing',
|
connectionStatus: 'connecting',
|
||||||
};
|
};
|
||||||
|
|
||||||
setWindows(prev => [...prev, newWindow]);
|
setWindows(prev => [...prev, newWindow]);
|
||||||
@ -123,9 +123,8 @@ export const SSHWindowManager: React.FC<SSHWindowManagerProps> = ({ onOpenWindow
|
|||||||
case 'disconnected':
|
case 'disconnected':
|
||||||
return 'bg-red-600 hover:bg-red-700';
|
return 'bg-red-600 hover:bg-red-700';
|
||||||
case 'connecting':
|
case 'connecting':
|
||||||
case 'initializing':
|
|
||||||
return 'bg-yellow-600 hover:bg-yellow-700';
|
return 'bg-yellow-600 hover:bg-yellow-700';
|
||||||
case 'disconnecting':
|
case 'reconnecting':
|
||||||
return 'bg-orange-600 hover:bg-orange-700';
|
return 'bg-orange-600 hover:bg-orange-700';
|
||||||
default:
|
default:
|
||||||
return 'bg-blue-600 hover:bg-blue-700';
|
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 ProtectedRoute from './ProtectedRoute';
|
||||||
import RouteLoading from '@/components/RouteLoading';
|
import RouteLoading from '@/components/RouteLoading';
|
||||||
import ErrorFallback from '@/components/ErrorFallback';
|
import ErrorFallback from '@/components/ErrorFallback';
|
||||||
|
import RootRedirect from './RootRedirect';
|
||||||
|
|
||||||
// 错误页面
|
// 错误页面
|
||||||
const Forbidden = lazy(() => import('@/pages/Error/403'));
|
const Forbidden = lazy(() => import('@/pages/Error/403'));
|
||||||
@ -97,7 +98,7 @@ export const createDynamicRouter = () => {
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
element: hasMenus ? <Navigate to="/dashboard" replace /> : <Navigate to="/no-permission" replace />,
|
element: <RootRedirect />,
|
||||||
},
|
},
|
||||||
// 写死的测试路由:表单设计器测试页面
|
// 写死的测试路由:表单设计器测试页面
|
||||||
{
|
{
|
||||||
@ -116,9 +117,11 @@ export const createDynamicRouter = () => {
|
|||||||
{
|
{
|
||||||
path: 'no-permission',
|
path: 'no-permission',
|
||||||
element: (
|
element: (
|
||||||
<Suspense fallback={<RouteLoading />}>
|
<ProtectedRoute requiresAuth={true}>
|
||||||
<NoPermission />
|
<Suspense fallback={<RouteLoading />}>
|
||||||
</Suspense>
|
<NoPermission />
|
||||||
|
</Suspense>
|
||||||
|
</ProtectedRoute>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
// 403 无权限页面
|
// 403 无权限页面
|
||||||
|
|||||||
@ -2,6 +2,8 @@ import axios, {AxiosRequestConfig, AxiosResponse} from 'axios';
|
|||||||
import {toast} from '@/components/ui/use-toast';
|
import {toast} from '@/components/ui/use-toast';
|
||||||
import { API_BASE_URL, isDev } from '@/config/env';
|
import { API_BASE_URL, isDev } from '@/config/env';
|
||||||
import { maintenanceDetector } from '@/services/maintenanceDetector';
|
import { maintenanceDetector } from '@/services/maintenanceDetector';
|
||||||
|
import store from '../store';
|
||||||
|
import { logout } from '../store/userSlice';
|
||||||
|
|
||||||
export interface Response<T = any> {
|
export interface Response<T = any> {
|
||||||
code: number;
|
code: number;
|
||||||
@ -100,11 +102,8 @@ const errorHandler = (error: any) => {
|
|||||||
variant: 'destructive'
|
variant: 'destructive'
|
||||||
});
|
});
|
||||||
|
|
||||||
// 清除本地存储的所有用户相关信息
|
// 使用统一的 logout action 清除所有用户数据
|
||||||
localStorage.removeItem('token');
|
store.dispatch(logout());
|
||||||
localStorage.removeItem('userInfo');
|
|
||||||
localStorage.removeItem('menus');
|
|
||||||
localStorage.removeItem('tenantId');
|
|
||||||
|
|
||||||
// 延迟跳转,确保提示能显示出来
|
// 延迟跳转,确保提示能显示出来
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user