feat: add --api flag to deploy and new web command for dashboard
This commit is contained in:
@@ -214,8 +214,11 @@ def deploy(
|
||||
no_cache: bool = typer.Option(False, "--no-cache", help="Force rebuild all images, ignoring Docker layer cache"),
|
||||
ipvlan: bool = typer.Option(False, "--ipvlan", help="Use IPvlan L2 instead of MACVLAN (required on WiFi interfaces)"),
|
||||
config_file: Optional[str] = typer.Option(None, "--config", "-c", help="Path to INI config file"),
|
||||
api: bool = typer.Option(False, "--api", help="Start the FastAPI backend to ingest and serve logs"),
|
||||
api_port: int = typer.Option(8000, "--api-port", help="Port for the backend API"),
|
||||
) -> None:
|
||||
"""Deploy deckies to the LAN."""
|
||||
import os
|
||||
if mode not in ("unihost", "swarm"):
|
||||
console.print("[red]--mode must be 'unihost' or 'swarm'[/]")
|
||||
raise typer.Exit(1)
|
||||
@@ -321,6 +324,11 @@ def deploy(
|
||||
effective_log_target = log_target
|
||||
effective_log_file = log_file
|
||||
|
||||
# Handle automatic log file for API
|
||||
if api and not effective_log_file:
|
||||
effective_log_file = os.path.join(os.getcwd(), "decnet.log")
|
||||
console.print(f"[cyan]API mode enabled: defaulting log-file to {effective_log_file}[/]")
|
||||
|
||||
config = DecnetConfig(
|
||||
mode=mode,
|
||||
interface=iface,
|
||||
@@ -341,6 +349,22 @@ def deploy(
|
||||
from decnet.deployer import deploy as _deploy
|
||||
_deploy(config, dry_run=dry_run, no_cache=no_cache)
|
||||
|
||||
if api and not dry_run:
|
||||
import subprocess
|
||||
console.print(f"[green]Starting DECNET API on port {api_port}...[/]")
|
||||
env = os.environ.copy()
|
||||
env["DECNET_INGEST_LOG_FILE"] = effective_log_file
|
||||
try:
|
||||
subprocess.Popen(
|
||||
["uvicorn", "decnet.web.api:app", "--host", "0.0.0.0", "--port", str(api_port)],
|
||||
env=env,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.STDOUT
|
||||
)
|
||||
console.print(f"[dim]API running at http://0.0.0.0:{api_port}[/]")
|
||||
except FileNotFoundError:
|
||||
console.print("[red]Failed to start API: 'uvicorn' not found. Is it installed?[/]")
|
||||
|
||||
|
||||
@app.command()
|
||||
def status() -> None:
|
||||
@@ -459,3 +483,39 @@ def list_archetypes() -> None:
|
||||
arch.description,
|
||||
)
|
||||
console.print(table)
|
||||
|
||||
|
||||
@app.command(name="web")
|
||||
def serve_web(
|
||||
web_port: int = typer.Option(5173, "--web-port", help="Port to serve the DECNET Web Dashboard"),
|
||||
) -> None:
|
||||
"""Serve the DECNET Web Dashboard frontend."""
|
||||
import http.server
|
||||
import socketserver
|
||||
from pathlib import Path
|
||||
|
||||
# Assuming decnet_web/dist is relative to the project root
|
||||
dist_dir = Path(__file__).parent.parent / "decnet_web" / "dist"
|
||||
|
||||
if not dist_dir.exists():
|
||||
console.print(f"[red]Frontend build not found at {dist_dir}. Make sure you run 'npm run build' inside 'decnet_web'.[/]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
class SPAHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
# Try to serve the requested file
|
||||
path = self.translate_path(self.path)
|
||||
if not Path(path).exists() or Path(path).is_dir():
|
||||
# If not found or is a directory, serve index.html (for React Router)
|
||||
self.path = "/index.html"
|
||||
return super().do_GET()
|
||||
|
||||
import os
|
||||
os.chdir(dist_dir)
|
||||
|
||||
with socketserver.TCPServer(("", web_port), SPAHTTPRequestHandler) as httpd:
|
||||
console.print(f"[green]Serving DECNET Web Dashboard on http://0.0.0.0:{web_port}[/]")
|
||||
try:
|
||||
httpd.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
console.print("\n[dim]Shutting down dashboard server.[/]")
|
||||
|
||||
Reference in New Issue
Block a user