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
10 * @apiDefine AgentDownloadGroup Agent Downloads (v2)
11 * Agent installer downloads from GitHub releases
14// routes/agent-download.js
15// Handle agent downloads from GitHub Releases
17const express = require('express');
18const router = express.Router();
19const fetch = require('node-fetch');
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
26// Get the latest release from GitHub
30async function getLatestRelease() {
31 const url = `https://api.github.com/repos/${GITHUB_ORG}/${GITHUB_REPO}/releases/latest`;
33 'Accept': 'application/vnd.github.v3+json',
34 'User-Agent': 'RMM-PSA-Backend'
38 headers['Authorization'] = `token ${GITHUB_TOKEN}`;
42 const response = await fetch(url, { headers });
44 throw new Error(`GitHub API returned ${response.status}: ${response.statusText}`);
46 return await response.json();
48 console.error('[Agent Download] Failed to fetch latest release:', err.message);
53// Get download URL for specific platform
59function getDownloadUrl(release, platform) {
60 const assets = release.assets || [];
62 // Map platform to asset name patterns
64 'windows': /win.*\.(exe|zip)$/i,
70 const pattern = patterns[platform.toLowerCase()];
72 throw new Error(`Unknown platform: ${platform}`);
75 const asset = assets.find(a => pattern.test(a.name));
77 throw new Error(`No asset found for platform: ${platform}`);
81 url: asset.browser_download_url,
84 downloadCount: asset.download_count
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"}
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;
108 console.log(`[Agent Download] Request for platform: ${platform}`);
110 const release = await getLatestRelease();
111 console.log(`[Agent Download] Latest release: ${release.tag_name} (${release.name})`);
113 const download = getDownloadUrl(release, platform);
114 console.log(`[Agent Download] Redirecting to: ${download.name} (${(download.size / 1024 / 1024).toFixed(2)} MB)`);
116 // Redirect to GitHub's download URL
117 res.redirect(302, download.url);
120 console.error('[Agent Download] Error:', err.message);
121 res.status(500).json({
122 error: 'Failed to fetch agent download',
123 message: err.message,
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):
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",
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}
160 * "githubUrl": "https://github.com/org/repo/releases/tag/v2.0.0"
163// GET /api/agent/info
164// Returns information about the latest release
165router.get('/info', async (req, res) => {
167 const release = await getLatestRelease();
169 const platforms = {};
170 ['windows', 'linux', 'macos'].forEach(platform => {
172 const download = getDownloadUrl(release, platform);
173 platforms[platform] = {
175 filename: download.name,
177 downloadCount: download.downloadCount,
178 downloadUrl: `/api/releases/download/${platform}`
181 platforms[platform] = {
189 version: release.tag_name,
191 published: release.published_at,
194 githubUrl: release.html_url
198 console.error('[Agent Info] Error:', err.message);
199 res.status(500).json({
200 error: 'Failed to fetch release information',
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):
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}
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`;
234 'Accept': 'application/vnd.github.v3+json',
235 'User-Agent': 'RMM-PSA-Backend'
239 headers['Authorization'] = `token ${GITHUB_TOKEN}`;
243 const response = await fetch(url, { headers });
245 throw new Error(`GitHub API returned ${response.status}`);
248 const releases = await response.json();
250 const versions = releases.map(r => ({
253 published: r.published_at,
254 prerelease: r.prerelease,
259 res.json({ versions });
262 console.error('[Agent Versions] Error:', err.message);
263 res.status(500).json({
264 error: 'Failed to fetch versions',
270module.exports = router;