EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
agent-download.js
Go to the documentation of this file.
1/**
2 * @file agent-download.js
3 * @description Agent v2 download proxy service. Fetches agent installers from GitHub Releases API and redirects
4 * clients to platform-specific assets. Requires GITHUB_TOKEN environment variable for private repositories.
5 * Supports Windows/Linux/macOS platforms with automatic asset pattern matching.
6 * @module routes/agent-download
7 */
8
9/**
10 * @apiDefine AgentDownloadGroup Agent Downloads (v2)
11 * Agent installer downloads from GitHub releases
12 */
13
14// routes/agent-download.js
15// Handle agent downloads from GitHub Releases
16
17const express = require('express');
18const router = express.Router();
19const fetch = require('node-fetch');
20
21// GitHub configuration
22const GITHUB_ORG = process.env.GITHUB_ORG || 'Independent-Business-Group';
23const GITHUB_REPO = process.env.GITHUB_AGENT_REPO || 'rmm-psa-agent-v2';
24const GITHUB_TOKEN = process.env.GITHUB_TOKEN; // Optional, for private repos
25
26// Get the latest release from GitHub
27/**
28 *
29 */
30async function getLatestRelease() {
31 const url = `https://api.github.com/repos/${GITHUB_ORG}/${GITHUB_REPO}/releases/latest`;
32 const headers = {
33 'Accept': 'application/vnd.github.v3+json',
34 'User-Agent': 'RMM-PSA-Backend'
35 };
36
37 if (GITHUB_TOKEN) {
38 headers['Authorization'] = `token ${GITHUB_TOKEN}`;
39 }
40
41 try {
42 const response = await fetch(url, { headers });
43 if (!response.ok) {
44 throw new Error(`GitHub API returned ${response.status}: ${response.statusText}`);
45 }
46 return await response.json();
47 } catch (err) {
48 console.error('[Agent Download] Failed to fetch latest release:', err.message);
49 throw err;
50 }
51}
52
53// Get download URL for specific platform
54/**
55 *
56 * @param release
57 * @param platform
58 */
59function getDownloadUrl(release, platform) {
60 const assets = release.assets || [];
61
62 // Map platform to asset name patterns
63 const patterns = {
64 'windows': /win.*\.(exe|zip)$/i,
65 'linux': /linux.*$/i,
66 'macos': /macos.*$/i,
67 'darwin': /macos.*$/i
68 };
69
70 const pattern = patterns[platform.toLowerCase()];
71 if (!pattern) {
72 throw new Error(`Unknown platform: ${platform}`);
73 }
74
75 const asset = assets.find(a => pattern.test(a.name));
76 if (!asset) {
77 throw new Error(`No asset found for platform: ${platform}`);
78 }
79
80 return {
81 url: asset.browser_download_url,
82 name: asset.name,
83 size: asset.size,
84 downloadCount: asset.download_count
85 };
86}
87
88/**
89 * @api {get} /api/agent/download/:platform Download Agent Installer
90 * @apiName DownloadAgentInstaller
91 * @apiGroup AgentDownloadGroup
92 * @apiDescription Fetches latest agent installer from GitHub Releases and redirects to download URL. Supports
93 * platform-specific asset matching (Windows: .exe/.zip, Linux: linux binaries, macOS: macos binaries).
94 * @apiParam {string} platform Platform identifier (windows, linux, macos, darwin)
95 * @apiSuccess (302) Redirect Redirects to GitHub asset download URL
96 * @apiError 500 GitHub API error or asset not found
97 * @apiExample {curl} Example:
98 * curl -L https://api.example.com/api/agent/download/windows -o agent-installer.exe
99 * @apiExample {json} Error-Response (500):
100 * {"error":"Failed to fetch agent download", "message":"No asset found for platform: linux", "platform":"linux"}
101 */
102// GET /api/agent/download/:platform
103// Redirects to the latest agent download for the specified platform
104router.get('/download/:platform', async (req, res) => {
105 const platform = req.params.platform;
106
107 try {
108 console.log(`[Agent Download] Request for platform: ${platform}`);
109
110 const release = await getLatestRelease();
111 console.log(`[Agent Download] Latest release: ${release.tag_name} (${release.name})`);
112
113 const download = getDownloadUrl(release, platform);
114 console.log(`[Agent Download] Redirecting to: ${download.name} (${(download.size / 1024 / 1024).toFixed(2)} MB)`);
115
116 // Redirect to GitHub's download URL
117 res.redirect(302, download.url);
118
119 } catch (err) {
120 console.error('[Agent Download] Error:', err.message);
121 res.status(500).json({
122 error: 'Failed to fetch agent download',
123 message: err.message,
124 platform
125 });
126 }
127});
128
129/**
130 * @api {get} /api/agent/info Get Release Info
131 * @apiName GetAgentReleaseInfo
132 * @apiGroup AgentDownloadGroup
133 * @apiDescription Retrieves detailed information about latest GitHub release including version, notes, and
134 * platform-specific download URLs with asset metadata (filename, size, download count).
135 * @apiSuccess {string} version Release tag (e.g., "v2.0.0")
136 * @apiSuccess {string} name Release name
137 * @apiSuccess {string} published ISO 8601 publication timestamp
138 * @apiSuccess {string} notes Release notes/changelog (Markdown)
139 * @apiSuccess {object} platforms Platform-specific download metadata
140 * @apiSuccess {object} platforms.windows Windows installer details
141 * @apiSuccess {boolean} platforms.windows.available Asset availability
142 * @apiSuccess {string} platforms.windows.filename Asset filename
143 * @apiSuccess {number} platforms.windows.size File size in bytes
144 * @apiSuccess {number} platforms.windows.downloadCount GitHub download count
145 * @apiSuccess {string} platforms.windows.downloadUrl Backend proxy download URL
146 * @apiSuccess {string} githubUrl GitHub release page URL
147 * @apiError 500 GitHub API error
148 * @apiExample {curl} Example:
149 * curl https://api.example.com/api/agent/info
150 * @apiExample {json} Success-Response (200):
151 * {
152 * "version": "v2.0.0",
153 * "name": "Agent v2.0.0 - Stable Release",
154 * "published": "2026-01-15T10:30:00Z",
155 * "notes": "# Changes\n- Fixed RDP connection\n- Added Linux support",
156 * "platforms": {
157 * "windows": {"available": true, "filename": "agent-win-x64.exe", "size": 8421376, "downloadCount": 42},
158 * "linux": {"available": true, "filename": "agent-linux-x64", "size": 7340032, "downloadCount": 15}
159 * },
160 * "githubUrl": "https://github.com/org/repo/releases/tag/v2.0.0"
161 * }
162 */
163// GET /api/agent/info
164// Returns information about the latest release
165router.get('/info', async (req, res) => {
166 try {
167 const release = await getLatestRelease();
168
169 const platforms = {};
170 ['windows', 'linux', 'macos'].forEach(platform => {
171 try {
172 const download = getDownloadUrl(release, platform);
173 platforms[platform] = {
174 available: true,
175 filename: download.name,
176 size: download.size,
177 downloadCount: download.downloadCount,
178 downloadUrl: `/api/releases/download/${platform}`
179 };
180 } catch (err) {
181 platforms[platform] = {
182 available: false,
183 error: err.message
184 };
185 }
186 });
187
188 res.json({
189 version: release.tag_name,
190 name: release.name,
191 published: release.published_at,
192 notes: release.body,
193 platforms,
194 githubUrl: release.html_url
195 });
196
197 } catch (err) {
198 console.error('[Agent Info] Error:', err.message);
199 res.status(500).json({
200 error: 'Failed to fetch release information',
201 message: err.message
202 });
203 }
204});
205
206/**
207 * @api {get} /api/agent/versions List All Versions
208 * @apiName ListAgentVersions
209 * @apiGroup AgentDownloadGroup
210 * @apiDescription Retrieves all available GitHub releases with metadata. Includes prerelease and draft flags.
211 * @apiSuccess {object[]} versions Array of release objects
212 * @apiSuccess {string} versions.version Release tag
213 * @apiSuccess {string} versions.name Release name
214 * @apiSuccess {string} versions.published ISO 8601 publication timestamp
215 * @apiSuccess {boolean} versions.prerelease Prerelease flag
216 * @apiSuccess {boolean} versions.draft Draft flag
217 * @apiSuccess {string} versions.url GitHub release page URL
218 * @apiError 500 GitHub API error
219 * @apiExample {curl} Example:
220 * curl https://api.example.com/api/agent/versions
221 * @apiExample {json} Success-Response (200):
222 * {
223 * "versions": [
224 * {"version":"v2.0.0", "name":"Stable", "published":"2026-01-15T10:30:00Z", "prerelease":false, "draft":false},
225 * {"version":"v2.0.0-rc1", "name":"Beta", "published":"2026-01-10T08:00:00Z", "prerelease":true, "draft":false}
226 * ]
227 * }
228 */
229// GET /api/agent/versions
230// Returns all available versions
231router.get('/versions', async (req, res) => {
232 const url = `https://api.github.com/repos/${GITHUB_ORG}/${GITHUB_REPO}/releases`;
233 const headers = {
234 'Accept': 'application/vnd.github.v3+json',
235 'User-Agent': 'RMM-PSA-Backend'
236 };
237
238 if (GITHUB_TOKEN) {
239 headers['Authorization'] = `token ${GITHUB_TOKEN}`;
240 }
241
242 try {
243 const response = await fetch(url, { headers });
244 if (!response.ok) {
245 throw new Error(`GitHub API returned ${response.status}`);
246 }
247
248 const releases = await response.json();
249
250 const versions = releases.map(r => ({
251 version: r.tag_name,
252 name: r.name,
253 published: r.published_at,
254 prerelease: r.prerelease,
255 draft: r.draft,
256 url: r.html_url
257 }));
258
259 res.json({ versions });
260
261 } catch (err) {
262 console.error('[Agent Versions] Error:', err.message);
263 res.status(500).json({
264 error: 'Failed to fetch versions',
265 message: err.message
266 });
267 }
268});
269
270module.exports = router;