Spaces:
Runtime error
Runtime error
File size: 4,852 Bytes
dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# server.py
from dataclasses import dataclass, asdict
import importlib.metadata
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 uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
from broker import SessionBroker, SessionDoesNotExist, ClientRequest, ClientResponse, ClientError
# Configuración
TIMEOUT: int = 40
LOG_LEVEL: int = logging.DEBUG
TRUSTED_HOSTS: list[str] = ["127.0.0.1"]
# Create app
app = Quart(__name__)
QuartSchema(app)
app.asgi_app = ProxyHeadersMiddleware(app.asgi_app, trusted_hosts=TRUSTED_HOSTS)
app.logger.setLevel(LOG_LEVEL)
broker = SessionBroker()
# Modelos de datos
@dataclass
class Status:
status: str
version: str
@dataclass
class Session:
session_id: str
@dataclass
class Command:
session_id: str
command: str
@dataclass
class Read:
session_id: str
path: str
@dataclass
class Write:
session_id: str
path: str
content: str
@dataclass
class CommandResponse:
return_code: int
stdout: str
stderr: str
@dataclass
class ReadResponse:
content: str
@dataclass
class WriteResponse:
size: int
@dataclass
class ErrorResponse:
error: str
# Rutas API
@app.get("/status")
@validate_response(Status)
async def status() -> Status:
return Status(status="OK", version=importlib.metadata.version('your-package-name'))
@app.websocket('/session')
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)
@app.post('/command')
@validate_request(Command)
@validate_response(CommandResponse, 200)
@validate_response(ErrorResponse, 500)
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
@app.post('/read')
@validate_request(Read)
@validate_response(ReadResponse, 200)
@validate_response(ErrorResponse, 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
@app.post('/write')
@validate_request(Write)
@validate_response(WriteResponse, 200)
@validate_response(ErrorResponse, 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
def run():
app.run(host='0.0.0.0', port=7860) |