feat(imggen): feature-gate «imggen» с контролем по классам/ученикам (Спринт2)

- server: requireFeature('imggen') на /api/imggen (глобальный гейт).
- imggenController: enforcement через isFeatureEnabledForUser в status()/generate()
  — учитывает глобальный флаг + оверлей класса + free_student (403 если выкл.).
- admin «games/features» + free-student: тумблер «Генерация картинок (ИИ)».
- classes.html: переключатель модуля imggen в настройках класса (per-class).
Дефолт — ON (opt-in disable), как у остальных фич. Проверено на features-движке.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-12 22:15:54 +03:00
parent 09c6c2b21d
commit 107ca2220c
4 changed files with 15 additions and 4 deletions
+6 -2
View File
@@ -735,6 +735,10 @@
<input type="checkbox" id="feat-pet" style="width:16px;height:16px;accent-color:var(--violet);cursor:pointer">
<svg class="ic" viewBox="0 0 24 24"><circle cx="11" cy="4" r="2"/><circle cx="18" cy="8" r="2"/><circle cx="20" cy="16" r="2"/><path d="M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z"/></svg> Питомец
</label>
<label style="display:flex;align-items:center;gap:10px;cursor:pointer;font-size:0.85rem;font-weight:600;color:var(--text-1)">
<input type="checkbox" id="feat-imggen" style="width:16px;height:16px;accent-color:var(--violet);cursor:pointer">
<svg class="ic" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="m21 15-5-5L5 21"/></svg> Генерация картинок (ИИ)
</label>
</div>
<button class="btn-primary" onclick="saveModules()">Сохранить модули</button>
</div>
@@ -2281,7 +2285,7 @@
document.getElementById('set-invite-code').textContent = currentClass.invite_code || '—';
// Populate module toggles (all enabled by default if no features set)
const f = currentClass.features || {};
const FEATS = ['gamification', 'collection', 'hangman', 'crossword', 'red_book', 'pet'];
const FEATS = ['gamification', 'collection', 'hangman', 'crossword', 'red_book', 'pet', 'imggen'];
for (const key of FEATS) {
const el = document.getElementById('feat-' + key);
if (el) el.checked = f[key] !== false; // default enabled
@@ -2290,7 +2294,7 @@
async function saveModules() {
if (!currentClass) return;
const FEATS = ['gamification', 'collection', 'hangman', 'crossword', 'red_book', 'pet'];
const FEATS = ['gamification', 'collection', 'hangman', 'crossword', 'red_book', 'pet', 'imggen'];
const features = {};
for (const key of FEATS) {
const el = document.getElementById('feat-' + key);
+2
View File
@@ -11,6 +11,7 @@
{ key: 'red_book', label: 'Красная книга', desc: 'Интерактивная Красная книга РБ: виды, биомы, пищевые сети, квесты', icon: 'leaf' },
{ key: 'collection', label: 'Коллекция', desc: 'Коллекция карточек и достижений — игровой прогресс ученика', icon: 'layers' },
{ key: 'flashcards', label: 'Флеш-карточки', desc: 'Карточки для запоминания терминов и понятий методом интервальных повторений', icon: 'square-stack' },
{ key: 'imggen', label: 'Генерация картинок (ИИ)', desc: 'ИИ-генерация изображений в ассистенте, флэшкартах, уроках, питомце, аватаре, доске', icon: 'image' },
{ key: 'knowledge_map', label: 'Карта знаний', desc: 'Визуальная карта тем и связей между биологическими понятиями', icon: 'share-2' },
{ key: 'board', label: 'Доска', desc: 'Классная доска с объявлениями, постами и обсуждениями', icon: 'layout-dashboard'},
{ key: 'biochem', label: 'Биохимия', desc: 'Молекулярный редактор, задачи на построение молекул и реакции', icon: 'flask-conical' },
@@ -28,6 +29,7 @@
{ key: 'lab', label: 'Лаборатория', desc: 'Виртуальные симуляции и интерактивные опыты', icon: 'flask-conical' },
{ key: 'knowledge_map',label: 'Карта знаний', desc: 'Визуальная карта тем и связей между понятиями', icon: 'map' },
{ key: 'flashcards', label: 'Флеш-карточки', desc: 'Карточки для повторения терминов и понятий', icon: 'square-stack' },
{ key: 'imggen', label: 'Генерация картинок (ИИ)', desc: 'ИИ-генерация изображений в ассистенте, флэшкартах, уроках, питомце', icon: 'image' },
{ key: 'board', label: 'Доска', desc: 'Классная доска с объявлениями и постами', icon: 'layout-dashboard' },
{ key: 'biochem', label: 'Биохимия', desc: 'Молекулярный редактор, задачи на построение молекул и реакции', icon: 'flask-conical' },
{ key: 'live_quiz', label: 'Живая викторина', desc: 'Синхронная викторина в реальном времени для всего класса', icon: 'radio' },