const express = require('express'); const fs = require('fs'); const path = require('path'); const router = express.Router(); const FEATURES_DIR = path.join(__dirname, '..', '..', 'frontend', 'components'); /* ========================= GET /api/features/list Walks frontend/components// and returns one entry per directory that contains a feature.json — the WebUI's page manifest, discovered from the folders themselves (exactly how /api/themes/list discovers themes). Drop a components// folder in and its page appears; delete it and the page is gone — no central edit. The navigation kernel fetches this and falls back to the checked-in components/manifest.dev.json if the API is unavailable. Each feature.json declares: id, routes[], optional module (self-registering index.js), optional handler (legacy fallback method), navId, nav{}, and order (controls list + route-precedence ordering — e.g. apps before app-detail so the '/apps*' wildcard wins over '/app*'). Public — the page list isn't sensitive and the kernel needs it before login to render the right route (same rationale as the themes list). ========================= */ router.get('/list', (req, res) => { const features = []; try { if (fs.existsSync(FEATURES_DIR)) { for (const entry of fs.readdirSync(FEATURES_DIR, { withFileTypes: true })) { if (!entry.isDirectory()) continue; const metaPath = path.join(FEATURES_DIR, entry.name, 'feature.json'); if (!fs.existsSync(metaPath)) continue; try { const meta = JSON.parse(fs.readFileSync(metaPath, 'utf8')); if (meta && meta.id && Array.isArray(meta.routes)) { features.push(meta); } else { console.warn(`[features] ${entry.name}/feature.json missing id/routes — skipped`); } } catch (e) { console.warn(`[features] ${entry.name}/feature.json is malformed — skipped:`, e.message); } } } } catch (err) { console.error('Error scanning features directory:', err); } // Ascending `order` controls both nav order and route-registration order // (the latter preserves wildcard precedence). Missing order sorts last. features.sort((a, b) => ((a.order ?? 999) - (b.order ?? 999))); res.json({ version: 1, source: 'scan', features }); }); module.exports = router;