diff --git a/backend/src/middleware/features.js b/backend/src/middleware/features.js index 24828dd..070d506 100644 --- a/backend/src/middleware/features.js +++ b/backend/src/middleware/features.js @@ -29,6 +29,19 @@ * if (feats.exam9 === false) { ... } */ const db = require('../db/db'); +const jwt = require('jsonwebtoken'); + +// Админ-оверрайд: requireFeature идёт ДО authMiddleware (req.user ещё нет), +// поэтому декодируем Bearer-токен сами — админ открывает и отключённые модули +// (зеркалит фронтовый _isAdminUser, см. project_gamification_killswitch). +function _isAdminReq(req) { + try { + const h = req.headers.authorization || ''; + if (!h.startsWith('Bearer ')) return false; + const p = jwt.verify(h.slice(7), process.env.JWT_SECRET, { algorithms: ['HS256'] }); + return !!(p && p.role === 'admin'); + } catch (e) { return false; } +} const _stmtSingle = db.prepare("SELECT value FROM app_settings WHERE key = ?"); const _stmtGlobalFeats = db.prepare("SELECT key, value FROM app_settings WHERE key LIKE 'feature_%'"); @@ -41,7 +54,7 @@ function requireFeature(name) { const settingKey = `feature_${name}_enabled`; return (req, res, next) => { const row = _stmtSingle.get(settingKey); - if (row && row.value === '0') { + if (row && row.value === '0' && !_isAdminReq(req)) { // админ проходит к API даже выключенного модуля return res.status(404).json({ error: 'Feature disabled' }); } next();