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>
34 lines
1.2 KiB
Python
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")
|