Comment-only tidy from the feng-shui audit — no code behavior changes. The features/ directory was renamed to components/ during modularization, but several header banners and inline comments still named the old path: - 6 component module headers (admin/tasks/backup/dashboard/updater/index.js + updater/js/updater-page.js) now name their real components/<id>/… path - core/kernel/js/spa.js + core/tasks/js/task-router.js comments - backend/routes/features.js doc-banner (drop a components/<id>/ folder …) - core/update-notifier/css/update-notifier.css header (js/update-notifier.js) Guarded the rewrite so the LIVE /api/features/list endpoint name (feature- registry.js sources + backend route) is untouched — only stale 'features/<path>' directory references were updated. Signed-off-by: librelad <librelad@digitalangels.vip>
59 lines
2.3 KiB
JavaScript
59 lines
2.3 KiB
JavaScript
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/<id>/ 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/<id>/ 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;
|