servers.py•10.4 kB
"""
API routes for server management.
"""
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from typing import Dict, Any, List, Optional
from src.database.database import get_db
from src.database import crud
from src.config.models import ServerCreate, ServerUpdate, ServerAction
router = APIRouter(
prefix="/servers",
tags=["servers"],
)
@router.get("/", response_model=List[Dict[str, Any]])
async def get_servers(section_id: Optional[int] = None, db: AsyncSession = Depends(get_db)):
"""Get all servers, optionally filtered by section."""
servers = await crud.get_servers(db, section_id)
return [
{
"id": server.id,
"name": server.name,
"section_id": server.section_id,
"description": server.description,
"runtime_definition": server.runtime_definition,
"settings": server.settings,
"status": server.status,
"process_id": server.process_id,
"created_at": server.created_at,
"updated_at": server.updated_at
}
for server in servers
]
@router.get("/{server_id}", response_model=Dict[str, Any])
async def get_server(server_id: int, db: AsyncSession = Depends(get_db)):
"""Get a server by ID."""
server = await crud.get_server_by_id(db, server_id)
if not server:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Server with ID {server_id} not found"
)
# Get section information
section = await crud.get_section_by_id(db, server.section_id)
return {
"id": server.id,
"name": server.name,
"section_id": server.section_id,
"section_name": section.name if section else None,
"description": server.description,
"runtime_definition": server.runtime_definition,
"settings": server.settings,
"status": server.status,
"process_id": server.process_id,
"created_at": server.created_at,
"updated_at": server.updated_at
}
@router.post("/", response_model=Dict[str, Any], status_code=status.HTTP_201_CREATED)
async def create_server(server_data: ServerCreate, db: AsyncSession = Depends(get_db)):
"""Create a new server."""
# Check if section exists
section = await crud.get_section_by_id(db, server_data.section_id)
if not section:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Section with ID {server_data.section_id} not found"
)
# Check if server with this name already exists in the section
existing_server = await crud.get_server_by_name(db, server_data.section_id, server_data.name)
if existing_server:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=f"Server with name '{server_data.name}' already exists in section '{section.name}'"
)
try:
server = await crud.create_server(db, server_data.dict())
return {
"id": server.id,
"name": server.name,
"section_id": server.section_id,
"section_name": section.name,
"description": server.description,
"runtime_definition": server.runtime_definition,
"settings": server.settings,
"status": server.status,
"process_id": server.process_id,
"created_at": server.created_at,
"updated_at": server.updated_at
}
except Exception as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Failed to create server: {str(e)}"
)
@router.put("/{server_id}", response_model=Dict[str, Any])
async def update_server(
server_id: int,
server_data: ServerUpdate,
db: AsyncSession = Depends(get_db)
):
"""Update a server."""
# Check if server exists
server = await crud.get_server_by_id(db, server_id)
if not server:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Server with ID {server_id} not found"
)
# If section_id is being updated, check if section exists
if server_data.section_id is not None and server_data.section_id != server.section_id:
section = await crud.get_section_by_id(db, server_data.section_id)
if not section:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Section with ID {server_data.section_id} not found"
)
# If name is being updated, check if it conflicts with existing server in the section
section_id = server_data.section_id if server_data.section_id is not None else server.section_id
if server_data.name and server_data.name != server.name:
existing_server = await crud.get_server_by_name(db, section_id, server_data.name)
if existing_server and existing_server.id != server_id:
section = await crud.get_section_by_id(db, section_id)
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=f"Server with name '{server_data.name}' already exists in section '{section.name if section else 'Unknown'}'"
)
# Check if server is running - can't update running server
if server.status == "Running":
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=f"Cannot update server '{server.name}' while it is running. Stop the server first."
)
try:
updated_server = await crud.update_server(db, server_id, server_data.dict(exclude_unset=True))
if not updated_server:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Server with ID {server_id} not found"
)
# Get section information
section = await crud.get_section_by_id(db, updated_server.section_id)
return {
"id": updated_server.id,
"name": updated_server.name,
"section_id": updated_server.section_id,
"section_name": section.name if section else None,
"description": updated_server.description,
"runtime_definition": updated_server.runtime_definition,
"settings": updated_server.settings,
"status": updated_server.status,
"process_id": updated_server.process_id,
"created_at": updated_server.created_at,
"updated_at": updated_server.updated_at
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Failed to update server: {str(e)}"
)
@router.delete("/{server_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_server(server_id: int, db: AsyncSession = Depends(get_db)):
"""Delete a server."""
# Check if server exists
server = await crud.get_server_by_id(db, server_id)
if not server:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Server with ID {server_id} not found"
)
# Check if server is running
if server.status == "Running":
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=f"Cannot delete server '{server.name}' while it is running. Stop the server first."
)
success = await crud.delete_server(db, server_id)
if not success:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to delete server with ID {server_id}"
)
@router.post("/{server_id}/{action}", response_model=Dict[str, Any])
async def server_action(
server_id: int,
action: str,
action_data: Optional[ServerAction] = None,
db: AsyncSession = Depends(get_db)
):
"""Perform an action on a server (start, stop, restart)."""
# Check if server exists
server = await crud.get_server_by_id(db, server_id)
if not server:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Server with ID {server_id} not found"
)
# Initialize orchestration engine
from src.orchestration.orchestration_engine import OrchestrationEngine
from src.config.config_manager import ConfigManager
from src.runtime.runtime_engine import RuntimeEngine
config_manager = ConfigManager()
runtime_engine = RuntimeEngine()
orchestration_engine = OrchestrationEngine(config_manager, runtime_engine)
# Set default values if action_data is not provided
force = False
timeout = 30
if action_data:
force = action_data.force or force
timeout = action_data.timeout or timeout
try:
# Perform the action
if action == "start":
success, message = await orchestration_engine.start_server(db, server_id)
elif action == "stop":
success, message = await orchestration_engine.stop_server(db, server_id, force, timeout)
elif action == "restart":
success, message = await orchestration_engine.restart_server(db, server_id, force, timeout)
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Invalid action: {action}. Must be one of: start, stop, restart"
)
if not success:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=message
)
# Get updated server status
updated_server = await crud.get_server_by_id(db, server_id)
return {
"id": server_id,
"name": server.name,
"action": action,
"status": updated_server.status if updated_server else "Unknown",
"message": message
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to perform {action} action: {str(e)}"
)