proxyrunflare / app.py
Opera8's picture
Update app.py
edfa3a1 verified
Raw
History Blame Contribute Delete
6.59 kB
# app.py (Lightweight Reverse Proxy for Runflare)
import os
import ssl
import asyncio
import httpx
import websockets
from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect
from fastapi.responses import StreamingResponse, HTMLResponse, FileResponse
app = FastAPI()
# 🟢 آدرس اسپیس هاگینگ فیس شما (مغز متفکر اصلی)
# می‌توانید این آدرس را تغییر دهید تا به اسپیس مد نظر شما اشاره کند
HF_SPACE_URL = os.environ.get("HF_SPACE_URL", "https://opera8-runflare.hf.space").strip()
HF_WS_URL = HF_SPACE_URL.replace("https://", "wss://") + "/ws"
# هدرهای امنیتی جهت اعطای دسترسی دوربین و میکروفون به مرورگر و وب‌ویو موبایل
PERMISSION_HEADERS = {
"Permissions-Policy": "camera=*, microphone=*, geolocation=*, autoplay=*",
"Feature-Policy": "camera *; microphone *; autoplay *",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Headers": "*"
}
# غیرفعال کردن بررسی سخت‌گیرانه SSL برای جلوگیری از خطاهای احتمالی ارتباط داخلی داکر رانفلر
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
# =================================================================
# ۱. مانیتورینگ و پروکسی اتصال‌های وب‌سوکت صوتی (/ws) به هاگینگ فیس
# =================================================================
@app.websocket("/ws")
async def websocket_proxy_route(client_ws: WebSocket):
await client_ws.accept()
# شبیه‌سازی دست‌تکانی وب‌سوکت برای عبور از سد فایروال هاگینگ‌فیس
headers = {
"Host": HF_SPACE_URL.replace("https://", ""),
"Origin": HF_SPACE_URL,
"User-Agent": client_ws.headers.get("user-agent", "Mozilla/5.0")
}
ws_kwargs = {}
try:
ws_version = getattr(websockets, "__version__", "10.0")
try:
major_version = int(ws_version.split('.')[0])
except ValueError:
major_version = 10
if major_version >= 14:
ws_kwargs["additional_headers"] = headers
else:
ws_kwargs["extra_headers"] = headers
except Exception:
ws_kwargs["extra_headers"] = headers
try:
# اتصال مستقیم و پایدار وب‌سوکت به هاگینگ‌فیس
async with websockets.connect(
HF_WS_URL,
ssl=ssl_context,
**ws_kwargs
) as server_ws:
async def client_to_server():
try:
while True:
data = await client_ws.receive()
if "text" in data and data["text"] is not None:
await server_ws.send(data["text"])
elif "bytes" in data and data["bytes"] is not None:
await server_ws.send(data["bytes"])
except Exception:
pass
async def server_to_client():
try:
async for message in server_ws:
if isinstance(message, str):
await client_ws.send_text(message)
else:
await client_ws.send_bytes(message)
except Exception:
pass
await asyncio.gather(client_to_server(), server_to_client())
except WebSocketDisconnect:
pass
except Exception as e:
print(f"[WS Proxy Error]: {e}")
finally:
try:
await client_ws.close()
except:
pass
# =================================================================
# ۲. مسیر یاب و پروکسی سراسری و استریمینگ تمام درخواست‌های وب و API
# =================================================================
@app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "PATCH"])
async def reverse_proxy_route(request: Request, path: str):
# کپی کردن هدرهای درخواست کاربر
headers = dict(request.headers)
# حذف هدرهای محلی رانفلر برای جلوگیری از تداخل مسیر در هاگینگ‌فیس
headers.pop("host", None)
headers.pop("content-length", None)
# فوروارد کردن آی‌پی واقعی کاربر جهت پایداری سهمیه اعتبارات
cf_ip = request.headers.get("CF-Connecting-IP")
if cf_ip:
headers["X-Forwarded-For"] = cf_ip
url = f"{HF_SPACE_URL}/{path}"
body = await request.body()
# دریافت استریم داده‌ها از هاگینگ‌فیس و تحویل آنی به کاربر (بدون بافر کردن در رم رانفلر)
async def generate_response():
async with httpx.AsyncClient(verify=False, timeout=300.0) as client_local:
async with client_local.stream(
request.method,
url,
content=body,
headers=headers,
params=dict(request.query_params)
) as resp:
async for chunk in resp.aiter_bytes():
yield chunk
# دریافت متادیتای هدرها از هاگینگ‌فیس
async with httpx.AsyncClient(verify=False, timeout=300.0) as client_headers:
resp_headers = await client_headers.request(
request.method,
url,
content=body,
headers=headers,
params=dict(request.query_params)
)
# آماده‌سازی هدرهای پاسخ برای بازگرداندن به مرورگر کاربر
headers_to_return = dict(resp_headers.headers)
headers_to_return.pop("content-encoding", None)
headers_to_return.pop("content-length", None)
headers_to_return.pop("transfer-encoding", None)
# تزریق هدرهای دسترسی و کرس (CORS)
for k, v in PERMISSION_HEADERS.items():
headers_to_return[k] = v
return StreamingResponse(
generate_response(),
status_code=resp_headers.status_code,
headers=headers_to_return,
media_type=resp_headers.headers.get("content-type")
)