Feature/roles and ban system (#32)#33
Conversation
* 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>
Руководство для ревьюераИнтегрирует обработку срока действия банов и рефакторит управление сессиями базы данных, подключая фабрику асинхронных сессий в общий модуль базы данных, добавляя плановую фоную задачу для отзыва просроченных банов и упрощая уникальное ограничение для банов. Диаграмма последовательности для планового отзыва просроченных банов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
Изменения на уровне файлов
Подсказки и командыВзаимодействие с Sourcery
Настройка работыПерейдите в вашу панель управления, чтобы:
Получение помощи
Original review guide in EnglishReviewer's GuideIntegrates 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 revocationsequenceDiagram
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
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
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
AsyncIOScheduleris shut down withshutdown()rather thandispose(), soscheduler.dispose()inlifespanshould be replaced or removed to avoid runtime errors. - The new global
SessionLocalpattern mixes direct usage (e.g., inremove_expired_bans) and theget_dbhelper; it would be more robust to centralize session creation/cleanup throughget_dbso 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") andSessionLocalis only checked there, so any direct use ofSessionLocal(like in tasks) should either be guarded similarly or refactored to go throughget_dbto 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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| scheduler = AsyncIOScheduler() # Планировщик | ||
|
|
||
| @asynccontextmanager | ||
| async def lifespan(app: FastAPI): |
There was a problem hiding this comment.
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).
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
feat(ci): Add Docker image building and pushing to the ghcr registry (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 (chore: remove old SSH deploy workflow #19)
Fix/put things in order (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 (fix(compose): Fix port forwarding to localhost only #21)
fix(compose): Fix the error of accessing the database outside the local network (fix(compose): Fix the error of accessing the database outside the loc… #22)
Revert "fix(compose): Fix the error of accessing the database outside the local network (fix(compose): Fix the error of accessing the database outside the loc… #22)" (Revert "fix(compose): Fix the error of accessing the database outside… #23)
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
fix: Add && to the docker image build command (fix: Add && to the docker image build command #27)
Feature/heartbit endpoint (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
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».
Новые возможности:
Исправления ошибок:
Улучшения:
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:
Bug Fixes:
Enhancements: