Skip to content

Chore/update migration#34

Merged
Fl1riX merged 32 commits into
mainfrom
chore/update-migration
Jun 2, 2026
Merged

Chore/update migration#34
Fl1riX merged 32 commits into
mainfrom
chore/update-migration

Conversation

@Fl1riX

@Fl1riX Fl1riX commented Jun 2, 2026

Copy link
Copy Markdown
Owner

Summary by Sourcery

Адаптировать начальную миграцию базы данных, чтобы схема банов и пользовательских ролей соответствовала текущей модели приложения.

Исправления ошибок:

  • Исправить определение перечисления user_role в начальной миграции и обеспечить установку роли пользователя по умолчанию.
  • Обновить уникальный индекс для активных банов так, чтобы он применялся только к неотозванным банам, приводя его в соответствие с доменной моделью.

Улучшения:

  • Упростить схему банов в начальной миграции, убрав логику истечения срока действия из условия частичного индекса.
Original summary in English

Summary by Sourcery

Adjust initial database migration to align bans and user role schema with current application model.

Bug Fixes:

  • Correct the user_role enum definition in the initial migration and ensure a default user role is set.
  • Update the unique index for active bans to apply only to non-revoked bans, matching the domain model.

Enhancements:

  • Simplify the bans schema in the initial migration by removing expiration logic from the partial index condition.

Новые возможности:

  • Добавлена периодическая задача для автоматического отзыва просроченных банов.

Исправления ошибок:

  • Исправлено определение перечисления user_role в начальной миграции и обеспечена установка роли по умолчанию.
  • Уникальный индекс для активных банов приведён в соответствие с моделью, теперь он строится только на основании неотозванных банов.

Улучшения:

  • Перенесена настройка AsyncIOScheduler в lifespan FastAPI с дополнительным заданием для очистки просроченных банов.
  • Рефакторинг доступа к базе данных: используется общий асинхронный SessionLocal в инфраструктурном слое вместо состояния приложения.
  • Упрощена модель банов путём удаления логики истечения срока действия из условия частичного индекса.
Original summary in English

Summary by Sourcery

Адаптировать начальную миграцию базы данных, чтобы схема банов и пользовательских ролей соответствовала текущей модели приложения.

Исправления ошибок:

  • Исправить определение перечисления user_role в начальной миграции и обеспечить установку роли пользователя по умолчанию.
  • Обновить уникальный индекс для активных банов так, чтобы он применялся только к неотозванным банам, приводя его в соответствие с доменной моделью.

Улучшения:

  • Упростить схему банов в начальной миграции, убрав логику истечения срока действия из условия частичного индекса.
Original summary in English

Summary by Sourcery

Adjust initial database migration to align bans and user role schema with current application model.

Bug Fixes:

  • Correct the user_role enum definition in the initial migration and ensure a default user role is set.
  • Update the unique index for active bans to apply only to non-revoked bans, matching the domain model.

Enhancements:

  • Simplify the bans schema in the initial migration by removing expiration logic from the partial index condition.

Squ1reX and others added 30 commits May 21, 2026 21:28
…d fix date fields by adding the timezone=True property to store time zone information. Fix and add missing relationships
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…d also fix the issue of multiple bans for one user simultaneously, while preserving the ban history.
* delete cloudflared.deb"

* feat(models): Start implementing the role system, add a ban table, and fix date fields by adding the timezone=True property to store time zone information. Fix and add missing relationships

* chore(.gitignore): remove tracking of different caches

* refactor(tg_link): rename MagicTokens to MagicToken

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(bot): send login-link URL on start when telegram is not linked

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(auth): add JWT unit tests

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor(models): Improve the readability of field checks in the bans table

* feat(models): Add validation of the revoked_at field at the database level

* feat(models): Add checks for the expires_at and revoked_at fields, and also fix the issue of multiple bans for one user simultaneously, while preserving the ban history.

* refactor(models): Rename MagicTokens to MagicToken across the codebase for consistency

* fix(modedls): Add foreigkey to the appointments table relationship

* fix(tasks): Fix database issues with the background task for clearing magic tokens

---------

Co-authored-by: Squ1reX <maksimhripinkov658@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…18)

* feat(ci): Add Docker image building and pushing to the ghcr registry

