refactor: modularize API routes into separate files and clean up dependencies

This commit is contained in:
2026-04-09 11:58:57 -04:00
parent 551664bc43
commit 29a2cf2738
45 changed files with 541 additions and 344 deletions

View File

@@ -0,0 +1,80 @@
import logging
import os
from fastapi import APIRouter, Depends, HTTPException
from decnet.config import DEFAULT_MUTATE_INTERVAL, DecnetConfig, load_state
from decnet.deployer import deploy as _deploy
from decnet.ini_loader import load_ini_from_string
from decnet.network import detect_interface, detect_subnet, get_host_ip
from decnet.web.dependencies import get_current_user
from decnet.web.models import DeployIniRequest
router = APIRouter()
@router.post("/deckies/deploy", tags=["Fleet Management"])
async def api_deploy_deckies(req: DeployIniRequest, current_user: str = Depends(get_current_user)) -> dict[str, str]:
from decnet.cli import _build_deckies_from_ini
try:
ini = load_ini_from_string(req.ini_content)
except Exception as e:
raise HTTPException(status_code=400, detail=f"Failed to parse INI: {e}")
state = load_state()
ingest_log_file = os.environ.get("DECNET_INGEST_LOG_FILE")
if state:
config, _ = state
subnet_cidr = ini.subnet or config.subnet
gateway = ini.gateway or config.gateway
host_ip = get_host_ip(config.interface)
randomize_services = False
# Always sync config log_file with current API ingestion target
if ingest_log_file:
config.log_file = ingest_log_file
else:
# If no state exists, we need to infer network details
iface = ini.interface or detect_interface()
subnet_cidr, gateway = ini.subnet, ini.gateway
if not subnet_cidr or not gateway:
detected_subnet, detected_gateway = detect_subnet(iface)
subnet_cidr = subnet_cidr or detected_subnet
gateway = gateway or detected_gateway
host_ip = get_host_ip(iface)
randomize_services = False
config = DecnetConfig(
mode="unihost",
interface=iface,
subnet=subnet_cidr,
gateway=gateway,
deckies=[],
log_target=ini.log_target,
log_file=ingest_log_file,
ipvlan=False,
mutate_interval=ini.mutate_interval or DEFAULT_MUTATE_INTERVAL
)
try:
new_decky_configs = _build_deckies_from_ini(
ini, subnet_cidr, gateway, host_ip, randomize_services, cli_mutate_interval=None
)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
# Merge deckies
existing_deckies_map = {d.name: d for d in config.deckies}
for new_decky in new_decky_configs:
existing_deckies_map[new_decky.name] = new_decky
config.deckies = list(existing_deckies_map.values())
# We call deploy(config) which regenerates docker-compose and runs `up -d --remove-orphans`.
try:
_deploy(config)
except Exception as e:
logging.getLogger("decnet.web.api").error(f"Deployment failed: {e}")
raise HTTPException(status_code=500, detail=f"Deployment failed: {e}")
return {"message": "Deckies deployed successfully"}

View File

@@ -0,0 +1,12 @@
from typing import Any
from fastapi import APIRouter, Depends
from decnet.web.dependencies import get_current_user, repo
router = APIRouter()
@router.get("/deckies", tags=["Fleet Management"])
async def get_deckies(current_user: str = Depends(get_current_user)) -> list[dict[str, Any]]:
return await repo.get_deckies()

View File

@@ -0,0 +1,14 @@
from fastapi import APIRouter, Depends, HTTPException
from decnet.mutator import mutate_decky
from decnet.web.dependencies import get_current_user
router = APIRouter()
@router.post("/deckies/{decky_name}/mutate", tags=["Fleet Management"])
async def api_mutate_decky(decky_name: str, current_user: str = Depends(get_current_user)) -> dict[str, str]:
success = mutate_decky(decky_name)
if success:
return {"message": f"Successfully mutated {decky_name}"}
raise HTTPException(status_code=404, detail=f"Decky {decky_name} not found or failed to mutate")

View File

@@ -0,0 +1,21 @@
from fastapi import APIRouter, Depends, HTTPException
from decnet.config import load_state, save_state
from decnet.web.dependencies import get_current_user
from decnet.web.models import MutateIntervalRequest
router = APIRouter()
@router.put("/deckies/{decky_name}/mutate-interval", tags=["Fleet Management"])
async def api_update_mutate_interval(decky_name: str, req: MutateIntervalRequest, current_user: str = Depends(get_current_user)) -> dict[str, str]:
state = load_state()
if not state:
raise HTTPException(status_code=500, detail="No active deployment")
config, compose_path = state
decky = next((d for d in config.deckies if d.name == decky_name), None)
if not decky:
raise HTTPException(status_code=404, detail="Decky not found")
decky.mutate_interval = req.mutate_interval
save_state(config, compose_path)
return {"message": "Mutation interval updated"}