66 lines
2.5 KiB
Python
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:
|
||
# В test/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()
|