"""Служебные команды. python -m app.cli bootstrap # создать первого администратора python -m app.cli gen-keys # сгенерировать SECRET_KEY и ENCRYPTION_KEY """ from __future__ import annotations import argparse import asyncio import secrets import sys from sqlalchemy import select from app.core.config import settings from app.core.security import hash_password from app.db.models.user import User, UserRole from app.db.session import session_scope async def bootstrap() -> int: """Создаёт первого администратора из переменных окружения. Идемпотентна: повторный запуск ничего не меняет и пароль не сбрасывает. """ if not settings.first_admin_password: print("FIRST_ADMIN_PASSWORD не задано в .env", file=sys.stderr) return 1 email = settings.first_admin_email.strip().lower() async with session_scope() as session: existing = await session.scalar(select(User).where(User.email == email)) if existing is not None: print(f"Користувач {email} уже існує — нічого не змінено.") return 0 session.add( User( email=email, full_name=settings.first_admin_name, password_hash=hash_password(settings.first_admin_password), role=UserRole.ADMIN, ) ) print(f"Адміністратора {email} створено.") print("Змініть пароль після першого входу та приберіть FIRST_ADMIN_PASSWORD з .env.") return 0 def gen_keys() -> int: from cryptography.fernet import Fernet print(f"SECRET_KEY={secrets.token_urlsafe(64)}") print(f"ENCRYPTION_KEY={Fernet.generate_key().decode()}") return 0 def main() -> int: parser = argparse.ArgumentParser(prog="app.cli", description="Службові команди lux_fiscal") parser.add_argument("command", choices=["bootstrap", "gen-keys"]) args = parser.parse_args() if args.command == "bootstrap": return asyncio.run(bootstrap()) return gen_keys() if __name__ == "__main__": raise SystemExit(main())