"""Command line entry points.""" from __future__ import annotations import secrets as secrets_module import typer import uvicorn from sqlalchemy import func, select from lembas import __version__ from lembas.config import settings app = typer.Typer( help="LLeMbas - a Middle-earth themed web UI for your language models.", no_args_is_help=True, add_completion=False, ) @app.command() def serve( host: str = typer.Option(None, help="Bind address. Defaults to LEMBAS_HOST."), port: int = typer.Option(None, help="Port. Defaults to LEMBAS_PORT."), reload: bool = typer.Option(None, "--reload/--no-reload", help="Autoreload on change."), ) -> None: """Run the web server.""" uvicorn.run( "lembas.main:app", host=host or settings.host, port=port or settings.port, reload=settings.reload if reload is None else reload, log_level=settings.log_level, # Access logs duplicate what the application already logs and drown out # anything useful during development. access_log=settings.log_level == "debug", ) @app.command("create-admin") def create_admin( email: str = typer.Option(..., prompt=True), name: str = typer.Option(..., prompt=True), password: str = typer.Option(..., prompt=True, hide_input=True, confirmation_prompt=True), ) -> None: """Create an administrator, or promote an existing account to one. The web sign-up already makes the first account an admin. This is the way back in when that account is lost, or when scripting a deployment. """ from lembas.db.models import ROLE_ADMIN, User from lembas.db.session import init_db, session_scope from lembas.security.passwords import hash_password, validate_password if (problem := validate_password(password)) is not None: typer.secho(problem, fg=typer.colors.RED) raise typer.Exit(1) init_db() with session_scope() as db: existing = db.scalar(select(User).where(User.email == email.strip().lower())) if existing is not None: existing.role = ROLE_ADMIN existing.password_hash = hash_password(password) existing.active = True typer.secho(f"Promoted {existing.email} to administrator.", fg=typer.colors.GREEN) return db.add( User( email=email.strip().lower(), name=name.strip(), password_hash=hash_password(password), role=ROLE_ADMIN, ) ) typer.secho(f"Created administrator {email}.", fg=typer.colors.GREEN) @app.command("secret-key") def secret_key() -> None: """Print a fresh value for LEMBAS_SECRET_KEY.""" typer.echo(secrets_module.token_urlsafe(48)) @app.command() def info() -> None: """Show where this instance keeps its data and what is configured.""" from lembas.db.models import Chat, Connection, User from lembas.db.session import init_db, session_scope init_db() typer.echo(f"LLeMbas {__version__}") typer.echo(f" data directory : {settings.data_dir.resolve()}") typer.echo(f" database : {settings.db_path.resolve()}") typer.echo(f" bind : {settings.host}:{settings.port}") typer.echo(f" default theme : {settings.default_theme}") typer.echo(f" signup open : {settings.allow_signup}") if settings.secret_key_is_ephemeral: typer.secho( " secret key : GENERATED (set LEMBAS_SECRET_KEY for a real install)", fg=typer.colors.YELLOW, ) with session_scope() as db: for label, model in (("users", User), ("connections", Connection), ("chats", Chat)): count = db.scalar(select(func.count()).select_from(model)) typer.echo(f" {label:<15}: {count}") if __name__ == "__main__": app()