Files
ForbiddenStarsApp/backend/app/bootstrap.py
T

66 lines
2.5 KiB
Python

"""Идемпотентный бутстрап: справочники + учётная запись администратора.
Запуск: `python -m app.bootstrap` (вызывается из entrypoint.sh после миграций).
"""
from __future__ import annotations
from sqlmodel import Session, select
from app.core.config import settings
from app.core.security import hash_password, verify_password
from app.db.session import engine
from app.models import User
from app.seed.reference_data import seed_reference_data
def _ensure_admin(session: Session) -> None:
if not settings.admin_bootstrap_enabled:
return
password = (settings.admin_password or "").strip()
username = (settings.admin_username or "admin").strip()
existing = session.exec(select(User).where(User.role == "admin")).first()
if existing is None:
if not password:
raise RuntimeError("Отказ создавать администратора без пароля (ADMIN_PASSWORD пуст).")
# Логин администратора = его никнейм (отдельной таблицы админов нет).
session.add(
User(
nickname=username,
role="admin",
auth_provider="local",
password_hash=hash_password(password),
)
)
session.commit()
print(f"[bootstrap] Создан администратор: {username}")
return
# Администратор уже существует.
if not settings.is_development:
# В prod пароль НЕ перезаписываем (мог быть изменён через панель).
return
# DEV: подтягиваем логин/пароль из .env (env — источник истины в деве).
changed: list[str] = []
if password and not verify_password(password, existing.password_hash or ""):
existing.password_hash = hash_password(password)
changed.append("пароль")
if username and existing.nickname != username:
existing.nickname = username
changed.append("логин")
if changed:
session.add(existing)
session.commit()
print(f"[bootstrap] DEV: администратор обновлён из .env ({', '.join(changed)})")
def bootstrap() -> None:
with Session(engine) as session:
seed_reference_data(session) # идемпотентно
_ensure_admin(session)
if __name__ == "__main__":
bootstrap()