EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
agent-v3.js
Go to the documentation of this file.
1/**
2 * @file agent-v3.js
3 * @description Agent v3 white-labeled MeshCentral installer distribution. Provides token-based and authenticated
4 * download endpoints for Windows (EXE), Linux (shell script), and macOS (PKG) installers. Proxies requests to
5 * MeshCentral server with tenant-specific mesh IDs. Includes installation scripts for automated deployment.
6 * @module routes/agent-v3
7 */
8
9/**
10 * @apiDefine AgentV3Group Agent v3 (MeshCentral)
11 * White-labeled MeshCentral agent installers
12 */
13
14/**
15 * Agent v3 Download URLs and Installer Management
16 *
17 * This route provides white-labeled MeshCentral agent installers
18 * branded as "EverydayTech Agent v3"
19 */
20
21const express = require('express');
22const router = express.Router();
23const authenticateToken = require('../middleware/auth');
24const axios = require('axios');
25const jwt = require('jsonwebtoken');
26const pool = require('../services/db');
27
28const MESHCENTRAL_URL = process.env.MESHCENTRAL_URL || 'https://rmm-psa-meshcentral-aq48h.ondigitalocean.app';
29const JWT_SECRET = process.env.JWT_SECRET;
30
31/**
32 * @api {get} /api/agent-v3/download-token Generate Download Token
33 * @apiName GenerateAgentV3DownloadToken
34 * @apiGroup AgentV3Group
35 * @apiDescription Generates temporary JWT token for agent installer downloads. Token expires in 7 days and is
36 * stateless (verified via JWT signature). No authentication required. Use token in /download/:platform/:token endpoints.
37 * @apiSuccess {boolean} success Operation status (true)
38 * @apiSuccess {string} token JWT download token
39 * @apiSuccess {number} expiresIn Token lifetime in seconds (604800)
40 * @apiError 500 Token generation failed
41 * @apiExample {curl} Example:
42 * curl https://api.example.com/api/agent-v3/download-token
43 * @apiExample {json} Success-Response (200):
44 * {"success": true, "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expiresIn": 300}
45 */
46/**
47 * Generate temporary download token (public, rate-limited)
48 * GET /api/agent-v3/download-token
49 *
50 * Returns a JWT token that expires in 7 days. The token is stateless
51 * and verified using JWT signature checking, so it persists across server restarts.
52 */
53router.get('/download-token', (req, res) => {
54 try {
55 // Generate JWT token with 7 day expiry
56 const token = jwt.sign(
57 { purpose: 'agent-download', timestamp: Date.now() },
58 JWT_SECRET,
59 { expiresIn: '7d' }
60 );
61
62 res.json({
63 success: true,
64 token,
65 expiresIn: 604800 // seconds (7 days)
66 });
67
68 } catch (error) {
69 console.error('❌ [AgentV3] Error generating download token:', error);
70 res.status(500).json({ error: 'Failed to generate download token' });
71 }
72});
73
74// ====================================================================================
75// DOWNLOAD ROUTES - IMPORTANT: Parametrized routes (:token) MUST come before
76// non-parametrized routes to prevent Express from matching /:token as auth route
77// ====================================================================================
78
79router.get('/download/windows/:token', async (req, res) => {
80 try {
81 const { token } = req.params;
82 const { tenant } = req.query;
83
84 console.log(`📥 [AgentV3] Windows download request - Token: ${token.substring(0, 20)}..., Tenant: ${tenant}`);
85
86 // Verify JWT signature and expiry (JWT handles expiry automatically)
87 let decoded;
88 try {
89 decoded = jwt.verify(token, JWT_SECRET);
90 console.log(`✅ [AgentV3] Token verified - Purpose: ${decoded.purpose}`);
91 } catch (err) {
92 console.error(`❌ [AgentV3] JWT verification failed:`, err.message);
93 return res.status(401).json({ error: 'Invalid or expired download token' });
94 }
95
96 // Get tenant's MeshCentral group ID
97 let meshId = process.env.MESHCENTRAL_DEFAULT_MESH_ID || '';
98 if (tenant) {
99 try {
100 const result = await pool.query(
101 'SELECT meshcentral_group_id FROM tenants WHERE tenant_id = $1',
102 [tenant]
103 );
104 if (result.rows.length > 0 && result.rows[0].meshcentral_group_id) {
105 meshId = result.rows[0].meshcentral_group_id;
106 console.log(`✅ [AgentV3] Windows agent for tenant ${tenant} - mesh: ${meshId.substring(0, 30)}...`);
107 } else {
108 console.warn(`⚠️ [AgentV3] Tenant ${tenant} has no meshcentral_group_id, using default`);
109 }
110 } catch (dbErr) {
111 console.error(`❌ [AgentV3] DB error fetching mesh ID:`, dbErr.message, dbErr.stack);
112 // Continue with default mesh ID
113 }
114 }
115
116 // Download the agent directly from MeshCentral (public endpoint, no auth required)
117 const installUrl = `${MESHCENTRAL_URL}/meshagents?id=4&meshid=${meshId}&installflags=0&meshinstall=6`;
118 console.log(`🔗 [AgentV3] Fetching from MeshCentral: ${installUrl.substring(0, 100)}...`);
119
120 const response = await axios({
121 method: 'GET',
122 url: installUrl,
123 responseType: 'stream',
124 headers: {
125 'User-Agent': 'EverydayTech-RMM/3.0'
126 },
127 timeout: 30000, // 30 second timeout
128 maxRedirects: 5 // Follow redirects
129 });
130
131 console.log(`✅ [AgentV3] MeshCentral response received, streaming to client`);
132
133 res.setHeader('Content-Type', 'application/octet-stream');
134 res.setHeader('Content-Disposition', 'attachment; filename="everydaytech-agent-v3.exe"');
135
136 // Handle stream errors
137 response.data.on('error', (streamErr) => {
138 console.error(`❌ [AgentV3] Stream error:`, streamErr.message);
139 });
140
141 response.data.pipe(res);
142
143 } catch (error) {
144 console.error('❌ [AgentV3] Error downloading Windows agent:', error.message);
145 console.error('Stack:', error.stack);
146 if (!res.headersSent) {
147 res.status(500).json({ error: 'Failed to download installer', details: error.message });
148 }
149 }
150});
151
152/**
153 * @api {get} /api/agent-v3/download/linux/:token Download Linux Installer (Token)
154 * @apiName DownloadLinuxAgentToken
155 * @apiGroup AgentV3Group
156 * @apiDescription Downloads Linux shell script installer using temporary token. Authenticates with MeshCentral admin
157 * credentials, fetches tenant-specific mesh ID if tenant parameter provided, and returns bash installation script.
158 * Token expires in 7 days.
159 * @apiParam {string} token Download token from /download-token endpoint
160 * @apiParam (Query) {UUID} [tenant] Tenant UUID for mesh-specific installer
161 * @apiSuccess (200) {String} script Linux installation shell script (text/plain)
162 * @apiError 401 Invalid or expired token
163 * @apiError 500 MeshCentral authentication/download error
164 * @apiExample {curl} Example:
165 * curl "https://api.example.com/api/agent-v3/download/linux/eyJhbGc...?tenant=550e8400-e29b-41d4-a716-446655440000" \
166 * | sudo bash
167 */
168/**
169 * Download Linux agent with temporary token
170 * GET /api/agent-v3/download/linux/:token
171
172router.get('/download/windows', authenticateToken, async (req, res) => {
173 try {
174 const meshId = process.env.MESHCENTRAL_DEFAULT_MESH_ID || '';
175 const installUrl = `${MESHCENTRAL_URL}/meshagents?id=4&meshid=${meshId}&installflags=0&meshinstall=6`;
176
177 // Stream the installer from MeshCentral to the client
178 const response = await axios({
179 method: 'GET',
180 url: installUrl,
181 responseType: 'stream',
182 // MeshCentral may require these headers
183 headers: {
184 'User-Agent': 'EverydayTech-RMM/3.0'
185 }
186 });
187
188 // Set appropriate headers for download
189 res.setHeader('Content-Type', 'application/octet-stream');
190 res.setHeader('Content-Disposition', 'attachment; filename="everydaytech-agent-v3.exe"');
191
192 // Pipe the response
193 response.data.pipe(res);
194
195 } catch (error) {
196 console.error('❌ [AgentV3] Error proxying Windows installer:', error.message);
197 res.status(500).json({
198 error: 'Failed to download installer',
199 details: error.message
200 });
201 }
202});
203
204/**
205 * @api {get} /api/agent-v3/download/linux Download Linux Installer (Auth)
206 * @apiName DownloadLinuxAgentAuth
207 * @apiGroup AgentV3Group
208 * @apiDescription Proxies Linux shell script installer from MeshCentral with JWT authentication. Returns bash script
209 * as text/plain. Requires valid Bearer token.
210 * @apiSuccess (200) {String} script Linux installation shell script (text/plain)
211 * @apiError 401 Authentication required
212 * @apiError 500 MeshCentral proxy error
213 * @apiExample {curl} Example:
214 * curl https://api.example.com/api/agent-v3/download/linux \
215 * -H "Authorization: Bearer YOUR_TOKEN" | sudo bash
216 */
217/**
218 * Proxy download endpoint for Linux agent script (JWT authenticated)
219 * GET /api/agent-v3/download/linux
220
221router.get('/download/linux/:token', async (req, res) => {
222 try {
223 const { token } = req.params;
224 const { tenant } = req.query;
225
226 console.log(`📥 [AgentV3] Linux download request - Token: ${token.substring(0, 20)}..., Tenant: ${tenant}`);
227
228 // Verify JWT signature and expiry
229 let decoded;
230 try {
231 decoded = jwt.verify(token, JWT_SECRET);
232 console.log(`✅ [AgentV3] Token verified - Purpose: ${decoded.purpose}`);
233 } catch (err) {
234 console.error(`❌ [AgentV3] JWT verification failed:`, err.message);
235 return res.status(401).send('#!/bin/bash\necho "Error: Invalid or expired download token"');
236 }
237
238 // Get tenant's MeshCentral group ID
239 let meshId = process.env.MESHCENTRAL_DEFAULT_MESH_ID || '';
240 if (tenant) {
241 try {
242 const result = await pool.query(
243 'SELECT meshcentral_group_id FROM tenants WHERE tenant_id = $1',
244 [tenant]
245 );
246 if (result.rows.length > 0 && result.rows[0].meshcentral_group_id) {
247 meshId = result.rows[0].meshcentral_group_id;
248 console.log(`✅ [AgentV3] Linux agent for tenant ${tenant} - mesh: ${meshId.substring(0, 30)}...`);
249 } else {
250 console.warn(`⚠️ [AgentV3] Tenant ${tenant} has no meshcentral_group_id, using default`);
251 }
252 } catch (dbErr) {
253 console.error(`❌ [AgentV3] DB error fetching mesh ID:`, dbErr.message, dbErr.stack);
254 }
255 }
256
257 // Download the agent directly from MeshCentral (public endpoint, no auth required)
258 const installUrl = `${MESHCENTRAL_URL}/meshagents?script=1&id=4&meshid=${meshId}&installflags=0`;
259 console.log(`🔗 [AgentV3] Fetching from MeshCentral: ${installUrl.substring(0, 100)}...`);
260
261 const response = await axios({
262 method: 'GET',
263 url: installUrl,
264 headers: {
265 'User-Agent': 'EverydayTech-RMM/3.0'
266 },
267 timeout: 30000,
268 maxRedirects: 5 // Follow redirects
269 });
270
271 console.log(`✅ [AgentV3] MeshCentral response received, sending to client`);
272
273 res.type('text/plain');
274 res.send(response.data);
275
276 } catch (error) {
277 console.error('❌ [AgentV3] Error downloading Linux agent:', error.message);
278 console.error('Stack:', error.stack);
279 if (!res.headersSent) {
280 res.status(500).send('#!/bin/bash\necho "Error: Failed to download installer"');
281 }
282 }
283});
284
285/**
286 * @api {get} /api/agent-v3/download/macos/:token Download macOS Installer (Token)
287 * @apiName DownloadMacOSAgentToken
288 * @apiGroup AgentV3Group
289 * @apiDescription Downloads macOS PKG installer using temporary token. Authenticates with MeshCentral admin credentials,
290 * fetches tenant-specific mesh ID if tenant parameter provided, and streams package file. Token expires in 7 days.
291 * @apiParam {string} token Download token from /download-token endpoint
292 * @apiParam (Query) {UUID} [tenant] Tenant UUID for mesh-specific installer
293 * @apiSuccess (200) {Binary} pkg macOS installer package (application/octet-stream)
294 * @apiError 401 Invalid or expired token
295 * @apiError 500 MeshCentral authentication/download error
296 * @apiExample {curl} Example:
297 * curl "https://api.example.com/api/agent-v3/download/macos/eyJhbGc...?tenant=550e8400-e29b-41d4-a716-446655440000" \
298 * -o everydaytech-agent-v3.pkg
299 */
300/**
301 * Download macOS agent with temporary token
302 * GET /api/agent-v3/download/macos/:token
303
304router.get('/download/linux', authenticateToken, async (req, res) => {
305 try {
306 const meshId = process.env.MESHCENTRAL_DEFAULT_MESH_ID || '';
307 const installUrl = `${MESHCENTRAL_URL}/meshagents?script=1&id=4&meshid=${meshId}&installflags=0`;
308
309 const response = await axios({
310 method: 'GET',
311 url: installUrl,
312 headers: {
313 'User-Agent': 'EverydayTech-RMM/3.0'
314 }
315 });
316
317 res.type('text/plain');
318 res.send(response.data);
319
320 } catch (error) {
321 console.error('❌ [AgentV3] Error proxying Linux installer:', error.message);
322 res.status(500).send('#!/bin/bash\necho "Error: Failed to download installer"');
323 }
324});
325
326/**
327 * @api {get} /api/agent-v3/download/macos Download macOS Installer (Auth)
328 * @apiName DownloadMacOSAgentAuth
329 * @apiGroup AgentV3Group
330 * @apiDescription Proxies macOS PKG installer from MeshCentral with JWT authentication. Streams package file to
331 * client with "everydaytech-agent-v3.pkg" filename. Requires valid Bearer token.
332 * @apiSuccess (200) {Binary} pkg macOS installer package (application/octet-stream)
333 * @apiError 401 Authentication required
334 * @apiError 500 MeshCentral proxy error
335 * @apiExample {curl} Example:
336 * curl https://api.example.com/api/agent-v3/download/macos \
337 * -H "Authorization: Bearer YOUR_TOKEN" \
338 * -o everydaytech-agent-v3.pkg
339 */
340/**
341 * Proxy download endpoint for macOS agent (JWT authenticated)
342 * GET /api/agent-v3/download/macos
343
344router.get('/download/macos/:token', async (req, res) => {
345 try {
346 const { token } = req.params;
347 const { tenant } = req.query;
348
349 console.log(`📥 [AgentV3] macOS download request - Token: ${token.substring(0, 20)}..., Tenant: ${tenant}`);
350
351 // Verify JWT signature and expiry
352 let decoded;
353 try {
354 decoded = jwt.verify(token, JWT_SECRET);
355 console.log(`✅ [AgentV3] Token verified - Purpose: ${decoded.purpose}`);
356 } catch (err) {
357 console.error(`❌ [AgentV3] JWT verification failed:`, err.message);
358 return res.status(401).json({ error: 'Invalid or expired download token' });
359 }
360
361 // Get tenant's MeshCentral group ID
362 let meshId = process.env.MESHCENTRAL_DEFAULT_MESH_ID || '';
363 if (tenant) {
364 try {
365 const result = await pool.query(
366 'SELECT meshcentral_group_id FROM tenants WHERE tenant_id = $1',
367 [tenant]
368 );
369 if (result.rows.length > 0 && result.rows[0].meshcentral_group_id) {
370 meshId = result.rows[0].meshcentral_group_id;
371 console.log(`✅ [AgentV3] macOS agent for tenant ${tenant} - mesh: ${meshId.substring(0, 30)}...`);
372 } else {
373 console.warn(`⚠️ [AgentV3] Tenant ${tenant} has no meshcentral_group_id, using default`);
374 }
375 } catch (dbErr) {
376 console.error(`❌ [AgentV3] DB error fetching mesh ID:`, dbErr.message, dbErr.stack);
377 }
378 }
379
380 // Download the agent directly from MeshCentral (public endpoint, no auth required)
381 const installUrl = `${MESHCENTRAL_URL}/meshagents?id=4&meshid=${meshId}&installflags=0&meshinstall=16`;
382 console.log(`🔗 [AgentV3] Fetching from MeshCentral: ${installUrl.substring(0, 100)}...`);
383
384 const response = await axios({
385 method: 'GET',
386 url: installUrl,
387 responseType: 'stream',
388 headers: {
389 'User-Agent': 'EverydayTech-RMM/3.0'
390 },
391 timeout: 30000,
392 maxRedirects: 5 // Follow redirects
393 });
394
395 console.log(`✅ [AgentV3] MeshCentral response received, streaming to client`);
396
397 res.setHeader('Content-Type', 'application/octet-stream');
398 res.setHeader('Content-Disposition', 'attachment; filename="everydaytech-agent-v3.pkg"');
399
400 // Handle stream errors
401 response.data.on('error', (streamErr) => {
402 console.error(`❌ [AgentV3] Stream error:`, streamErr.message);
403 });
404
405 response.data.pipe(res);
406
407 } catch (error) {
408 console.error('❌ [AgentV3] Error downloading macOS agent:', error.message);
409 console.error('Stack:', error.stack);
410 if (!res.headersSent) {
411 res.status(500).json({ error: 'Failed to download installer', details: error.message });
412 }
413 }
414});
415
416/**
417 * @api {get} /api/agent-v3/downloads Get Download URLs
418 * @apiName GetAgentV3DownloadURLs
419 * @apiGroup AgentV3Group
420 * @apiDescription Returns authenticated download URLs for all platforms and architectures. Includes installation
421 * instructions and MeshCentral server metadata. Requires JWT authentication.
422 * @apiSuccess {boolean} success Operation status (true)
423 * @apiSuccess {string} meshcentralUrl MeshCentral server URL
424 * @apiSuccess {string} meshId Default mesh ID
425 * @apiSuccess {object} downloads Platform-specific download URLs
426 * @apiSuccess {object} downloads.windows Windows download options
427 * @apiSuccess {object} downloads.windows.x64 64-bit Windows installer URL
428 * @apiSuccess {object} downloads.linux Linux download options
429 * @apiSuccess {object} downloads.macos macOS download options
430 * @apiSuccess {Object} instructions Platform-specific installation instructions
431 * @apiError 401 Authentication required
432 * @apiError 500 Server error
433 * @apiExample {curl} Example:
434 * curl https://api.example.com/api/agent-v3/downloads \
435 * -H "Authorization: Bearer YOUR_TOKEN"
436 * @apiExample {json} Success-Response (200):
437 * {
438 * "success": true,
439 * "meshcentralUrl": "https://mesh.example.com",
440 * "downloads": {
441 * "windows": {"x64": {"exe": "https://api.example.com/api/agent-v3/download/windows", "name": "EverydayTech Agent v3 (Windows 64-bit)"}}
442 * },
443 * "instructions": {"windows": "Run the .exe or .msi installer as Administrator"}
444 * }
445 */
446/**
447 * Get agent download URLs for all platforms
448 * GET /api/agent-v3/downloads
449
450router.get('/download/macos', authenticateToken, async (req, res) => {
451 try {
452 const meshId = process.env.MESHCENTRAL_DEFAULT_MESH_ID || '';
453 const installUrl = `${MESHCENTRAL_URL}/meshagents?id=4&meshid=${meshId}&installflags=0&meshinstall=16`;
454
455 const response = await axios({
456 method: 'GET',
457 url: installUrl,
458 responseType: 'stream',
459 headers: {
460 'User-Agent': 'EverydayTech-RMM/3.0'
461 }
462 });
463
464 res.setHeader('Content-Type', 'application/octet-stream');
465 res.setHeader('Content-Disposition', 'attachment; filename="everydaytech-agent-v3.pkg"');
466 response.data.pipe(res);
467
468 } catch (error) {
469 console.error('❌ [AgentV3] Error proxying macOS installer:', error.message);
470 res.status(500).json({ error: 'Failed to download installer' });
471 }
472});
473
474/**
475 * @api {get} /api/agent-v3/download/windows/:token Download Windows Installer (Token)
476 * @apiName DownloadWindowsAgentToken
477 * @apiGroup AgentV3Group
478 * @apiDescription Downloads Windows EXE installer using temporary token from /download-token endpoint. Authenticates
479 * with MeshCentral admin credentials, fetches tenant-specific mesh ID from database if tenant query parameter provided,
480 * and streams installer binary. Token expires in 7 days. Logs detailed operation trace for debugging.
481 * @apiParam {string} token Download token from /download-token endpoint
482 * @apiParam (Query) {UUID} [tenant] Tenant UUID for mesh-specific installer
483 * @apiSuccess (200) {Binary} exe Windows installer binary (application/octet-stream)
484 * @apiError 401 Invalid or expired token
485 * @apiError 500 MeshCentral authentication/download error
486 * @apiExample {curl} Example:
487 * curl "https://api.example.com/api/agent-v3/download/windows/eyJhbGc...?tenant=550e8400-e29b-41d4-a716-446655440000" \
488 * -o everydaytech-agent-v3.exe
489 */
490/**
491 * Download Windows agent with temporary token
492 * GET /api/agent-v3/download/windows/:token
493
494 */
495router.get('/downloads', authenticateToken, async (req, res) => {
496 try {
497 // MeshCentral agent download URLs
498 // Proxied through our backend with JWT authentication
499
500 const backendUrl = process.env.BACKEND_URL || 'https://rmm-psa-backend-t9f7k.ondigitalocean.app';
501
502 const downloads = {
503 windows: {
504 x64: {
505 exe: `${backendUrl}/api/agent-v3/download/windows`,
506 msi: `${backendUrl}/api/agent-v3/download/windows`,
507 name: 'EverydayTech Agent v3 (Windows 64-bit)'
508 },
509 x86: {
510 exe: `${backendUrl}/api/agent-v3/download/windows`,
511 name: 'EverydayTech Agent v3 (Windows 32-bit)'
512 }
513 },
514 linux: {
515 x64: {
516 sh: `${backendUrl}/api/agent-v3/download/linux`,
517 name: 'EverydayTech Agent v3 (Linux 64-bit)'
518 },
519 arm: {
520 sh: `${backendUrl}/api/agent-v3/download/linux`,
521 name: 'EverydayTech Agent v3 (Linux ARM)'
522 }
523 },
524 macos: {
525 x64: {
526 pkg: `${backendUrl}/api/agent-v3/download/macos`,
527 name: 'EverydayTech Agent v3 (macOS Intel)'
528 },
529 arm64: {
530 pkg: `${backendUrl}/api/agent-v3/download/macos`,
531 name: 'EverydayTech Agent v3 (macOS Apple Silicon)'
532 }
533 }
534 };
535
536 res.json({
537 success: true,
538 meshcentralUrl: MESHCENTRAL_URL,
539 meshId: meshId,
540 downloads: downloads,
541 instructions: {
542 windows: 'Run the .exe or .msi installer as Administrator',
543 linux: 'Download and run: wget -O install.sh [URL] && sudo bash install.sh',
544 macos: 'Download the .pkg file and double-click to install'
545 }
546 });
547
548 } catch (error) {
549 console.error('❌ [AgentV3] Error getting downloads:', error);
550 res.status(500).json({ error: 'Failed to get agent downloads' });
551 }
552});
553
554/**
555 * @api {get} /api/agent-v3/install-script Get Linux/macOS Install Script
556 * @apiName GetAgentV3InstallScript
557 * @apiGroup AgentV3Group
558 * @apiDescription Returns bash installation script for Linux/macOS. Script generates temporary download token,
559 * downloads installer from /download/linux/:token endpoint, and executes with sudo. No authentication required.
560 * @apiSuccess (200) {String} script Bash installation script (text/plain)
561 * @apiError 500 Script generation error
562 * @apiExample {curl} Example:
563 * curl https://api.example.com/api/agent-v3/install-script | sudo bash
564 */
565/**
566 * Get agent installation script for Linux/Mac
567 * GET /api/agent-v3/install-script
568 */
569router.get('/install-script', async (req, res) => {
570 try {
571 const backendUrl = process.env.BACKEND_URL || 'https://rmm-psa-backend-t9f7k.ondigitalocean.app';
572
573 res.type('text/plain');
574 res.send(`#!/bin/bash
575# EverydayTech Agent v3 Installation Script
576#
577# This script downloads and installs the EverydayTech RMM Agent
578# The agent will appear in your dashboard: https://everydaytech.au
579
580echo "🚀 Installing EverydayTech Agent v3..."
581echo ""
582
583# Get temporary download token
584echo "Getting download token..."
585TOKEN_RESPONSE=$(curl -s "${backendUrl}/api/agent-v3/download-token")
586TOKEN=$(echo $TOKEN_RESPONSE | grep -o '"token":"[^"]*' | cut -d'"' -f4)
587
588if [ -z "$TOKEN" ]; then
589 echo "❌ Failed to get download token"
590 exit 1
591fi
592
593# Download and run the installer
594echo "Downloading installer..."
595wget -qO- "${backendUrl}/api/agent-v3/download/linux/$TOKEN" | sudo bash
596
597if [ $? -eq 0 ]; then
598 echo ""
599 echo "✅ EverydayTech Agent v3 installed successfully!"
600 echo " The agent will appear in your dashboard within 30 seconds."
601 echo " Dashboard: https://everydaytech.au"
602else
603 echo ""
604 echo "❌ Installation failed. Please check the error messages above."
605 exit 1
606fi
607`);
608
609 } catch (error) {
610 console.error('❌ [AgentV3] Error generating install script:', error);
611 res.status(500).send('# Error generating install script');
612 }
613});
614
615/**
616 * @api {get} /api/agent-v3/install-script/windows Get Windows PowerShell Install Script
617 * @apiName GetAgentV3WindowsInstallScript
618 * @apiGroup AgentV3Group
619 * @apiDescription Returns PowerShell installation script for Windows. Script generates temporary download token,
620 * downloads EXE to %TEMP%, executes with silent flag (/S), checks service status, and cleans up. No authentication required.
621 * @apiSuccess (200) {String} script PowerShell installation script (text/plain)
622 * @apiError 500 Script generation error
623 * @apiExample {powershell} Example:
624 * iwr -useb https://api.example.com/api/agent-v3/install-script/windows | iex
625 */
626/**
627 * Get Windows PowerShell installation script
628 * GET /api/agent-v3/install-script/windows
629 */
630router.get('/install-script/windows', async (req, res) => {
631 try {
632 const backendUrl = process.env.BACKEND_URL || 'https://rmm-psa-backend-t9f7k.ondigitalocean.app';
633
634 res.type('text/plain');
635 res.send(`# EverydayTech Agent v3 Installation Script (Windows)
636#
637# Run this in PowerShell as Administrator:
638# iwr -useb https://rmm-psa-backend-t9f7k.ondigitalocean.app/api/agent-v3/install-script/windows | iex
639
640Write-Host "🚀 Installing EverydayTech Agent v3..." -ForegroundColor Cyan
641Write-Host ""
642
643# Get temporary download token
644Write-Host "Getting download token..."
645$tokenResponse = Invoke-RestMethod -Uri "${backendUrl}/api/agent-v3/download-token"
646$token = $tokenResponse.token
647
648if (-not $token) {
649 Write-Host "❌ Failed to get download token" -ForegroundColor Red
650 exit 1
651}
652
653# Download installer
654$installerPath = "$env:TEMP\\everydaytech-agent-v3.exe"
655Write-Host "Downloading installer..."
656Invoke-WebRequest -Uri "${backendUrl}/api/agent-v3/download/windows/$token" -OutFile $installerPath
657
658# Run installer
659Write-Host "Running installer..."
660Start-Process -FilePath $installerPath -ArgumentList "/S" -Wait
661
662# Check if service is running
663$service = Get-Service -Name "EverydayTechAgent" -ErrorAction SilentlyContinue
664if ($service -and $service.Status -eq "Running") {
665 Write-Host ""
666 Write-Host "✅ EverydayTech Agent v3 installed successfully!" -ForegroundColor Green
667 Write-Host " The agent will appear in your dashboard within 30 seconds." -ForegroundColor Green
668 Write-Host " Dashboard: https://everydaytech.au" -ForegroundColor Cyan
669} else {
670 Write-Host ""
671 Write-Host "⚠️ Installation completed but service may not be running." -ForegroundColor Yellow
672 Write-Host " Please check Windows Services for 'EverydayTech Agent'" -ForegroundColor Yellow
673}
674
675# Clean up
676Remove-Item $installerPath -Force -ErrorAction SilentlyContinue
677`);
678
679 } catch (error) {
680 console.error('❌ [AgentV3] Error generating Windows install script:', error);
681 res.status(500).send('# Error generating install script');
682 }
683});
684
685/**
686 * @api {get} /api/agent-v3/config Get Agent v3 Configuration
687 * @apiName GetAgentV3Config
688 * @apiGroup AgentV3Group
689 * @apiDescription Returns agent v3 configuration including MeshCentral server URL, default mesh ID, version, and
690 * feature list (remote desktop, terminal access, file transfer, hardware inventory, auto-updates, cross-platform,
691 * real-time status, event logging). Requires JWT authentication.
692 * @apiSuccess {boolean} success Operation status (true)
693 * @apiSuccess {string} meshcentralUrl MeshCentral server URL
694 * @apiSuccess {string} meshId Default mesh ID
695 * @apiSuccess {string} version Agent version ("3.0.0")
696 * @apiSuccess {string[]} features Feature list
697 * @apiError 401 Authentication required
698 * @apiError 500 Server error
699 * @apiExample {curl} Example:
700 * curl https://api.example.com/api/agent-v3/config \
701 * -H "Authorization: Bearer YOUR_TOKEN"
702 * @apiExample {json} Success-Response (200):
703 * {
704 * "success": true,
705 * "meshcentralUrl": "https://mesh.example.com",
706 * "meshId": "mesh/abc123...",
707 * "version": "3.0.0",
708 * "features": ["Remote Desktop (High Performance)", "Terminal/SSH Access", "File Transfer"]
709 * }
710 */
711/**
712 * Get agent configuration info
713 * GET /api/agent-v3/config
714 */
715router.get('/config', authenticateToken, async (req, res) => {
716 try {
717 res.json({
718 success: true,
719 meshcentralUrl: MESHCENTRAL_URL,
720 meshId: process.env.MESHCENTRAL_DEFAULT_MESH_ID || 'Not configured',
721 version: '3.0.0',
722 features: [
723 'Remote Desktop (High Performance)',
724 'Terminal/SSH Access',
725 'File Transfer',
726 'System Information Collection',
727 'Hardware Inventory',
728 'Auto-Updates',
729 'Cross-Platform (Windows, Linux, macOS)',
730 'Real-time Connection Status',
731 'Event Logging'
732 ],
733 setupRequired: !process.env.MESHCENTRAL_DEFAULT_MESH_ID,
734 setupInstructions: 'Login to MeshCentral, create a device group, and add MESHCENTRAL_DEFAULT_MESH_ID to environment variables'
735 });
736 } catch (error) {
737 console.error('❌ [AgentV3] Error getting config:', error);
738 res.status(500).json({ error: 'Failed to get agent config' });
739 }
740});
741
742module.exports = router;