diff --git a/backend/src/controllers/adminController.js b/backend/src/controllers/adminController.js index 598b69c..c4db1f0 100644 --- a/backend/src/controllers/adminController.js +++ b/backend/src/controllers/adminController.js @@ -525,7 +525,7 @@ function getFeatures(_req, res) { function updateFeatures(req, res) { const allowed = ['crossword', 'hangman', 'pet', 'red_book', 'collection', 'flashcards', 'knowledge_map', 'board', 'biochem', 'live_quiz', 'classroom', - 'gamification', 'assistant']; + 'gamification', 'assistant', 'sim_builder']; const updates = req.body; const stmt = db.prepare("INSERT OR REPLACE INTO app_settings (key, value) VALUES (?, ?)"); const getOld = db.prepare("SELECT value FROM app_settings WHERE key = ?"); diff --git a/backend/src/routes/customSims.js b/backend/src/routes/customSims.js index db1d9e8..13e3a42 100644 --- a/backend/src/routes/customSims.js +++ b/backend/src/routes/customSims.js @@ -6,29 +6,35 @@ const express = require('express'); const router = express.Router(); const { authMiddleware, requireRole } = require('../middleware/auth'); +const { requireFeature } = require('../middleware/features'); const c = require('../controllers/customSimController'); router.use(authMiddleware); +// «Конструктор симуляций» можно отключить в админке (feature_sim_builder_enabled). +// Чтение/проигрывание уже сохранённых симуляций остаётся доступным; гейтим только +// авторинг — создание/правку/удаление/раздачу/клон/связи. +const gate = requireFeature('sim_builder'); + router.get('/', c.list); // @public-by-design: router-level authMiddleware (above) + ownership/published check in handler router.get('/:id', c.get); // @public-by-design: router-level authMiddleware (above) + ownership/published check in handler router.get('/:id/related', c.related); -router.post('/', requireRole('teacher', 'admin'), c.create); +router.post('/', gate, requireRole('teacher', 'admin'), c.create); // @public-by-design: router-level authMiddleware (above) + per-row ownership check in handler -router.put('/:id', requireRole('teacher', 'admin'), c.update); +router.put('/:id', gate, requireRole('teacher', 'admin'), c.update); // @public-by-design: router-level authMiddleware (above) + per-row ownership check in handler -router.delete('/:id', requireRole('teacher', 'admin'), c.remove); +router.delete('/:id', gate, requireRole('teacher', 'admin'), c.remove); // Фаза 6 — раздача классу / клон / курикулумные связи. Мутации — inline // requireRole(teacher,admin) + per-row ownership в хендлере. -router.post('/:id/share', requireRole('teacher', 'admin'), c.share); -router.post('/:id/clone', requireRole('teacher', 'admin'), c.clone); +router.post('/:id/share', gate, requireRole('teacher', 'admin'), c.share); +router.post('/:id/clone', gate, requireRole('teacher', 'admin'), c.clone); // @public-by-design: router-level authMiddleware (above) + per-row ownership check in handler -router.post('/:id/links', requireRole('teacher', 'admin'), c.addLink); +router.post('/:id/links', gate, requireRole('teacher', 'admin'), c.addLink); // @public-by-design: router-level authMiddleware (above) + per-row ownership check in handler -router.delete('/:id/links/:linkId', requireRole('teacher', 'admin'), c.removeLink); +router.delete('/:id/links/:linkId', gate, requireRole('teacher', 'admin'), c.removeLink); module.exports = router; diff --git a/frontend/js/admin/sections/games.js b/frontend/js/admin/sections/games.js index 96c2a10..1e25a16 100644 --- a/frontend/js/admin/sections/games.js +++ b/frontend/js/admin/sections/games.js @@ -17,6 +17,7 @@ { key: 'biochem', label: 'Биохимия', desc: 'Молекулярный редактор, задачи на построение молекул и реакции', icon: 'flask-conical' }, { key: 'live_quiz', label: 'Живая викторина', desc: 'Синхронная викторина в реальном времени для всего класса', icon: 'radio' }, { key: 'classroom', label: 'Онлайн-уроки (classroom)', desc: 'Синхронные онлайн-уроки с доской и видео', icon: 'video' }, + { key: 'sim_builder', label: 'Конструктор симуляций', desc: 'Создание учителем своих интерактивных симуляций (рабочее поле, формулы, физика, графики)', icon: 'pencil-ruler' }, ]; const FS_FEATURES = [ diff --git a/frontend/sim-builder.html b/frontend/sim-builder.html index 2811423..5f62851 100644 --- a/frontend/sim-builder.html +++ b/frontend/sim-builder.html @@ -189,6 +189,13 @@ var ip = LS.initPage() || {}; if (!(ip.isTeacher || ip.isAdmin)) { location.href = '/dashboard'; return; } + // Фича-гейт: «Конструктор симуляций» можно отключить в админке (feature_sim_builder_enabled). + if (LS.loadFeatures) { + LS.loadFeatures().then(function (feats) { + if (feats && feats.sim_builder === false) { LS.toast && LS.toast('Конструктор симуляций отключён', 'warn'); location.href = '/dashboard'; } + }).catch(function () {}); + } + if (!window.SimEngine || !window.SimExpr || !window.SimBuilder) { document.getElementById('sbu-preview').innerHTML = '