|
|
|
import http.server |
|
import socketserver |
|
import json |
|
import logging |
|
import sys |
|
import os |
|
import urllib.request |
|
|
|
|
|
logging.basicConfig(level=logging.INFO, |
|
format='%(asctime)s [%(levelname)s] %(message)s', |
|
datefmt='%Y-%m-%d %H:%M:%S') |
|
|
|
logger = logging.getLogger('ten-proxy') |
|
|
|
|
|
PROXY_PORT = int(os.environ.get('PROXY_PORT', '9090')) |
|
API_PORT = 8080 |
|
|
|
|
|
GRAPHS_DATA = [ |
|
{ |
|
"name": "Voice Agent", |
|
"description": "Voice Agent with OpenAI", |
|
"file": "voice_agent.json", |
|
"id": "voice_agent", |
|
"package": "default" |
|
}, |
|
{ |
|
"name": "Chat Agent", |
|
"description": "Chat Agent", |
|
"file": "chat_agent.json", |
|
"id": "chat_agent", |
|
"package": "default" |
|
} |
|
] |
|
|
|
|
|
DESIGNER_DATA = { |
|
"success": True, |
|
"packages": [ |
|
{ |
|
"name": "default", |
|
"description": "Default package", |
|
"graphs": [ |
|
{ |
|
"name": "Voice Agent", |
|
"description": "Voice Agent with OpenAI", |
|
"file": "voice_agent.json", |
|
"id": "voice_agent", |
|
"package": "default" |
|
}, |
|
{ |
|
"name": "Chat Agent", |
|
"description": "Chat Agent", |
|
"file": "chat_agent.json", |
|
"id": "chat_agent", |
|
"package": "default" |
|
} |
|
] |
|
} |
|
] |
|
} |
|
|
|
class SimpleProxyHandler(http.server.BaseHTTPRequestHandler): |
|
def do_GET(self): |
|
logger.info(f"PROXY: GET запрос: {self.path}") |
|
|
|
|
|
if self.path == "/graphs": |
|
logger.info("PROXY: Возвращаем заготовленные данные о графах") |
|
self._send_response(200, json.dumps(GRAPHS_DATA)) |
|
return |
|
|
|
|
|
if self.path.startswith("/api/designer/") or self.path.startswith("/api/dev/"): |
|
logger.info(f"PROXY: Обработка запроса к Designer API: {self.path}") |
|
self._send_response(200, json.dumps(DESIGNER_DATA)) |
|
return |
|
|
|
|
|
try: |
|
self._proxy_to_api("GET") |
|
except Exception as e: |
|
logger.error(f"PROXY: Ошибка при проксировании GET запроса: {e}") |
|
self._send_response(200, json.dumps({"success": True})) |
|
|
|
def do_POST(self): |
|
logger.info(f"PROXY: POST запрос: {self.path}") |
|
|
|
|
|
if self.path.startswith("/api/designer/") or self.path.startswith("/api/dev/"): |
|
logger.info(f"PROXY: Обработка POST запроса к Designer API: {self.path}") |
|
self._send_response(200, json.dumps({"success": True})) |
|
return |
|
|
|
|
|
try: |
|
self._proxy_to_api("POST") |
|
except Exception as e: |
|
logger.error(f"PROXY: Ошибка при проксировании POST запроса: {e}") |
|
self._send_response(200, json.dumps({"success": True})) |
|
|
|
def do_OPTIONS(self): |
|
logger.info(f"PROXY: OPTIONS запрос: {self.path}") |
|
self.send_response(200) |
|
self.send_header('Access-Control-Allow-Origin', '*') |
|
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') |
|
self.send_header('Access-Control-Allow-Headers', 'Content-Type') |
|
self.end_headers() |
|
|
|
def _proxy_to_api(self, method): |
|
"""Проксирует запрос к API серверу""" |
|
url = f"http://localhost:{API_PORT}{self.path}" |
|
logger.info(f"PROXY: Проксирование запроса к API: {url}") |
|
|
|
req = urllib.request.Request(url, method=method) |
|
|
|
|
|
for header, value in self.headers.items(): |
|
if header.lower() not in ["host", "content-length"]: |
|
req.add_header(header, value) |
|
|
|
|
|
if method == "POST": |
|
content_length = int(self.headers.get('Content-Length', 0)) |
|
body = self.rfile.read(content_length) |
|
req.data = body |
|
|
|
|
|
with urllib.request.urlopen(req) as response: |
|
|
|
self.send_response(response.status) |
|
|
|
|
|
for header, value in response.getheaders(): |
|
if header.lower() != "transfer-encoding": |
|
self.send_header(header, value) |
|
|
|
|
|
self.send_header('Access-Control-Allow-Origin', '*') |
|
self.end_headers() |
|
|
|
|
|
self.wfile.write(response.read()) |
|
|
|
def _send_response(self, status_code, data): |
|
"""Отправляет ответ с указанным статусом и данными""" |
|
self.send_response(status_code) |
|
self.send_header('Content-Type', 'application/json') |
|
self.send_header('Access-Control-Allow-Origin', '*') |
|
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') |
|
self.send_header('Access-Control-Allow-Headers', 'Content-Type') |
|
self.end_headers() |
|
|
|
if isinstance(data, str): |
|
self.wfile.write(data.encode('utf-8')) |
|
else: |
|
self.wfile.write(data) |
|
|
|
def log_message(self, format, *args): |
|
"""Перенаправляем логи сервера в наш логгер""" |
|
logger.debug(f"PROXY: {self.address_string()} - {format % args}") |
|
|
|
|
|
def main(): |
|
port = PROXY_PORT |
|
|
|
logger.info(f"Запуск автономного прокси-сервера на порту {port}") |
|
|
|
|
|
for attempt in range(3): |
|
try: |
|
httpd = socketserver.TCPServer(("", port), SimpleProxyHandler) |
|
logger.info(f"Автономный прокси-сервер успешно запущен на порту {port}") |
|
httpd.serve_forever() |
|
break |
|
except OSError as e: |
|
if e.errno == 98: |
|
logger.warning(f"Порт {port} уже занят, пробуем порт {port+1}") |
|
port += 1 |
|
else: |
|
logger.error(f"Ошибка при запуске сервера: {e}") |
|
raise |
|
|
|
if __name__ == "__main__": |
|
main() |