File size: 4,608 Bytes
5f84cf1
7f857aa
e2f3a86
5f84cf1
61b054f
7f857aa
5f84cf1
7f857aa
b35f69a
7f857aa
5f84cf1
7f857aa
5f84cf1
7f857aa
 
e2f3a86
 
7f857aa
 
 
b35f69a
54f10bf
7f857aa
 
 
 
 
54f10bf
7f857aa
 
 
 
67639c3
dd32f04
 
7f857aa
 
5f84cf1
7f857aa
 
e2f3a86
7f857aa
 
3fe9d85
 
61b054f
 
 
 
 
7f857aa
 
 
 
3fe9d85
 
7f857aa
 
 
a5cb03d
7f857aa
 
 
 
 
a5cb03d
7f857aa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a5cb03d
7f857aa
 
 
 
3fe9d85
 
7f857aa
 
 
 
e2f3a86
7f857aa
 
 
 
79f5a3d
7f857aa
3fe9d85
7f857aa
 
 
 
 
8eabd7e
7f857aa
 
 
8eabd7e
7f857aa
8eabd7e
 
7f857aa
8eabd7e
3fe9d85
a5cb03d
 
 
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
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)