78 lines
2.6 KiB
Python
78 lines
2.6 KiB
Python
"""Management CLI (``mcma``).
|
|
|
|
Commands:
|
|
* ``mcma version`` — print the backend version.
|
|
* ``mcma create-admin`` — create the first (or another) superuser. Private
|
|
instance, so there is no public sign-up — bootstrap
|
|
admins here (plan §11 step 3).
|
|
"""
|
|
|
|
import argparse
|
|
import asyncio
|
|
import getpass
|
|
|
|
from app import __version__
|
|
|
|
|
|
async def _create_admin(username: str, password: str) -> None:
|
|
from app.application.user_service import UserService
|
|
from app.core.security import Argon2PasswordHasher
|
|
from app.infrastructure.db import session_scope
|
|
from app.infrastructure.db.repositories import (
|
|
SqlAlchemyRefreshTokenRepository,
|
|
SqlAlchemyUserRepository,
|
|
)
|
|
|
|
async with session_scope() as session:
|
|
service = UserService(
|
|
users=SqlAlchemyUserRepository(session),
|
|
refresh_tokens=SqlAlchemyRefreshTokenRepository(session),
|
|
hasher=Argon2PasswordHasher(),
|
|
)
|
|
user = await service.create_user(username=username, password=password, is_superuser=True)
|
|
print(f"Created admin {user.username!r} ({user.id}).")
|
|
|
|
|
|
def _cmd_create_admin(args: argparse.Namespace) -> None:
|
|
username: str = args.username or input("Username: ").strip()
|
|
if not username:
|
|
raise SystemExit("Username is required.")
|
|
password: str = args.password or getpass.getpass("Password: ")
|
|
if len(password) < 8:
|
|
raise SystemExit("Password must be at least 8 characters.")
|
|
if args.password is None and getpass.getpass("Confirm password: ") != password:
|
|
raise SystemExit("Passwords do not match.")
|
|
|
|
from app.domain.errors import AlreadyExistsError
|
|
|
|
try:
|
|
asyncio.run(_create_admin(username, password))
|
|
except AlreadyExistsError as exc:
|
|
raise SystemExit(str(exc)) from exc
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(prog="mcma", description="mcma-backend management CLI")
|
|
sub = parser.add_subparsers(dest="command")
|
|
|
|
sub.add_parser("version", help="Print the backend version")
|
|
|
|
admin = sub.add_parser("create-admin", help="Create a superuser")
|
|
admin.add_argument("username", nargs="?", help="Username (prompted if omitted)")
|
|
admin.add_argument(
|
|
"--password",
|
|
help="Password (prompted securely if omitted; avoid on shared shells)",
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
if args.command == "version":
|
|
print(__version__)
|
|
elif args.command == "create-admin":
|
|
_cmd_create_admin(args)
|
|
else:
|
|
parser.print_help()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|