Mr-Help commited on
Commit
46bebe6
ยท
verified ยท
1 Parent(s): df35178

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +109 -69
main.py CHANGED
@@ -1,85 +1,125 @@
1
- from fastapi import FastAPI, Response
2
- import requests
3
  import json
4
- import sys
5
- from http.cookiejar import Cookie
 
 
 
6
 
7
- app = FastAPI()
 
 
8
 
9
- def cookies_to_log_list(cookiejar) -> list:
10
- """ุญูˆู‘ู„ ุงู„ูƒูˆูƒูŠุฒ ู„ู‚ุงุฆู…ุฉ ุณู‡ู„ุฉ ุงู„ู‚ุฑุงุกุฉ ู„ู„ู‘ูˆุฌ."""
11
- out = []
12
- for c in cookiejar: # type: Cookie
13
- out.append({
14
- "name": c.name,
15
- "value": c.value,
16
- "domain": c.domain,
17
- "path": c.path,
18
- "secure": c.secure,
19
- "expires": c.expires,
20
- })
21
- return out
22
 
23
- @app.get("/get-token")
24
- def get_token():
25
- BASE = "https://odoo.binrushd.care"
26
- PREFLIGHT_URL = f"{BASE}/" # ู†ุฌูŠุจ ู…ู†ู‡ ุงู„ูƒูˆูƒูŠุฒ (ุฌู„ุณุฉ/ุชููˆูŠุถ CF)
27
- TOKEN_URL = f"{BASE}/api/auth/token"
28
 
