ten / temp_proxy_server.py
3v324v23's picture
Добавление автономного прокси-сервера и создание файлов во временной директории для обхода проблем с правами доступа в HF Space
967b096
#!/usr/bin/env python3
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"
}
]
# Данные для API дизайнера
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}")
# Для запросов к /graphs ВСЕГДА возвращаем заранее подготовленный ответ
if self.path == "/graphs":
logger.info("PROXY: Возвращаем заготовленные данные о графах")
self._send_response(200, json.dumps(GRAPHS_DATA))
return
# Для запросов к Designer API ВСЕГДА возвращаем заранее подготовленный ответ
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
# Для других запросов пробуем проксировать на API сервер
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}")
# Для запросов к Designer API ВСЕГДА возвращаем заранее подготовленный ответ
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
# Для других запросов пробуем проксировать на API сервер
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)
# Для POST-запросов копируем тело
if method == "POST":
content_length = int(self.headers.get('Content-Length', 0))
body = self.rfile.read(content_length)
req.data = body
# Выполняем запрос к API серверу
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)
# Добавляем CORS заголовки
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: # Address already in use
logger.warning(f"Порт {port} уже занят, пробуем порт {port+1}")
port += 1
else:
logger.error(f"Ошибка при запуске сервера: {e}")
raise
if __name__ == "__main__":
main()