Commit Graph

3 Commits

Author SHA1 Message Date
Maxim Dolgolyov 81bf5d75eb fix(features): админ-оверрайд в requireFeature — API отключённого модуля 404-ил админа
Симптом: collection выключен, админ открывал страницу (фронтовый админ-оверрайд),
но GET /api/collection отдавал 404 — requireFeature 404-ил всех. requireFeature
идёт ДО authMiddleware (req.user нет), поэтому сам декодирую Bearer-токен: если
роль admin — пропускаем к API даже выключенного модуля. Для student/teacher всё
по-прежнему 404 (модуль скрыт). Зеркалит фронтовый _isAdminUser. Чинит ВСЕ
отключённые модули для админа, не только коллекцию.

Проверено: admin→bypass, student/teacher/нет токена/мусор/чужой секрет→404 (6/6).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 21:41:25 +03:00
Maxim Dolgolyov 41ca41d69c feat(gamification): hide locked achievements of disabled modules
When a teacher / admin turns off a module (per-class, per-role, or
globally), the matching achievements no longer clutter the user's
'Достижения' tab — but only the ones the user hasn't earned yet.
Already-unlocked achievements stay visible forever. We never take a
reward away after the fact.

Backend:
  • migration 034 adds achievements.required_feature + backfills 42
    rows (9 exam9, 8 red_book, 6 lab, 5 classroom, 4 textbooks, 3 each
    of biochem/flashcards, 2 live_quiz, 2 pet). 32 core rows stay
    NULL = always visible.
  • middleware/features.js gains computeFeaturesForUser(userId, role)
    + isFeatureEnabledForUser — extracted from server.js#/api/features
    so multiple consumers (gam achievements, future shop filter, etc.)
    apply the same global+class+free_student merge.
  • service.seedAchievements derives required_feature from track/group
    when ACHIEVEMENT_DEFS doesn't spell one out, and UPDATE-syncs it on
    every boot — keeps catalogue consistent across upgrades.
  • _shared.getAllAchs SELECT now returns required_feature.
  • gamification/api.getAchievements filters: drop locked rows whose
    required_feature is === false for this user. Missing flag = ON
    (opt-in disable model).

Verified: with exam9 + pet disabled, 12 locked achievements vanish from
the response while unlocked ones in those tracks remain.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 20:40:16 +03:00
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