Files
tgit/app/db/migrations/versions/20261022_0003_demo_invite.py
ElebobaandClaude Opus 5.5 e8c3a321a3
CI / ruff · mypy · pytest (pull_request) Successful in 1h3m54s
feat(access): демо-QR — многоразовое приглашение для показа
- 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>
2026-10-22 18:11:53 +03:00

34 lines
1.2 KiB
Python

"""multi-use demo invite (#55): invites.max_uses/uses, users.guest
Revision ID: 0003
Revises: 0002
Create Date: 2026-10-22 12:00:00
"""
from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op
revision: str = "0003"
down_revision: str | None = "0002"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
# Plain ADD COLUMN, not batch: on SQLite a batch rebuilds `users`, which invites reference, and the FKs fail
op.add_column("invites", sa.Column("max_uses", sa.Integer(), server_default="1", nullable=False))
op.add_column("invites", sa.Column("uses", sa.Integer(), server_default="0", nullable=False))
op.add_column("users", sa.Column("guest", sa.Boolean(), server_default=sa.false(), nullable=False))
# Invites redeemed before this migration are spent
op.execute("UPDATE invites SET uses = 1 WHERE used_by IS NOT NULL")
def downgrade() -> None:
with op.batch_alter_table("users", schema=None) as batch_op:
batch_op.drop_column("guest")
with op.batch_alter_table("invites", schema=None) as batch_op:
batch_op.drop_column("uses")
batch_op.drop_column("max_uses")