alabuga/backend/app/main.py
2025-09-27 22:38:59 +03:00

91 lines
3.4 KiB
Python

"""Точка входа FastAPI."""
from __future__ import annotations
from pathlib import Path
from alembic import command
from alembic.config import Config
from alembic.script import ScriptDirectory
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy import inspect, text
from app.api.routes import admin, auth, journal, missions, onboarding, store, users
from app.core.config import settings
from app.db.session import engine
ALEMBIC_CONFIG = Path(__file__).resolve().parents[1] / "alembic.ini"
app = FastAPI(title=settings.project_name)
app.add_middleware(
CORSMiddleware,
allow_origins=settings.backend_cors_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
def run_migrations() -> None:
"""Гарантируем, что база обновлена до последней схемы Alembic."""
config = Config(str(ALEMBIC_CONFIG))
config.set_main_option("sqlalchemy.url", str(settings.database_url))
inspector = inspect(engine)
script = ScriptDirectory.from_config(config)
head_revision = script.get_current_head()
tables = inspector.get_table_names()
current_revision: str | None = None
if "alembic_version" in tables:
with engine.begin() as conn:
row = conn.execute(text("SELECT version_num FROM alembic_version LIMIT 1")).fetchone()
current_revision = row[0] if row else None
if "alembic_version" not in tables or current_revision is None:
if tables:
# База создана через Base.metadata.create_all. Добавляем отсутствующие поля вручную
# и фиксируем версию как актуальную, чтобы последующие миграции применялись штатно.
user_columns = {col["name"] for col in inspector.get_columns("users")}
with engine.begin() as conn:
if "preferred_branch" not in user_columns:
conn.execute(text("ALTER TABLE users ADD COLUMN preferred_branch VARCHAR(160)"))
if "motivation" not in user_columns:
conn.execute(text("ALTER TABLE users ADD COLUMN motivation TEXT"))
conn.execute(text("CREATE TABLE IF NOT EXISTS alembic_version (version_num VARCHAR(32) NOT NULL)"))
conn.execute(text("DELETE FROM alembic_version"))
conn.execute(text("INSERT INTO alembic_version (version_num) VALUES (:rev)"), {"rev": head_revision})
return
# Таблиц ещё нет — создадим их миграциями.
command.upgrade(config, "head")
return
command.upgrade(config, "head")
@app.on_event("startup")
def on_startup() -> None:
"""Прогоняем миграции перед обработкой запросов."""
run_migrations()
app.include_router(auth.router)
app.include_router(users.router)
app.include_router(missions.router)
app.include_router(journal.router)
app.include_router(onboarding.router)
app.include_router(store.router)
app.include_router(admin.router)
@app.get("/", summary="Проверка работоспособности")
def healthcheck() -> dict[str, str]:
"""Простой ответ для Docker healthcheck."""
return {"status": "ok", "environment": settings.environment}