feat(firmware): in-app MikroTik release notes + posture findings for pending updates (v0.16.6-beta)
- New GET /api/firmware/changelog/:version proxies MikroTik's official per-version release notes (download.mikrotik.com/routeros/<ver>/CHANGELOG) with strict version validation and process-lifetime caching - ChangelogModal: "What's new in X.Y.Z" on the device Config tab's update box and on the Firmware page's latest-version cells opens the changelog in-app, with an external mikrotik.com link and graceful fallback when the CDN is unreachable - Security posture: a pending RouterOS update is now a medium-severity finding and a pending RouterBOOT upgrade a low-severity one; titles are version-free so Common Findings aggregates the fleet into one row - chore(deps): npm audit fix bumps morgan (GHSA-4vj7-5mj6-jm8m); both workspaces audit clean Validation: backend tsc/lint/92 tests, frontend tsc/lint/build/11 tests, endpoint tested (valid/invalid/unknown versions), browser-verified on Firmware page, device Config tab, Security Center, and device Security tab; security review of the diff found no findings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
8307bcd291
commit
27f155f778
@@ -117,6 +117,38 @@ router.post('/rollouts/:id/start', requireWrite, async (req: Request, res: Respo
|
||||
}
|
||||
});
|
||||
|
||||
// GET /api/firmware/changelog/:version — proxy MikroTik's per-version release
|
||||
// notes (download.mikrotik.com/routeros/<ver>/CHANGELOG) so the UI can show
|
||||
// "what's new" without a CORS-blocked cross-origin fetch. Released changelogs
|
||||
// are immutable, so successful fetches are cached for the process lifetime.
|
||||
const changelogCache = new Map<string, string>();
|
||||
const CHANGELOG_CACHE_MAX = 100;
|
||||
|
||||
router.get('/changelog/:version', async (req: Request, res: Response) => {
|
||||
const version = String(req.params.version).trim();
|
||||
// RouterOS versions: 7.23.1, 7.16, 6.49.10, plus rc/beta suffixes (7.20rc3)
|
||||
if (!/^\d+\.\d+(\.\d+)?(rc\d+|beta\d+)?$/i.test(version)) {
|
||||
return res.status(400).json({ error: 'Invalid RouterOS version' });
|
||||
}
|
||||
const url = `https://download.mikrotik.com/routeros/${version}/CHANGELOG`;
|
||||
|
||||
const cached = changelogCache.get(version);
|
||||
if (cached) return res.json({ version, url, text: cached });
|
||||
|
||||
try {
|
||||
const resp = await fetch(url, { signal: AbortSignal.timeout(10_000) });
|
||||
if (!resp.ok) {
|
||||
return res.status(404).json({ error: `No changelog found for ${version}`, url });
|
||||
}
|
||||
const text = await resp.text();
|
||||
if (changelogCache.size >= CHANGELOG_CACHE_MAX) changelogCache.clear();
|
||||
changelogCache.set(version, text);
|
||||
res.json({ version, url, text });
|
||||
} catch {
|
||||
res.status(502).json({ error: 'Could not reach the MikroTik changelog server', url });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/firmware/rollouts/:id/cancel — stops before the next device (an
|
||||
// in-flight upgrade is never interrupted mid-write)
|
||||
router.post('/rollouts/:id/cancel', requireWrite, async (req: Request, res: Response) => {
|
||||
|
||||
Reference in New Issue
Block a user