diff --git a/.env b/.env index ae1bf42..e70809c 100644 --- a/.env +++ b/.env @@ -1,13 +1,8 @@ # local/docker DEPLOYMENT_MODE=local -RDP_SERVICE_URL_DOCKER=ws://rdp:8081 -RDP_SERVICE_URL_LOCAL=ws://localhost:8081 -VNC_SERVICE_URL_DOCKER=ws://vnc:8082 -VNC_SERVICE_URL_LOCAL=ws://localhost:8082 - # Backend API Base URLs -RDP_BACKEND_API_BASE_DOCKER=http://nexus-rdp:9090 -RDP_BACKEND_API_BASE_LOCAL=http://localhost:9090 -VNC_BACKEND_API_BASE_DOCKER=http://nexus-vnc:9091 -VNC_BACKEND_API_BASE_LOCAL=http://localhost:9091 +REMOTE_GATEWAY_API_BASE_LOCAL=http://localhost:9090 +REMOTE_GATEWAY_API_BASE_DOCKER=http://remote-gateway:9090 +REMOTE_GATEWAY_WS_URL_LOCAL=ws://localhost:8080 +REMOTE_GATEWAY_WS_URL_DOCKER=ws://remote-gateway:8080 diff --git a/docker-compose.yml b/docker-compose.yml index 9893f85..692eb75 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: - "18111:80" depends_on: - backend - - rdp + - remote-gateway # 更新依赖 networks: - nexus-terminal-network @@ -14,44 +14,32 @@ services: image: heavrnl/nexus-terminal-backend:latest container_name: nexus-terminal-backend env_file: - - .env + - .env environment: NODE_ENV: production PORT: 3001 - RDP_BACKEND_API_BASE: http://rdp:9090 + REMOTE_GATEWAY_API_BASE: http://remote-gateway:9090 # 更新环境变量 volumes: - ./data:/app/data networks: - nexus-terminal-network - rdp: - image: heavrnl/nexus-terminal-rdp:latest - container_name: nexus-terminal-rdp + remote-gateway: + build: + context: . + dockerfile: packages/remote-gateway/Dockerfile + container_name: nexus-terminal-remote-gateway environment: GUACD_HOST: guacd GUACD_PORT: 4822 - API_PORT: 9090 - GUAC_WS_PORT: 8081 - FRONTEND_URL: http://frontend - MAIN_BACKEND_URL: http://backend:3001 - NODE_ENV: production - networks: - - nexus-terminal-network - depends_on: - - guacd - - backend - - vnc: - image: heavrnl/nexus-terminal-vnc:latest - container_name: nexus-terminal-vnc - environment: - GUACD_HOST: guacd - GUACD_PORT: 4822 - API_PORT: 9091 - GUAC_WS_PORT: 8082 - FRONTEND_URL: http://frontend + REMOTE_GATEWAY_API_PORT: 9090 + REMOTE_GATEWAY_WS_PORT: 8080 # 与 server.ts 中的默认值一致 + FRONTEND_URL: http://frontend # 或者实际的前端部署地址 MAIN_BACKEND_URL: http://backend:3001 NODE_ENV: production + ports: # 可选:如果需要从主机直接访问 API 或 WS 端口 + - "9090:9090" + - "8080:8080" networks: - nexus-terminal-network depends_on: diff --git a/packages/backend/src/connections/connections.controller.ts b/packages/backend/src/connections/connections.controller.ts index 3744e3c..2356021 100644 --- a/packages/backend/src/connections/connections.controller.ts +++ b/packages/backend/src/connections/connections.controller.ts @@ -321,7 +321,13 @@ export const getRdpSessionToken = async (req: Request, res: Response): Promise= 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= 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 }); diff --git a/packages/backend/src/services/guacamole.service.ts b/packages/backend/src/services/guacamole.service.ts index 3dd2218..d991da2 100644 --- a/packages/backend/src/services/guacamole.service.ts +++ b/packages/backend/src/services/guacamole.service.ts @@ -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 => { - 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 => { + 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(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 => { - 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}`); } }; diff --git a/packages/backend/src/websocket.ts b/packages/backend/src/websocket.ts index 21110d3..9523c5f 100644 --- a/packages/backend/src/websocket.ts +++ b/packages/backend/src/websocket.ts @@ -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) 连接处理 --- diff --git a/packages/frontend/src/components/VncModal.vue b/packages/frontend/src/components/VncModal.vue index cf8fff1..d363830 100644 --- a/packages/frontend/src/components/VncModal.vue +++ b/packages/frontend/src/components/VncModal.vue @@ -49,14 +49,17 @@ let dragOffsetX = 0; let dragOffsetY = 0; let hasDragged = false; -let vncWsBaseUrl: string; -const VNC_WS_PORT_FROM_ENV = import.meta.env.VITE_VNC_WS_PORT || '8082'; +let remoteDesktopWsBaseUrl: string; // Renamed for clarity +const LOCAL_BACKEND_URL_FOR_PROXY = 'ws://localhost:3001'; // Main backend's WebSocket for proxying if (window.location.hostname === 'localhost') { - vncWsBaseUrl = `ws://localhost:${VNC_WS_PORT_FROM_ENV}`; + // For local development, VNC will also go through the main backend's proxy + remoteDesktopWsBaseUrl = `${LOCAL_BACKEND_URL_FOR_PROXY}/ws/rdp-proxy`; // Use the same RDP proxy path } else { const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - vncWsBaseUrl = `${wsProtocol}//${window.location.hostname}:${VNC_WS_PORT_FROM_ENV}`; + const wsHostAndPort = window.location.host; + // For deployed environments, assume the proxy is at /ws/rdp-proxy relative to the main backend + remoteDesktopWsBaseUrl = `${wsProtocol}//${wsHostAndPort}/ws/rdp-proxy`; } const handleConnection = async () => { @@ -76,12 +79,17 @@ const handleConnection = async () => { try { const connectionsStore = useConnectionsStore(); + // Pass width and height to the token generation, backend will forward to gateway const token = await connectionsStore.getVncSessionToken(props.connection.id, desiredModalWidth.value, desiredModalHeight.value); if (!token) { throw new Error('VNC Token not found from store action'); } statusMessage.value = t('remoteDesktopModal.status.connectingWs'); - const tunnelUrl = `${vncWsBaseUrl}?token=${encodeURIComponent(token)}`; + // The backend proxy (/ws/rdp-proxy) expects token, width, height, dpi. + // For VNC, DPI is less critical but the proxy might expect it. Send a default or let backend handle. + // The backend's websocket.ts rdp-proxy handler now calculates DPI if not provided or uses a default. + // We need to ensure width and height are passed for the proxy to correctly forward. + const tunnelUrl = `${remoteDesktopWsBaseUrl}?token=${encodeURIComponent(token)}&width=${desiredModalWidth.value}&height=${desiredModalHeight.value}`; // @ts-ignore diff --git a/packages/rdp/package-lock.json b/packages/rdp/package-lock.json deleted file mode 100644 index 5fd4be6..0000000 --- a/packages/rdp/package-lock.json +++ /dev/null @@ -1,1610 +0,0 @@ -{ - "name": "backend", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "backend", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "cors": "^2.8.5", - "express": "^5.1.0", - "guacamole-lite": "^0.7.3", - "ws": "^8.18.1" - }, - "devDependencies": { - "@types/cors": "^2.8.17", - "@types/express": "^5.0.1", - "@types/node": "^22.15.2", - "@types/ws": "^8.18.1", - "nodemon": "^3.1.10", - "ts-node": "^10.9.2", - "typescript": "^5.8.3" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/cors": { - "version": "2.8.17", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", - "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.1.tgz", - "integrity": "sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", - "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.15.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.2.tgz", - "integrity": "sha512-uKXqKN9beGoMdBfcaTY1ecwz6ctxuJAcUlwE55938g0ZJ8lRxwAZqRz2AJ4pzpt5dHdTPMB863UZ0ESiFUcP7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/qs": { - "version": "6.9.18", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", - "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "license": "MIT" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", - "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.0", - "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", - "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", - "license": "MIT", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.0", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/guacamole-lite": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/guacamole-lite/-/guacamole-lite-0.7.3.tgz", - "integrity": "sha512-BXq7BpleikHe+B/Z1NSJIlTzzAnEX87eFW9WCl3aZ5DoEaGcNbVD9Tzj8CUQI3FFYk1LqtZ31MxlRdVojDLVLw==", - "license": "Apache-2.0", - "dependencies": { - "deep-extend": "^0.6.0", - "moment": "^2.17.1", - "ws": "^1.1.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/guacamole-lite/node_modules/ws": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz", - "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==", - "license": "MIT", - "dependencies": { - "options": ">=0.0.5", - "ultron": "1.0.x" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true, - "license": "ISC" - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/nodemon": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", - "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^4", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^7.5.3", - "simple-update-notifier": "^2.0.0", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/options": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", - "integrity": "sha512-bOj3L1ypm++N+n7CEbbe473A414AB7z+amKYshRb//iuL3MpdDCLhPnw6aVTdKB9g5ZRVHIEp8eUln6L2NUStg==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", - "license": "MIT", - "engines": { - "node": ">=16" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true, - "license": "MIT" - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.6.3", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/simple-update-notifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/touch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", - "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", - "dev": true, - "license": "ISC", - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/ultron": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", - "integrity": "sha512-QMpnpVtYaWEeY+MwKDN/UdKlE/LsFZXM5lO1u7GaZzNgmIbGixHEmVMIKT+vqYOALu3m5GYQy9kz4Xu4IVn7Ow==", - "license": "MIT" - }, - "node_modules/undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true, - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "license": "MIT" - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - } - } -} diff --git a/packages/rdp/src/server.ts b/packages/rdp/src/server.ts deleted file mode 100644 index fa292b1..0000000 --- a/packages/rdp/src/server.ts +++ /dev/null @@ -1,213 +0,0 @@ -// @ts-ignore - Still need this for the import as no types exist -import GuacamoleLite from 'guacamole-lite'; -import express, { Request, Response } from 'express'; -import http from 'http'; -import crypto from 'crypto'; -import cors from 'cors'; - - -// --- 配置 --- -const GUAC_WS_PORT = process.env.GUAC_WS_PORT || 8081; -const API_PORT = process.env.API_PORT || 9090; -const GUACD_HOST = process.env.GUACD_HOST || 'localhost'; -const GUACD_PORT = parseInt(process.env.GUACD_PORT || '4822', 10); - -// --- 启动时生成内存加密密钥 --- -console.log("正在为此会话生成新的内存加密密钥..."); -const ENCRYPTION_KEY_STRING = crypto.randomBytes(32).toString('hex'); -const ENCRYPTION_KEY_BUFFER = Buffer.from(ENCRYPTION_KEY_STRING, 'hex'); -console.log("内存加密密钥已生成。"); - -// --- Express 应用设置 --- -const app = express(); -const apiServer = http.createServer(app); - -const allowedOrigins = [ - process.env.FRONTEND_URL || 'http://localhost:5173', - process.env.MAIN_BACKEND_URL || 'http://localhost:3000' -]; -app.use(cors({ origin: allowedOrigins })); - - -const guacdOptions = { - host: GUACD_HOST, - port: GUACD_PORT, -}; - -const websocketOptions = { - port: GUAC_WS_PORT, - host: '0.0.0.0', -}; - -const clientOptions = { - crypt: { - // 将实际的密钥 Buffer 传递给 guacamole-lite 用于其内部加密操作 - key: ENCRYPTION_KEY_BUFFER, - cypher: 'aes-256-cbc' // 确保加密和解密之间的密码算法一致 - }, - // 默认连接设置 - connectionDefaultSettings: { - rdp: { - 'security': 'nla', - 'ignore-cert': 'true', - } - }, -}; - -let guacServer: any; - -try { - console.log(`[RDP 服务] 正在使用选项初始化 GuacamoleLite: WS 端口=${websocketOptions.port}, Guacd=${guacdOptions.host}:${guacdOptions.port}`); - guacServer = new GuacamoleLite(websocketOptions, guacdOptions, clientOptions); - console.log(`[RDP 服务] GuacamoleLite 初始化成功。`); - - if (guacServer.on) { - guacServer.on('error', (error: Error) => { - console.error(`[RDP 服务] GuacamoleLite 服务器错误:`, error); - }); - guacServer.on('connection', (client: any) => { - const clientId = client.id || '未知客户端ID'; - console.log(`[RDP 服务] Guacd 连接事件触发。客户端 ID: ${clientId}`); - - - if (client && typeof client.on === 'function') { - client.on('disconnect', (reason: string) => { - console.log(`[RDP 服务] Guacd 连接断开。客户端 ID: ${clientId}, 原因: ${reason || '未知'}`); - }); - client.on('error', (err: Error) => { - console.error(`[RDP 服务] Guacd 客户端错误。客户端 ID: ${clientId}, 错误:`, err); - }); - - client.on('message', (message: Buffer | string) => { - // 在回滚状态下移除了消息处理 - }); - - } else { - // 对没有 'on' 方法的客户端进行最小化处理 - } - }); - } -} catch (error) { - console.error(`[RDP 服务] 初始化 GuacamoleLite 失败:`, error); - process.exit(1); -} - -// 更新了 encryptToken 以匹配 guacamole-lite 期望的格式 (aes-256-cbc 和特定的 JSON 结构) -// 现在直接接受密钥 Buffer 以进行正确的加密操作 -const encryptToken = (data: string, keyBuffer: Buffer): string => { - try { - const iv = crypto.randomBytes(16); // AES-CBC 通常使用 16 字节的 IV - // 使用密钥 Buffer 进行 Node.js 加密操作 - const cipher = crypto.createCipheriv('aes-256-cbc', keyBuffer, iv); - - let encrypted = cipher.update(data, 'utf8', 'base64'); - encrypted += cipher.final('base64'); - - // 构建 guacamole-lite 的解密函数期望的 JSON 对象 - const output = { - iv: iv.toString('base64'), - value: encrypted - }; - - // 将 JSON 字符串化,然后对整个字符串进行 Base64 编码 - const jsonString = JSON.stringify(output); - return Buffer.from(jsonString).toString('base64'); - - } catch (e) { - console.error("令牌加密失败:", e); - throw new Error("令牌加密失败。"); - } -}; - - - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -app.get('/api/get-token', (req: any, res: any) => { - const { hostname, port, username, password, security = 'any', ignoreCert = 'true' } = req.query; - - if (!hostname || !port || !username || typeof password === 'undefined') { - return res.status(400).json({ error: '缺少必需的 RDP 参数 (hostname, port, username, password)' }); - } - - const connectionParams = { - connection: { - type: 'rdp', - settings: { - hostname: hostname as string, - port: port as string, - username: username as string, - password: password as string, - // 从查询中包含动态(或默认)的大小参数 - width: String(req.query.width || '1024'), - height: String(req.query.height || '768'), - dpi: String(req.query.dpi || '96'), - } - } - }; - - try { - const tokenData = JSON.stringify(connectionParams); - const encryptedToken = encryptToken(tokenData, ENCRYPTION_KEY_BUFFER); - res.json({ token: encryptedToken }); - } catch (error) { - console.error("/api/get-token 接口出错:", error); - res.status(500).json({ error: '生成令牌失败' }); - } -}); - -apiServer.listen(API_PORT, () => { - console.log(`[RDP 服务] API 服务器正在监听端口 ${API_PORT}`); - console.log(`[RDP 服务] Guacamole WebSocket 服务器应在端口 ${GUAC_WS_PORT} 上运行 (由 GuacamoleLite 管理)`); -}); - -const gracefulShutdown = (signal: string) => { - console.log(`收到 ${signal} 信号。正在优雅地关闭...`); - - let guacClosed = false; - let apiClosed = false; - - const tryExit = () => { - if (guacClosed && apiClosed) { - console.log("所有服务器已关闭。正在退出。"); - process.exit(0); - } - }; - - apiServer.close((err) => { - if (err) { - console.error("关闭 API 服务器时出错:", err); - } else { - console.log("API 服务器已关闭。"); - } - apiClosed = true; - tryExit(); - }); - - // @ts-ignore - 假设基于通用模式存在 close 方法 - if (typeof guacServer !== 'undefined' && guacServer && typeof guacServer.close === 'function') { - console.log("正在关闭 Guacamole 服务器..."); // 添加了关闭日志 - // @ts-ignore - guacServer.close(() => { - console.log("Guacamole 服务器已关闭。"); // 添加了关闭日志 - guacClosed = true; - tryExit(); - }); - } else { - console.log("Guacamole 服务器未运行或不支持 close() 方法。"); - guacClosed = true; - tryExit(); - } - - // 超时后强制退出 - setTimeout(() => { - console.error("关闭超时。强制退出。"); - process.exit(1); - }, 10000); // 10 秒超时 -}; - -process.on('SIGINT', () => gracefulShutdown('SIGINT')); -process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); - -process.on('SIGUSR2', () => { - gracefulShutdown('SIGUSR2 (nodemon restart)'); -}); \ No newline at end of file diff --git a/packages/rdp/Dockerfile b/packages/remote-gateway/Dockerfile similarity index 77% rename from packages/rdp/Dockerfile rename to packages/remote-gateway/Dockerfile index 77fdbd8..a8735d2 100644 --- a/packages/rdp/Dockerfile +++ b/packages/remote-gateway/Dockerfile @@ -5,14 +5,15 @@ FROM node:20-alpine AS builder WORKDIR /app # Copy package.json and package-lock.json -COPY packages/rdp/package.json packages/rdp/package-lock.json* ./ +COPY packages/remote-gateway/package.json packages/remote-gateway/package-lock.json* ./ # Install ALL dependencies (including devDependencies like typescript) RUN npm install # Copy source code and tsconfig -COPY packages/rdp/src ./src -COPY packages/rdp/tsconfig.json ./tsconfig.json +COPY packages/remote-gateway/src ./src +COPY packages/remote-gateway/tsconfig.json ./tsconfig.json +COPY packages/remote-gateway/guacamole-lite.d.ts ./guacamole-lite.d.ts # Build the TypeScript code RUN npm run build @@ -28,7 +29,7 @@ WORKDIR /app # Copy built code and node_modules from builder stage COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules -COPY packages/rdp/package.json ./package.json +COPY packages/remote-gateway/package.json ./package.json # --- Add patch application steps --- # Copy the patches directory from the build context (relative to project root) @@ -47,6 +48,7 @@ RUN npm uninstall patch-package # --- End patch application steps --- # Expose the API and WebSocket ports +# These will be configurable via environment variables, but good to have defaults EXPOSE 9090 EXPOSE 8081 diff --git a/packages/rdp/guacamole-lite.d.ts b/packages/remote-gateway/guacamole-lite.d.ts similarity index 100% rename from packages/rdp/guacamole-lite.d.ts rename to packages/remote-gateway/guacamole-lite.d.ts diff --git a/packages/rdp/package.json b/packages/remote-gateway/package.json similarity index 84% rename from packages/rdp/package.json rename to packages/remote-gateway/package.json index 0616bc5..0d70af6 100644 --- a/packages/rdp/package.json +++ b/packages/remote-gateway/package.json @@ -1,5 +1,5 @@ { - "name": "@nexus-terminal/rdp", + "name": "@nexus-terminal/remote-gateway", "version": "1.0.0", "main": "index.js", "scripts": { @@ -11,7 +11,7 @@ "keywords": [], "author": "", "license": "ISC", - "description": "", + "description": "Unified Remote Desktop Gateway for Nexus Terminal", "devDependencies": { "@types/cors": "^2.8.17", "@types/express": "^5.0.1", @@ -27,4 +27,4 @@ "guacamole-lite": "^0.7.3", "ws": "^8.18.1" } -} +} \ No newline at end of file diff --git a/packages/remote-gateway/src/server.ts b/packages/remote-gateway/src/server.ts new file mode 100644 index 0000000..84033c7 --- /dev/null +++ b/packages/remote-gateway/src/server.ts @@ -0,0 +1,220 @@ +// @ts-ignore - Still need this for the import as no types exist +import GuacamoleLite from 'guacamole-lite'; +import express, { Request, Response } from 'express'; +import http from 'http'; +import crypto from 'crypto'; +import cors from 'cors'; + +// --- 配置 --- +const REMOTE_GATEWAY_WS_PORT = process.env.REMOTE_GATEWAY_WS_PORT || 8080; // 统一端口,或按需分开 +const REMOTE_GATEWAY_API_PORT = process.env.REMOTE_GATEWAY_API_PORT || 9090; +const GUACD_HOST = process.env.GUACD_HOST || 'localhost'; +const GUACD_PORT = parseInt(process.env.GUACD_PORT || '4822', 10); +const FRONTEND_URL = process.env.FRONTEND_URL || 'http://localhost:5173'; +const MAIN_BACKEND_URL = process.env.MAIN_BACKEND_URL || 'http://localhost:3000'; + +// --- 启动时生成内存加密密钥 --- +console.log("[Remote Gateway] 正在为此会话生成新的内存加密密钥..."); +const ENCRYPTION_KEY_STRING = crypto.randomBytes(32).toString('hex'); +const ENCRYPTION_KEY_BUFFER = Buffer.from(ENCRYPTION_KEY_STRING, 'hex'); +console.log("[Remote Gateway] 内存加密密钥已生成。"); + +// --- Express 应用设置 --- +const app = express(); +app.use(express.json()); // 用于解析请求体中的 JSON +const apiServer = http.createServer(app); + +const allowedOrigins = [ + FRONTEND_URL, + MAIN_BACKEND_URL +]; +console.log(`[Remote Gateway] CORS 允许的来源: ${allowedOrigins.join(', ')}`); +app.use(cors({ origin: allowedOrigins })); + + +const guacdOptions = { + host: GUACD_HOST, + port: GUACD_PORT, +}; + +const websocketOptions = { + port: REMOTE_GATEWAY_WS_PORT, + host: '0.0.0.0', // 监听所有接口 +}; + +const clientOptions = { + crypt: { + key: ENCRYPTION_KEY_BUFFER, + cypher: 'aes-256-cbc' + }, + // 默认连接设置将根据协议动态调整 + connectionDefaultSettings: {}, +}; + +let guacServer: any; + +try { + console.log(`[Remote Gateway] 正在使用选项初始化 GuacamoleLite: WS 端口=${websocketOptions.port}, Guacd=${guacdOptions.host}:${guacdOptions.port}`); + guacServer = new GuacamoleLite(websocketOptions, guacdOptions, clientOptions); + console.log(`[Remote Gateway] GuacamoleLite 初始化成功。`); + + if (guacServer.on) { + guacServer.on('error', (error: Error) => { + console.error(`[Remote Gateway] GuacamoleLite 服务器错误:`, error); + }); + guacServer.on('connection', (client: any) => { + const clientId = client.id || '未知客户端ID'; + console.log(`[Remote Gateway] Guacd 连接事件触发。客户端 ID: ${clientId}`); + + if (client && typeof client.on === 'function') { + client.on('disconnect', (reason: string) => { + console.log(`[Remote Gateway] Guacd 连接断开。客户端 ID: ${clientId}, 原因: ${reason || '未知'}`); + }); + client.on('error', (err: Error) => { + console.error(`[Remote Gateway] Guacd 客户端错误。客户端 ID: ${clientId}, 错误:`, err); + }); + } + }); + } +} catch (error) { + console.error(`[Remote Gateway] 初始化 GuacamoleLite 失败:`, error); + process.exit(1); +} + +const encryptToken = (data: string, keyBuffer: Buffer): string => { + try { + const iv = crypto.randomBytes(16); + const cipher = crypto.createCipheriv('aes-256-cbc', keyBuffer, iv); + let encrypted = cipher.update(data, 'utf8', 'base64'); + encrypted += cipher.final('base64'); + const output = { + iv: iv.toString('base64'), + value: encrypted + }; + const jsonString = JSON.stringify(output); + return Buffer.from(jsonString).toString('base64'); + } catch (e) { + console.error("[Remote Gateway] 令牌加密失败:", e); + throw new Error("令牌加密失败。"); + } +}; + +app.post('/api/remote-desktop/token', (req: Request, res: Response): void => { + const { protocol, connectionConfig } = req.body; + + if (!protocol || !connectionConfig) { + res.status(400).json({ error: '缺少必需的参数 (protocol, connectionConfig)' }); + return; + } + + if (protocol !== 'rdp' && protocol !== 'vnc') { + res.status(400).json({ error: '无效的协议类型。支持 "rdp" 或 "vnc"。' }); + return; + } + + const { hostname, port, username, password, width, height, dpi, security, ignoreCert } = connectionConfig; + + if (!hostname || !port) { + res.status(400).json({ error: '缺少必需的连接参数 (hostname, port)' }); + return; + } + + let settings: any = { + hostname: hostname as string, + port: port as string, + width: String(width || '1024'), + height: String(height || '768'), + }; + + if (protocol === 'rdp') { + if (typeof username === 'undefined' || typeof password === 'undefined') { + res.status(400).json({ error: 'RDP 连接缺少 username 或 password' }); + return; + } + settings.username = username as string; + settings.password = password as string; + settings.security = security || 'any'; // RDP 特有,使用默认值 'any' + settings['ignore-cert'] = String(ignoreCert || 'true'); // RDP 特有 + settings.dpi = String(dpi || '96'); // RDP 特有 + } else if (protocol === 'vnc') { + if (typeof password === 'undefined') { + res.status(400).json({ error: 'VNC 连接缺少 password' }); + return; + } + settings.password = password as string; + if (username) { // VNC 可选 username + settings.username = username as string; + } + // VNC 特有的其他参数可以根据需要从 connectionConfig 中获取并添加 + // 例如: settings['enable-audio'] = connectionConfig.enableAudio || 'false'; + } + + const connectionParams = { + connection: { + type: protocol, // 'rdp' or 'vnc' + settings: settings + } + }; + + try { + const tokenData = JSON.stringify(connectionParams); + const encryptedToken = encryptToken(tokenData, ENCRYPTION_KEY_BUFFER); + res.json({ token: encryptedToken }); + } catch (error) { + console.error("[Remote Gateway] /api/remote-desktop/token 接口出错:", error); + res.status(500).json({ error: '生成令牌失败' }); + } +}); + +apiServer.listen(REMOTE_GATEWAY_API_PORT, () => { + console.log(`[Remote Gateway] API 服务器正在监听端口 ${REMOTE_GATEWAY_API_PORT}`); + console.log(`[Remote Gateway] Guacamole WebSocket 服务器应在端口 ${REMOTE_GATEWAY_WS_PORT} 上运行 (由 GuacamoleLite 管理)`); +}); + +const gracefulShutdown = (signal: string) => { + console.log(`[Remote Gateway] 收到 ${signal} 信号。正在优雅地关闭...`); + + let guacClosed = false; + let apiClosed = false; + + const tryExit = () => { + if (guacClosed && apiClosed) { + console.log("[Remote Gateway] 所有服务器已关闭。正在退出。"); + process.exit(0); + } + }; + + apiServer.close((err) => { + if (err) { + console.error("[Remote Gateway] 关闭 API 服务器时出错:", err); + } else { + console.log("[Remote Gateway] API 服务器已关闭。"); + } + apiClosed = true; + tryExit(); + }); + + if (typeof guacServer !== 'undefined' && guacServer && typeof guacServer.close === 'function') { + console.log("[Remote Gateway] 正在关闭 Guacamole 服务器..."); + guacServer.close(() => { + console.log("[Remote Gateway] Guacamole 服务器已关闭。"); + guacClosed = true; + tryExit(); + }); + } else { + console.log("[Remote Gateway] Guacamole 服务器未运行或不支持 close() 方法。"); + guacClosed = true; + tryExit(); + } + + setTimeout(() => { + console.error("[Remote Gateway] 关闭超时。强制退出。"); + process.exit(1); + }, 10000); // 10 秒超时 +}; + +process.on('SIGINT', () => gracefulShutdown('SIGINT')); +process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); +process.on('SIGUSR2', () => { + gracefulShutdown('SIGUSR2 (nodemon restart)'); +}); \ No newline at end of file diff --git a/packages/rdp/tsconfig.json b/packages/remote-gateway/tsconfig.json similarity index 99% rename from packages/rdp/tsconfig.json rename to packages/remote-gateway/tsconfig.json index 517ed39..e1b4b9a 100644 --- a/packages/rdp/tsconfig.json +++ b/packages/remote-gateway/tsconfig.json @@ -114,4 +114,4 @@ "src/**/*", // Include all files in the src directory "guacamole-lite.d.ts" // Include the specific .d.ts file ] -} +} \ No newline at end of file