4 * @description Remote Desktop Streaming (RDS) session management endpoints.
5 * Handles WebSocket-based remote desktop sessions between agents and viewers.
6 * Provides session lifecycle control, authentication tokens, and status monitoring.
8 * @requires jsonwebtoken
10 * @requires services/websocketManager
11 * @author RMM-PSA Development Team
12 * @copyright 2026 RMM-PSA Platform
13 * @license Proprietary
17 * @apiDefine RDS Remote Desktop Streaming
18 * WebSocket-based remote desktop session management
21const crypto = require('crypto');
22const jwt = require('jsonwebtoken');
23const express = require('express');
24const router = express.Router();
26// Import WebSocket manager
27const wsManager = require('../services/websocketManager');
29// Utility: create session object
34function makeSession(agentId) {
37 createdAt: Date.now(),
45 * @api {post} /api/rds/start/:agent_uuid Start RDS session
46 * @apiName StartRDSSession
48 * @apiDescription Initiate remote desktop streaming session with agent.
49 * Generates session ID, sends rds_start message to agent via WebSocket,
50 * and returns JWT viewer token for authentication. Requires agent to be online.
51 * @apiParam {string} agent_uuid Agent UUID
52 * @apiSuccess {string} sessionId Generated session UUID
53 * @apiSuccess {string} viewerToken JWT token for viewer authentication (expires in 10 minutes)
54 * @apiSuccess {string} viewerUrl Viewer URL with embedded token
55 * @apiError (404) {String} error="Agent not connected" Agent not online or WebSocket not ready
56 * @apiError (404) {String[]} connectedAgents List of currently connected agents (for debugging)
57 * @apiError (500) {String} error="Failed to start session" Internal error
58 * @apiExample {curl} Example:
59 * curl -X POST http://localhost:3000/api/rds/start/abc-123-uuid
60 * @apiSuccessExample {json} Success-Response:
63 * "sessionId": "550e8400-e29b-41d4-a716-446655440000",
64 * "viewerToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
65 * "viewerUrl": "/api/rds/view/550e8400-e29b-41d4-a716-446655440000?t=eyJhbGc..."
68router.post('/start/:agent_uuid', async (req, res) => {
70 const { agent_uuid } = req.params;
72 const ws = wsManager.agentConnections.get(agent_uuid);
73 if (!ws || ws.readyState !== 1) {
74 const connectedAgents = Array.from(wsManager.agentConnections.keys());
75 console.error(`[RDS] Agent not connected: ${agent_uuid}. Connected agents:`, connectedAgents);
76 return res.status(404).json({ error: 'Agent not connected', connectedAgents });
79 const sessionId = crypto.randomUUID();
82 wsManager.rdsSessions.set(sessionId, makeSession(agent_uuid));
84 // Tell agent to start streaming
89 console.log(`[RDS] Sending rds_start to agent ${agent_uuid}:`, rdsStartMsg);
90 ws.send(JSON.stringify(rdsStartMsg));
92 console.log(`[RDS] ▶ Session STARTED: ${sessionId} for ${agent_uuid}`);
95 const token = jwt.sign(
96 { sessionId, agent_uuid },
97 process.env.AGENT_SIGN_KEY,
104 viewerUrl: `/api/rds/view/${sessionId}?t=${token}`
107 console.error('[RDS] Start error:', err);
108 res.status(500).json({ error: 'Failed to start session' });
113 * @api {post} /api/rds/stop/:agent_uuid Stop RDS session
114 * @apiName StopRDSSession
116 * @apiDescription Stop all remote desktop sessions for specified agent.
117 * Sends rds_stop message to agent via WebSocket and removes all sessions from memory.
118 * Safe to call even if agent is offline.
119 * @apiParam {string} agent_uuid Agent UUID
120 * @apiSuccess {boolean} ok=true Sessions stopped successfully
121 * @apiError (500) {String} error="Failed to stop session" Internal error
122 * @apiExample {curl} Example:
123 * curl -X POST http://localhost:3000/api/rds/stop/abc-123-uuid
124 * @apiSuccessExample {json} Success-Response:
130router.post('/stop/:agent_uuid', (req, res) => {
132 const { agent_uuid } = req.params;
134 const ws = wsManager.agentConnections.get(agent_uuid);
135 if (ws && ws.readyState === 1) {
136 const rdsStopMsg = { type: "rds_stop" };
137 console.log(`[RDS] Sending rds_stop to agent ${agent_uuid}:`, rdsStopMsg);
138 ws.send(JSON.stringify(rdsStopMsg));
141 // Remove all sessions for this agent
142 for (const [id, session] of wsManager.rdsSessions.entries()) {
143 if (session.agentId === agent_uuid) {
144 wsManager.rdsSessions.delete(id);
145 console.log(`[RDS] ⛔ Session STOPPED: ${id} for ${agent_uuid}`);
149 return res.json({ ok: true });
151 console.error('[RDS] Stop error:', err);
152 res.status(500).json({ error: 'Failed to stop session' });
157 * @api {get} /api/rds/status/:sessionId Get session status
158 * @apiName GetRDSSessionStatus
160 * @apiDescription Retrieve current status of remote desktop session.
161 * Returns session metadata including activity status and viewer connection state.
162 * @apiParam {string} sessionId Session UUID
163 * @apiSuccess {string} sessionId Session UUID
164 * @apiSuccess {boolean} active Session active flag
165 * @apiSuccess {string} agentId Associated agent UUID
166 * @apiSuccess {boolean} viewerConnected Whether viewer WebSocket is connected
167 * @apiError (404) {String} error="Not found" Session ID not found
168 * @apiExample {curl} Example:
169 * curl -X GET http://localhost:3000/api/rds/status/550e8400-e29b-41d4-a716-446655440000
170 * @apiSuccessExample {json} Success-Response:
173 * "sessionId": "550e8400-e29b-41d4-a716-446655440000",
175 * "agentId": "abc-123-uuid",
176 * "viewerConnected": true
179router.get('/status/:sessionId', (req, res) => {
180 const session = wsManager.rdsSessions.get(req.params.sessionId);
181 if (!session) return res.status(404).json({ error: 'Not found' });
184 sessionId: req.params.sessionId,
185 active: session.active,
186 agentId: session.agentId,
187 viewerConnected: !!session.viewer
192 * @api {post} /api/rds/ping/:agent_uuid Ping agent
193 * @apiName PingRDSAgent
195 * @apiDescription Test agent WebSocket connectivity by sending ping message.
196 * Used for debugging and health checks. Requires agent to be online.
197 * @apiParam {string} agent_uuid Agent UUID
198 * @apiSuccess {boolean} sent=true Ping message sent successfully
199 * @apiError (404) {String} error="Agent not connected" Agent not online or WebSocket not ready
200 * @apiExample {curl} Example:
201 * curl -X POST http://localhost:3000/api/rds/ping/abc-123-uuid
202 * @apiSuccessExample {json} Success-Response:
208router.post('/ping/:agent_uuid', (req, res) => {
209 const { agent_uuid } = req.params;
210 const ws = wsManager.agentConnections.get(agent_uuid);
211 if (!ws || ws.readyState !== 1) {
212 return res.status(404).json({ error: 'Agent not connected' });
214 ws.send(JSON.stringify({ type: "ping", timestamp: Date.now() }));
215 res.json({ sent: true });
218module.exports = router;