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
10 * @apiDefine AgentV2Group Agent v2 (Helper Architecture)
11 * Agent v2 source distribution with tenant bootstrap
14const express = require('express');
15const jwt = require('jsonwebtoken');
16const fs = require('fs');
17const path = require('path');
18const router = express.Router();
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';
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):
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}
51// =============================================
52// Agent v2 Status and Version Info
53// =============================================
54router.get('/status', (req, res) => {
56 console.log('[Agent v2] Status check');
59 version: AGENT_V2_VERSION,
61 platforms: ['windows', 'linux', 'macos'],
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'
70 lastUpdated: new Date().toISOString()
73 // Check if source files exist
74 if (fs.existsSync(AGENT_V2_SOURCE_PATH)) {
75 status.sourceAvailable = true;
77 // Check for specific components - using actual files that exist
81 'helpers/rdp-helper.js',
82 'lib/hardwareInfo.js',
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);
92 status.sourceAvailable = false;
96 console.log('[Agent v2] Status response sent');
99 console.error('[Agent v2] Status check failed:', err);
100 res.status(500).json({ error: 'Failed to get status' });
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" \
120 * @apiExample {json} bootstrap.json (embedded in ZIP):
122 * "tenantId": "550e8400-e29b-41d4-a716-446655440000",
123 * "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
124 * "backend": "https://everydaytech.au/api",
125 * "version": "2.0.0",
126 * "platform": "windows"
129// =============================================
130// Agent v2 Download (No auth required, generates agent config)
131// =============================================
132router.get('/download/:platform', async (req, res) => {
133 const archiver = require('archiver');
135 const { platform } = req.params;
136 const { tenantId } = req.query;
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);
143 // Validate required parameters
145 console.log('[Agent v2] ❌ Missing tenantId');
146 return res.status(400).json({ error: 'Missing tenantId parameter' });
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' });
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');
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' });
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}"`);
172 const archive = archiver('zip', {
173 zlib: { level: 6 } // Compression level
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' });
183 archive.on('end', () => {
184 console.log(`[Agent v2] ✅ Archive created: ${filename} (${archive.pointer()} bytes)`);
187 // Pipe archive to response
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')) {
205 // Add bootstrap configuration file for agent authentication
206 const bootstrapConfig = {
209 backend: process.env.BACKEND_URL || 'https://everydaytech.au/api',
210 version: AGENT_V2_VERSION,
214 archive.append(JSON.stringify(bootstrapConfig, null, 2), { name: 'agent-v2/bootstrap.json' });
215 console.log('[Agent v2] ✅ Added bootstrap configuration');
218 await archive.finalize();
220 console.log('[Agent v2] Download completed');
221 console.log('==============================');
224 console.error('[Agent v2] Download failed:', err);
225 if (!res.headersSent) {
226 res.status(500).json({ error: 'Failed to download agent' });
228 console.log('==============================');
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):
243 * "error": "This endpoint is deprecated. Use /download/{platform}?tenantId={tenantId} instead.",
245 * "old": "/api/agent/v2/download-token?tenantId={tenantId}&os={platform}",
246 * "new": "/api/agent/v2/download/{platform}?tenantId={tenantId}"
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.',
258 old: '/api/agent/v2/download-token?tenantId={tenantId}&os={platform}',
259 new: '/api/agent/v2/download/{platform}?tenantId={tenantId}'
264module.exports = router;