Seth commited on
Commit
f1ef860
·
1 Parent(s): 4cf3d77
Files changed (1) hide show
  1. 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 = data.get("message") if isinstance(data, dict) else None
 
 
 
 
 
 
 
 
 
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
- if not FRONTEND_ORIGIN:
1020
- raise HTTPException(
1021
- status_code=400,
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"{FRONTEND_ORIGIN}/api/unipile/linkedin/hosted-callback",
1043
- "success_redirect_url": f"{FRONTEND_ORIGIN}/?tab=linkedin-connected",
1044
- "failure_redirect_url": f"{FRONTEND_ORIGIN}/?tab=linkedin-connect-failed",
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)