Seth commited on
Commit ·
f1ef860
1
Parent(s): 4cf3d77
update
Browse files- backend/app/main.py +32 -10
backend/app/main.py
CHANGED
|
@@ -200,14 +200,37 @@ def _unipile_request(method: str, path: str, payload: Optional[dict] = None):
|
|
| 200 |
except Exception:
|
| 201 |
data = {"raw": resp.text}
|
| 202 |
if resp.status_code >= 400:
|
| 203 |
-
msg =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
raise HTTPException(
|
| 205 |
status_code=resp.status_code,
|
| 206 |
-
detail=msg or f"UniPile error ({resp.status_code})",
|
| 207 |
)
|
| 208 |
return data
|
| 209 |
|
| 210 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
def _contact_value(contact: Contact, field: str):
|
| 212 |
if field == "first_name":
|
| 213 |
return contact.first_name or _pick_from_raw(contact.raw_data, ["first name", "firstname", "first_name"])
|
|
@@ -1014,13 +1037,12 @@ async def list_unipile_linkedin_accounts(t: TenantContext = Depends(get_tenant_c
|
|
| 1014 |
@app.post("/api/unipile/linkedin/hosted-link")
|
| 1015 |
async def create_unipile_linkedin_hosted_link(
|
| 1016 |
body: UnipileHostedLinkRequest,
|
|
|
|
| 1017 |
t: TenantContext = Depends(get_tenant_context),
|
| 1018 |
):
|
| 1019 |
-
|
| 1020 |
-
|
| 1021 |
-
|
| 1022 |
-
detail="FRONTEND_ORIGIN is required to use UniPile Hosted Auth redirect flow.",
|
| 1023 |
-
)
|
| 1024 |
token = str(uuid.uuid4())
|
| 1025 |
expires_at = datetime.utcnow() + timedelta(minutes=20)
|
| 1026 |
state = UnipileHostedAuthState(
|
|
@@ -1039,9 +1061,9 @@ async def create_unipile_linkedin_hosted_link(
|
|
| 1039 |
"providers": ["LINKEDIN"],
|
| 1040 |
"api_url": UNIPILE_API_BASE,
|
| 1041 |
"expiresOn": expires_at.isoformat() + "Z",
|
| 1042 |
-
"notify_url": f"{
|
| 1043 |
-
"success_redirect_url": f"{
|
| 1044 |
-
"failure_redirect_url": f"{
|
| 1045 |
"name": token,
|
| 1046 |
}
|
| 1047 |
response = _unipile_request("POST", "/api/v1/hosted/accounts/link", payload)
|
|
|
|
| 200 |
except Exception:
|
| 201 |
data = {"raw": resp.text}
|
| 202 |
if resp.status_code >= 400:
|
| 203 |
+
msg = None
|
| 204 |
+
if isinstance(data, dict):
|
| 205 |
+
msg = (
|
| 206 |
+
data.get("message")
|
| 207 |
+
or data.get("detail")
|
| 208 |
+
or data.get("error")
|
| 209 |
+
or data.get("errors")
|
| 210 |
+
)
|
| 211 |
+
if isinstance(msg, (dict, list)):
|
| 212 |
+
msg = json.dumps(msg)
|
| 213 |
raise HTTPException(
|
| 214 |
status_code=resp.status_code,
|
| 215 |
+
detail=msg or f"UniPile error ({resp.status_code}): {resp.text}",
|
| 216 |
)
|
| 217 |
return data
|
| 218 |
|
| 219 |
|
| 220 |
+
def _public_origin_from_request(request: Request) -> str:
|
| 221 |
+
"""
|
| 222 |
+
Resolve browser-facing origin behind proxies (HF Spaces, ingress).
|
| 223 |
+
Falls back to request URL if forwarded headers are absent.
|
| 224 |
+
"""
|
| 225 |
+
fwd_proto = request.headers.get("x-forwarded-proto")
|
| 226 |
+
proto = (fwd_proto.split(",")[0].strip().lower() if fwd_proto else request.url.scheme or "https")
|
| 227 |
+
fwd_host = request.headers.get("x-forwarded-host")
|
| 228 |
+
host = (fwd_host.split(",")[0].strip() if fwd_host else request.headers.get("host") or request.url.netloc or "")
|
| 229 |
+
if host:
|
| 230 |
+
return f"{proto}://{host}".rstrip("/")
|
| 231 |
+
return str(request.base_url).rstrip("/")
|
| 232 |
+
|
| 233 |
+
|
| 234 |
def _contact_value(contact: Contact, field: str):
|
| 235 |
if field == "first_name":
|
| 236 |
return contact.first_name or _pick_from_raw(contact.raw_data, ["first name", "firstname", "first_name"])
|
|
|
|
| 1037 |
@app.post("/api/unipile/linkedin/hosted-link")
|
| 1038 |
async def create_unipile_linkedin_hosted_link(
|
| 1039 |
body: UnipileHostedLinkRequest,
|
| 1040 |
+
request: Request,
|
| 1041 |
t: TenantContext = Depends(get_tenant_context),
|
| 1042 |
):
|
| 1043 |
+
origin = (FRONTEND_ORIGIN or "").strip().rstrip("/") or _public_origin_from_request(request)
|
| 1044 |
+
if not origin:
|
| 1045 |
+
raise HTTPException(status_code=400, detail="Could not determine frontend origin for hosted auth.")
|
|
|
|
|
|
|
| 1046 |
token = str(uuid.uuid4())
|
| 1047 |
expires_at = datetime.utcnow() + timedelta(minutes=20)
|
| 1048 |
state = UnipileHostedAuthState(
|
|
|
|
| 1061 |
"providers": ["LINKEDIN"],
|
| 1062 |
"api_url": UNIPILE_API_BASE,
|
| 1063 |
"expiresOn": expires_at.isoformat() + "Z",
|
| 1064 |
+
"notify_url": f"{origin}/api/unipile/linkedin/hosted-callback",
|
| 1065 |
+
"success_redirect_url": f"{origin}/?tab=linkedin-connected",
|
| 1066 |
+
"failure_redirect_url": f"{origin}/?tab=linkedin-connect-failed",
|
| 1067 |
"name": token,
|
| 1068 |
}
|
| 1069 |
response = _unipile_request("POST", "/api/v1/hosted/accounts/link", payload)
|