Skip to content

Feature/roles and ban system (#32)#33

Closed
Fl1riX wants to merge 1 commit into
feature/roles-and-ban-systemfrom
main
Closed

Feature/roles and ban system (#32)#33
Fl1riX wants to merge 1 commit into
feature/roles-and-ban-systemfrom
main

Conversation

@Fl1riX

@Fl1riX Fl1riX commented Jun 2, 2026

Copy link
Copy Markdown
Owner
  • 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

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

  • test(auth): add JWT unit tests

  • 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/backround clear magic tokens (Fix/backround clear magic tokens #17)

  • 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

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

  • test(auth): add JWT unit tests

  • 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


This reverts commit 73920aa.

  • Fix/magic link bot auth (Fix/magic link bot auth #24)

  • 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

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


  • Feature/docker security improvements (Feature/docker security improvements #25)

  • 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



  • feat: Bring the architecture to a clean archeticture. In the structur… (feat: Bring the architecture to a clean archeticture. In the structur… #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


  • Fix/alembic migrations (Fix/alembic migrations #30)

  • 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


  • fix(migrations): Add alembic.ini file (fix(migrations): Add alembic.ini file #31)

  • refactor: refactor: simplify ban uniqueness constraint and database session handling

  • feat(tasks): add background task for revoking expired bans


Summary by Sourcery

Введение централизованного управления асинхронными сессиями базы данных и плановых задач обслуживания для банов и «magic tokens».

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

  • Добавлен фоновый задача, которая по расписанию автоматически отзывает просроченные баны.

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

  • Обеспечен запуск и корректное завершение фоновых планировщиков задач обслуживания в рамках жизненного цикла FastAPI lifespan, чтобы избежать утечек ресурсов.
  • Исправлены вспомогательные функции доступа к базе данных за счёт инициализации и повторного использования общей фабрики асинхронных сессий вместо опоры на состояние запроса.

Улучшения:

  • Упрощено ограничение уникальности для банов: теперь оно зависит только от поля revoked_at, при этом отзыв просроченных банов выполняется планировщиком.
  • Реорганизовано управление сессиями базы данных в общий модульный SessionLocal для повторного использования в задачах и обработчиках запросов.
Original summary in English

Summary by Sourcery

Introduce centralized async database session management and scheduled maintenance tasks for bans and magic tokens.

New Features:

  • Add a background task that automatically revokes expired bans on a schedule.

Bug Fixes:

  • Ensure background schedulers for maintenance tasks are started and disposed within the FastAPI lifespan lifecycle to avoid resource leaks.
  • Fix database access helpers by initializing and reusing a shared async session factory instead of relying on request state.

Enhancements:

  • Simplify the ban uniqueness constraint to depend only on the revoked_at field, relying on scheduled revocation for expired bans.
  • Refactor database session handling into a shared module-level SessionLocal for reuse across tasks and request handlers.

* 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/backround clear magic tokens (#17)

* 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>

* feat(ci): Add Docker image building and pushing to the ghcr registry (#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

* chore: remove old SSH deploy workflow (#19)

* Fix/put things in order (#20)

* 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

* fix(compose): Fix port forwarding to localhost only (#21)

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

* fix(compose): Fix the error of accessing the database outside the local network (#22)

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

* Revert "fix(compose): Fix the error of accessing the database outside the local network (#22)" (#23)

This reverts commit 73920aa.

* Fix/magic link bot auth (#24)

* 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>

* Feature/docker security improvements (#25)

* 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>

* fix: Add && to the docker image build command (#27)

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

* Feature/heartbit endpoint (#28)

* 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>

* feat: Bring the architecture to a clean archeticture. In the structur… (#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/alembic migrations (#30)

* 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>

* fix(migrations): Add alembic.ini file (#31)

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

* refactor: refactor: simplify ban uniqueness constraint and database session handling

* feat(tasks): add background task for revoking expired bans

---------

Co-authored-by: Squ1reX <maksimhripinkov658@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
@sourcery-ai

sourcery-ai Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

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

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

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

sequenceDiagram
    participant Scheduler as AsyncIOScheduler
    participant App as FastAPI_app
    participant Task as remove_expired_bans
    participant DBFactory as SessionLocal
    participant DB as AsyncSession
    participant Ban as Ban

    App->>Scheduler: add_job(remove_expired_bans, interval 10m)
    Scheduler->>Task: remove_expired_bans()
    activate Task
    Task->>DBFactory: SessionLocal()
    DBFactory-->>Task: AsyncSession
    activate DB
    Task->>DB: execute(update(Ban).where(...).values(...))
    Task->>DB: commit()
    deactivate DB
    deactivate Task
Loading

Изменения на уровне файлов

Изменение Подробности Файлы
Перенос инициализации фабрики асинхронных сессий в общий модуль базы данных и изменение сигнатуры зависимости.
  • Назначать экземпляр async_sessionmaker в database.SessionLocal во время lifespan FastAPI вместо app.state
  • Рефакторинг get_db для использования глобального SessionLocal без Request с выбрасыванием понятной ошибки, если он не инициализирован
main.py
src/infrastructure/db/database.py
Интеграция планировщика задач APScheduler в lifespan приложения и добавление фоновой задачи для истекающих банов.
  • Создавать AsyncIOScheduler на уровне модуля и запускать его внутри контекста lifespan
  • Регистрировать cleanup_telegram_tokens и remove_expired_bans как периодические задачи при запуске приложения с разными интервалами
  • Удалить старый обработчик события startup, который только планировал cleanup_telegram_tokens
  • Пытаться корректно завершить работу планировщика при остановке (примечание: стандартный API APScheduler использует shutdown(), а не dispose())
main.py
src/infrastructure/tasks/cleanup_magic_tokens.py
src/infrastructure/tasks/remove_bans.py
Настройка ограничений модели бана и добавление задачи для автоматического отзыва просроченных банов.
  • Упростить частичный уникальный индекс на Ban так, чтобы он учитывал только неотозванные баны, убрав условие по expires_at
  • Добавить задачу remove_expired_bans, которая помечает просроченные, неотозванные баны как отозванные с типовой причиной и коммитит изменения
  • Использовать временные метки в UTC с информацией о часовом поясе при проверке срока действия бана
src/domain/models/ban_model.py
src/infrastructure/tasks/remove_bans.py

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

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

  • Запустить новое ревью: Оставьте комментарий @sourcery-ai review в pull request.
  • Продолжить обсуждение: Отвечайте напрямую на комментарии ревью от Sourcery.
  • Создать GitHub-issue из комментария ревью: Попросите 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

Integrates ban expiration handling and refactors database session management by wiring the async session factory into a shared database module, adding a scheduled background task to revoke expired bans, and simplifying the ban uniqueness constraint.

Sequence diagram for scheduled ban expiration revocation

sequenceDiagram
    participant Scheduler as AsyncIOScheduler
    participant App as FastAPI_app
    participant Task as remove_expired_bans
    participant DBFactory as SessionLocal
    participant DB as AsyncSession
    participant Ban as Ban

    App->>Scheduler: add_job(remove_expired_bans, interval 10m)
    Scheduler->>Task: remove_expired_bans()
    activate Task
    Task->>DBFactory: SessionLocal()
    DBFactory-->>Task: AsyncSession
    activate DB
    Task->>DB: execute(update(Ban).where(...).values(...))
    Task->>DB: commit()
    deactivate DB
    deactivate Task
Loading

File-Level Changes

Change Details Files
Move async session factory initialization into the shared database module and change dependency signature.
  • Assign async_sessionmaker instance to database.SessionLocal during FastAPI lifespan instead of app.state
  • Refactor get_db to use a global SessionLocal without Request, raising a clear error if it is uninitialized
main.py
src/infrastructure/db/database.py
Integrate APScheduler job scheduling into application lifespan and add a background task for expiring bans.
  • Instantiate AsyncIOScheduler at module scope and start it inside the lifespan context
  • Register cleanup_telegram_tokens and remove_expired_bans as recurring jobs on application startup with different intervals
  • Remove the old startup event handler that only scheduled cleanup_telegram_tokens
  • Attempt to dispose the scheduler during shutdown (note: APScheduler’s standard API uses shutdown(), not dispose())
main.py
src/infrastructure/tasks/cleanup_magic_tokens.py
src/infrastructure/tasks/remove_bans.py
Adjust ban model constraints and add a task to automatically revoke expired bans.
  • Simplify the partial unique index on Ban to only consider non-revoked bans, removing the expires_at condition
  • Introduce remove_expired_bans task that marks expired, non-revoked bans as revoked with a standard reason and commits the change
  • Use timezone-aware UTC timestamps for ban expiration checks
src/domain/models/ban_model.py
src/infrastructure/tasks/remove_bans.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 closed this Jun 2, 2026

@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.

Hey - я нашёл одну проблему и оставил несколько общих комментариев:

  • AsyncIOScheduler корректно останавливается методом shutdown(), а не dispose(), поэтому scheduler.dispose() в lifespan нужно заменить или удалить, чтобы избежать ошибок во время выполнения.
  • Новый глобальный паттерн SessionLocal сочетает прямое использование (например, в remove_expired_bans) и хелпер get_db; было бы надёжнее централизовать создание/освобождение сессий через get_db, чтобы фоновые задачи не обходили проверку инициализации и не дублировали логику.
  • В database.get_db() в сообщении об ошибке опечатка ("Database is not initialied"), и SessionLocal проверяется только там, поэтому любое прямое использование SessionLocal (например, в задачах) тоже должно быть защищено аналогичной проверкой или переработано так, чтобы проходить через get_db и быстро падать с понятным сообщением.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `AsyncIOScheduler` is shut down with `shutdown()` rather than `dispose()`, so `scheduler.dispose()` in `lifespan` should be replaced or removed to avoid runtime errors.
- The new global `SessionLocal` pattern mixes direct usage (e.g., in `remove_expired_bans`) and the `get_db` helper; it would be more robust to centralize session creation/cleanup through `get_db` so background tasks don’t bypass the initialization check or duplicate logic.
- In `database.get_db()` the error message has a typo (`"Database is not initialied"`) and `SessionLocal` is only checked there, so any direct use of `SessionLocal` (like in tasks) should either be guarded similarly or refactored to go through `get_db` to fail fast with a clear message.

## Individual Comments

### Comment 1
<location path="main.py" line_range="21-24" />
<code_context>
+from src.infrastructure.tasks.remove_bans import remove_expired_bans
+from src.infrastructure.db import database
+
+scheduler = AsyncIOScheduler() # Планировщик

 @asynccontextmanager
</code_context>
<issue_to_address>
**issue (bug_risk):** Используйте поддерживаемый API остановки планировщика вместо `dispose()` и обеспечьте корректное управление его жизненным циклом.

В APScheduler 3.x у `AsyncIOScheduler` нет метода `dispose()`, поэтому при завершении работы будет выброшен `AttributeError`. Вместо этого используйте `scheduler.shutdown(wait=False)` (или `wait=True`, если нужно дождаться завершения задач). Поскольку планировщик является синглтоном на уровне модуля, также важно гарантировать, что его остановка идемпотентна и что `start()`/`shutdown()` не могут быть вызваны в неверном порядке (например, в тестах или при перезапуске приложения).
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Помогите мне стать полезнее! Пожалуйста, ставьте 👍 или 👎 под каждым комментарием — я буду использовать эту обратную связь, чтобы улучшать свои ревью.
Original comment in English

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

  • The AsyncIOScheduler is shut down with shutdown() rather than dispose(), so scheduler.dispose() in lifespan should be replaced or removed to avoid runtime errors.
  • The new global SessionLocal pattern mixes direct usage (e.g., in remove_expired_bans) and the get_db helper; it would be more robust to centralize session creation/cleanup through get_db so background tasks don’t bypass the initialization check or duplicate logic.
  • In database.get_db() the error message has a typo ("Database is not initialied") and SessionLocal is only checked there, so any direct use of SessionLocal (like in tasks) should either be guarded similarly or refactored to go through get_db to fail fast with a clear message.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `AsyncIOScheduler` is shut down with `shutdown()` rather than `dispose()`, so `scheduler.dispose()` in `lifespan` should be replaced or removed to avoid runtime errors.
- The new global `SessionLocal` pattern mixes direct usage (e.g., in `remove_expired_bans`) and the `get_db` helper; it would be more robust to centralize session creation/cleanup through `get_db` so background tasks don’t bypass the initialization check or duplicate logic.
- In `database.get_db()` the error message has a typo (`"Database is not initialied"`) and `SessionLocal` is only checked there, so any direct use of `SessionLocal` (like in tasks) should either be guarded similarly or refactored to go through `get_db` to fail fast with a clear message.

## Individual Comments

### Comment 1
<location path="main.py" line_range="21-24" />
<code_context>
+from src.infrastructure.tasks.remove_bans import remove_expired_bans
+from src.infrastructure.db import database
+
+scheduler = AsyncIOScheduler() # Планировщик

 @asynccontextmanager
</code_context>
<issue_to_address>
**issue (bug_risk):** Use the scheduler’s supported shutdown API instead of `dispose()` and ensure proper lifecycle handling.

`AsyncIOScheduler` in APScheduler 3.x doesn’t have `dispose()`, so this will raise `AttributeError` on teardown. Use `scheduler.shutdown(wait=False)` (or `wait=True` if needed) instead. Since the scheduler is a module-level singleton, also ensure shutdown is idempotent and that `start()`/`shutdown()` can’t be called in an inconsistent order (e.g., across tests or reloads).
</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.

Comment thread main.py
Comment on lines +21 to 24
scheduler = AsyncIOScheduler() # Планировщик

@asynccontextmanager
async def lifespan(app: FastAPI):

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.

issue (bug_risk): Используйте поддерживаемый API остановки планировщика вместо dispose() и обеспечьте корректное управление его жизненным циклом.

В APScheduler 3.x у AsyncIOScheduler нет метода dispose(), поэтому при завершении работы будет выброшен AttributeError. Вместо этого используйте scheduler.shutdown(wait=False) (или wait=True, если нужно дождаться завершения задач). Поскольку планировщик является синглтоном на уровне модуля, также важно гарантировать, что его остановка идемпотентна и что start()/shutdown() не могут быть вызваны в неверном порядке (например, в тестах или при перезапуске приложения).

Original comment in English

issue (bug_risk): Use the scheduler’s supported shutdown API instead of dispose() and ensure proper lifecycle handling.

AsyncIOScheduler in APScheduler 3.x doesn’t have dispose(), so this will raise AttributeError on teardown. Use scheduler.shutdown(wait=False) (or wait=True if needed) instead. Since the scheduler is a module-level singleton, also ensure shutdown is idempotent and that start()/shutdown() can’t be called in an inconsistent order (e.g., across tests or reloads).

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.

1 participant