test-api / main.py
Mr-Help's picture
Update main.py
46bebe6 verified
raw
history blame
5.63 kB
# app.py
import os
import json
import requests
from fastapi import FastAPI, Request, HTTPException
from starlette.concurrency import run_in_threadpool
from playwright.sync_api import sync_playwright
from typing import Optional
# ------------ إعداد المتغيرات عبر البيئة ------------
ODOO_BASE = os.getenv("ODOO_BASE", "https://odoo.binrushd.care")
ODOO_API = f"{ODOO_BASE}/api/crm.lead"
CF_ID = os.getenv("CF_ACCESS_CLIENT_ID", "0491b36d7dcabce5b04f1a53f347bb4e.access")
CF_SECRET = os.getenv("CF_ACCESS_CLIENT_SECRET", "22152cb41b62393e159daaff7dce433006c3744c5850e6adc15fa3544bb5eb09")
ACCESS_TOKEN= os.getenv("ODOO_ACCESS_TOKEN", "") # من /api/auth/token
SESSION_ID = os.getenv("ODOO_SESSION_ID", "") # من /web/session/authenticate (اختياري)
USER_AGENT = os.getenv("USER_AGENT",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
# ضبط وقت الانتظار عند طلب Odoo
REQUEST_TIMEOUT = int(os.getenv("REQUEST_TIMEOUT", "15"))
app = FastAPI(title="HF Proxy -> Odoo (with Cloudflare cookie bootstrap)")
# ---- دالة مزودة: تشغّل Playwright متزامنًا وترجع Cookie header ----
def _get_cloudflare_cookie_header_sync(base_url: str, wait_ms: int = 2000) -> str:
"""
يفتح الصفحة الرئيسية بالدومين، يترك الـ JS يتم تنفيذه، ويجمع الكوكيز في صيغة Cookie header.
يعاد string مثل: "cf_clearance=xxx; __cf_bm=yyy"
"""
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context(user_agent=USER_AGENT, locale="en-US")
page = context.new_page()
# افتح الصفحة الرئيسية (ستنفّذ أي JS challenge مثل Cloudflare)
page.goto(base_url, timeout=30000)
# انتظر ms بسيطة لتكميل الـ JS (يمكن رفعها لو لازم)
page.wait_for_timeout(wait_ms)
cookies = context.cookies()
browser.close()
if not cookies:
return ""
return "; ".join(f"{c['name']}={c['value']}" for c in cookies)
# ---- endpoint رئيسي: يستقبل أي JSON payload ثم يرسله لـ Odoo بعد الحصول على الكوكيز ----
@app.post("/forward")
async def forward_lead(request: Request):
"""
استقبل أي JSON body من العميل، شغّل Playwright للحصول على كوكيز Cloudflare،
ثم أرسل نفس الـ body الى Odoo مع الهيدرز المطلوبة.
"""
try:
payload = await request.json()
except Exception:
# لو الجسم مش JSON نقرأه كسطر خام
body_text = await request.body()
try:
payload = json.loads(body_text.decode("utf-8") or "{}")
except Exception:
payload = {}
# 1) اجلب كوكيز Cloudflare (تشغيل Playwright في threadpool حتى لا يعرقل event loop)
try:
cookie_header = await run_in_threadpool(_get_cloudflare_cookie_header_sync, ODOO_BASE, 2000)
except Exception as e:
# لو Playwright فشل، نستمر بدون كوكيز (ربما CF لا يطلب JS). أرجع خطأ لكن لا تقطع التنفيذ نهائيًا.
cookie_header = ""
app.logger = getattr(app, "logger", None)
# لا نرمي استثناء لأن أحيان كثيرة ممكن ينجح بدون JS
# ولكن نُسجل السبب لو حابب تفحص الـ logs
print("Playwright error (will continue without cookies):", str(e))
# 2) جهّز الهيدرز (نفس التي كنت تستخدمها محليًا)
headers = {
"Accept": "application/json",
"Content-Type": "application/json",
"User-Agent": USER_AGENT,
"Accept-Encoding": "gzip, deflate",
"Connection": "keep-alive",
# Cloudflare Access service token headers (لو مستخدمة)
"CF-Access-Client-Id": CF_ID,
"CF-Access-Client-Secret": CF_SECRET,
# Odoo access token لو متوفر
}
if ACCESS_TOKEN:
headers["access-token"] = ACCESS_TOKEN
# أضف كوكيز الصفحة التي حصلنا عليها (إن وُجدت)
cookie_list = []
if cookie_header:
cookie_list.append(cookie_header)
if SESSION_ID:
cookie_list.append(f"session_id={SESSION_ID}")
if cookie_list:
headers["Cookie"] = "; ".join(cookie_list)
# 3) نفّذ طلب POST إلى Odoo
try:
resp = requests.post(ODOO_API, headers=headers, json=payload, timeout=REQUEST_TIMEOUT, allow_redirects=False)
except requests.RequestException as e:
# لو فشل الاتصال خارجيًا، نعيد 502 مع رسالة الخطأ
raise HTTPException(status_code=502, detail=f"Error connecting to Odoo: {str(e)}")
# 4) حاول نرجع الرد كما هو (JSON أو نص)
status = resp.status_code
ctype = resp.headers.get("Content-Type", "")
text = resp.text
# إذا كان JSON نعيده مفككًا، وإلا نعيد النص الخام
if "application/json" in ctype:
try:
return resp.json()
except Exception:
return {"status": status, "body": text}
else:
return {"status": status, "body": text}
@app.get("/health")
def health():
return {"ok": True, "note": "ready to bootstrap cookies and forward requests"}
# ---------- نهاية الملف ----------