YF-proxy / app.py
vakabunga
chore: adds logging secrets
67639c3
import os
import json
import gradio as gr
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse, RedirectResponse
from gradio_client import Client
# ==================== Конфигурация ====================
PRIVATE_SPACE_URL = os.getenv("PRIVATE_SPACE_URL")
PRIVATE_TOKEN = os.getenv("PRIVATE_TOKEN")
PUBLIC_ENDPOINT = os.getenv("PUBLIC_ENDPOINT")
PRIVATE_ENDPOINT = os.getenv("PRIVATE_ENDPOINT")
# ==================== Инициализация Клиента ====================
client = None
def init_client():
"""Ленивая или повторная инициализация клиента"""
global client
if not PRIVATE_SPACE_URL or not PRIVATE_TOKEN:
print("⚠️ Config missing: PRIVATE_SPACE_URL or TOKEN not set.")
return None
print("🔄 Initializing upstream connection...")
try:
# Клиент создается
new_client = Client(PRIVATE_SPACE_URL, token=PRIVATE_TOKEN, verbose=False)
print("✅ Upstream connection established.")
return new_client
except Exception as e:
# Логируем только факт ошибки, не печатаем само исключение, так как оно может содержать URL
print(PRIVATE_SPACE_URL)
print("❌ Connection failed.")
print(e)
return None
# Пробуем соединиться при старте
client = init_client()
# ==================== FastAPI (Прокси) ====================
fastapi_app = FastAPI(title="Secure Proxy")
@fastapi_app.get("/")
async def root_redirect():
return RedirectResponse(url="/ui")
@fastapi_app.get("/health")
async def health_check():
# Не показываем детали, просто жив сервис или нет
return {"status": "ok" if client else "connecting"}
@fastapi_app.post(f"/{PUBLIC_ENDPOINT}")
async def proxy_webhook(request: Request):
global client
# Если клиент отвалился или не был создан, пробуем пересоздать
if not client:
client = init_client()
if not client:
raise HTTPException(status_code=503, detail="Upstream unavailable")
try:
# 1. Получаем JSON (без логирования содержимого)
payload = await request.json()
# 2. Конвертируем в строку для Gradio
payload_str = json.dumps(payload, ensure_ascii=False)
# 3. Вызываем приватный API
# Используем api_name, это безопаснее при изменениях кода.
result = client.predict(payload_str, api_name=f"/{PRIVATE_ENDPOINT}")
# 4. Возвращаем успех, не показывая ответ приватного сервера (если там есть данные)
# Если нужно вернуть ID для отладки Яндекс.Форм, можно вернуть минимальный JSON
return JSONResponse(
content={
"status": "forwarded",
"timestamp": os.urandom(4).hex(), # Просто уникальный маркер
}
)
except Exception:
# В случае ошибки не возвращаем её текст клиенту, чтобы не светить внутренности
print("❌ Error during forwarding.")
raise HTTPException(status_code=500, detail="Internal forwarding error")
# ==================== Безопасный Gradio UI ====================
def create_secure_ui():
with gr.Blocks(title="System Status") as demo:
gr.Markdown("## 🛡️ Gateway Status")
# Индикатор статуса без деталей
status_indicator = gr.Textbox(
label="Service State", value="Checking...", interactive=False
)
btn = gr.Button("Refresh Status")
def check_safe():
if client:
return "✅ Online / Connected to Upstream"
else:
return "⚠️ Offline / Reconnecting..."
# При загрузке и нажатии проверяем статус
demo.load(check_safe, outputs=status_indicator)
btn.click(check_safe, outputs=status_indicator)
return demo
app = gr.mount_gradio_app(fastapi_app, create_secure_ui(), path="/ui")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)