feat(access): демо-QR — многоразовое приглашение для показа
CI / ruff · mypy · pytest (pull_request) Successful in 1h3m54s
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:
@@ -22,6 +22,7 @@ from sqlalchemy import (
|
||||
String,
|
||||
Text,
|
||||
UniqueConstraint,
|
||||
false,
|
||||
)
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
|
||||
@@ -128,6 +129,8 @@ class User(Base):
|
||||
full_name: Mapped[str] = mapped_column(String(256))
|
||||
role: Mapped[Role] = mapped_column(enum(Role, "role"), default=Role.MEMBER)
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
# Came in by a multi-use demo invite: cut off all at once after the talk (#55)
|
||||
guest: Mapped[bool] = mapped_column(Boolean, default=False, server_default=false())
|
||||
invited_by: Mapped[int | None] = mapped_column(TgId, ForeignKey("users.tg_id"))
|
||||
joined_at: Mapped[datetime] = mapped_column(default=utcnow)
|
||||
last_seen_at: Mapped[datetime | None]
|
||||
@@ -142,6 +145,9 @@ class Invite(Base):
|
||||
created_by: Mapped[int] = mapped_column(TgId, ForeignKey("users.tg_id"))
|
||||
created_at: Mapped[datetime] = mapped_column(default=utcnow)
|
||||
expires_at: Mapped[datetime]
|
||||
# 1 — a personal invite; more — the demo QR shown to an audience (#55). used_by/used_at — the latest entrant.
|
||||
max_uses: Mapped[int] = mapped_column(Integer, default=1, server_default="1")
|
||||
uses: Mapped[int] = mapped_column(Integer, default=0, server_default="0")
|
||||
used_by: Mapped[int | None] = mapped_column(TgId, ForeignKey("users.tg_id"))
|
||||
used_at: Mapped[datetime | None]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user