Add admin user management

This commit is contained in:
2026-04-03 12:23:44 +00:00
parent 3d37186fa7
commit 5168e52e4b
18 changed files with 989 additions and 19 deletions
+98
View File
@@ -0,0 +1,98 @@
"""User CLI commands."""
from __future__ import annotations
import typer
from app.cli.core.utils import execute_command, render_output
from app.cli.commands.portfolio import _confirm
from app.schemas.user import CreateUserRequest, UpdateUserRequest, UserResponse
from app.services.user_service import UserService
user_app = typer.Typer(help="User management commands.")
def _with_service(ctx: typer.Context, callback):
app_context = ctx.obj
repository = app_context.repository_factory(app_context.settings)
service = UserService(repository)
try:
return execute_command(app_context.console, callback=lambda: callback(service, app_context))
finally:
if hasattr(repository, "close"):
repository.close()
def _to_user_response(user) -> UserResponse:
return UserResponse(
id=user.id,
username=user.username,
email=user.email,
created_at=user.created_at,
updated_at=user.updated_at,
)
@user_app.command("list")
def list_users(ctx: typer.Context) -> None:
users = _with_service(ctx, lambda service, app_context: [_to_user_response(user) for user in service.list()])
render_output(ctx.obj.console, ctx.obj.output_format, users, empty_message="No users found.")
@user_app.command("get")
def get_user(
ctx: typer.Context,
user_id: str = typer.Argument(..., help="User identifier."),
) -> None:
user = _with_service(ctx, lambda service, app_context: _to_user_response(service.get(user_id)))
render_output(ctx.obj.console, ctx.obj.output_format, user)
@user_app.command("create")
def create_user(
ctx: typer.Context,
username: str = typer.Option(..., help="Username."),
email: str | None = typer.Option(None, help="Email address."),
force: bool = typer.Option(False, "--force", help="Skip confirmation prompt."),
) -> None:
_confirm(force, f"Create user {username}?")
user = _with_service(
ctx,
lambda service, app_context: _to_user_response(
service.create(CreateUserRequest(username=username, email=email))
),
)
render_output(ctx.obj.console, ctx.obj.output_format, user)
@user_app.command("update")
def update_user(
ctx: typer.Context,
user_id: str = typer.Argument(..., help="User identifier."),
username: str | None = typer.Option(None, help="New username."),
email: str | None = typer.Option(None, help="New email address."),
force: bool = typer.Option(False, "--force", help="Skip confirmation prompt."),
) -> None:
_confirm(force, f"Update user {user_id}?")
user = _with_service(
ctx,
lambda service, app_context: _to_user_response(
service.update(user_id, UpdateUserRequest(username=username, email=email))
),
)
render_output(ctx.obj.console, ctx.obj.output_format, user)
@user_app.command("delete")
def delete_user(
ctx: typer.Context,
user_id: str = typer.Argument(..., help="User identifier."),
force: bool = typer.Option(False, "--force", help="Skip confirmation prompt."),
) -> None:
_confirm(force, f"Delete user {user_id}?")
_with_service(ctx, lambda service, app_context: service.delete(user_id))
if ctx.obj.output_format == "json":
render_output(ctx.obj.console, ctx.obj.output_format, {"deleted": user_id})
else:
ctx.obj.console.print(f"Deleted user: {user_id}")