Spaces:
Runtime error
Runtime error
Silicon Valley - Admin
Enhance server.py with CORS support, versioning, and health check endpoints
49a61bb
# server.py | |
from dataclasses import dataclass, asdict | |
import secrets | |
import logging | |
import asyncio | |
import json | |
from typing import Tuple | |
from quart import Quart, websocket, request | |
from quart_schema import QuartSchema, validate_request, validate_response | |
from quart_cors import cors | |
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware | |
from broker import SessionBroker, SessionDoesNotExist, ClientRequest, ClientResponse, ClientError | |
# Configuración | |
VERSION = "1.0.0" # Versión de la API | |
TIMEOUT: int = 40 | |
LOG_LEVEL: int = logging.DEBUG | |
TRUSTED_HOSTS: list[str] = ["127.0.0.1", "10.16.38.136", "10.16.3.13", "10.16.13.73"] | |
# Create app | |
app = Quart(__name__) | |
app = cors(app, | |
allow_origin=["https://*.hf.space", "https://*.huggingface.co"], | |
allow_methods=["GET", "POST", "OPTIONS"], | |
allow_headers=["Content-Type"], | |
max_age=3600 | |
) | |
QuartSchema(app) | |
app.asgi_app = ProxyHeadersMiddleware(app.asgi_app, trusted_hosts=TRUSTED_HOSTS) | |
app.logger.setLevel(LOG_LEVEL) | |
broker = SessionBroker() | |
# Modelos de datos | |
class Status: | |
status: str | |
version: str | |
class Session: | |
session_id: str | |
class Command: | |
session_id: str | |
command: str | |
class Read: | |
session_id: str | |
path: str | |
class Write: | |
session_id: str | |
path: str | |
content: str | |
class CommandResponse: | |
return_code: int | |
stdout: str | |
stderr: str | |
class ReadResponse: | |
content: str | |
class WriteResponse: | |
size: int | |
class ErrorResponse: | |
error: str | |
# Rutas API | |
async def status() -> Status: | |
return Status(status="OK", version=VERSION) | |
async def session_handler(): | |
session_id = secrets.token_hex() | |
app.logger.info(f"{websocket.remote_addr} - NEW SESSION - {session_id}") | |
await websocket.send_as(Session(session_id=session_id), Session) | |
task = None | |
try: | |
task = asyncio.ensure_future(_receive(session_id)) | |
async for request in broker.subscribe(session_id): | |
app.logger.info(f"{websocket.remote_addr} - REQUEST - {session_id} - {json.dumps(asdict(request))}") | |
await websocket.send_as(request, ClientRequest) | |
finally: | |
if task is not None: | |
task.cancel() | |
await task | |
async def _receive(session_id: str) -> None: | |
while True: | |
response = await websocket.receive_as(ClientResponse) | |
app.logger.info(f"{websocket.remote_addr} - RESPONSE - {session_id} - {json.dumps(asdict(response))}") | |
await broker.receive_response(session_id, response) | |
async def command(data: Command) -> Tuple[CommandResponse | ErrorResponse, int]: | |
try: | |
response = CommandResponse(**await broker.send_request( | |
data.session_id, | |
{'action': 'command', 'command': data.command}, | |
timeout=TIMEOUT)) | |
return response, 200 | |
except SessionDoesNotExist: | |
app.logger.warning(f"{request.remote_addr} - INVALID SESSION ID - {repr(data.session_id)}") | |
return ErrorResponse('Session does not exist.'), 500 | |
except ClientError as e: | |
return ErrorResponse(e.message), 500 | |
except asyncio.TimeoutError: | |
return ErrorResponse('Timeout when waiting for client.'), 500 | |
async def read(data: Read) -> Tuple[ReadResponse | ErrorResponse, int]: | |
try: | |
response = ReadResponse(**await broker.send_request( | |
data.session_id, | |
{'action': 'read', 'path': data.path}, | |
timeout=TIMEOUT)) | |
return response, 200 | |
except SessionDoesNotExist: | |
app.logger.warning(f"{request.remote_addr} - INVALID SESSION ID - {repr(data.session_id)}") | |
return ErrorResponse('Session does not exist.'), 500 | |
except ClientError as e: | |
return ErrorResponse(e.message), 500 | |
except asyncio.TimeoutError: | |
return ErrorResponse('Timeout when waiting for client.'), 500 | |
async def write(data: Write) -> Tuple[WriteResponse | ErrorResponse, int]: | |
try: | |
response = WriteResponse(**await broker.send_request( | |
data.session_id, | |
{'action': 'write', 'path': data.path, 'content': data.content}, | |
timeout=TIMEOUT)) | |
return response, 200 | |
except SessionDoesNotExist: | |
app.logger.warning(f"{request.remote_addr} - INVALID SESSION ID - {repr(data.session_id)}") | |
return ErrorResponse('Session does not exist.'), 500 | |
except ClientError as e: | |
return ErrorResponse(e.message), 500 | |
except asyncio.TimeoutError: | |
return ErrorResponse('Timeout when waiting for client.'), 500 | |
# Agregar un endpoint de health check y root | |
async def root(): | |
return {"message": "Kaio API Server", "version": VERSION} | |
async def health_check(): | |
return {"status": "healthy"} | |
def run(): | |
app.run(host='0.0.0.0', port=7860) | |
if __name__ == "__main__": | |
run() |