29
- # ู‡ูŠุฏุฑุฒ ู…ูˆุญุฏุฉ ู„ู„ุฌู„ุณุฉ (CF Access + UA)
30
- common_headers = {
31
- "CF-Access-Client-Id": "0491b36d7dcabce5b04f1a53f347bb4e.access",
32
- "CF-Access-Client-Secret": "22152cb41b62393e159daaff7dce433006c3744c5850e6adc15fa3544bb5eb09",
33
- # ู„ูˆ ุงู„ู€ API ูุนู„ุงู‹ ุจุชู‚ุฑุฃ login/password/db ู…ู† ุงู„ู‡ูŠุฏุฑุฒ ุฎูŽู„ู‘ููŠู‡ูู… ู…ูˆุฌูˆุฏูŠู†:
34
- "login": "binrushd.automation@gmail.com",
35
- "password": "BR2025",
36
- "db": "BR_EMR_16.0_202401",
37
 
38
- # ุชู‚ู„ูŠุฏ ู…ุชุตูุญ ู„ุชู‚ู„ูŠู„ ุดุจู‡ุฉ ุงู„ุจูˆุช
39
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
40
- "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
41
- "Accept": "application/json, text/plain, */*",
42
- "Accept-Language": "en-US,en;q=0.9,ar;q=0.8",
43
- "Connection": "keep-alive",
44
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
- s = requests.Session()
47
- s.headers.update(common_headers)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
 
49
  try:
50
- # (1) ุทู„ุจ ุชู…ู‡ูŠุฏูŠ ู„ุงุณุชุฎุฑุงุฌ ุงู„ูƒูˆูƒูŠุฒ ู…ู† Cloudflare/Access
51
- pre = s.get(PREFLIGHT_URL, timeout=20, allow_redirects=True)
 
 
 
 
 
 
52
 
53
- # ู„ูˆุฌ ู„ู„ูƒูˆูƒูŠุฒ ุจุนุฏ ุงู„ุฎุทูˆุฉ ุงู„ุฃูˆู„ู‰
54
- pre_cookies_log = cookies_to_log_list(s.cookies)
55
- print("[PRE-FLIGHT COOKIES]", json.dumps(pre_cookies_log, ensure_ascii=False), file=sys.stderr)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
 
57
- # (2) ุงู„ุทู„ุจ ุงู„ุฃุณุงุณูŠ ุจุงุณุชุฎุฏุงู… ู†ูุณ ุงู„ุฌู„ุณุฉ (ุงู„ู‡ูŠุฏุฑุฒ + ุงู„ูƒูˆูƒูŠุฒ ูŠุชุจุนุชูˆุง ุชู„ู‚ุงุฆูŠุงู‹)
58
- resp = s.get(TOKEN_URL, timeout=20)
 
 
 
 
59
 
60
- # ู„ูˆุฌ ู…ูุตู„ ู„ู†ุชูŠุฌุฉ ุทู„ุจ ุงู„ุชูˆูƒู†
 
 
 
 
 
 
61
  try:
62
- body_preview = resp.text[:1000]
63
  except Exception:
64
- body_preview = "<unreadable>"
65
-
66
- log_obj = {
67
- "status_code": resp.status_code,
68
- "reason": resp.reason,
69
- "headers": dict(resp.headers),
70
- "body_preview": body_preview
71
- }
72
- print("[TOKEN RESPONSE]", json.dumps(log_obj, ensure_ascii=False), file=sys.stderr)
73
 
74
- # ุฑุฌู‘ุน ู†ูุณ ุงู„ุญุงู„ุฉ ูˆุงู„ู…ุญุชูˆู‰
75
- content_type = resp.headers.get("Content-Type", "application/json")
76
- return Response(
77
- content=resp.text,
78
- status_code=resp.status_code,
79
- media_type=content_type
80
- )
81
 
82
- except requests.RequestException as e:
83
- err_txt = f"Network error: {type(e).__name__} - {str(e)}"
84
- print("[TOKEN ERROR]", err_txt, file=sys.stderr)
85
- return Response(content=err_txt, status_code=502, media_type="text/plain")
 
1
+ # app.py
2
+ import os
3
  import json
4
+ import requests
5
+ from fastapi import FastAPI, Request, HTTPException
6
+ from starlette.concurrency import run_in_threadpool
7
+ from playwright.sync_api import sync_playwright
8
+ from typing import Optional
9
 
10
+ # ------------ ุฅุนุฏุงุฏ ุงู„ู…ุชุบูŠุฑุงุช ุนุจุฑ ุงู„ุจูŠุฆุฉ ------------
11
+ ODOO_BASE = os.getenv("ODOO_BASE", "https://odoo.binrushd.care")
12
+ ODOO_API = f"{ODOO_BASE}/api/crm.lead"
13
 
14
+ CF_ID = os.getenv("CF_ACCESS_CLIENT_ID", "0491b36d7dcabce5b04f1a53f347bb4e.access")
15
+ CF_SECRET = os.getenv("CF_ACCESS_CLIENT_SECRET", "22152cb41b62393e159daaff7dce433006c3744c5850e6adc15fa3544bb5eb09")
16
+ ACCESS_TOKEN= os.getenv("ODOO_ACCESS_TOKEN", "") # ู…ู† /api/auth/token
17
+ SESSION_ID = os.getenv("ODOO_SESSION_ID", "") # ู…ู† /web/session/authenticate (ุงุฎุชูŠุงุฑูŠ)
 
 
 
 
 
 
 
 
 
18
 
19
+ USER_AGENT = os.getenv("USER_AGENT",
20
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
21
+ "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
 
 
22
 
23
+ # ุถุจุท ูˆู‚ุช ุงู„ุงู†ุชุธุงุฑ ุนู†ุฏ ุทู„ุจ Odoo
24
+ REQUEST_TIMEOUT = int(os.getenv("REQUEST_TIMEOUT", "15"))
 
 
 
 
 
 
25
 
26
+ app = FastAPI(title="HF Proxy -> Odoo (with Cloudflare cookie bootstrap)")
27
+
28
+ # ---- ุฏุงู„ุฉ ู…ุฒูˆุฏุฉ: ุชุดุบู‘ู„ Playwright ู…ุชุฒุงู…ู†ู‹ุง ูˆุชุฑุฌุน Cookie header ----
29
+ def _get_cloudflare_cookie_header_sync(base_url: str, wait_ms: int = 2000) -> str:
30
+ """
31
+ ูŠูุชุญ ุงู„ุตูุญุฉ ุงู„ุฑุฆูŠุณูŠุฉ ุจุงู„ุฏูˆู…ูŠู†ุŒ ูŠุชุฑูƒ ุงู„ู€ JS ูŠุชู… ุชู†ููŠุฐู‡ุŒ ูˆูŠุฌู…ุน ุงู„ูƒูˆูƒูŠุฒ ููŠ ุตูŠุบุฉ Cookie header.
32
+ ูŠุนุงุฏ string ู…ุซู„: "cf_clearance=xxx; __cf_bm=yyy"
33
+ """
34
+ with sync_playwright() as p:
35
+ browser = p.chromium.launch(headless=True)
36
+ context = browser.new_context(user_agent=USER_AGENT, locale="en-US")
37
+ page = context.new_page()
38
+ # ุงูุชุญ ุงู„ุตูุญุฉ ุงู„ุฑุฆูŠุณูŠุฉ (ุณุชู†ูู‘ุฐ ุฃูŠ JS challenge ู…ุซู„ Cloudflare)
39
+ page.goto(base_url, timeout=30000)
40
+ # ุงู†ุชุธุฑ ms ุจุณูŠุทุฉ ู„ุชูƒู…ูŠู„ ุงู„ู€ JS (ูŠู…ูƒู† ุฑูุนู‡ุง ู„ูˆ ู„ุงุฒู…)
41
+ page.wait_for_timeout(wait_ms)
42
+ cookies = context.cookies()
43
+ browser.close()
44
+ if not cookies:
45
+ return ""
46
+ return "; ".join(f"{c['name']}={c['value']}" for c in cookies)
47
 
48
+ # ---- endpoint ุฑุฆูŠุณูŠ: ูŠุณุชู‚ุจู„ ุฃูŠ JSON payload ุซู… ูŠุฑุณู„ู‡ ู„ู€ Odoo ุจุนุฏ ุงู„ุญุตูˆู„ ุนู„ู‰ ุงู„ูƒูˆูƒูŠุฒ ----
49
+ @app.post("/forward")
50
+ async def forward_lead(request: Request):
51
+ """
52
+ ุงุณุชู‚ุจู„ ุฃูŠ JSON body ู…ู† ุงู„ุนู…ูŠู„ุŒ ุดุบู‘ู„ Playwright ู„ู„ุญุตูˆู„ ุนู„ู‰ ูƒูˆูƒูŠุฒ CloudflareุŒ
53
+ ุซู… ุฃุฑุณู„ ู†ูุณ ุงู„ู€ body ุงู„ู‰ Odoo ู…ุน ุงู„ู‡ูŠุฏุฑุฒ ุงู„ู…ุทู„ูˆุจุฉ.
54
+ """
55
+ try:
56
+ payload = await request.json()
57
+ except Exception:
58
+ # ู„ูˆ ุงู„ุฌุณู… ู…ุด JSON ู†ู‚ุฑุฃู‡ ูƒุณุทุฑ ุฎุงู…
59
+ body_text = await request.body()
60
+ try:
61
+ payload = json.loads(body_text.decode("utf-8") or "{}")
62
+ except Exception:
63
+ payload = {}
64
 
65
+ # 1) ุงุฌู„ุจ ูƒูˆูƒูŠุฒ Cloudflare (ุชุดุบูŠู„ Playwright ููŠ threadpool ุญุชู‰ ู„ุง ูŠุนุฑู‚ู„ event loop)
66
  try:
67
+ cookie_header = await run_in_threadpool(_get_cloudflare_cookie_header_sync, ODOO_BASE, 2000)
68
+ except Exception as e:
69
+ # ู„ูˆ Playwright ูุดู„ุŒ ู†ุณุชู…ุฑ ุจุฏูˆู† ูƒูˆูƒูŠุฒ (ุฑุจู…ุง CF ู„ุง ูŠุทู„ุจ JS). ุฃุฑุฌุน ุฎุทุฃ ู„ูƒู† ู„ุง ุชู‚ุทุน ุงู„ุชู†ููŠุฐ ู†ู‡ุงุฆูŠู‹ุง.
70
+ cookie_header = ""
71
+ app.logger = getattr(app, "logger", None)
72
+ # ู„ุง ู†ุฑู…ูŠ ุงุณุชุซู†ุงุก ู„ุฃู† ุฃุญูŠุงู† ูƒุซูŠุฑุฉ ู…ู…ูƒู† ูŠู†ุฌุญ ุจุฏูˆู† JS
73
+ # ูˆู„ูƒู† ู†ูุณุฌู„ ุงู„ุณุจุจ ู„ูˆ ุญุงุจุจ ุชูุญุต ุงู„ู€ logs
74
+ print("Playwright error (will continue without cookies):", str(e))
75
 
76
+ # 2) ุฌู‡ู‘ุฒ ุงู„ู‡ูŠุฏุฑุฒ (ู†ูุณ ุงู„ุชูŠ ูƒู†ุช ุชุณุชุฎุฏู…ู‡ุง ู…ุญู„ูŠู‹ุง)
77
+ headers = {
78
+ "Accept": "application/json",
79
+ "Content-Type": "application/json",
80
+ "User-Agent": USER_AGENT,
81
+ "Accept-Encoding": "gzip, deflate",
82
+ "Connection": "keep-alive",
83
+ # Cloudflare Access service token headers (ู„ูˆ ู…ุณุชุฎุฏู…ุฉ)
84
+ "CF-Access-Client-Id": CF_ID,
85
+ "CF-Access-Client-Secret": CF_SECRET,
86
+ # Odoo access token ู„ูˆ ู…ุชูˆูุฑ
87
+ }
88
+ if ACCESS_TOKEN:
89
+ headers["access-token"] = ACCESS_TOKEN
90
+
91
+ # ุฃุถู ูƒูˆูƒูŠุฒ ุงู„ุตูุญุฉ ุงู„ุชูŠ ุญุตู„ู†ุง ุนู„ูŠู‡ุง (ุฅู† ูˆูุฌุฏุช)
92
+ cookie_list = []
93
+ if cookie_header:
94
+ cookie_list.append(cookie_header)
95
+ if SESSION_ID:
96
+ cookie_list.append(f"session_id={SESSION_ID}")
97
+ if cookie_list:
98
+ headers["Cookie"] = "; ".join(cookie_list)
99
 
100
+ # 3) ู†ูู‘ุฐ ุทู„ุจ POST ุฅู„ู‰ Odoo
101
+ try:
102
+ resp = requests.post(ODOO_API, headers=headers, json=payload, timeout=REQUEST_TIMEOUT, allow_redirects=False)
103
+ except requests.RequestException as e:
104
+ # ู„ูˆ ูุดู„ ุงู„ุงุชุตุงู„ ุฎุงุฑุฌูŠู‹ุงุŒ ู†ุนูŠุฏ 502 ู…ุน ุฑุณุงู„ุฉ ุงู„ุฎุทุฃ
105
+ raise HTTPException(status_code=502, detail=f"Error connecting to Odoo: {str(e)}")
106
 
107
+ # 4) ุญุงูˆู„ ู†ุฑุฌุน ุงู„ุฑุฏ ูƒู…ุง ู‡ูˆ (JSON ุฃูˆ ู†ุต)
108
+ status = resp.status_code
109
+ ctype = resp.headers.get("Content-Type", "")
110
+ text = resp.text
111
+
112
+ # ุฅุฐุง ูƒุงู† JSON ู†ุนูŠุฏู‡ ู…ููƒูƒู‹ุงุŒ ูˆุฅู„ุง ู†ุนูŠุฏ ุงู„ู†ุต ุงู„ุฎุงู…
113
+ if "application/json" in ctype:
114
  try:
115
+ return resp.json()
116
  except Exception:
117
+ return {"status": status, "body": text}
118
+ else:
119
+ return {"status": status, "body": text}
 
 
 
 
 
 
120
 
121
+ @app.get("/health")
122
+ def health():
123
+ return {"ok": True, "note": "ready to bootstrap cookies and forward requests"}
 
 
 
 
124
 
125
+ # ---------- ู†ู‡ุงูŠุฉ ุงู„ู…ู„ู ----------