"""HTTP client for scheduled system jobs — call the portal API, do not import views. The daily inbox sync goes through POST /email/sync so enqueue, coalescing, and the mailbox_sync worker stay on one code path with the UI Sync Inbox button. """ from __future__ import annotations import os import httpx from dotenv import load_dotenv load_dotenv() INBOX_SYNC_PATH="/email/sync" INBOX_SYNC_TIMEOUT=float(os.getenv("INBOX_SYNC_TIMEOUT_SECONDS","30")) def _backend_url() -> str: return (os.getenv("BACKEND_URL") or "http://localhost:8000").rstrip("/") def _cron_token() -> str: return (os.getenv("CRON_INBOX_SYNC_TOKEN") or "").strip() async def call_inbox_sync_api(*, top=100, skip=0, test_on=True) -> dict: """POST /email/sync on this service -> the JSON envelope. Uses CRON_INBOX_SYNC_TOKEN as Bearer. The route accepts that shared secret in place of a recruiter JWT so the 05:00 PKT scheduler can enqueue a run. """ token=_cron_token() if not token: raise RuntimeError("CRON_INBOX_SYNC_TOKEN is not set") params={ "top":int(top if top is not None else 100), "skip":int(skip if skip is not None else 0), "test_on":bool(test_on) if test_on is not None else True, } async with httpx.AsyncClient(timeout=INBOX_SYNC_TIMEOUT) as client: response=await client.post( f"{_backend_url()}{INBOX_SYNC_PATH}", params=params, headers={"Authorization":f"Bearer {token}"}, ) if response.status_code>=400: raise httpx.HTTPStatusError( response.text, request=response.request, response=response, ) return response.json()