dashboard-nanobot/dashboard-edge/app/api/router.py

251 lines
8.2 KiB
Python

from typing import List, Optional
from fastapi import APIRouter, Depends, File, HTTPException, Query, Request, UploadFile
from app.dependencies.auth import require_edge_auth
from app.schemas.edge import (
EdgeCommandRequest,
EdgeLogsResponse,
EdgeNativePreflightRequest,
EdgeNativePreflightResponse,
EdgeNodeHeartbeatResponse,
EdgeMonitorPacketsResponse,
EdgeMarkdownWriteRequest,
EdgeMonitorEnsureResponse,
EdgeNodeResourcesResponse,
EdgeNodeSelfResponse,
EdgeStateResponse,
EdgeStateWriteRequest,
EdgeStatusResponse,
EdgeWorkspaceSyncRequest,
)
from app.schemas.runtime import EdgeStartBotRequest
from app.services import provision_service as provision_service_module
from app.services import state_store_service as state_store_service_module
from app.services.runtime_service import edge_runtime_service
from app.services import workspace_service as workspace_service_module
router = APIRouter(dependencies=[Depends(require_edge_auth)])
@router.get("/api/edge/node/self", response_model=EdgeNodeSelfResponse)
def get_edge_node_self():
return edge_runtime_service.get_node_identity()
@router.get("/api/edge/node/resources", response_model=EdgeNodeResourcesResponse)
def get_edge_node_resources():
return edge_runtime_service.get_node_resource_summary()
@router.post("/api/edge/node/heartbeat", response_model=EdgeNodeHeartbeatResponse)
def heartbeat_edge_node():
return edge_runtime_service.heartbeat()
@router.post("/api/edge/runtime/native/preflight", response_model=EdgeNativePreflightResponse)
def native_preflight(payload: EdgeNativePreflightRequest):
return edge_runtime_service.native_preflight(
native_command=str(payload.native_command or "").strip() or None,
native_workdir=str(payload.native_workdir or "").strip() or None,
)
@router.post("/api/edge/bots/{bot_id}/start", response_model=EdgeStatusResponse)
async def start_bot(bot_id: str, payload: EdgeStartBotRequest):
return await edge_runtime_service.start_bot(bot_id=bot_id, payload=payload)
@router.post("/api/edge/bots/{bot_id}/stop", response_model=EdgeStatusResponse)
def stop_bot(bot_id: str):
return edge_runtime_service.stop_bot(bot_id=bot_id)
@router.post("/api/edge/bots/{bot_id}/command", response_model=EdgeStatusResponse)
def send_command(bot_id: str, payload: EdgeCommandRequest):
return edge_runtime_service.send_command(bot_id=bot_id, payload=payload)
@router.post("/api/edge/bots/{bot_id}/monitor/ensure", response_model=EdgeMonitorEnsureResponse)
def ensure_monitor(bot_id: str):
return edge_runtime_service.ensure_monitor(bot_id=bot_id)
@router.get("/api/edge/bots/{bot_id}/monitor/packets", response_model=EdgeMonitorPacketsResponse)
def get_monitor_packets(bot_id: str, after_seq: int = 0, limit: int = 200):
return edge_runtime_service.get_monitor_packets(bot_id=bot_id, after_seq=after_seq, limit=limit)
@router.get("/api/edge/bots/{bot_id}/logs", response_model=EdgeLogsResponse)
def get_logs(bot_id: str, tail: int = Query(300, ge=1, le=2000)):
return edge_runtime_service.get_recent_logs(bot_id=bot_id, tail=tail)
@router.get("/api/edge/bots/{bot_id}/runtime/status", response_model=EdgeStatusResponse)
def get_runtime_status(bot_id: str):
return edge_runtime_service.get_runtime_status(bot_id=bot_id)
@router.get("/api/edge/bots/{bot_id}/resources")
def get_resource_snapshot(bot_id: str):
return edge_runtime_service.get_resource_snapshot(bot_id=bot_id)
@router.post("/api/edge/bots/{bot_id}/workspace/sync", response_model=EdgeStatusResponse)
def sync_workspace(bot_id: str, payload: EdgeWorkspaceSyncRequest):
return provision_service_module.edge_provision_service.sync_bot_workspace(bot_id=bot_id, payload=payload)
@router.get("/api/edge/bots/{bot_id}/state/{state_key}", response_model=EdgeStateResponse)
def read_bot_state(bot_id: str, state_key: str, workspace_root: str | None = None):
return state_store_service_module.edge_state_store_service.read_state(
bot_id=bot_id,
state_key=state_key,
workspace_root=workspace_root,
)
@router.put("/api/edge/bots/{bot_id}/state/{state_key}", response_model=EdgeStateResponse)
def write_bot_state(bot_id: str, state_key: str, payload: EdgeStateWriteRequest):
return state_store_service_module.edge_state_store_service.write_state(
bot_id=bot_id,
state_key=state_key,
data=dict(payload.data or {}),
workspace_root=str(payload.workspace_root or "").strip() or None,
)
@router.get("/api/edge/bots/{bot_id}/workspace/tree")
def list_workspace_tree(
bot_id: str,
path: str | None = None,
recursive: bool = False,
workspace_root: str | None = None,
):
return workspace_service_module.edge_workspace_service.list_tree(
bot_id=bot_id,
path=path,
recursive=recursive,
workspace_root=workspace_root,
)
@router.get("/api/edge/bots/{bot_id}/workspace/file")
def read_workspace_file(
bot_id: str,
path: str = Query(...),
max_bytes: int = Query(200000, ge=4096, le=1000000),
workspace_root: str | None = None,
):
return workspace_service_module.edge_workspace_service.read_file(
bot_id=bot_id,
path=path,
max_bytes=max_bytes,
workspace_root=workspace_root,
)
@router.put("/api/edge/bots/{bot_id}/workspace/file/markdown")
def write_workspace_markdown(
bot_id: str,
path: str = Query(...),
payload: EdgeMarkdownWriteRequest = None,
workspace_root: str | None = None,
):
if payload is None:
raise HTTPException(status_code=400, detail="markdown payload is required")
return workspace_service_module.edge_workspace_service.write_markdown(
bot_id=bot_id,
path=path,
content=payload.content,
workspace_root=workspace_root,
)
@router.put("/api/edge/bots/{bot_id}/workspace/file/text")
def write_workspace_text(
bot_id: str,
path: str = Query(...),
payload: EdgeMarkdownWriteRequest = None,
workspace_root: str | None = None,
):
if payload is None:
raise HTTPException(status_code=400, detail="text payload is required")
return workspace_service_module.edge_workspace_service.write_text_file(
bot_id=bot_id,
path=path,
content=payload.content,
workspace_root=workspace_root,
)
@router.post("/api/edge/bots/{bot_id}/workspace/upload")
async def upload_workspace_files(
bot_id: str,
files: List[UploadFile] = File(...),
path: Optional[str] = None,
workspace_root: str | None = None,
):
return await workspace_service_module.edge_workspace_service.upload_files(
bot_id=bot_id,
files=files,
path=path,
workspace_root=workspace_root,
)
@router.delete("/api/edge/bots/{bot_id}/workspace/file")
def delete_workspace_path(
bot_id: str,
path: str = Query(...),
workspace_root: str | None = None,
):
return workspace_service_module.edge_workspace_service.delete_path(
bot_id=bot_id,
path=path,
workspace_root=workspace_root,
)
@router.get("/api/edge/bots/{bot_id}/workspace/download")
def download_workspace_file(
bot_id: str,
path: str = Query(...),
download: bool = False,
request: Request = None,
workspace_root: str | None = None,
):
return workspace_service_module.edge_workspace_service.serve_file(
bot_id=bot_id,
path=path,
download=download,
request=request,
workspace_root=workspace_root,
)
@router.get("/api/edge/bots/{bot_id}/workspace/raw/{path:path}")
def raw_workspace_file(
bot_id: str,
path: str,
download: bool = False,
request: Request = None,
workspace_root: str | None = None,
):
return workspace_service_module.edge_workspace_service.serve_file(
bot_id=bot_id,
path=path,
download=download,
request=request,
workspace_root=workspace_root,
)
@router.post("/api/edge/bots/{bot_id}/workspace/purge", response_model=EdgeStatusResponse)
def purge_workspace(bot_id: str, workspace_root: str | None = None):
result = workspace_service_module.edge_workspace_service.purge_bot_workspace(
bot_id=bot_id,
workspace_root=workspace_root,
)
return EdgeStatusResponse(status="deleted" if bool(result.get("deleted")) else "not_found")