Исправление автомиграции и создания администратора

This commit is contained in:
2025-12-19 21:28:52 +03:00
parent f6ac03a869
commit 719631158d
4 changed files with 84 additions and 55 deletions
+12 -8
View File
@@ -11,7 +11,7 @@ from sqlmodel import Session, select
from library_service.models.db import Role, User
from library_service.models.dto import TokenData
from library_service.settings import get_session
from library_service.settings import get_session, get_logger
# Конфигурация из переменных окружения
@@ -20,12 +20,16 @@ SECRET_KEY = os.getenv("SECRET_KEY", "your-secret-key-change-in-production")
ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "30"))
REFRESH_TOKEN_EXPIRE_DAYS = int(os.getenv("REFRESH_TOKEN_EXPIRE_DAYS", "7"))
# Хэширование паролей
pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")
# Получение логгера
logger = get_logger("uvicorn")
# OAuth2 схема
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")
# Хэширование паролей
pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""Проверка пароль по его хешу."""
@@ -161,7 +165,7 @@ def seed_roles(session: Session) -> dict[str, Role]:
session.commit()
session.refresh(role)
roles[role_data["name"]] = role
print(f"[+] Created role: {role_data['name']}")
logger.info(f"[+] Created role: {role_data['name']}")
return roles
@@ -173,7 +177,7 @@ def seed_admin(session: Session, admin_role: Role) -> User | None:
).all()
if existing_admins:
print(f"[*] Admin already exists: {existing_admins[0].username}")
logger.info(f"[*] Admin already exists: {existing_admins[0].username}")
return None
admin_username = os.getenv("DEFAULT_ADMIN_USERNAME", "admin")
@@ -183,8 +187,8 @@ def seed_admin(session: Session, admin_role: Role) -> User | None:
if not admin_password:
import secrets
admin_password = secrets.token_urlsafe(16)
print(f"[!] Generated admin password: {admin_password}")
print("[!] Please save this password and set DEFAULT_ADMIN_PASSWORD env var")
logger.warning(f"[!] Generated admin password: {admin_password}")
logger.warning("[!] Please save this password and set DEFAULT_ADMIN_PASSWORD env var")
admin_user = User(
username=admin_username,
@@ -200,7 +204,7 @@ def seed_admin(session: Session, admin_role: Role) -> User | None:
session.commit()
session.refresh(admin_user)
print(f"[+] Created admin user: {admin_username}")
logger.info(f"[+] Created admin user: {admin_username}")
return admin_user
+28 -12
View File
@@ -6,27 +6,43 @@ from alembic import command
from alembic.config import Config
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from sqlmodel import Session
from .auth import run_seeds
from .routers import api_router
from .settings import engine, get_app
app = get_app()
alembic_cfg = Config("alembic.ini")
from .settings import engine, get_app, get_logger
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Жизененый цикл сервиса"""
print("[+] Initializing...")
"""Жизненный цикл сервиса"""
logger = get_logger("uvicorn")
logger.info("[+] Initializing database...")
# Настройка базы данных
with engine.begin() as connection:
alembic_cfg.attributes["connection"] = connection
command.upgrade(alembic_cfg, "head")
try:
with engine.begin() as connection:
alembic_cfg = Config("alembic.ini")
alembic_cfg.attributes["configure_logging"] = False
alembic_cfg.attributes["connection"] = connection
command.upgrade(alembic_cfg, "head")
except Exception as e:
logger.error(f"[-] Migration failed: {e}")
raise e
print("[+] Starting...")
logger.info("[+] Running seeds...")
try:
with Session(engine) as session:
run_seeds(session)
logger.info("[+] Database setup completed.")
except Exception as e:
logger.error(f"[-] Seeding failed: {e}")
logger.info("[+] Starting application...")
yield # Обработка запросов
print("[+] Application shutdown")
logger.info("[+] Application shutdown")
app = get_app(lifespan)
# Подключение маршрутов
+42 -34
View File
@@ -1,5 +1,5 @@
"""Модуль настроек проекта"""
import os
import os, logging
from dotenv import load_dotenv
from fastapi import FastAPI
@@ -12,39 +12,42 @@ with open("pyproject.toml", 'r', encoding='utf-8') as f:
config = load(f)
def get_app() -> FastAPI:
"""Dependency, для получение экземплярра FastAPI application"""
return FastAPI(
title=config["tool"]["poetry"]["name"],
description=config["tool"]["poetry"]["description"],
version=config["tool"]["poetry"]["version"],
openapi_tags=[
{
"name": "authentication",
"description": "Авторизация пользователя."
},
{
"name": "authors",
"description": "Действия с авторами.",
},
{
"name": "books",
"description": "Действия с книгами.",
},
{
"name": "genres",
"description": "Действия с жанрами.",
},
{
"name": "relations",
"description": "Действия с связями.",
},
{
"name": "misc",
"description": "Прочие.",
},
],
)
def get_app(lifespan=None) -> FastAPI:
"""Dependency для получения экземпляра FastAPI application"""
if not hasattr(get_app, 'instance'):
get_app.instance = FastAPI(
title=config["tool"]["poetry"]["name"],
description=config["tool"]["poetry"]["description"],
version=config["tool"]["poetry"]["version"],
lifespan=lifespan,
openapi_tags=[
{
"name": "authentication",
"description": "Авторизация пользователя."
},
{
"name": "authors",
"description": "Действия с авторами.",
},
{
"name": "books",
"description": "Действия с книгами.",
},
{
"name": "genres",
"description": "Действия с жанрами.",
},
{
"name": "relations",
"description": "Действия с связями.",
},
{
"name": "misc",
"description": "Прочие.",
},
],
)
return get_app.instance
HOST = os.getenv("POSTGRES_HOST")
@@ -64,3 +67,8 @@ def get_session():
"""Dependency, для получение сессии БД"""
with Session(engine) as session:
yield session
def get_logger(name: str = "uvicorn"):
"""Dependency, для получение логгера"""
return logging.getLogger(name)
+2 -1
View File
@@ -16,7 +16,8 @@ config.set_main_option("sqlalchemy.url", POSTGRES_DATABASE_URL)
# Interpret the config file for Python logging.
# This line sets up loggers basically.
if config.config_file_name is not None:
fileConfig(config.config_file_name)
if config.attributes.get("configure_logging", True):
fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support