const express = require('express'); const router = express.Router(); const fs = require('fs'); const path = require('path'); const HISTORY_PATH = path.join(__dirname, '..', 'HISTORY.md'); let pkg; try { pkg = require('../package.json'); } catch { pkg = { version: '0.1.0' }; } /** * Parses HISTORY.md and returns the latest version's structured notes. * Reads from disk on every call so the file can be updated without restart. */ function parseHistory() { if (!fs.existsSync(HISTORY_PATH)) { return { version: '0.1', notes: [] }; } const content = fs.readFileSync(HISTORY_PATH, 'utf8'); // Match first (topmost) version block const blockMatch = content.match(/## (v[\d.]+)\n([\s\S]*?)(?=\n## v|\s*$)/); if (!blockMatch) return { version: '0.1', notes: [] }; const version = blockMatch[1].slice(1); // strip the 'v' const body = blockMatch[2]; // Parse ### Added / Changed / Fixed / Removed sections const notes = []; const sectionRe = /### (Added|Changed|Fixed|Removed|Deprecated|Security)\n([\s\S]*?)(?=###|\s*$)/g; let m; while ((m = sectionRe.exec(body)) !== null) { const category = m[1]; const items = m[2] .split('\n') .filter(l => l.trim().startsWith('- ')) .map(l => ({ category, text: l.trim().slice(2).trim() })); notes.push(...items); } return { version, notes }; } // GET /api/version router.get('/', (req, res) => { res.json(parseHistory()); }); // GET /api/version/history router.get('/history', (req, res) => { try { if (!fs.existsSync(HISTORY_PATH)) { return res.status(404).json({ error: 'Release history not found', version: pkg.version, history: '', updated_at: null, }); } const history = fs.readFileSync(HISTORY_PATH, 'utf8'); let updatedAt = null; try { updatedAt = fs.statSync(HISTORY_PATH).mtime.toISOString(); } catch { updatedAt = null; } res.json({ version: pkg.version, history, updated_at: updatedAt, }); } catch (err) { res.status(500).json({ error: err.message || 'Failed to read release history' }); } }); module.exports = router;