feat(admin): online dictionary update — upload archive, preview word diff, install & activate
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 23s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Successful in 45s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m5s

Replace the dictionary hot-reload with an online update flow in the GM console
(/_gm/dictionary): the operator uploads scrabble-dawg-vX.Y.Z.tar.gz, previews the
per-variant words added/removed against the active dictionary, and confirms to
install + activate it. Versions are immutable; in-progress games keep their pinned
version while new games use the new one.

- engine: DiffWords (enumerate both DAWGs, decode only the differences), OpenFinder,
  Registry.Finder, DictFiles; OpenWithVersions skips the .staging area.
- dictadmin: hardened release-archive validation + extraction (path-traversal,
  symlink, oversize, entry-count rejection) and staging -> install (atomic rename).
- game: active dictionary version persisted in the dictionary_state singleton
  (single source of truth, restored on boot), concurrency-safe accessor.
- storage: BACKEND_DICT_DIR is a named volume seeded from the image (nonroot-owned),
  so uploaded versions persist across redeploys; the build's DICT_VERSION labels the
  seed and equals the resident tag (BACKEND_DICT_VERSION).
- docs: ARCHITECTURE §5, FUNCTIONAL (+ru), backend README, TESTING, PRERELEASE.

Tests: engine + dictadmin unit; integration upload->preview->install->activate->
restart->pin->immutability->CSRF.
This commit is contained in:
Ilia Denisov
2026-06-13 23:29:06 +02:00
parent 5ff07da025
commit 0f3671f42d
31 changed files with 1590 additions and 78 deletions
+7 -3
View File
@@ -188,9 +188,13 @@ backend; gateway закрывает её HTTP Basic Auth на публичном
один-в-один. В консоли можно смотреть **пользователей** (профиль, статистика,
identity, их игры) и **игры** (сводка + места), разбирать **очередь жалоб на слова**
закрывая каждую как reject / accept-add / accept-remove — и управлять **словарём**:
резидентные версии по вариантам, **горячая перезагрузка** новой версии из
`BACKEND_DICT_DIR/<version>/` и **список ожидающих правок**, выведенный из принятых
жалоб (он питает офлайн-пересборку и отмечается применённым после перезагрузки). Если
активная версия, на которую опираются новые партии, резидентные версии по вариантам,
онлайн-**обновление словаря** (загрузить релизный архив `scrabble-dawg-vX.Y.Z.tar.gz`,
посмотреть предпросмотр добавленных и удалённых слов по каждому варианту относительно
активного словаря, затем установить — версия записывается, загружается и становится
активной; версии неизменяемы, а идущие партии остаются на своей) и **список ожидающих
правок**, выведенный из принятых жалоб (он питает офлайн-пересборку и отмечается
применённым после обновления). Если
подключён Telegram-коннектор, оператор также может **написать пользователю** (по его
Telegram-identity) или **отправить пост в игровой канал**. Изменяющие действия
защищены проверкой same-origin; личность оператора не отслеживается.