Files
RelayOps/backend/app/api/routes/servers.py
2026-06-07 02:32:28 +03:00

376 lines
12 KiB
Python

from fastapi import APIRouter, Depends, Query, status
from sqlalchemy import select
from sqlalchemy.orm import Session
from app.api.deps import get_current_user, require_roles
from app.api.deps import get_log_service
from app.api.deps import get_service_control_service
from app.api.deps import get_server_runtime_service
from app.core.errors import raise_api_error
from app.db.session import get_db
from app.models.action import Action
from app.models.managed_service import ManagedService
from app.models.server import Server
from app.models.user import User
from app.schemas.action import ActionRead
from app.schemas.logs import ServerLogRead, ServerLogSourcesRead
from app.schemas.managed_service import (
ManagedServiceCreate,
ManagedServiceRead,
ManagedServiceUpdate,
)
from app.schemas.server import (
ServerCreate,
ServerHealthRead,
ServerMetricsRead,
ServerRead,
ServerUpdate,
)
from app.services.log_service import LogService
from app.services.service_control import ServiceControlService
from app.services.server_runtime import ServerRuntimeService
router = APIRouter()
@router.get("", response_model=list[ServerRead])
def list_servers(
_: User = Depends(get_current_user),
db: Session = Depends(get_db),
) -> list[Server]:
return list(db.scalars(select(Server).order_by(Server.name)))
@router.post("", response_model=ServerRead, status_code=status.HTTP_201_CREATED)
def create_server(
payload: ServerCreate,
_: User = Depends(require_roles("admin")),
db: Session = Depends(get_db),
) -> Server:
existing_server = db.scalar(select(Server).where(Server.name == payload.name))
if existing_server is not None:
raise_api_error(
status_code=status.HTTP_409_CONFLICT,
code="server_name_taken",
message="A server with this name already exists.",
)
server = Server(**payload.model_dump())
db.add(server)
db.commit()
db.refresh(server)
return server
@router.get("/{server_id}", response_model=ServerRead)
def get_server(
server_id: int,
_: User = Depends(get_current_user),
db: Session = Depends(get_db),
) -> Server:
server = db.get(Server, server_id)
if server is None:
raise_api_error(
status_code=status.HTTP_404_NOT_FOUND,
code="server_not_found",
message="Requested server was not found.",
)
return server
@router.patch("/{server_id}", response_model=ServerRead)
def update_server(
server_id: int,
payload: ServerUpdate,
_: User = Depends(require_roles("admin")),
db: Session = Depends(get_db),
) -> Server:
server = db.get(Server, server_id)
if server is None:
raise_api_error(
status_code=status.HTTP_404_NOT_FOUND,
code="server_not_found",
message="Requested server was not found.",
)
if payload.name is not None and payload.name != server.name:
existing_server = db.scalar(select(Server).where(Server.name == payload.name))
if existing_server is not None:
raise_api_error(
status_code=status.HTTP_409_CONFLICT,
code="server_name_taken",
message="A server with this name already exists.",
)
for field, value in payload.model_dump(exclude_unset=True).items():
setattr(server, field, value)
db.add(server)
db.commit()
db.refresh(server)
return server
@router.delete("/{server_id}")
def delete_server(
server_id: int,
_: User = Depends(require_roles("admin")),
db: Session = Depends(get_db),
) -> dict[str, bool]:
server = db.get(Server, server_id)
if server is None:
raise_api_error(
status_code=status.HTTP_404_NOT_FOUND,
code="server_not_found",
message="Requested server was not found.",
)
server.is_active = False
db.add(server)
db.commit()
return {"success": True}
@router.get("/{server_id}/health", response_model=ServerHealthRead)
def get_server_health(
server_id: int,
_: User = Depends(get_current_user),
runtime_service: ServerRuntimeService = Depends(get_server_runtime_service),
db: Session = Depends(get_db),
) -> ServerHealthRead:
server = db.get(Server, server_id)
if server is None:
raise_api_error(
status_code=status.HTTP_404_NOT_FOUND,
code="server_not_found",
message="Requested server was not found.",
)
return runtime_service.get_server_health(server)
@router.get("/{server_id}/metrics", response_model=ServerMetricsRead)
def get_server_metrics(
server_id: int,
_: User = Depends(get_current_user),
runtime_service: ServerRuntimeService = Depends(get_server_runtime_service),
db: Session = Depends(get_db),
) -> ServerMetricsRead:
server = db.get(Server, server_id)
if server is None:
raise_api_error(
status_code=status.HTTP_404_NOT_FOUND,
code="server_not_found",
message="Requested server was not found.",
)
return runtime_service.get_server_metrics(server)
@router.post("/{server_id}/services", response_model=ManagedServiceRead, status_code=status.HTTP_201_CREATED)
def create_managed_service(
server_id: int,
payload: ManagedServiceCreate,
_: User = Depends(require_roles("admin")),
control_service: ServiceControlService = Depends(get_service_control_service),
db: Session = Depends(get_db),
) -> ManagedServiceRead:
server = db.get(Server, server_id)
if server is None:
raise_api_error(
status_code=status.HTTP_404_NOT_FOUND,
code="server_not_found",
message="Requested server was not found.",
)
managed_service = control_service.create_managed_service(
db,
server=server,
name=payload.name,
service_type=payload.service_type,
control_enabled=payload.control_enabled,
log_source=payload.log_source,
)
return control_service.get_managed_service(server, managed_service)
@router.get("/{server_id}/services", response_model=list[ManagedServiceRead])
def list_managed_services(
server_id: int,
_: User = Depends(get_current_user),
control_service: ServiceControlService = Depends(get_service_control_service),
db: Session = Depends(get_db),
) -> list[ManagedServiceRead]:
server = db.get(Server, server_id)
if server is None:
raise_api_error(
status_code=status.HTTP_404_NOT_FOUND,
code="server_not_found",
message="Requested server was not found.",
)
return control_service.list_managed_services(db, server)
@router.get("/{server_id}/services/{service_name}", response_model=ManagedServiceRead)
def get_managed_service(
server_id: int,
service_name: str,
_: User = Depends(get_current_user),
control_service: ServiceControlService = Depends(get_service_control_service),
db: Session = Depends(get_db),
) -> ManagedServiceRead:
server = db.get(Server, server_id)
if server is None:
raise_api_error(
status_code=status.HTTP_404_NOT_FOUND,
code="server_not_found",
message="Requested server was not found.",
)
managed_service = db.scalar(
select(ManagedService).where(
ManagedService.server_id == server.id,
ManagedService.name == service_name,
)
)
if managed_service is None:
raise_api_error(
status_code=status.HTTP_404_NOT_FOUND,
code="service_not_found",
message="Requested managed service was not found.",
)
return control_service.get_managed_service(server, managed_service)
@router.patch("/{server_id}/services/{service_name}", response_model=ManagedServiceRead)
def update_managed_service(
server_id: int,
service_name: str,
payload: ManagedServiceUpdate,
_: User = Depends(require_roles("admin")),
control_service: ServiceControlService = Depends(get_service_control_service),
db: Session = Depends(get_db),
) -> ManagedServiceRead:
server = db.get(Server, server_id)
if server is None:
raise_api_error(
status_code=status.HTTP_404_NOT_FOUND,
code="server_not_found",
message="Requested server was not found.",
)
managed_service = db.scalar(
select(ManagedService).where(
ManagedService.server_id == server.id,
ManagedService.name == service_name,
)
)
if managed_service is None:
raise_api_error(
status_code=status.HTTP_404_NOT_FOUND,
code="service_not_found",
message="Requested managed service was not found.",
)
updated_service = control_service.update_managed_service(
db,
managed_service,
name=payload.name,
service_type=payload.service_type,
control_enabled=payload.control_enabled,
log_source=payload.log_source,
update_log_source="log_source" in payload.model_fields_set,
)
return control_service.get_managed_service(server, updated_service)
@router.post("/{server_id}/services/{service_name}/{action_name}", response_model=ActionRead)
def run_service_action(
server_id: int,
service_name: str,
action_name: str,
current_user: User = Depends(require_roles("admin", "operator")),
control_service: ServiceControlService = Depends(get_service_control_service),
db: Session = Depends(get_db),
) -> Action:
server = db.get(Server, server_id)
if server is None:
raise_api_error(
status_code=status.HTTP_404_NOT_FOUND,
code="server_not_found",
message="Requested server was not found.",
)
managed_service = db.scalar(
select(ManagedService).where(
ManagedService.server_id == server.id,
ManagedService.name == service_name,
)
)
if managed_service is None:
raise_api_error(
status_code=status.HTTP_404_NOT_FOUND,
code="service_not_found",
message="Requested managed service was not found.",
)
return control_service.run_service_action(
db,
server=server,
managed_service=managed_service,
action_name=action_name,
actor=current_user,
)
@router.get("/{server_id}/logs", response_model=ServerLogSourcesRead)
def list_server_logs(
server_id: int,
_: User = Depends(get_current_user),
log_service: LogService = Depends(get_log_service),
db: Session = Depends(get_db),
) -> ServerLogSourcesRead:
server = db.get(Server, server_id)
if server is None:
raise_api_error(
status_code=status.HTTP_404_NOT_FOUND,
code="server_not_found",
message="Requested server was not found.",
)
managed_services = list(
db.scalars(
select(ManagedService)
.where(ManagedService.server_id == server.id)
.order_by(ManagedService.name)
)
)
return log_service.list_log_sources(server, managed_services)
@router.get("/{server_id}/logs/{service_name}", response_model=ServerLogRead)
def get_server_logs(
server_id: int,
service_name: str,
_: User = Depends(get_current_user),
log_service: LogService = Depends(get_log_service),
db: Session = Depends(get_db),
lines: int = Query(default=100, ge=1, le=500),
) -> ServerLogRead:
server = db.get(Server, server_id)
if server is None:
raise_api_error(
status_code=status.HTTP_404_NOT_FOUND,
code="server_not_found",
message="Requested server was not found.",
)
managed_service = db.scalar(
select(ManagedService).where(
ManagedService.server_id == server.id,
ManagedService.name == service_name,
)
)
if managed_service is None:
raise_api_error(
status_code=status.HTTP_404_NOT_FOUND,
code="service_not_found",
message="Requested managed service was not found.",
)
return log_service.read_logs(
server=server,
managed_service=managed_service,
lines=lines,
)