""" Servers API routes. Provides CRUD endpoints for Gitea server management: - POST /api/servers - Create a new server - GET /api/servers - List all servers - GET /api/servers/{id} - Get a specific server - PUT /api/servers/{id} - Update a server - DELETE /api/servers/{id} - Delete a server """ from typing import List from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from app.api.deps import get_db_session, require_auth from app.schemas.server import ServerCreate, ServerUpdate, ServerResponse from app.schemas.common import SuccessResponse from app.services.server_service import ServerService router = APIRouter(prefix="/api/servers", tags=["Servers"]) @router.post("", response_model=SuccessResponse[ServerResponse], status_code=status.HTTP_201_CREATED) def create_server( server_data: ServerCreate, db: Session = Depends(get_db_session), _auth: None = Depends(require_auth) ): """ Create a new Gitea server. The API token will be encrypted before storage. The name must be unique across all servers. A local storage path will be automatically generated based on the server name. Args: server_data: Server creation data db: Database session (injected) _auth: Authentication requirement (injected) Returns: SuccessResponse containing the created server Raises: HTTPException 400: If validation fails or name already exists HTTPException 401: If authentication fails """ service = ServerService(db) try: server = service.create_server( name=server_data.name, url=server_data.url, api_token=server_data.api_token, ssh_key_id=server_data.ssh_key_id, sync_enabled=server_data.sync_enabled, schedule_cron=server_data.schedule_cron ) return SuccessResponse( code=0, data=ServerResponse( id=server.id, name=server.name, url=server.url, ssh_key_id=server.ssh_key_id, sync_enabled=server.sync_enabled, schedule_cron=server.schedule_cron, local_path=server.local_path, status=server.status, created_at=server.created_at, updated_at=server.updated_at ), message="Server created successfully" ) except ValueError as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=str(e) ) @router.get("", response_model=SuccessResponse[List[ServerResponse]]) def list_servers( db: Session = Depends(get_db_session), _auth: None = Depends(require_auth) ): """ List all servers. Returns all servers ordered by creation time. API tokens are not included in the response. Args: db: Database session (injected) _auth: Authentication requirement (injected) Returns: SuccessResponse containing list of servers Raises: HTTPException 401: If authentication fails """ service = ServerService(db) servers = service.list_servers() return SuccessResponse( code=0, data=[ ServerResponse( id=server.id, name=server.name, url=server.url, ssh_key_id=server.ssh_key_id, sync_enabled=server.sync_enabled, schedule_cron=server.schedule_cron, local_path=server.local_path, status=server.status, created_at=server.created_at, updated_at=server.updated_at ) for server in servers ], message=f"Retrieved {len(servers)} server(s)" ) @router.get("/{server_id}", response_model=SuccessResponse[ServerResponse]) def get_server( server_id: int, db: Session = Depends(get_db_session), _auth: None = Depends(require_auth) ): """ Get a specific server by ID. Args: server_id: ID of the server db: Database session (injected) _auth: Authentication requirement (injected) Returns: SuccessResponse containing the server Raises: HTTPException 401: If authentication fails HTTPException 404: If server not found """ service = ServerService(db) server = service.get_server(server_id) if server is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Server with ID {server_id} not found" ) return SuccessResponse( code=0, data=ServerResponse( id=server.id, name=server.name, url=server.url, ssh_key_id=server.ssh_key_id, sync_enabled=server.sync_enabled, schedule_cron=server.schedule_cron, local_path=server.local_path, status=server.status, created_at=server.created_at, updated_at=server.updated_at ), message="Server retrieved successfully" ) @router.put("/{server_id}", response_model=SuccessResponse[ServerResponse]) def update_server( server_id: int, server_data: ServerUpdate, db: Session = Depends(get_db_session), _auth: None = Depends(require_auth) ): """ Update a server. Only the fields provided in the request body will be updated. If api_token is provided, it will be encrypted before storage. If name is changed, the local_path will be updated accordingly. Args: server_id: ID of the server to update server_data: Server update data (all fields optional) db: Database session (injected) _auth: Authentication requirement (injected) Returns: SuccessResponse containing the updated server Raises: HTTPException 400: If validation fails HTTPException 401: If authentication fails HTTPException 404: If server not found """ service = ServerService(db) # Build update dict from non-None fields update_data = {} if server_data.name is not None: update_data['name'] = server_data.name if server_data.url is not None: update_data['url'] = server_data.url if server_data.api_token is not None: update_data['api_token'] = server_data.api_token if server_data.ssh_key_id is not None: update_data['ssh_key_id'] = server_data.ssh_key_id if server_data.sync_enabled is not None: update_data['sync_enabled'] = server_data.sync_enabled if server_data.schedule_cron is not None: update_data['schedule_cron'] = server_data.schedule_cron if server_data.status is not None: update_data['status'] = server_data.status if not update_data: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="No fields provided for update" ) try: server = service.update_server(server_id, **update_data) return SuccessResponse( code=0, data=ServerResponse( id=server.id, name=server.name, url=server.url, ssh_key_id=server.ssh_key_id, sync_enabled=server.sync_enabled, schedule_cron=server.schedule_cron, local_path=server.local_path, status=server.status, created_at=server.created_at, updated_at=server.updated_at ), message="Server updated successfully" ) except ValueError as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=str(e) ) @router.delete("/{server_id}", response_model=SuccessResponse[dict]) def delete_server( server_id: int, db: Session = Depends(get_db_session), _auth: None = Depends(require_auth) ): """ Delete a server. Args: server_id: ID of the server to delete db: Database session (injected) _auth: Authentication requirement (injected) Returns: SuccessResponse with empty data Raises: HTTPException 401: If authentication fails HTTPException 404: If server not found """ service = ServerService(db) deleted = service.delete_server(server_id) if not deleted: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Server with ID {server_id} not found" ) return SuccessResponse( code=0, data={}, message="Server deleted successfully" )