mirror of
https://github.com/furyhawk/agent_delta.git
synced 2026-07-21 10:15:36 +00:00
330 lines
9.0 KiB
Python
330 lines
9.0 KiB
Python
"""Project management CLI."""
|
|
|
|
import click
|
|
from tabulate import tabulate
|
|
|
|
|
|
@click.group()
|
|
@click.version_option(version="0.1.0", prog_name="agent_delta")
|
|
def cli():
|
|
"""agent_delta management CLI."""
|
|
pass
|
|
|
|
|
|
# === Server Commands ===
|
|
@cli.group("server")
|
|
def server_cli():
|
|
"""Server commands."""
|
|
pass
|
|
|
|
|
|
@server_cli.command("run")
|
|
@click.option("--host", default="0.0.0.0", help="Host to bind to")
|
|
@click.option("--port", default=8000, type=int, help="Port to bind to")
|
|
@click.option("--reload", is_flag=True, help="Enable auto-reload")
|
|
def server_run(host: str, port: int, reload: bool):
|
|
"""Run the development server."""
|
|
import uvicorn
|
|
|
|
uvicorn.run(
|
|
"app.main:app",
|
|
host=host,
|
|
port=port,
|
|
reload=reload,
|
|
)
|
|
|
|
|
|
@server_cli.command("routes")
|
|
def server_routes():
|
|
"""Show all registered routes."""
|
|
from app.main import app
|
|
|
|
routes = []
|
|
for route in app.routes:
|
|
if hasattr(route, "methods"):
|
|
for method in route.methods - {"HEAD", "OPTIONS"}:
|
|
routes.append([method, route.path, getattr(route, "name", "-")])
|
|
|
|
click.echo(tabulate(routes, headers=["Method", "Path", "Name"]))
|
|
|
|
|
|
# === Database Commands ===
|
|
@cli.group("db")
|
|
def db_cli():
|
|
"""Database commands."""
|
|
pass
|
|
|
|
|
|
@db_cli.command("init")
|
|
def db_init():
|
|
"""Initialize the database (run all migrations)."""
|
|
from alembic.config import Config
|
|
|
|
from alembic import command
|
|
|
|
click.echo("Initializing database...")
|
|
alembic_cfg = Config("alembic.ini")
|
|
command.upgrade(alembic_cfg, "head")
|
|
click.secho("Database initialized.", fg="green")
|
|
|
|
|
|
@db_cli.command("migrate")
|
|
@click.option("-m", "--message", required=True, help="Migration message")
|
|
def db_migrate(message: str):
|
|
"""Create a new migration."""
|
|
from alembic.config import Config
|
|
|
|
from alembic import command
|
|
|
|
alembic_cfg = Config("alembic.ini")
|
|
command.revision(alembic_cfg, message=message, autogenerate=True)
|
|
click.secho(f"Migration created: {message}", fg="green")
|
|
|
|
|
|
@db_cli.command("upgrade")
|
|
@click.option("--revision", default="head", help="Revision to upgrade to")
|
|
def db_upgrade(revision: str):
|
|
"""Run database migrations."""
|
|
from alembic.config import Config
|
|
|
|
from alembic import command
|
|
|
|
alembic_cfg = Config("alembic.ini")
|
|
command.upgrade(alembic_cfg, revision)
|
|
click.secho(f"Upgraded to: {revision}", fg="green")
|
|
|
|
|
|
@db_cli.command("downgrade")
|
|
@click.option("--revision", default="-1", help="Revision to downgrade to")
|
|
def db_downgrade(revision: str):
|
|
"""Rollback database migrations."""
|
|
from alembic.config import Config
|
|
|
|
from alembic import command
|
|
|
|
alembic_cfg = Config("alembic.ini")
|
|
command.downgrade(alembic_cfg, revision)
|
|
click.secho(f"Downgraded to: {revision}", fg="green")
|
|
|
|
|
|
@db_cli.command("current")
|
|
def db_current():
|
|
"""Show current migration revision."""
|
|
from alembic.config import Config
|
|
|
|
from alembic import command
|
|
|
|
alembic_cfg = Config("alembic.ini")
|
|
command.current(alembic_cfg)
|
|
|
|
|
|
@db_cli.command("history")
|
|
def db_history():
|
|
"""Show migration history."""
|
|
from alembic.config import Config
|
|
|
|
from alembic import command
|
|
|
|
alembic_cfg = Config("alembic.ini")
|
|
command.history(alembic_cfg)
|
|
|
|
|
|
# === Taskiq Commands ===
|
|
@cli.group("taskiq")
|
|
def taskiq_cli():
|
|
"""Taskiq worker commands."""
|
|
pass
|
|
|
|
|
|
@taskiq_cli.command("worker")
|
|
@click.option("--workers", default=2, type=int, help="Number of workers")
|
|
@click.option("--reload", is_flag=True, help="Enable auto-reload for development")
|
|
def taskiq_worker(workers: int, reload: bool):
|
|
"""Start Taskiq worker."""
|
|
import subprocess
|
|
|
|
cmd = [
|
|
"taskiq",
|
|
"worker",
|
|
"app.worker.taskiq_app:broker",
|
|
f"--workers={workers}",
|
|
]
|
|
if reload:
|
|
cmd.append("--reload")
|
|
subprocess.run(cmd)
|
|
|
|
|
|
@taskiq_cli.command("scheduler")
|
|
def taskiq_scheduler():
|
|
"""Start Taskiq scheduler for periodic tasks."""
|
|
import subprocess
|
|
|
|
subprocess.run(
|
|
[
|
|
"taskiq",
|
|
"scheduler",
|
|
"app.worker.taskiq_app:scheduler",
|
|
]
|
|
)
|
|
|
|
|
|
# === User Commands ===
|
|
@cli.group("user")
|
|
def user_cli():
|
|
"""User management commands."""
|
|
pass
|
|
|
|
|
|
@user_cli.command("create")
|
|
@click.option("--email", prompt=True, help="User email")
|
|
@click.option(
|
|
"--password", prompt=True, hide_input=True, confirmation_prompt=True, help="User password"
|
|
)
|
|
@click.option("--role", type=click.Choice(["user", "admin"]), default="user", help="User role")
|
|
@click.option("--superuser", is_flag=True, default=False, help="Create as superuser")
|
|
def user_create(email: str, password: str, role: str, superuser: bool):
|
|
"""Create a new user."""
|
|
import asyncio
|
|
|
|
from app.core.exceptions import AlreadyExistsError
|
|
from app.db.models.user import UserRole
|
|
from app.db.session import async_session_maker
|
|
from app.schemas.user import UserCreate
|
|
from app.services.user import UserService
|
|
|
|
async def _create():
|
|
async with async_session_maker() as session:
|
|
user_service = UserService(session)
|
|
try:
|
|
user_in = UserCreate(email=email, password=password, role=UserRole(role))
|
|
user = await user_service.register(user_in)
|
|
|
|
if superuser:
|
|
user.role = UserRole.ADMIN.value
|
|
session.add(user)
|
|
|
|
await session.commit()
|
|
return user
|
|
except AlreadyExistsError:
|
|
click.secho(f"User already exists: {email}", fg="red")
|
|
return None
|
|
|
|
user = asyncio.run(_create())
|
|
if user:
|
|
click.secho(f"User created: {user.email} (role: {user.role})", fg="green")
|
|
|
|
|
|
@user_cli.command("create-admin")
|
|
@click.option("--email", prompt=True, help="Admin email")
|
|
@click.option(
|
|
"--password", prompt=True, hide_input=True, confirmation_prompt=True, help="Admin password"
|
|
)
|
|
def user_create_admin(email: str, password: str):
|
|
"""Create an admin user.
|
|
|
|
This is a shortcut for creating a user with admin role and superuser privileges.
|
|
Use this to create the initial admin account after setting up the database.
|
|
"""
|
|
import asyncio
|
|
|
|
from app.core.exceptions import AlreadyExistsError
|
|
from app.db.models.user import UserRole
|
|
from app.db.session import async_session_maker
|
|
from app.schemas.user import UserCreate
|
|
from app.services.user import UserService
|
|
|
|
async def _create():
|
|
async with async_session_maker() as session:
|
|
user_service = UserService(session)
|
|
try:
|
|
user_in = UserCreate(email=email, password=password, role=UserRole.ADMIN)
|
|
user = await user_service.register(user_in)
|
|
|
|
session.add(user)
|
|
|
|
await session.commit()
|
|
return user
|
|
except AlreadyExistsError:
|
|
click.secho(f"User already exists: {email}", fg="red")
|
|
return None
|
|
|
|
user = asyncio.run(_create())
|
|
if user:
|
|
click.secho(f"Admin user created: {user.email}", fg="green")
|
|
click.echo("This user has admin role and superuser privileges.")
|
|
|
|
|
|
@user_cli.command("set-role")
|
|
@click.argument("email")
|
|
@click.option("--role", type=click.Choice(["user", "admin"]), required=True, help="New role")
|
|
def user_set_role(email: str, role: str):
|
|
"""Change a user's role."""
|
|
import asyncio
|
|
|
|
from app.core.exceptions import NotFoundError
|
|
from app.db.models.user import UserRole
|
|
from app.db.session import async_session_maker
|
|
from app.services.user import UserService
|
|
|
|
async def _update():
|
|
async with async_session_maker() as session:
|
|
user_service = UserService(session)
|
|
try:
|
|
user = await user_service.get_by_email(email)
|
|
user.role = UserRole(role).value
|
|
session.add(user)
|
|
await session.commit()
|
|
return user
|
|
except NotFoundError:
|
|
click.secho(f"User not found: {email}", fg="red")
|
|
return None
|
|
|
|
user = asyncio.run(_update())
|
|
if user:
|
|
click.secho(f"User {email} role updated to: {role}", fg="green")
|
|
|
|
|
|
@user_cli.command("list")
|
|
def user_list():
|
|
"""List all users."""
|
|
import asyncio
|
|
|
|
from app.db.session import async_session_maker
|
|
from app.services.user import UserService
|
|
|
|
async def _list():
|
|
async with async_session_maker() as session:
|
|
user_service = UserService(session)
|
|
return await user_service.get_multi()
|
|
|
|
users = asyncio.run(_list())
|
|
|
|
if not users:
|
|
click.echo("No users found.")
|
|
return
|
|
|
|
table = [[u.id, u.email, u.role, u.is_active] for u in users]
|
|
click.echo(tabulate(table, headers=["ID", "Email", "Role", "Active", "Superuser"]))
|
|
|
|
|
|
# === Custom Commands ===
|
|
@cli.group("cmd")
|
|
def cmd_cli():
|
|
"""Custom commands."""
|
|
pass
|
|
|
|
|
|
# Register all custom commands from app/commands/
|
|
from app.commands import register_commands
|
|
|
|
register_commands(cmd_cli)
|
|
|
|
|
|
def main():
|
|
"""Main entry point."""
|
|
cli()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|