"""chat guest authors Разрешить сообщения чата (`chat_messages`) от гостей и хранить снапшот имени автора: - `user_id` становится nullable — автором может быть гость; - `guest_access_id` — необязательная ссылка на `guest_access`, `ON DELETE CASCADE` (удаление гостевой записи удаляет и его сообщения чата); - `author_name` — снапшот отображаемого имени из LiveKit-токена на момент отправки; добавляется nullable, backfill из `users.name_user` для уже существующих строк (все они с `user_id`, т.к. гостевого автора раньше не было), затем ужесточается до `NOT NULL`; - `ck_chat_messages_author` — ровно один из `user_id`/`guest_access_id` обязателен (как у `ConferenceParticipant`, ADR-001, п.6). Revision ID: 504791847d4f Revises: 9d37822e4513 Create Date: 2026-07-18 18:45:20.921653 """ from typing import Sequence, Union from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision: str = '504791847d4f' down_revision: Union[str, Sequence[str], None] = '9d37822e4513' branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: """Upgrade schema.""" op.add_column('chat_messages', sa.Column('guest_access_id', sa.UUID(), nullable=True)) op.add_column('chat_messages', sa.Column('author_name', sa.String(length=255), nullable=True)) op.alter_column('chat_messages', 'user_id', existing_type=sa.UUID(), nullable=True) op.create_foreign_key( 'fk_chat_messages_guest_access_id_guest_access', 'chat_messages', 'guest_access', ['guest_access_id'], ['id'], ondelete='CASCADE', ) # Backfill: до этой миграции автор всегда был зарегистрированным # пользователем — берём снапшот его текущего имени. op.execute( """ UPDATE chat_messages cm SET author_name = u.name_user FROM users u WHERE cm.user_id = u.id """ ) op.alter_column('chat_messages', 'author_name', existing_type=sa.String(length=255), nullable=False) op.create_check_constraint( 'ck_chat_messages_author', 'chat_messages', 'user_id IS NOT NULL OR guest_access_id IS NOT NULL', ) def downgrade() -> None: """Downgrade schema.""" op.drop_constraint('ck_chat_messages_author', 'chat_messages', type_='check') op.drop_constraint('fk_chat_messages_guest_access_id_guest_access', 'chat_messages', type_='foreignkey') op.alter_column('chat_messages', 'user_id', existing_type=sa.UUID(), nullable=False) op.drop_column('chat_messages', 'author_name') op.drop_column('chat_messages', 'guest_access_id')