Объявления: модель, API игрока и админки, очистка HTML
Объявление администрации показывается игроку окном в свой период, пока игрок
не закроет его («Понятно»). Отметка о закрытии хранится на сервере с номером
версии: правка с «показать заново» поднимает версию, и закрывшие прежнюю
увидят объявление снова — ответ помечен 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>
This commit is contained in:
+3
-1
@@ -17,6 +17,7 @@ from app.core.errors import AppError, app_error_handler
|
||||
from app.routers import (
|
||||
achievements,
|
||||
admin,
|
||||
announcements,
|
||||
auth,
|
||||
events,
|
||||
groups,
|
||||
@@ -199,7 +200,8 @@ def create_app() -> FastAPI:
|
||||
# API-роутеры под /api.
|
||||
api_routers = [auth.router, users.router, groups.router, invitations.router,
|
||||
matches.router, reference.router, stats.router, achievements.router,
|
||||
events.router, notifications.router, admin.router]
|
||||
events.router, notifications.router, announcements.router,
|
||||
admin.router]
|
||||
for r in api_routers:
|
||||
app.include_router(r, prefix="/api")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user