EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
agentV2.js
Go to the documentation of this file.
1/**
2 * @file agentV2.js
3 * @description Agent v2 architecture with helper-based privilege separation. Distributes source code as ZIP
4 * archives with embedded tenant bootstrap configuration. Requires AGENT_V2_SOURCE_PATH pointing to agent repository
5 * with Node.js source files. Supports Windows service model, RDP configuration, IPC communication layer.
6 * @module routes/agentV2
7 */
8
9/**
10 * @apiDefine AgentV2Group Agent v2 (Helper Architecture)
11 * Agent v2 source distribution with tenant bootstrap
12 */
13
14const express = require('express');
15const jwt = require('jsonwebtoken');
16const fs = require('fs');
17const path = require('path');
18const router = express.Router();
19
20// Agent v2 Configuration with absolute paths
21const AGENT_V2_VERSION = '2.0.0';
22// Use the correct production path for the server
23const AGENT_V2_SOURCE_PATH = '/opt/apps/IBG_HUB/rmm-psa-platform/agent';
24
25/**
26 * @api {get} /api/agent/v2/status Get Agent v2 Status
27 * @apiName GetAgentV2Status
28 * @apiGroup AgentV2Group
29 * @apiDescription Returns agent v2 architecture status and component availability. Checks source path for
30 * core files (ws.js, ipcServer.js, rdp-helper.js, hardwareInfo.js, updater.js). No authentication required.
31 * @apiSuccess {string} version Agent version (e.g., "2.0.0")
32 * @apiSuccess {boolean} available Agent availability
33 * @apiSuccess {string[]} platforms Supported platforms (windows, linux, macos)
34 * @apiSuccess {string[]} features Feature list (helper-based architecture, Windows service, RDP support, plugin system, IPC layer)
35 * @apiSuccess {string} lastUpdated ISO 8601 timestamp
36 * @apiSuccess {boolean} [sourceAvailable] Source files availability
37 * @apiSuccess {object} [components] Component file existence map
38 * @apiError 500 Status check failed
39 * @apiExample {curl} Example:
40 * curl https://api.example.com/api/agent/v2/status
41 * @apiExample {json} Success-Response (200):
42 * {
43 * "version": "2.0.0",
44 * "available": true,
45 * "platforms": ["windows", "linux", "macos"],
46 * "features": ["Helper-based architecture", "Windows service privilege separation"],
47 * "sourceAvailable": true,
48 * "components": {"lib/ws.js": true, "helpers/rdp-helper.js": true}
49 * }
50 */
51// =============================================
52// Agent v2 Status and Version Info
53// =============================================
54router.get('/status', (req, res) => {
55 try {
56 console.log('[Agent v2] Status check');
57
58 const status = {
59 version: AGENT_V2_VERSION,
60 available: true,
61 platforms: ['windows', 'linux', 'macos'],
62 features: [
63 'Helper-based architecture',
64 'Windows service privilege separation',
65 'RDP configuration support',
66 'Enhanced security model',
67 'Plugin system with helpers',
68 'IPC communication layer'
69 ],
70 lastUpdated: new Date().toISOString()
71 };
72
73 // Check if source files exist
74 if (fs.existsSync(AGENT_V2_SOURCE_PATH)) {
75 status.sourceAvailable = true;
76
77 // Check for specific components - using actual files that exist
78 const coreFiles = [
79 'lib/ws.js',
80 'lib/ipcServer.js',
81 'helpers/rdp-helper.js',
82 'lib/hardwareInfo.js',
83 'lib/updater.js'
84 ];
85
86 status.components = {};
87 for (const file of coreFiles) {
88 const filePath = path.join(AGENT_V2_SOURCE_PATH, file);
89 status.components[file] = fs.existsSync(filePath);
90 }
91 } else {
92 status.sourceAvailable = false;
93 }
94
95 res.json(status);
96 console.log('[Agent v2] Status response sent');
97
98 } catch (err) {
99 console.error('[Agent v2] Status check failed:', err);
100 res.status(500).json({ error: 'Failed to get status' });
101 }
102});
103
104/**
105 * @api {get} /api/agent/v2/download/:platform Download Agent v2 Source
106 * @apiName DownloadAgentV2Source
107 * @apiGroup AgentV2Group
108 * @apiDescription Generates ZIP archive containing agent v2 Node.js source code with embedded bootstrap configuration.
109 * Excludes node_modules, .git, logs, and build artifacts. Injects tenant-specific JWT (365-day expiry) into
110 * bootstrap.json for agent-to-server authentication. No auth required for download (open enrollment).
111 * @apiParam {string} platform Platform identifier (windows, linux, macos)
112 * @apiParam (Query) {UUID} tenantId Tenant UUID for bootstrap configuration
113 * @apiSuccess (200) {Binary} zip ZIP archive with agent source + bootstrap.json
114 * @apiError 400 Missing tenantId or unsupported platform
115 * @apiError 404 Source files not found
116 * @apiError 500 Archive creation failed
117 * @apiExample {curl} Example:
118 * curl "https://api.example.com/api/agent/v2/download/windows?tenantId=550e8400-e29b-41d4-a716-446655440000" \
119 * -o agent-v2.zip
120 * @apiExample {json} bootstrap.json (embedded in ZIP):
121 * {
122 * "tenantId": "550e8400-e29b-41d4-a716-446655440000",
123 * "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
124 * "backend": "https://everydaytech.au/api",
125 * "version": "2.0.0",
126 * "platform": "windows"
127 * }
128 */
129// =============================================
130// Agent v2 Download (No auth required, generates agent config)
131// =============================================
132router.get('/download/:platform', async (req, res) => {
133 const archiver = require('archiver');
134 try {
135 const { platform } = req.params;
136 const { tenantId } = req.query;
137
138 console.log('==============================');
139 console.log(`[Agent v2] 🏁 /download/${platform} called`);
140 console.log('[Agent v2] Platform:', platform);
141 console.log('[Agent v2] TenantId:', tenantId);
142
143 // Validate required parameters
144 if (!tenantId) {
145 console.log('[Agent v2] ❌ Missing tenantId');
146 return res.status(400).json({ error: 'Missing tenantId parameter' });
147 }
148
149 // Supported platforms
150 const supportedPlatforms = ['windows', 'linux', 'macos'];
151 if (!supportedPlatforms.includes(platform)) {
152 console.log('[Agent v2] ❌ Unsupported platform:', platform);
153 return res.status(400).json({ error: 'Unsupported platform' });
154 }
155
156 // Generate agent authentication JWT (not for download auth, but for agent-to-server communication)
157 const agentJWT = jwt.sign({ tenantId, version: 'v2' }, process.env.AGENT_SIGN_KEY, { expiresIn: '365d' });
158 console.log('[Agent v2] ✅ Generated agent authentication JWT');
159
160 // Check if source files exist
161 if (!fs.existsSync(AGENT_V2_SOURCE_PATH)) {
162 console.log('[Agent v2] ❌ Source path not found:', AGENT_V2_SOURCE_PATH);
163 return res.status(404).json({ error: 'Agent v2 source files not found' });
164 }
165
166 // Set up response headers for ZIP download
167 const filename = `agent-v2-${platform}-${AGENT_V2_VERSION}.zip`;
168 res.setHeader('Content-Type', 'application/zip');
169 res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
170
171 // Create archive
172 const archive = archiver('zip', {
173 zlib: { level: 6 } // Compression level
174 });
175
176 archive.on('error', (err) => {
177 console.error('[Agent v2] Archive error:', err);
178 if (!res.headersSent) {
179 res.status(500).json({ error: 'Archive creation failed' });
180 }
181 });
182
183 archive.on('end', () => {
184 console.log(`[Agent v2] ✅ Archive created: ${filename} (${archive.pointer()} bytes)`);
185 });
186
187 // Pipe archive to response
188 archive.pipe(res);
189
190 // Add source files to archive, excluding node_modules and other unnecessary files
191 console.log('[Agent v2] Adding source files to archive...');
192 archive.directory(AGENT_V2_SOURCE_PATH, 'agent-v2', (entry) => {
193 // Exclude node_modules, dist, logs, and other build artifacts
194 if (entry.name.includes('node_modules') ||
195 entry.name.includes('.git') ||
196 entry.name.includes('package-lock.json') ||
197 entry.name.includes('.log') ||
198 entry.name.includes('dist/') ||
199 entry.name.includes('.tmp')) {
200 return false;
201 }
202 return entry;
203 });
204
205 // Add bootstrap configuration file for agent authentication
206 const bootstrapConfig = {
207 tenantId: tenantId,
208 jwt: agentJWT,
209 backend: process.env.BACKEND_URL || 'https://everydaytech.au/api',
210 version: AGENT_V2_VERSION,
211 platform: platform
212 };
213
214 archive.append(JSON.stringify(bootstrapConfig, null, 2), { name: 'agent-v2/bootstrap.json' });
215 console.log('[Agent v2] ✅ Added bootstrap configuration');
216
217 // Finalize archive
218 await archive.finalize();
219
220 console.log('[Agent v2] Download completed');
221 console.log('==============================');
222
223 } catch (err) {
224 console.error('[Agent v2] Download failed:', err);
225 if (!res.headersSent) {
226 res.status(500).json({ error: 'Failed to download agent' });
227 }
228 console.log('==============================');
229 }
230});
231
232/**
233 * @api {get} /api/agent/v2/download-token Get Download Token (DEPRECATED)
234 * @apiName GetAgentV2DownloadToken
235 * @apiGroup AgentV2Group
236 * @apiDescription **DEPRECATED**: Legacy token-based download endpoint. Use direct download with tenantId query parameter instead.
237 * Returns 400 error with migration instructions.
238 * @apiError 400 Endpoint deprecated
239 * @apiExample {curl} Example (deprecated):
240 * curl https://api.example.com/api/agent/v2/download-token?tenantId=550e8400-e29b-41d4-a716-446655440000
241 * @apiExample {json} Error-Response (400):
242 * {
243 * "error": "This endpoint is deprecated. Use /download/{platform}?tenantId={tenantId} instead.",
244 * "migration": {
245 * "old": "/api/agent/v2/download-token?tenantId={tenantId}&os={platform}",
246 * "new": "/api/agent/v2/download/{platform}?tenantId={tenantId}"
247 * }
248 * }
249 */
250// =============================================
251// Agent v2 Download Token (DEPRECATED - keeping for compatibility)
252// =============================================
253router.get('/download-token', async (req, res) => {
254 console.log('[Agent v2] ⚠️ /download-token is deprecated, use direct download with tenantId');
255 res.status(400).json({
256 error: 'This endpoint is deprecated. Use /download/{platform}?tenantId={tenantId} instead.',
257 migration: {
258 old: '/api/agent/v2/download-token?tenantId={tenantId}&os={platform}',
259 new: '/api/agent/v2/download/{platform}?tenantId={tenantId}'
260 }
261 });
262});
263
264module.exports = router;