"""Server inventory routes.""" from datetime import UTC, datetime from fastapi import APIRouter, Depends, Request from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.api.deps import client_ip, get_current_user from app.core.database import get_db from app.core.exceptions import NotFoundError from app.models.credential import Credential from app.models.server import Server, ServerType from app.models.user import User from app.schemas.server import HealthCheckResult, ServerCreate, ServerRead, ServerUpdate from app.services.audit import AuditService from app.services.cau import CAUService from app.services.job_runner import JobRunner from app.services.ssh import SSHService from app.services.winrm import WinRMService router = APIRouter() @router.get("", response_model=list[ServerRead]) async def list_servers( db: AsyncSession = Depends(get_db), _user: User = Depends(get_current_user), ) -> list[Server]: result = await db.execute(select(Server).order_by(Server.name)) return list(result.scalars().all()) @router.post("", response_model=ServerRead, status_code=201) async def create_server( payload: ServerCreate, request: Request, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), ) -> Server: server = Server(**payload.model_dump()) db.add(server) await db.flush() await AuditService(db).log( username=user.username, action="server.create", target=server.name, ip_address=client_ip(request), ) return server @router.get("/{server_id}", response_model=ServerRead) async def get_server( server_id: int, db: AsyncSession = Depends(get_db), _user: User = Depends(get_current_user), ) -> Server: server = await db.get(Server, server_id) if not server: raise NotFoundError("Server nicht gefunden") return server @router.patch("/{server_id}", response_model=ServerRead) async def update_server( server_id: int, payload: ServerUpdate, request: Request, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), ) -> Server: server = await db.get(Server, server_id) if not server: raise NotFoundError("Server nicht gefunden") for field, value in payload.model_dump(exclude_unset=True).items(): setattr(server, field, value) await AuditService(db).log( username=user.username, action="server.update", target=server.name, ip_address=client_ip(request), ) return server @router.delete("/{server_id}", status_code=204) async def delete_server( server_id: int, request: Request, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), ) -> None: server = await db.get(Server, server_id) if not server: raise NotFoundError("Server nicht gefunden") await AuditService(db).log( username=user.username, action="server.delete", target=server.name, ip_address=client_ip(request), ) await db.delete(server) @router.get("/{server_id}/health", response_model=HealthCheckResult) async def check_server_health( server_id: int, db: AsyncSession = Depends(get_db), _user: User = Depends(get_current_user), ) -> HealthCheckResult: server = await db.get(Server, server_id) if not server: raise NotFoundError("Server nicht gefunden") credential = await db.get(Credential, server.credential_id) if server.credential_id else None if server.type == ServerType.LINUX: service = SSHService( server.hostname, port=server.port, credentials=JobRunner._ssh_creds(credential), ) elif server.type == ServerType.CAU_CLUSTER: cau = CAUService( server.hostname, access_node=server.hostname, port=server.port, credentials=JobRunner._winrm_creds(credential), ) ok, message = await cau.test_cluster() server.last_health_at = datetime.now(UTC) server.last_health_ok = ok return HealthCheckResult( server_id=server.id, ok=ok, message=message, checked_at=server.last_health_at, ) else: service = WinRMService( server.hostname, port=server.port, credentials=JobRunner._winrm_creds(credential), ) started = datetime.now(UTC) ok, message = await service.test_connection() latency_ms = (datetime.now(UTC) - started).total_seconds() * 1000 server.last_health_at = datetime.now(UTC) server.last_health_ok = ok return HealthCheckResult( server_id=server.id, ok=ok, latency_ms=round(latency_ms, 1), message=message, checked_at=server.last_health_at, )