Files
Learn_System/backend/src/middleware/features.js
T
Maxim Dolgolyov 19c16bdfe8 feat(perm): block API endpoints for globally-disabled features (B-lite)
Adds backend/src/middleware/features.js with requireFeature(name)

that returns 404 when app_settings.feature_<name>_enabled='0'.

Wired on 8 routes:

- /api/pet            (pet)

- /api/collection     (collection)

- /api/red-book       (red_book)

- /api/flashcards     (flashcards)

- /api/knowledge-map  (knowledge_map)

- /api/biochem        (biochem)

- /api/games/hangman/*   (hangman, per-route inside games router)

- /api/games/crossword/* (crossword, per-route)

Scope: GLOBAL only. Per-class disable (classes.features JSON) and the

free_student role overlay remain UI-gated. Add user-aware merge later

if needed (extract logic from /api/features endpoint into shared helper).

Not gated (intentional, core teacher tools): board, classroom, live_quiz.

Smoke: pet disabled → 404; enabled → 401 (auth-required passthrough).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 14:35:29 +03:00

42 lines
1.4 KiB
JavaScript

'use strict';
/**
* Feature-flag middleware: blocks the request when the named feature is
* globally disabled in app_settings.
*
* Scope (B-lite): GLOBAL only — checks the app_settings.feature_<name>_enabled
* row that admin toggles in the admin panel. Per-class disable
* (classes.features JSON) and the free_student role-level overlay
* (app_settings.free_student_features) are NOT checked here — those layers
* remain UI-gated in /api/features. A student bypassing the UI gate via
* direct curl is the documented limitation; can be tightened later by
* extracting the merge logic from server.js → /api/features into a shared
* helper.
*
* Default: missing key = enabled (opt-in disable model).
*
* Response: 404 on disabled feature (intentional — don't leak endpoint shape).
*
* Usage:
* app.use('/api/pet', requireFeature('pet'), petRoutes);
* router.get('/hangman/word', requireFeature('hangman'), authMiddleware, handler);
*/
const db = require('../db/db');
const _stmt = db.prepare(
"SELECT value FROM app_settings WHERE key = ?"
);
function requireFeature(name) {
const settingKey = `feature_${name}_enabled`;
return (req, res, next) => {
const row = _stmt.get(settingKey);
if (row && row.value === '0') {
return res.status(404).json({ error: 'Feature disabled' });
}
next();
};
}
module.exports = { requireFeature };