update
This commit is contained in:
@@ -321,7 +321,13 @@ export const getRdpSessionToken = async (req: Request, res: Response): Promise<v
|
||||
}
|
||||
|
||||
// 4. 调用 GuacamoleService 获取 RDP 令牌
|
||||
const guacamoleToken = await GuacamoleService.getRdpToken(connection, decryptedPassword);
|
||||
// 注意:从 connection.extras 或其他地方获取 RDP 特定的 width, height, dpi
|
||||
const { width, height, dpi } = req.query; // 或者从 connection.extras 获取
|
||||
const rdpWidth = width ? parseInt(width as string, 10) : undefined;
|
||||
const rdpHeight = height ? parseInt(height as string, 10) : undefined;
|
||||
const rdpDpi = dpi ? dpi as string : undefined;
|
||||
|
||||
const guacamoleToken = await GuacamoleService.getRemoteDesktopToken('rdp', connection, decryptedPassword, rdpWidth, rdpHeight, rdpDpi);
|
||||
|
||||
console.log(`[Controller:getRdpSessionToken] Received Guacamole token via GuacamoleService for RDP connection ${connectionId}`);
|
||||
|
||||
@@ -329,39 +335,35 @@ export const getRdpSessionToken = async (req: Request, res: Response): Promise<v
|
||||
res.status(200).json({ token: guacamoleToken });
|
||||
|
||||
} catch (error: any) {
|
||||
console.error(`Controller: 获取 RDP 会话令牌时发生错误 (ID: ${req.params.id}):`, error.message); // Log error message
|
||||
console.error(`Controller: 获取 RDP 会话令牌时发生错误 (ID: ${req.params.id}):`, error.message);
|
||||
|
||||
let statusCode = 500;
|
||||
let responseMessage = '获取 RDP 会话令牌时发生内部服务器错误。';
|
||||
|
||||
// 检查错误是否来自 GuacamoleService 或其内部的 axios 调用
|
||||
if (error.message.includes('调用 RDP 后端服务失败') || error.message.includes('从 RDP 后端获取令牌失败')) {
|
||||
if (error.message.includes('调用 RDP 后端服务失败') || error.message.includes('从 RDP 后端获取令牌失败') || error.message.includes('调用 Remote Gateway API 时出错 (RDP)')) {
|
||||
responseMessage = error.message;
|
||||
// 尝试从错误消息中提取状态码,或者根据消息内容判断
|
||||
if (error.message.includes('(状态: 4')) statusCode = 400; // 例如 400, 401, 404
|
||||
else if (error.message.includes('(状态: 5')) statusCode = 502; // 例如 500, 502, 503, 504
|
||||
else statusCode = 503; // Service Unavailable or other specific error
|
||||
} else if (error.message.includes('RDP 连接需要使用密码认证') || error.message.includes('密码解密失败')) {
|
||||
if (error.message.includes('(状态: 4')) statusCode = 400;
|
||||
else if (error.message.includes('(状态: 5')) statusCode = 502;
|
||||
else statusCode = 503;
|
||||
} else if (error.message.includes('RDP 连接需要使用密码认证') || error.message.includes('密码解密失败') || error.message.includes('RDP 连接使用密码认证,但密码解密失败或未提供密码')) {
|
||||
responseMessage = error.message;
|
||||
statusCode = 400;
|
||||
} else if (error.message.includes('连接类型必须是 RDP')) {
|
||||
responseMessage = error.message;
|
||||
statusCode = 400;
|
||||
}
|
||||
// 可以保留对 axios.isAxiosError 的检查,以防 GuacamoleService 抛出未包装的 axios 错误
|
||||
// 但理想情况下,GuacamoleService 应该抛出更具体的错误。
|
||||
else if (axios.isAxiosError(error)) {
|
||||
responseMessage = '调用 RDP 后端服务时发生网络或请求错误。';
|
||||
responseMessage = '调用远程桌面网关服务时发生网络或请求错误。';
|
||||
if (error.response) {
|
||||
console.error('[Controller:getRdpSessionToken] RDP backend error response (unhandled by GuacamoleService):', error.response.data);
|
||||
console.error('[Controller:getRdpSessionToken] Remote Gateway error response:', error.response.data);
|
||||
responseMessage += ` (状态: ${error.response.status})`;
|
||||
statusCode = error.response.status >= 500 ? 502 : 400;
|
||||
} else if (error.request) {
|
||||
console.error('[Controller:getRdpSessionToken] No response from RDP backend (unhandled by GuacamoleService).');
|
||||
console.error('[Controller:getRdpSessionToken] No response from Remote Gateway.');
|
||||
responseMessage += ' (无法连接或超时)';
|
||||
statusCode = 504;
|
||||
}
|
||||
} else if (error.message.includes('解密失败')) { // General decryption error from ConnectionService
|
||||
} else if (error.message.includes('解密失败')) {
|
||||
responseMessage = '获取 RDP 会话令牌时发生内部错误(凭证处理失败)。';
|
||||
}
|
||||
res.status(statusCode).json({ message: responseMessage });
|
||||
@@ -380,7 +382,6 @@ export const getVncSessionToken = async (req: Request, res: Response): Promise<v
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 获取连接信息和解密后的凭证
|
||||
const connectionData = await ConnectionService.getConnectionWithDecryptedCredentials(connectionId);
|
||||
|
||||
if (!connectionData) {
|
||||
@@ -390,13 +391,11 @@ export const getVncSessionToken = async (req: Request, res: Response): Promise<v
|
||||
|
||||
const { connection, decryptedPassword } = connectionData;
|
||||
|
||||
// 2. 验证连接类型是否为 VNC
|
||||
if (connection.type !== 'VNC') {
|
||||
res.status(400).json({ message: '此连接类型不是 VNC。' });
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 更新 last_connected_at
|
||||
try {
|
||||
const currentTimeSeconds = Math.floor(Date.now() / 1000);
|
||||
await ConnectionRepository.updateLastConnected(connectionId, currentTimeSeconds);
|
||||
@@ -405,58 +404,52 @@ export const getVncSessionToken = async (req: Request, res: Response): Promise<v
|
||||
console.error(`[Controller:getVncSessionToken] 更新 VNC 连接 ${connectionId} 的 last_connected_at 时出错:`, updateError);
|
||||
}
|
||||
|
||||
// 4. 验证 VNC 连接是否使用密码认证 (VNC 通常总是需要密码)
|
||||
if (connection.auth_method !== 'password' || !decryptedPassword) {
|
||||
console.warn(`[Controller:getVncSessionToken] VNC connection ${connectionId} does not use password auth or password decryption failed.`);
|
||||
res.status(400).json({ message: 'VNC 连接需要使用密码认证,或密码解密失败。' });
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. 从查询参数中获取可选的 width 和 height
|
||||
const { width, height } = req.query;
|
||||
const initialWidth = width ? parseInt(width as string, 10) : undefined;
|
||||
const initialHeight = height ? parseInt(height as string, 10) : undefined;
|
||||
|
||||
// 6. 调用 GuacamoleService 获取 VNC 令牌,传递尺寸信息
|
||||
const guacamoleToken = await GuacamoleService.getVncToken(connection, decryptedPassword, initialWidth, initialHeight);
|
||||
const guacamoleToken = await GuacamoleService.getRemoteDesktopToken('vnc', connection, decryptedPassword, initialWidth, initialHeight);
|
||||
|
||||
console.log(`[Controller:getVncSessionToken] Received Guacamole token via GuacamoleService for VNC connection ${connectionId} with size ${initialWidth}x${initialHeight}`);
|
||||
|
||||
// 6. 将 Guacamole 令牌返回给前端
|
||||
res.status(200).json({ token: guacamoleToken });
|
||||
|
||||
} catch (error: any) {
|
||||
console.error(`Controller: 获取 VNC 会话令牌时发生错误 (ID: ${req.params.id}):`, error.message); // Log error message
|
||||
console.error(`Controller: 获取 VNC 会话令牌时发生错误 (ID: ${req.params.id}):`, error.message);
|
||||
|
||||
let statusCode = 500;
|
||||
let responseMessage = '获取 VNC 会话令牌时发生内部服务器错误。';
|
||||
|
||||
// 检查错误是否来自 GuacamoleService 或其内部的 axios 调用
|
||||
if (error.message.includes('调用 VNC 后端服务失败') || error.message.includes('从 VNC 后端获取令牌失败')) {
|
||||
if (error.message.includes('调用 VNC 后端服务失败') || error.message.includes('从 VNC 后端获取令牌失败') || error.message.includes('调用 Remote Gateway API 时出错 (VNC)')) {
|
||||
responseMessage = error.message;
|
||||
if (error.message.includes('(状态: 4')) statusCode = 400;
|
||||
else if (error.message.includes('(状态: 5')) statusCode = 502;
|
||||
else statusCode = 503;
|
||||
} else if (error.message.includes('VNC 连接需要使用密码认证') || error.message.includes('密码解密失败')) {
|
||||
} else if (error.message.includes('VNC 连接需要使用密码认证') || error.message.includes('密码解密失败') || error.message.includes('VNC 连接使用密码认证,但密码解密失败或未提供密码')) {
|
||||
responseMessage = error.message;
|
||||
statusCode = 400;
|
||||
} else if (error.message.includes('连接类型必须是 VNC')) {
|
||||
responseMessage = error.message;
|
||||
statusCode = 400;
|
||||
}
|
||||
// 可以保留对 axios.isAxiosError 的检查
|
||||
else if (axios.isAxiosError(error)) {
|
||||
responseMessage = '调用 VNC 后端服务时发生网络或请求错误。';
|
||||
responseMessage = '调用远程桌面网关服务时发生网络或请求错误。';
|
||||
if (error.response) {
|
||||
console.error('[Controller:getVncSessionToken] VNC backend error response (unhandled by GuacamoleService):', error.response.data);
|
||||
console.error('[Controller:getVncSessionToken] Remote Gateway error response:', error.response.data);
|
||||
responseMessage += ` (状态: ${error.response.status})`;
|
||||
statusCode = error.response.status >= 500 ? 502 : 400;
|
||||
} else if (error.request) {
|
||||
console.error('[Controller:getVncSessionToken] No response from VNC backend (unhandled by GuacamoleService).');
|
||||
console.error('[Controller:getVncSessionToken] No response from Remote Gateway.');
|
||||
responseMessage += ' (无法连接或超时)';
|
||||
statusCode = 504;
|
||||
}
|
||||
} else if (error.message.includes('解密失败')) { // General decryption error from ConnectionService
|
||||
} else if (error.message.includes('解密失败')) {
|
||||
responseMessage = '获取 VNC 会话令牌时发生内部错误(凭证处理失败)。';
|
||||
}
|
||||
res.status(statusCode).json({ message: responseMessage });
|
||||
|
||||
@@ -1,131 +1,93 @@
|
||||
import axios from 'axios';
|
||||
import { ConnectionWithTags } from '../types/connection.types';
|
||||
|
||||
// RDP 后端服务的 Base URL
|
||||
const RDP_BACKEND_API_BASE = process.env.DEPLOYMENT_MODE === 'local'
|
||||
? (process.env.RDP_BACKEND_API_BASE_LOCAL || 'http://localhost:9090')
|
||||
: (process.env.RDP_BACKEND_API_BASE_DOCKER || 'http://nexus-rdp:9090');
|
||||
// 统一远程桌面网关服务的 Base URL
|
||||
const REMOTE_GATEWAY_API_BASE = process.env.DEPLOYMENT_MODE === 'local'
|
||||
? process.env.REMOTE_GATEWAY_API_BASE_LOCAL || 'http://localhost:9090'
|
||||
: process.env.REMOTE_GATEWAY_API_BASE_DOCKER || 'http://remote-gateway:9090';
|
||||
|
||||
console.log(`[GuacamoleService] DEPLOYMENT_MODE: ${process.env.DEPLOYMENT_MODE}`);
|
||||
console.log(`[GuacamoleService] RDP_BACKEND_API_BASE_LOCAL: ${process.env.RDP_BACKEND_API_BASE_LOCAL}`);
|
||||
console.log(`[GuacamoleService] RDP_BACKEND_API_BASE_DOCKER: ${process.env.RDP_BACKEND_API_BASE_DOCKER}`);
|
||||
console.log(`[GuacamoleService] Using RDP Backend API Base: ${RDP_BACKEND_API_BASE}`);
|
||||
|
||||
console.log(`[GuacamoleService] Using Remote Gateway API Base (Local): ${process.env.REMOTE_GATEWAY_API_BASE_LOCAL}`);
|
||||
console.log(`[GuacamoleService] Using Remote Gateway API Base (Docker): ${process.env.REMOTE_GATEWAY_API_BASE_DOCKER}`);
|
||||
console.log(`[GuacamoleService] Effective Remote Gateway API Base: ${REMOTE_GATEWAY_API_BASE}`);
|
||||
|
||||
// VNC 后端服务的 Base URL
|
||||
const VNC_BACKEND_API_BASE = process.env.DEPLOYMENT_MODE === 'local'
|
||||
? (process.env.VNC_BACKEND_API_BASE_LOCAL || 'http://localhost:9091')
|
||||
: (process.env.VNC_BACKEND_API_BASE_DOCKER || 'http://nexus-vnc:9091');
|
||||
|
||||
console.log(`[GuacamoleService] VNC_BACKEND_API_BASE_LOCAL: ${process.env.VNC_BACKEND_API_BASE_LOCAL}`);
|
||||
console.log(`[GuacamoleService] VNC_BACKEND_API_BASE_DOCKER: ${process.env.VNC_BACKEND_API_BASE_DOCKER}`);
|
||||
console.log(`[GuacamoleService] Using VNC Backend API Base: ${VNC_BACKEND_API_BASE}`);
|
||||
interface TokenResponse {
|
||||
token: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 RDP 后端服务获取 Guacamole 令牌
|
||||
* 从统一远程桌面网关服务获取 Guacamole 令牌
|
||||
* @param protocol 'rdp' 或 'vnc'
|
||||
* @param connection 连接对象
|
||||
* @param decryptedPassword 解密后的密码
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param dpi DPI (主要用于 RDP)
|
||||
* @returns Guacamole 令牌
|
||||
*/
|
||||
export const getRdpToken = async (connection: ConnectionWithTags, decryptedPassword?: string): Promise<string> => {
|
||||
if (connection.type !== 'RDP') {
|
||||
throw new Error('连接类型必须是 RDP。');
|
||||
export const getRemoteDesktopToken = async (
|
||||
protocol: 'rdp' | 'vnc',
|
||||
connection: ConnectionWithTags,
|
||||
decryptedPassword?: string,
|
||||
width?: number,
|
||||
height?: number,
|
||||
dpi?: string // DPI 主要用于 RDP
|
||||
): Promise<string> => {
|
||||
if ((protocol === 'rdp' || protocol === 'vnc') && connection.auth_method === 'password' && !decryptedPassword) {
|
||||
console.warn(`[GuacamoleService:getRemoteDesktopToken] ${protocol.toUpperCase()} connection ${connection.id} uses password auth but password decryption failed or password not provided.`);
|
||||
throw new Error(`${protocol.toUpperCase()} 连接使用密码认证,但密码解密失败或未提供密码。`);
|
||||
}
|
||||
if (connection.auth_method !== 'password' || !decryptedPassword) {
|
||||
console.warn(`[GuacamoleService:getRdpToken] RDP connection ${connection.id} does not use password auth or password decryption failed.`);
|
||||
throw new Error('RDP 连接需要使用密码认证,或密码解密失败。');
|
||||
}
|
||||
|
||||
const rdpApiParams = new URLSearchParams({
|
||||
|
||||
const connectionConfig: any = {
|
||||
hostname: connection.host,
|
||||
port: connection.port.toString(),
|
||||
username: connection.username,
|
||||
password: decryptedPassword,
|
||||
// 确保传递 RDP 特定的参数,如果存在的话
|
||||
security: (connection as any).rdp_security || 'any', // 从连接对象中获取,如果存在
|
||||
ignoreCert: String((connection as any).rdp_ignore_cert ?? true), // 从连接对象中获取,如果存在
|
||||
// 可以根据需要添加更多参数,例如 domain, width, height, dpi 等
|
||||
});
|
||||
const rdpTokenUrl = `${RDP_BACKEND_API_BASE}/api/get-token?${rdpApiParams.toString()}`;
|
||||
width: String(width || 1024), // 提供默认值
|
||||
height: String(height || 768), // 提供默认值
|
||||
};
|
||||
|
||||
console.log(`[GuacamoleService:getRdpToken] Calling RDP backend API: ${RDP_BACKEND_API_BASE}/api/get-token?... for connection ${connection.id}`);
|
||||
if (protocol === 'rdp') {
|
||||
if (!connection.username) {
|
||||
console.warn(`[GuacamoleService:getRemoteDesktopToken] RDP connection ${connection.id} is missing username.`);
|
||||
// 对于RDP,用户名通常是必需的,但让网关决定是否可以为空
|
||||
}
|
||||
connectionConfig.username = connection.username || ''; // RDP 通常需要用户名
|
||||
connectionConfig.password = decryptedPassword || ''; // RDP 通常需要密码
|
||||
connectionConfig.dpi = dpi || '96';
|
||||
connectionConfig.security = (connection as any).rdp_security || 'any';
|
||||
connectionConfig.ignoreCert = String((connection as any).rdp_ignore_cert ?? true);
|
||||
} else if (protocol === 'vnc') {
|
||||
connectionConfig.password = decryptedPassword || ''; // VNC 通常需要密码
|
||||
if (connection.username) { // VNC 用户名是可选的
|
||||
connectionConfig.username = connection.username;
|
||||
}
|
||||
// 其他 VNC 特定参数可以从 connection.extras 获取
|
||||
// 例如: if (connection.extras?.enableAudio) connectionConfig.enableAudio = connection.extras.enableAudio;
|
||||
}
|
||||
|
||||
const requestBody = {
|
||||
protocol,
|
||||
connectionConfig
|
||||
};
|
||||
|
||||
const tokenUrl = `${REMOTE_GATEWAY_API_BASE}/api/remote-desktop/token`;
|
||||
console.log(`[GuacamoleService:getRemoteDesktopToken] Calling Remote Gateway API: ${tokenUrl} for protocol ${protocol}, connection ${connection.id}`);
|
||||
|
||||
try {
|
||||
const rdpResponse = await axios.get<{ token: string }>(rdpTokenUrl, {
|
||||
const response = await axios.post<TokenResponse>(tokenUrl, requestBody, {
|
||||
timeout: 10000 // 10 秒超时
|
||||
});
|
||||
|
||||
if (rdpResponse.status !== 200 || !rdpResponse.data?.token) {
|
||||
console.error(`[GuacamoleService:getRdpToken] RDP backend API call failed or returned invalid data. Status: ${rdpResponse.status}`, rdpResponse.data);
|
||||
throw new Error('从 RDP 后端获取令牌失败。');
|
||||
if (response.status !== 200 || !response.data?.token) {
|
||||
console.error(`[GuacamoleService:getRemoteDesktopToken] ${protocol.toUpperCase()} backend API call failed or returned invalid data. Status: ${response.status}`, response.data);
|
||||
throw new Error(`从 ${protocol.toUpperCase()} 后端获取令牌失败。`);
|
||||
}
|
||||
console.log(`[GuacamoleService:getRdpToken] Received Guacamole token from RDP backend for connection ${connection.id}`);
|
||||
return rdpResponse.data.token;
|
||||
console.log(`[GuacamoleService:getRemoteDesktopToken] Received Guacamole token from ${protocol.toUpperCase()} backend for connection ${connection.id}`);
|
||||
return response.data.token;
|
||||
} catch (error: any) {
|
||||
console.error(`[GuacamoleService:getRdpToken] Error calling RDP backend for connection ${connection.id}:`, error.message);
|
||||
console.error(`[GuacamoleService:getRemoteDesktopToken] Error calling ${protocol.toUpperCase()} backend for connection ${connection.id}:`, error.message);
|
||||
if (axios.isAxiosError(error) && error.response) {
|
||||
throw new Error(`调用 RDP 后端服务失败 (状态: ${error.response.status}): ${error.response.data?.message || error.message}`);
|
||||
throw new Error(`调用 ${protocol.toUpperCase()} 后端服务失败 (状态: ${error.response.status}): ${error.response.data?.message || error.message}`);
|
||||
}
|
||||
throw new Error(`调用 RDP 后端服务时发生错误: ${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 从 VNC 后端服务获取 Guacamole 令牌
|
||||
* @param connection 连接对象
|
||||
* @param decryptedPassword 解密后的密码 (VNC 通常需要密码)
|
||||
* @returns Guacamole 令牌
|
||||
*/
|
||||
export const getVncToken = async (connection: ConnectionWithTags, decryptedPassword?: string, width?: number, height?: number): Promise<string> => {
|
||||
if (connection.type !== 'VNC') {
|
||||
throw new Error('连接类型必须是 VNC。');
|
||||
}
|
||||
// VNC 通常总是需要密码,并且 auth_method 应该被设置为 'password'
|
||||
if (connection.auth_method !== 'password' || !decryptedPassword) {
|
||||
console.warn(`[GuacamoleService:getVncToken] VNC connection ${connection.id} does not use password auth or password decryption failed.`);
|
||||
throw new Error('VNC 连接需要使用密码认证,或密码解密失败。');
|
||||
}
|
||||
|
||||
const vncApiParams = new URLSearchParams({
|
||||
hostname: connection.host,
|
||||
port: connection.port.toString(),
|
||||
password: decryptedPassword, // VNC 通常只需要密码
|
||||
// VNC 特有的参数可以根据 @nexus-terminal/vnc 的 API 进行添加
|
||||
// 例如: username (如果 VNC 服务器需要), colorDepth, etc.
|
||||
// username: connection.username, // 如果 VNC 服务支持用户名
|
||||
});
|
||||
|
||||
if (width !== undefined) {
|
||||
vncApiParams.append('width', String(width));
|
||||
}
|
||||
if (height !== undefined) {
|
||||
vncApiParams.append('height', String(height));
|
||||
}
|
||||
|
||||
// 如果 VNC 服务也支持用户名,可以取消注释上面的 username 参数
|
||||
// 注意:标准的 VNC 协议主要通过密码进行认证,用户名不是标准部分,但某些实现可能支持。
|
||||
// 这里假设 @nexus-terminal/vnc 的 /api/get-vnc-token 接受这些参数。
|
||||
|
||||
const vncTokenUrl = `${VNC_BACKEND_API_BASE}/api/get-vnc-token?${vncApiParams.toString()}`;
|
||||
|
||||
console.log(`[GuacamoleService:getVncToken] Calling VNC backend API: ${VNC_BACKEND_API_BASE}/api/get-vnc-token?... for connection ${connection.id}`);
|
||||
|
||||
try {
|
||||
const vncResponse = await axios.get<{ token: string }>(vncTokenUrl, {
|
||||
timeout: 10000 // 10 秒超时
|
||||
});
|
||||
|
||||
if (vncResponse.status !== 200 || !vncResponse.data?.token) {
|
||||
console.error(`[GuacamoleService:getVncToken] VNC backend API call failed or returned invalid data. Status: ${vncResponse.status}`, vncResponse.data);
|
||||
throw new Error('从 VNC 后端获取令牌失败。');
|
||||
}
|
||||
console.log(`[GuacamoleService:getVncToken] Received Guacamole token from VNC backend for connection ${connection.id}`);
|
||||
return vncResponse.data.token;
|
||||
} catch (error: any) {
|
||||
console.error(`[GuacamoleService:getVncToken] Error calling VNC backend for connection ${connection.id}:`, error.message);
|
||||
if (axios.isAxiosError(error) && error.response) {
|
||||
throw new Error(`调用 VNC 后端服务失败 (状态: ${error.response.status}): ${error.response.data?.message || error.message}`);
|
||||
}
|
||||
throw new Error(`调用 VNC 后端服务时发生错误: ${error.message}`);
|
||||
throw new Error(`调用 ${protocol.toUpperCase()} 后端服务时发生错误: ${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -530,26 +530,27 @@ export const initializeWebSocket = async (server: http.Server, sessionParser: Re
|
||||
|
||||
|
||||
// Determine RDP target URL based on deployment mode
|
||||
const deploymentMode = process.env.DEPLOYMENT_MODE; // Default to docker mode
|
||||
let rdpBaseUrl: string;
|
||||
const deploymentMode = process.env.DEPLOYMENT_MODE;
|
||||
let remoteGatewayWsBaseUrl: string;
|
||||
if (deploymentMode === 'local') {
|
||||
rdpBaseUrl = process.env.RDP_SERVICE_URL_LOCAL || 'ws://localhost:8081'; // Default for local, fallback to localhost:3001
|
||||
console.log(`[WebSocket RDP Proxy] Using LOCAL deployment mode. RDP Target Base: ${rdpBaseUrl}`);
|
||||
} else if (deploymentMode === 'docker') { // Explicitly check for docker mode
|
||||
rdpBaseUrl = process.env.RDP_SERVICE_URL_DOCKER || 'ws://rdp:8081'; // Default for docker, fallback to localhost:3001
|
||||
console.log(`[WebSocket RDP Proxy] Using DOCKER deployment mode. RDP Target Base: ${rdpBaseUrl}`);
|
||||
} else { // Handle unknown modes
|
||||
rdpBaseUrl = 'ws://localhost:8081'; // Fallback to a safe default for unknown modes
|
||||
console.warn(`[WebSocket RDP Proxy] Unknown deployment mode '${deploymentMode}'. Defaulting to safe fallback RDP Target Base: ${rdpBaseUrl}`);
|
||||
remoteGatewayWsBaseUrl = process.env.REMOTE_GATEWAY_WS_URL_LOCAL || 'ws://localhost:8080'; // 更新端口和环境变量名
|
||||
console.log(`[WebSocket Remote Desktop Proxy] Using LOCAL deployment mode. Target Base: ${remoteGatewayWsBaseUrl}`);
|
||||
} else if (deploymentMode === 'docker') {
|
||||
remoteGatewayWsBaseUrl = process.env.REMOTE_GATEWAY_WS_URL_DOCKER || 'ws://remote-gateway:8080'; // 更新服务名、端口和环境变量名
|
||||
console.log(`[WebSocket Remote Desktop Proxy] Using DOCKER deployment mode. Target Base: ${remoteGatewayWsBaseUrl}`);
|
||||
} else {
|
||||
remoteGatewayWsBaseUrl = 'ws://localhost:8080'; // 更新默认端口
|
||||
console.warn(`[WebSocket Remote Desktop Proxy] Unknown deployment mode '${deploymentMode}'. Defaulting to safe fallback Target Base: ${remoteGatewayWsBaseUrl}`);
|
||||
}
|
||||
|
||||
const cleanRdpBaseUrl = rdpBaseUrl.endsWith('/') ? rdpBaseUrl.slice(0, -1) : rdpBaseUrl;
|
||||
const cleanRemoteGatewayWsBaseUrl = remoteGatewayWsBaseUrl.endsWith('/') ? remoteGatewayWsBaseUrl.slice(0, -1) : remoteGatewayWsBaseUrl;
|
||||
|
||||
const rdpTargetUrl = `${cleanRdpBaseUrl}/?token=${encodeURIComponent(rdpToken)}&width=${encodeURIComponent(rdpWidth)}&height=${encodeURIComponent(rdpHeight)}&dpi=${encodeURIComponent(calculatedDpi)}`; // 使用 calculatedDpi
|
||||
// 构建目标 URL 时,协议 (RDP/VNC) 信息现在由 remote-gateway 处理,我们只需要传递令牌和尺寸
|
||||
const remoteDesktopTargetUrl = `${cleanRemoteGatewayWsBaseUrl}/?token=${encodeURIComponent(rdpToken)}&width=${encodeURIComponent(rdpWidth)}&height=${encodeURIComponent(rdpHeight)}&dpi=${encodeURIComponent(calculatedDpi)}`;
|
||||
|
||||
console.log(`WebSocket: RDP Proxy for ${ws.username} attempting to connect to ${rdpTargetUrl}`);
|
||||
console.log(`WebSocket: Remote Desktop Proxy for ${ws.username} attempting to connect to ${remoteDesktopTargetUrl}`);
|
||||
|
||||
const rdpWs = new WebSocket(rdpTargetUrl);
|
||||
const rdpWs = new WebSocket(remoteDesktopTargetUrl);
|
||||
let clientWsClosed = false;
|
||||
let rdpWsClosed = false;
|
||||
|
||||
@@ -586,7 +587,7 @@ export const initializeWebSocket = async (server: http.Server, sessionParser: Re
|
||||
});
|
||||
rdpWs.on('error', (error) => {
|
||||
|
||||
console.error(`[RDP 代理 RDP WS 错误] 用户: ${ws.username}, 会话: ${ws.sessionId}, 连接到 ${rdpTargetUrl} 时出错:`, error);
|
||||
console.error(`[RDP 代理 RDP WS 错误] 用户: ${ws.username}, 会话: ${ws.sessionId}, 连接到 ${remoteDesktopTargetUrl} 时出错:`, error);
|
||||
if (!clientWsClosed && ws.readyState !== WebSocket.CLOSED && ws.readyState !== WebSocket.CLOSING) {
|
||||
console.log(`[RDP 代理] 因 RDP WS 错误关闭客户端 WS。会话: ${ws.sessionId}`);
|
||||
ws.close(1011, `RDP WS Error: ${error.message}`);
|
||||
@@ -610,7 +611,7 @@ export const initializeWebSocket = async (server: http.Server, sessionParser: Re
|
||||
rdpWs.on('close', (code, reason) => {
|
||||
rdpWsClosed = true;
|
||||
// --- 添加中文日志 ---
|
||||
console.log(`[RDP 代理 RDP WS 关闭] 用户: ${ws.username}, 会话: ${ws.sessionId}, 到 ${rdpTargetUrl} 的连接已关闭。代码: ${code}, 原因: ${reason.toString()}`);
|
||||
console.log(`[RDP 代理 RDP WS 关闭] 用户: ${ws.username}, 会话: ${ws.sessionId}, 到 ${remoteDesktopTargetUrl} 的连接已关闭。代码: ${code}, 原因: ${reason.toString()}`);
|
||||
// --- 结束日志 ---
|
||||
if (!clientWsClosed && ws.readyState !== WebSocket.CLOSED && ws.readyState !== WebSocket.CLOSING) {
|
||||
console.log(`[RDP 代理] 因 RDP WS 关闭而关闭客户端 WS。会话: ${ws.sessionId}`);
|
||||
@@ -620,7 +621,7 @@ export const initializeWebSocket = async (server: http.Server, sessionParser: Re
|
||||
});
|
||||
|
||||
rdpWs.on('open', () => {
|
||||
console.log(`[RDP 代理 RDP WS 打开] 用户: ${ws.username}, 会话: ${ws.sessionId}, 到 ${rdpTargetUrl} 的连接已建立。开始转发消息。`);
|
||||
console.log(`[RDP 代理 RDP WS 打开] 用户: ${ws.username}, 会话: ${ws.sessionId}, 到 ${remoteDesktopTargetUrl} 的连接已建立。开始转发消息。`);
|
||||
});
|
||||
|
||||
// --- 标准 (SSH/SFTP/Docker) 连接处理 ---
|
||||
|
||||
Reference in New Issue
Block a user