* feat(docker-compose): Add watchtower to query the registry and download dcoker images from there.

* feat(ci): Add a commit hash tag to Docker images
* fix: Remove the get_db file from infrastructure and remove logging of all SQL queries to reduce the IO load.

* chore(jwt): Remove dprecated definition of the current time
Co-authored-by: Squ1reX <maksimhripinkov658@gmail.com>
…al network (#22)

Co-authored-by: Squ1reX <maksimhripinkov658@gmail.com>
* fix(auth): Fix a vulnerability in the process of linking a Telegram bot to an account by adding X-Bot-Secret to the header

* chore(auth): Improve logging of the create_telegram_magic_link function

* fix(auth): Fix X-Bot-Secret authentication. Eliminates timig attack.

* chore(requirements): update requrements.txt list

* fix(bot): Add sending of the X-Bot-Secret header from the Telegram bot

* fix(config): Remove os.environ, which causes an error in tests

* Update comment src/presentation/api/v1/auth/tg_link.py

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* docs(services): Fix logging and doc string errors

---------

Co-authored-by: Squ1reX <maksimhripinkov658@gmail.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
* feat(docker): Isolate services by separating docker networks

* feat(docker): Add a system user app. Remove buffering and .pyc, __pycache__ files.

* feat(docker): Remove root privileges. Add cap_drop to disable root privileges. Enable write access to the /tmp folder.

* feat(logger): Remove logging to a file

* feat(docker): Write a user creation command in the api container and transfer the user change to the bot container

* docs: Correct syntax errors

---------

Co-authored-by: Squ1reX <maksimhripinkov658@gmail.com>
Co-authored-by: Squ1reX <maksimhripinkov658@gmail.com>
* feat: Add a health endpoint to check if the API is online

* feat(docker): Add cleaning of outdated Docker images

* fix: Change your HealthCheck internet connection to avoid being blocked by RateLimiting

---------

Co-authored-by: Squ1reX <maksimhripinkov658@gmail.com>
#29)

* feat: Bring the architecture to a clean archeticture. In the structure, repeat the project structure for easier navigation. Separate the models into different files.

* fix(models): Fix circular import

* fix(models): Fix circular import

* fix(models): correct error texts in models

---------

Co-authored-by: Squ1reX <maksimhripinkov658@gmail.com>
* fix: Delete alembic.ini

* chore: add alembic.ini into .gitignore

* chore: update alembic scheme

* fix: Remove the backup key from config.py

* fix: Fix the SECRET_KEY test environment variable

* fix(auth): Fixing a missing SECRET_KEY error in .env

* fix(tests): Fix E401 [*] Multiple imports on one line

* feat(config): Add a check for the absence of DATABSE_URL in .env

* Add a database engine to lifespan's fastapi

* refactor: Move session factory to lifespan

---------

Co-authored-by: Squ1reX <maksimhripinkov658@gmail.com>
Co-authored-by: Squ1reX <maksimhripinkov658@gmail.com>
@sourcery-ai

sourcery-ai Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor
Руководство для ревьюера (свёрнуто для небольших PR)

Руководство для ревьюера

Корректирует исходную миграцию Alembic, чтобы упростить условие уникальности для активного бана и корректно определить enum user_role со значением по умолчанию, одновременно удаляя неиспользуемые изменения колонок service и шаблонные комментарии Alembic.

Диаграмма сущностей для обновлённого индекса банов и enum user_role

erDiagram
    Users {
        int id PK
        enum user_role "default: user"
    }

    Bans {
        int id PK
        int user_id FK
        timestamp revoked_at "uq_active_ban: user_id WHERE revoked_at IS NULL"
    }

    Users ||--o{ Bans : has_bans
Loading

Изменения по файлам

Change Details Files
Упростить уникальный индекс активного бана, чтобы он зависел только от неотозванных банов.
  • Заменить частичное условие уникального индекса для активных банов так, чтобы оно фильтровало только по revoked_at IS NULL в upgrade.
  • Обновить соответствующее условие частичного индекса в downgrade, чтобы оно соответствовало новому определению.
alembic/versions/4972fe91500d_v0_0_1.py
Исправить определение колонки users.user_role и гарантировать установку роли по умолчанию.
  • Переопределить enum user_role, чтобы он включал именованный тип enum PostgreSQL и был добавлен в таблицу users.
  • Установить server_default='user' для user_role, чтобы гарантировать значение роли по умолчанию.
  • Убедиться, что downgrade удаляет колонку user_role согласованно с новым определением.
alembic/versions/4972fe91500d_v0_0_1.py
Привести исходную миграцию в порядок, удалив устаревшие изменения колонок и шаблонный код Alembic.
  • Удалить docstring-и для upgrade/downgrade и автоматически сгенерированные комментарии Alembic.
  • Убрать неиспользуемые изменения для services.description и services.address, чтобы отразить актуальную схему.
  • Внести небольшие косметические правки, такие как пробелы и комментарии, не меняя поведение других объектов.
alembic/versions/4972fe91500d_v0_0_1.py

Подсказки и команды

Взаимодействие с Sourcery

  • Запустить новое ревью: Оставьте комментарий @sourcery-ai review в pull request.
  • Продолжать обсуждения: Отвечайте напрямую на комментарии ревью от Sourcery.
  • Создать задачу GitHub из комментария ревью: Попросите Sourcery создать
    issue из комментария ревью, ответив на него. Также можно ответить на
    комментарий ревью с @sourcery-ai issue, чтобы создать issue на его основе.
  • Сгенерировать заголовок pull request: Напишите @sourcery-ai в любом месте
    заголовка pull request, чтобы в любой момент сгенерировать заголовок. Также можно
    оставить комментарий @sourcery-ai title в pull request, чтобы (пере)сгенерировать
    заголовок в любой момент.
  • Сгенерировать описание pull request: Напишите @sourcery-ai summary в любом месте
    тела pull request, чтобы в любой момент сгенерировать описание PR именно там,
    где вы этого хотите. Также можно оставить комментарий @sourcery-ai summary
    в pull request, чтобы (пере)сгенерировать описание в любой момент.
  • Сгенерировать руководство для ревьюера: Оставьте комментарий @sourcery-ai guide
    в pull request, чтобы в любой момент (пере)сгенерировать руководство для ревьюера.
  • Разрешить все комментарии Sourcery: Оставьте комментарий @sourcery-ai resolve
    в pull request, чтобы разрешить все комментарии Sourcery. Полезно, если вы уже
    учли все замечания и больше не хотите их видеть.
  • Отклонить все ревью Sourcery: Оставьте комментарий @sourcery-ai dismiss
    в pull request, чтобы отклонить все существующие ревью от Sourcery. Особенно полезно,
    если вы хотите начать заново с нового ревью — не забудьте затем оставить комментарий
    @sourcery-ai review, чтобы запустить новое ревью!

Настройка работы

Зайдите в свою панель управления, чтобы:

  • Включать или отключать функции ревью, такие как сгенерированное Sourcery
    описание pull request, руководство для ревьюера и другие.
  • Изменить язык ревью.
  • Добавлять, удалять или редактировать собственные инструкции для ревью.
  • Настроить другие параметры ревью.

Получение помощи

Original review guide in English
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Adjusts the initial Alembic migration to simplify the active ban uniqueness condition and correctly define the user_role enum with a default value, while cleaning up unused service column alterations and Alembic boilerplate comments.

Entity relationship diagram for updated bans index and user_role enum

erDiagram
    Users {
        int id PK
        enum user_role "default: user"
    }

    Bans {
        int id PK
        int user_id FK
        timestamp revoked_at "uq_active_ban: user_id WHERE revoked_at IS NULL"
    }

    Users ||--o{ Bans : has_bans
Loading

File-Level Changes

Change Details Files
Simplify the unique active-ban index to depend only on non-revoked bans.
  • Replace the partial unique index condition for active bans to filter solely on revoked_at IS NULL in upgrade.
  • Update the corresponding partial index condition in downgrade to match the new definition.
alembic/versions/4972fe91500d_v0_0_1.py
Correct the users.user_role column definition and ensure a default role is set.
  • Redefine the user_role enum to include a named PostgreSQL enum type and add it to the users table.
  • Set server_default='user' on user_role to guarantee a default role value.
  • Ensure downgrade drops the user_role column consistently with the new definition.
alembic/versions/4972fe91500d_v0_0_1.py
Clean up the initial migration by removing obsolete column alterations and Alembic boilerplate.
  • Remove upgrade/downgrade docstrings and autogenerated Alembic comments.
  • Drop unused alterations for services.description and services.address to reflect the current schema.
  • Make minor cosmetic edits such as spacing and comments without changing behavior of other objects.
alembic/versions/4972fe91500d_v0_0_1.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@Fl1riX Fl1riX merged commit 485eae1 into main Jun 2, 2026
3 checks passed
@Fl1riX Fl1riX deleted the chore/update-migration branch June 2, 2026 16:07
@coveralls

coveralls commented Jun 2, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 26832245115

Coverage remained the same at 30.025%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 1209
Covered Lines: 363
Line Coverage: 30.02%
Coverage Strength: 0.6 hits per line

💛 - Coveralls

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Привет — я нашёл 1 проблему и оставил несколько общих замечаний:

  • В миграции остался лишний комментарий # hi; пожалуйста, уберите его, чтобы миграция оставалась аккуратной и сфокусированной.
  • Так как это начальная миграция, перепроверьте, что добавление именованного enum’а user_role с server_default='user' согласовано с названием Enum и значением по умолчанию в модели SQLAlchemy, чтобы будущие миграции не генерировали лишние отличия.
Подсказка для AI-агентов
Please address the comments from this code review:

## Overall Comments
- There is a stray `# hi` comment left in the migration; please remove it to keep the migration clean and focused.
- Since this is an initial migration, double-check that introducing the named enum `user_role` with `server_default='user'` aligns with the SQLAlchemy model’s Enum name and default, so future migrations don’t generate unnecessary diffs.

## Individual Comments

### Comment 1
<location path="alembic/versions/4972fe91500d_v0_0_1.py" line_range="51" />
<code_context>
-    op.add_column('users', sa.Column('user_role', sa.Enum('user', 'admin', 'moderator'), nullable=False))
+
+# hi
+    op.add_column('users', sa.Column('user_role', sa.Enum('user', 'admin', 'moderator', name='user_role'), nullable=False, server_default='user'))
     op.alter_column('users', 'email',
                existing_type=sa.VARCHAR(length=50),
</code_context>
<issue_to_address>
**suggestion (bug_risk):** The named ENUM type `user_role` is created on upgrade but never dropped on downgrade.

Because `name='user_role'` is specified, Alembic/SQLAlchemy creates a concrete ENUM type in the DB. The downgrade currently only drops the column, leaving the ENUM type behind, which can conflict with future migrations that reuse `user_role` with different values.

Please also drop the ENUM type in `downgrade` (for example, `op.execute("DROP TYPE user_role")` after dropping the column, or the dialect-appropriate equivalent).

Suggested implementation:

```python
def downgrade() -> None:
    # Drop the user_role column before dropping the ENUM type
    op.drop_column('users', 'user_role')

    # Drop the underlying ENUM type created in upgrade()
    op.execute("DROP TYPE user_role")

```

If `downgrade()` already has other operations in this file, you should merge the `op.drop_column('users', 'user_role')` and `op.execute("DROP TYPE user_role")` calls into the existing body rather than replacing it entirely. They should appear in the correct reverse order relative to the `upgrade()` steps (i.e., after reverting indexes/constraints that depend on `user_role`, but before any operations that assume the type no longer exists).
</issue_to_address>

Sourcery бесплатен для open source — если вам нравятся наши обзоры, пожалуйста, расскажите о нас ✨
Помогите мне стать полезнее! Пожалуйста, нажимайте 👍 или 👎 под каждым комментарием — я буду использовать этот фидбэк, чтобы улучшать обзоры.
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • There is a stray # hi comment left in the migration; please remove it to keep the migration clean and focused.
  • Since this is an initial migration, double-check that introducing the named enum user_role with server_default='user' aligns with the SQLAlchemy model’s Enum name and default, so future migrations don’t generate unnecessary diffs.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- There is a stray `# hi` comment left in the migration; please remove it to keep the migration clean and focused.
- Since this is an initial migration, double-check that introducing the named enum `user_role` with `server_default='user'` aligns with the SQLAlchemy model’s Enum name and default, so future migrations don’t generate unnecessary diffs.

## Individual Comments

### Comment 1
<location path="alembic/versions/4972fe91500d_v0_0_1.py" line_range="51" />
<code_context>
-    op.add_column('users', sa.Column('user_role', sa.Enum('user', 'admin', 'moderator'), nullable=False))
+
+# hi
+    op.add_column('users', sa.Column('user_role', sa.Enum('user', 'admin', 'moderator', name='user_role'), nullable=False, server_default='user'))
     op.alter_column('users', 'email',
                existing_type=sa.VARCHAR(length=50),
</code_context>
<issue_to_address>
**suggestion (bug_risk):** The named ENUM type `user_role` is created on upgrade but never dropped on downgrade.

Because `name='user_role'` is specified, Alembic/SQLAlchemy creates a concrete ENUM type in the DB. The downgrade currently only drops the column, leaving the ENUM type behind, which can conflict with future migrations that reuse `user_role` with different values.

Please also drop the ENUM type in `downgrade` (for example, `op.execute("DROP TYPE user_role")` after dropping the column, or the dialect-appropriate equivalent).

Suggested implementation:

```python
def downgrade() -> None:
    # Drop the user_role column before dropping the ENUM type
    op.drop_column('users', 'user_role')

    # Drop the underlying ENUM type created in upgrade()
    op.execute("DROP TYPE user_role")

```

If `downgrade()` already has other operations in this file, you should merge the `op.drop_column('users', 'user_role')` and `op.execute("DROP TYPE user_role")` calls into the existing body rather than replacing it entirely. They should appear in the correct reverse order relative to the `upgrade()` steps (i.e., after reverting indexes/constraints that depend on `user_role`, but before any operations that assume the type no longer exists).
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

op.add_column('users', sa.Column('user_role', sa.Enum('user', 'admin', 'moderator'), nullable=False))

# hi
op.add_column('users', sa.Column('user_role', sa.Enum('user', 'admin', 'moderator', name='user_role'), nullable=False, server_default='user'))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Именованный тип ENUM user_role создаётся при upgrade, но не удаляется при downgrade.

Поскольку указано name='user_role', Alembic/SQLAlchemy создаёт конкретный тип ENUM в базе данных. Текущая реализация downgrade удаляет только столбец, оставляя тип ENUM, что может привести к конфликтам с будущими миграциями, которые повторно используют user_role с другим набором значений.

Пожалуйста, также удалите тип ENUM в downgrade (например, с помощью op.execute("DROP TYPE user_role") после удаления столбца, либо эквивалента для используемого диалекта).

Предлагаемая реализация:

def downgrade() -> None:
    # Drop the user_role column before dropping the ENUM type
    op.drop_column('users', 'user_role')

    # Drop the underlying ENUM type created in upgrade()
    op.execute("DROP TYPE user_role")

Если в downgrade() уже есть другие операции в этом файле, вам следует добавить вызовы op.drop_column('users', 'user_role') и op.execute("DROP TYPE user_role") в существующее тело функции, а не заменять его целиком. Эти операции должны идти в корректном обратном порядке по отношению к шагам upgrade() (то есть после отката индексов/ограничений, зависящих от user_role, но до любых операций, которые предполагают, что этот тип больше не существует).

Original comment in English

suggestion (bug_risk): The named ENUM type user_role is created on upgrade but never dropped on downgrade.

Because name='user_role' is specified, Alembic/SQLAlchemy creates a concrete ENUM type in the DB. The downgrade currently only drops the column, leaving the ENUM type behind, which can conflict with future migrations that reuse user_role with different values.

Please also drop the ENUM type in downgrade (for example, op.execute("DROP TYPE user_role") after dropping the column, or the dialect-appropriate equivalent).

Suggested implementation:

def downgrade() -> None:
    # Drop the user_role column before dropping the ENUM type
    op.drop_column('users', 'user_role')

    # Drop the underlying ENUM type created in upgrade()
    op.execute("DROP TYPE user_role")

If downgrade() already has other operations in this file, you should merge the op.drop_column('users', 'user_role') and op.execute("DROP TYPE user_role") calls into the existing body rather than replacing it entirely. They should appear in the correct reverse order relative to the upgrade() steps (i.e., after reverting indexes/constraints that depend on user_role, but before any operations that assume the type no longer exists).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants