EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
rds.js
Go to the documentation of this file.
1/**
2 * @file rds.js
3 * @module routes/rds
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.
7 * @requires crypto
8 * @requires jsonwebtoken
9 * @requires express
10 * @requires services/websocketManager
11 * @author RMM-PSA Development Team
12 * @copyright 2026 RMM-PSA Platform
13 * @license Proprietary
14 */
15
16/**
17 * @apiDefine RDS Remote Desktop Streaming
18 * WebSocket-based remote desktop session management
19 */
20
21const crypto = require('crypto');
22const jwt = require('jsonwebtoken');
23const express = require('express');
24const router = express.Router();
25
26// Import WebSocket manager
27const wsManager = require('../services/websocketManager');
28
29// Utility: create session object
30/**
31 *
32 * @param agentId
33 */
34function makeSession(agentId) {
35 return {
36 agentId,
37 createdAt: Date.now(),
38 active: true,
39 viewer: null,
40 agent: null
41 };
42}
43
44/**
45 * @api {post} /api/rds/start/:agent_uuid Start RDS session
46 * @apiName StartRDSSession
47 * @apiGroup RDS
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:
61 * HTTP/1.1 200 OK
62 * {
63 * "sessionId": "550e8400-e29b-41d4-a716-446655440000",
64 * "viewerToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
65 * "viewerUrl": "/api/rds/view/550e8400-e29b-41d4-a716-446655440000?t=eyJhbGc..."
66 * }
67 */
68router.post('/start/:agent_uuid', async (req, res) => {
69 try {
70 const { agent_uuid } = req.params;
71
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 });
77 }
78
79 const sessionId = crypto.randomUUID();
80
81 // Store session
82 wsManager.rdsSessions.set(sessionId, makeSession(agent_uuid));
83
84 // Tell agent to start streaming
85 const rdsStartMsg = {
86 type: "rds_start",
87 sessionId
88 };
89 console.log(`[RDS] Sending rds_start to agent ${agent_uuid}:`, rdsStartMsg);
90 ws.send(JSON.stringify(rdsStartMsg));
91
92 console.log(`[RDS] ▶ Session STARTED: ${sessionId} for ${agent_uuid}`);
93
94 // Sign viewer token
95 const token = jwt.sign(
96 { sessionId, agent_uuid },
97 process.env.AGENT_SIGN_KEY,
98 { expiresIn: "10m" }
99 );
100
101 return res.json({
102 sessionId,
103 viewerToken: token,
104 viewerUrl: `/api/rds/view/${sessionId}?t=${token}`
105 });
106 } catch (err) {
107 console.error('[RDS] Start error:', err);
108 res.status(500).json({ error: 'Failed to start session' });
109 }
110});
111
112/**
113 * @api {post} /api/rds/stop/:agent_uuid Stop RDS session
114 * @apiName StopRDSSession
115 * @apiGroup RDS
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:
125 * HTTP/1.1 200 OK
126 * {
127 * "ok": true
128 * }
129 */
130router.post('/stop/:agent_uuid', (req, res) => {
131 try {
132 const { agent_uuid } = req.params;
133
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));
139 }
140
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}`);
146 }
147 }
148
149 return res.json({ ok: true });
150 } catch (err) {
151 console.error('[RDS] Stop error:', err);
152 res.status(500).json({ error: 'Failed to stop session' });
153 }
154});
155
156/**
157 * @api {get} /api/rds/status/:sessionId Get session status
158 * @apiName GetRDSSessionStatus
159 * @apiGroup RDS
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:
171 * HTTP/1.1 200 OK
172 * {
173 * "sessionId": "550e8400-e29b-41d4-a716-446655440000",
174 * "active": true,
175 * "agentId": "abc-123-uuid",
176 * "viewerConnected": true
177 * }
178 */
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' });
182
183 res.json({
184 sessionId: req.params.sessionId,
185 active: session.active,
186 agentId: session.agentId,
187 viewerConnected: !!session.viewer
188 });
189});
190
191/**
192 * @api {post} /api/rds/ping/:agent_uuid Ping agent
193 * @apiName PingRDSAgent
194 * @apiGroup RDS
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:
203 * HTTP/1.1 200 OK
204 * {
205 * "sent": true
206 * }
207 */
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' });
213 }
214 ws.send(JSON.stringify({ type: "ping", timestamp: Date.now() }));
215 res.json({ sent: true });
216});
217
218module.exports = router;