Объявления администрации по выбранным макетам: окно E у игрока и редактор R1 в админке.
Игрок. Окно всплывает на любой странице, но только после окна установки пароля. Заголовок в плашке-шапке, ниже текст, точки очереди и «Понятно»; крестика нет. Объявления с пересекающимися периодами идут от старого к новому, следующее — после закрытия предыдущего. Закрытым считается по «Понятно»; отметка хранится на сервере (announcement_views), так что между устройствами объявление не повторяется.
Админка. Вкладка «Объявления»: заголовок, текст с панелью (жирный, курсив, золотой и красный акцент, эмодзи, снятие оформления), период показа по МСК, переключатель «Показывать новым игрокам» (выключен — не видят зарегистрировавшиеся после начала показа), при правке — «Показать заново тем, кто уже закрыл» (они увидят объявление снова с красной мелкой пометкой «обновлено» под заголовком). Предпросмотр — тем же окном, что у игрока. Список со статусом (запланировано / идёт / завершено), счётчиком «закрыли N из M» и действиями: изменить, снять с показа, дублировать, удалить. Выделение текста в форме — полупрозрачный оранжевый (--accent-2) вместо синего.
Безопасность. Текст приходит HTML-ом из contenteditable и сохраняется только после очистки на сервере: HTML пересобирается из разобранных токенов, остаются b / em / mark (+ class="red") / p / br без атрибутов, script / style / svg / iframe … выбрасываются вместе с содержимым, весь текст экранируется заново. Фронт вставляет в окно игрока только этот HTML.
Коммиты
2e496a3 Гитигнор: локальный AGENTS.md не идёт в репозиторий
a0a0e52 Объявления: модель, API игрока и админки, очистка HTML
ab1fc3f Объявления: окно у игрока и редактор в админке
ec0445f Миграции: объявления отходят от 0013, слияние с рейтингом в 0016
Миграции
Объявления выпускаются в main раньше рейтинга (PR #86 — те же изменения на main без рейтинга), а там последняя миграция — 0013. Поэтому 0015_announcements отходит от 0013, как и рейтинговая 0014, а пустая 0016 сводит обе ветки в одну голову. Базы на 0014 (dev) получат 0015 и 0016; прод, стоящий на 0015 после релиза объявлений, при переходе на dev получит 0014 и 0016. Базы, уже прогнанные первой версией этого PR (где 0015 шла после 0014), повторно получат идемпотентную 0014 — проверено.
Проверки
pytest — 208 passed (было 194; +14 в tests/test_announcements.py: очистка HTML и попытки XSS, права, период и порядок, «новые игроки», повторный показ с пометкой, снятие с показа, удаление, валидация).
npm run build (typecheck + сборка) — зелёный.
alembic upgrade head — проверено на копии dev-БД (на 0014 и на 0015 из первой версии PR), на пустой базе и по пути прода 0013 → 0015 (main) → 0014 + 0016 (dev).
npm run gen:api — schema.d.ts пересобран из живого OpenAPI (только добавления).
/security-review по диффу dev...issue-84-announcements — уязвимостей HIGH/MEDIUM не найдено. Очистка HTML дополнительно проверена ~40 вредоносными вставками (<img onerror>, xmp, noscript, svg/style-мутации, CDATA, битые теги, инъекция атрибутов в mark) — на выходе только разрешённые теги и экранированный текст.
В браузере не проверялось: расширение Chrome в сессии не подключено. Поведение редактора (execCommand) и окна стоит прокликать вручную.
Модель угрозы. Писать объявления может только админ (отдельный вход, get_current_admin, CSRF double-submit на всех изменяющих запросах). Игрок только читает активные объявления и отмечает закрытие — user_id берётся из сессии, чужую отметку поставить нельзя. Главный риск — хранимый XSS через текст объявления, который видят все игроки; его закрывает серверная очистка (и тесты на неё).
Отклонения от плана
Черновиков нет (в макете R1 был статус «Черновик»): период показа обязателен, вместо черновика — «Дублировать».
«Новые игроки» — зарегистрированные после начала показа; пометку «обновлено» видят только те, кто закрывал прежнюю версию.
Объявление с отложенным началом появляется у открытой вкладки при перезапросе раз в 5 минут (или при возврате во вкладку) — наступление времени не событие в данных, SSE его не шлёт.
Добавлен PR #86 в main и миграция-слияние 0016 — по просьбе владельца выпустить объявления раньше рейтинга.
После мёржа
Перед запуском dev нужны миграции (run.ps1 делает alembic upgrade head сам); в проде — entrypoint.sh.
## Что сделано
Объявления администрации по выбранным макетам: окно **E** у игрока и редактор **R1** в админке.
**Игрок.** Окно всплывает на любой странице, но только после окна установки пароля. Заголовок в плашке-шапке, ниже текст, точки очереди и «Понятно»; крестика нет. Объявления с пересекающимися периодами идут от старого к новому, следующее — после закрытия предыдущего. Закрытым считается по «Понятно»; отметка хранится на сервере (`announcement_views`), так что между устройствами объявление не повторяется.
**Админка.** Вкладка «Объявления»: заголовок, текст с панелью (жирный, курсив, золотой и красный акцент, эмодзи, снятие оформления), период показа по МСК, переключатель «Показывать новым игрокам» (выключен — не видят зарегистрировавшиеся после начала показа), при правке — «Показать заново тем, кто уже закрыл» (они увидят объявление снова с красной мелкой пометкой «обновлено» под заголовком). Предпросмотр — тем же окном, что у игрока. Список со статусом (запланировано / идёт / завершено), счётчиком «закрыли N из M» и действиями: изменить, снять с показа, дублировать, удалить. Выделение текста в форме — полупрозрачный оранжевый (`--accent-2`) вместо синего.
**Безопасность.** Текст приходит HTML-ом из `contenteditable` и сохраняется только после очистки на сервере: HTML пересобирается из разобранных токенов, остаются `b / em / mark (+ class="red") / p / br` без атрибутов, `script / style / svg / iframe …` выбрасываются вместе с содержимым, весь текст экранируется заново. Фронт вставляет в окно игрока только этот HTML.
## Коммиты
- `2e496a3` Гитигнор: локальный AGENTS.md не идёт в репозиторий
- `a0a0e52` Объявления: модель, API игрока и админки, очистка HTML
- `ab1fc3f` Объявления: окно у игрока и редактор в админке
- `ec0445f` Миграции: объявления отходят от 0013, слияние с рейтингом в 0016
## Миграции
Объявления выпускаются в `main` раньше рейтинга (PR #86 — те же изменения на `main` без рейтинга), а там последняя миграция — `0013`. Поэтому `0015_announcements` отходит от `0013`, как и рейтинговая `0014`, а пустая `0016` сводит обе ветки в одну голову. Базы на `0014` (dev) получат `0015` и `0016`; прод, стоящий на `0015` после релиза объявлений, при переходе на `dev` получит `0014` и `0016`. Базы, уже прогнанные первой версией этого PR (где `0015` шла после `0014`), повторно получат идемпотентную `0014` — проверено.
## Проверки
- `pytest` — **208 passed** (было 194; +14 в `tests/test_announcements.py`: очистка HTML и попытки XSS, права, период и порядок, «новые игроки», повторный показ с пометкой, снятие с показа, удаление, валидация).
- `npm run build` (typecheck + сборка) — зелёный.
- `alembic upgrade head` — проверено на копии dev-БД (на `0014` и на `0015` из первой версии PR), на пустой базе и по пути прода `0013 → 0015 (main) → 0014 + 0016 (dev)`.
- `npm run gen:api` — `schema.d.ts` пересобран из живого OpenAPI (только добавления).
- `/security-review` по диффу `dev...issue-84-announcements` — уязвимостей HIGH/MEDIUM не найдено. Очистка HTML дополнительно проверена ~40 вредоносными вставками (`<img onerror>`, `xmp`, `noscript`, `svg/style`-мутации, CDATA, битые теги, инъекция атрибутов в `mark`) — на выходе только разрешённые теги и экранированный текст.
- **В браузере не проверялось**: расширение Chrome в сессии не подключено. Поведение редактора (`execCommand`) и окна стоит прокликать вручную.
**Модель угрозы.** Писать объявления может только админ (отдельный вход, `get_current_admin`, CSRF double-submit на всех изменяющих запросах). Игрок только читает активные объявления и отмечает закрытие — `user_id` берётся из сессии, чужую отметку поставить нельзя. Главный риск — хранимый XSS через текст объявления, который видят все игроки; его закрывает серверная очистка (и тесты на неё).
## Отклонения от плана
- **Черновиков нет** (в макете R1 был статус «Черновик»): период показа обязателен, вместо черновика — «Дублировать».
- «Новые игроки» — зарегистрированные **после начала показа**; пометку «обновлено» видят только те, кто закрывал прежнюю версию.
- Объявление с отложенным началом появляется у открытой вкладки при перезапросе раз в 5 минут (или при возврате во вкладку) — наступление времени не событие в данных, SSE его не шлёт.
- Добавлен PR #86 в `main` и миграция-слияние `0016` — по просьбе владельца выпустить объявления раньше рейтинга.
## После мёржа
Перед запуском dev нужны миграции (`run.ps1` делает `alembic upgrade head` сам); в проде — `entrypoint.sh`.
Closes #84
🤖 Generated with [Claude Code](https://claude.com/claude-code)
AGENTS.md — такой же локальный гайд для ассистента, как CLAUDE.md, только
для другого инструмента. Он лежал в рабочем дереве незакоммиченным и висел
в git status; теперь он в блоке «AI-ассистенты» рядом с CLAUDE.md
и .claude/, так что случайный `git add .` его не подхватит. #84
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Объявление администрации показывается игроку окном в свой период, пока игрок
не закроет его («Понятно»). Отметка о закрытии хранится на сервере с номером
версии: правка с «показать заново» поднимает версию, и закрывшие прежнюю
увидят объявление снова — ответ помечен updated («обновлено»). Флаг
show_to_new_players=false прячет объявление от зарегистрировавшихся после
начала показа. Пересекающиеся объявления идут от старого к новому.
Текст приходит HTML-ом из редактора админки и сохраняется только после
очистки по белому списку (b, em, mark и mark.red, p, br): атрибуты
отбрасываются, script/style/svg — вместе с содержимым, текст экранируется
заново. Фронт вставляет только этот HTML.
API: GET /api/announcements/pending, POST /api/announcements/{id}/ack;
админка — список со статусом и счётчиком «закрыли N из M», создание, правка,
«снять с показа», удаление, всё в аудит. SSE-событие announcements активным
игрокам. Миграция 0015 идемпотентная.
Тесты: очистка (XSS-попытки, вложенные div), права, период и порядок,
«новые игроки», повторный показ, снятие, удаление, валидация. #84
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Окно — вариант E из макетов: заголовок в плашке-шапке, текст, точки очереди
и «Понятно», крестика нет. Очередь идёт от старого к новому; закрытое сразу
уходит из кэша, следующее открывается без ожидания сети. У повторно
показанного объявления под заголовком — красная пометка «обновлено».
AppShell показывает окно на любой странице и только когда пароль задан:
обязательное окно пароля всегда первое. Новые и изменённые объявления
приходят SSE-событием announcements, отложенное начало показа — перезапросом
раз в 5 минут.
Админка — вкладка «Объявления» с редактором R1: contenteditable и панель
(жирный, курсив, золотой и красный акцент, эмодзи, снятие оформления),
вставка и перетаскивание только простым текстом, счётчик символов, период
показа по МСК, переключатели «показывать новым игрокам» и «показать заново».
Предпросмотр — тем же окном, что у игрока. Список со статусом, счётчиком
«закрыли N из M» и действиями: изменить, снять с показа, дублировать,
удалить. Выделение текста в форме — полупрозрачный оранжевый вместо синего.
schema.d.ts пересобран из OpenAPI. #84
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Объявления выпускаются в main раньше рейтинга, а в main последняя миграция —
0013. Если бы 0015 оставалась после 0014, прод, получивший объявления,
встал бы на 0015, и при будущем переходе на dev alembic счёл бы базу
актуальной — рейтинговая 0014 не применилась бы никогда.
Теперь 0015 отходит от 0013 (как и 0014), а пустая 0016 сводит обе ветки
в одну голову: прод на 0015 докатит 0014 и 0016, база на 0014 — 0015 и 0016.
Базы, уже прогнанные прежней версией (0015 после 0014), повторно получат
идемпотентную 0014 — проверено на копии dev-БД, на пустой базе и на такой
базе. #84
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Что сделано
Объявления администрации по выбранным макетам: окно E у игрока и редактор R1 в админке.
Игрок. Окно всплывает на любой странице, но только после окна установки пароля. Заголовок в плашке-шапке, ниже текст, точки очереди и «Понятно»; крестика нет. Объявления с пересекающимися периодами идут от старого к новому, следующее — после закрытия предыдущего. Закрытым считается по «Понятно»; отметка хранится на сервере (
announcement_views), так что между устройствами объявление не повторяется.Админка. Вкладка «Объявления»: заголовок, текст с панелью (жирный, курсив, золотой и красный акцент, эмодзи, снятие оформления), период показа по МСК, переключатель «Показывать новым игрокам» (выключен — не видят зарегистрировавшиеся после начала показа), при правке — «Показать заново тем, кто уже закрыл» (они увидят объявление снова с красной мелкой пометкой «обновлено» под заголовком). Предпросмотр — тем же окном, что у игрока. Список со статусом (запланировано / идёт / завершено), счётчиком «закрыли N из M» и действиями: изменить, снять с показа, дублировать, удалить. Выделение текста в форме — полупрозрачный оранжевый (
--accent-2) вместо синего.Безопасность. Текст приходит HTML-ом из
contenteditableи сохраняется только после очистки на сервере: HTML пересобирается из разобранных токенов, остаютсяb / em / mark (+ class="red") / p / brбез атрибутов,script / style / svg / iframe …выбрасываются вместе с содержимым, весь текст экранируется заново. Фронт вставляет в окно игрока только этот HTML.Коммиты
2e496a3Гитигнор: локальный AGENTS.md не идёт в репозиторийa0a0e52Объявления: модель, API игрока и админки, очистка HTMLab1fc3fОбъявления: окно у игрока и редактор в админкеec0445fМиграции: объявления отходят от 0013, слияние с рейтингом в 0016Миграции
Объявления выпускаются в
mainраньше рейтинга (PR #86 — те же изменения наmainбез рейтинга), а там последняя миграция —0013. Поэтому0015_announcementsотходит от0013, как и рейтинговая0014, а пустая0016сводит обе ветки в одну голову. Базы на0014(dev) получат0015и0016; прод, стоящий на0015после релиза объявлений, при переходе наdevполучит0014и0016. Базы, уже прогнанные первой версией этого PR (где0015шла после0014), повторно получат идемпотентную0014— проверено.Проверки
pytest— 208 passed (было 194; +14 вtests/test_announcements.py: очистка HTML и попытки XSS, права, период и порядок, «новые игроки», повторный показ с пометкой, снятие с показа, удаление, валидация).npm run build(typecheck + сборка) — зелёный.alembic upgrade head— проверено на копии dev-БД (на0014и на0015из первой версии PR), на пустой базе и по пути прода0013 → 0015 (main) → 0014 + 0016 (dev).npm run gen:api—schema.d.tsпересобран из живого OpenAPI (только добавления)./security-reviewпо диффуdev...issue-84-announcements— уязвимостей HIGH/MEDIUM не найдено. Очистка HTML дополнительно проверена ~40 вредоносными вставками (<img onerror>,xmp,noscript,svg/style-мутации, CDATA, битые теги, инъекция атрибутов вmark) — на выходе только разрешённые теги и экранированный текст.execCommand) и окна стоит прокликать вручную.Модель угрозы. Писать объявления может только админ (отдельный вход,
get_current_admin, CSRF double-submit на всех изменяющих запросах). Игрок только читает активные объявления и отмечает закрытие —user_idберётся из сессии, чужую отметку поставить нельзя. Главный риск — хранимый XSS через текст объявления, который видят все игроки; его закрывает серверная очистка (и тесты на неё).Отклонения от плана
mainи миграция-слияние0016— по просьбе владельца выпустить объявления раньше рейтинга.После мёржа
Перед запуском dev нужны миграции (
run.ps1делаетalembic upgrade headсам); в проде —entrypoint.sh.Closes #84
🤖 Generated with Claude Code
Объявление администрации показывается игроку окном в свой период, пока игрок не закроет его («Понятно»). Отметка о закрытии хранится на сервере с номером версии: правка с «показать заново» поднимает версию, и закрывшие прежнюю увидят объявление снова — ответ помечен updated («обновлено»). Флаг show_to_new_players=false прячет объявление от зарегистрировавшихся после начала показа. Пересекающиеся объявления идут от старого к новому. Текст приходит HTML-ом из редактора админки и сохраняется только после очистки по белому списку (b, em, mark и mark.red, p, br): атрибуты отбрасываются, script/style/svg — вместе с содержимым, текст экранируется заново. Фронт вставляет только этот HTML. API: GET /api/announcements/pending, POST /api/announcements/{id}/ack; админка — список со статусом и счётчиком «закрыли N из M», создание, правка, «снять с показа», удаление, всё в аудит. SSE-событие announcements активным игрокам. Миграция 0015 идемпотентная. Тесты: очистка (XSS-попытки, вложенные div), права, период и порядок, «новые игроки», повторный показ, снятие, удаление, валидация. #84 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>