letsbe-orchestrator/app/routes/env.py

159 lines
4.4 KiB
Python

"""Env management endpoints for creating ENV_INSPECT and ENV_UPDATE tasks."""
import uuid
from fastapi import APIRouter, HTTPException, status
from sqlalchemy import select
from app.db import AsyncSessionDep
from app.models.agent import Agent
from app.models.task import Task, TaskStatus
from app.models.tenant import Tenant
from app.schemas.env import EnvInspectRequest, EnvUpdateRequest
from app.schemas.task import TaskResponse
router = APIRouter(prefix="/agents/{agent_id}/env", tags=["Env Management"])
# --- Helper functions ---
async def get_tenant_by_id(db: AsyncSessionDep, tenant_id: uuid.UUID) -> Tenant | None:
"""Retrieve a tenant by ID."""
result = await db.execute(select(Tenant).where(Tenant.id == tenant_id))
return result.scalar_one_or_none()
async def get_agent_by_id(db: AsyncSessionDep, agent_id: uuid.UUID) -> Agent | None:
"""Retrieve an agent by ID."""
result = await db.execute(select(Agent).where(Agent.id == agent_id))
return result.scalar_one_or_none()
# --- Route handlers ---
@router.post(
"/inspect",
response_model=TaskResponse,
status_code=status.HTTP_201_CREATED,
)
async def inspect_env(
agent_id: uuid.UUID,
request: EnvInspectRequest,
db: AsyncSessionDep,
) -> Task:
"""
Create an ENV_INSPECT task to read env file contents.
The SysAdmin Agent will execute this task and return the env file
key-value pairs in the task result.
## Request Body
- **tenant_id**: UUID of the tenant
- **path**: Path to the env file (e.g., `/opt/letsbe/env/chatwoot.env`)
- **keys**: Optional list of specific keys to inspect (returns all if omitted)
## Response
Returns the created Task with type="ENV_INSPECT" and status="pending".
"""
# Validate tenant exists
tenant = await get_tenant_by_id(db, request.tenant_id)
if tenant is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Tenant {request.tenant_id} not found",
)
# Validate agent exists
agent = await get_agent_by_id(db, agent_id)
if agent is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Agent {agent_id} not found",
)
# Build payload
payload: dict = {"path": request.path}
if request.keys is not None:
payload["keys"] = request.keys
# Create the task
task = Task(
tenant_id=request.tenant_id,
agent_id=agent_id,
type="ENV_INSPECT",
payload=payload,
status=TaskStatus.PENDING.value,
)
db.add(task)
await db.commit()
await db.refresh(task)
return task
@router.post(
"/update",
response_model=TaskResponse,
status_code=status.HTTP_201_CREATED,
)
async def update_env(
agent_id: uuid.UUID,
request: EnvUpdateRequest,
db: AsyncSessionDep,
) -> Task:
"""
Create an ENV_UPDATE task to modify env file contents.
The SysAdmin Agent will execute this task to update or remove
key-value pairs in the specified env file.
## Request Body
- **tenant_id**: UUID of the tenant
- **path**: Path to the env file (e.g., `/opt/letsbe/env/chatwoot.env`)
- **updates**: Optional dict of key-value pairs to set or update
- **remove_keys**: Optional list of keys to remove from the env file
## Response
Returns the created Task with type="ENV_UPDATE" and status="pending".
"""
# Validate tenant exists
tenant = await get_tenant_by_id(db, request.tenant_id)
if tenant is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Tenant {request.tenant_id} not found",
)
# Validate agent exists
agent = await get_agent_by_id(db, agent_id)
if agent is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Agent {agent_id} not found",
)
# Build payload
payload: dict = {"path": request.path}
if request.updates is not None:
payload["updates"] = request.updates
if request.remove_keys is not None:
payload["remove_keys"] = request.remove_keys
# Create the task
task = Task(
tenant_id=request.tenant_id,
agent_id=agent_id,
type="ENV_UPDATE",
payload=payload,
status=TaskStatus.PENDING.value,
)
db.add(task)
await db.commit()
await db.refresh(task)
return task