feat(access): демо-QR — многоразовое приглашение для показа
CI / ruff · mypy · pytest (pull_request) Successful in 1h3m54s

- invites.max_uses/uses: обычное приглашение — 1 вход, демо-QR — до DEMO_INVITE_MAX_USES (50),
  живёт DEMO_INVITE_TTL_HOURS (3 ч); захват атомарный, UPDATE … WHERE uses < max_uses
- users.guest: вошедшие по демо-QR; «🧹 Отключить гостей демо» отзывает всех и гасит демо-QR
- «👥 Участники»: кнопка «🎤 Демо-QR», гости в списке — 🎟
- миграция 0003 (ADD COLUMN без batch — SQLite не пересоздаёт users); проверена на Postgres
- SPEC A-3a, ADR-017, DEMO.md, .env.example

Refs #55

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 18:11:53 +03:00
co-authored by Claude Opus 5.5
parent 7a12b3525a
commit e8c3a321a3
14 changed files with 247 additions and 15 deletions
+3
View File
@@ -23,6 +23,9 @@ class AccessSettings(BaseSettings):
# Bootstrap admins (SPEC A-8). Comma-separated in the env: ADMIN_IDS=1,2
admin_ids: Annotated[frozenset[int], NoDecode] = frozenset()
invite_ttl_hours: float = Field(default=24.0, gt=0)
# Demo QR for the audience at the talk (#55): many entrants, short life
demo_invite_max_uses: int = Field(default=50, ge=2)
demo_invite_ttl_hours: float = Field(default=3.0, gt=0)
# More failed /start <token> per hour than this — silence (SPEC A-6)
max_failed_attempts: int = Field(default=5, ge=1)