def _real_send(): with SyncSession() as s: acc = s.get(GmailAccount, account.id) token = s.query(OAuthToken).filter_by(account_id=account.id).first() if not token: return {"ok": False, "error": "No OAuth token stored for this account", "error_class": "auth"} client = build_gmail_client_from_account(acc, token) if client.method == "smtp": return client.send_mime_smtp(mime) return client.send_mime_api(mime) try: result = await asyncio.get_event_loop().run_in_executor(None, _real_send) except Exception as e: await log_audit(db, user.id, "test_send.error", "gmail_account", account.id, {"error": str(e)[:200]}) return {"ok": False, "stage": "temp_failed", "detail": str(e)[:400], "error_class": "unknown", "elapsed_ms": int((time.monotonic() - t0) * 1000)} elapsed = int((time.monotonic() - t0) * 1000) if result.get("ok"): await log_audit(db, user.id, "test_send.success", "gmail_account", account.id, {"to": str(payload.to_email), "message_id": result.get("message_id")}) return {"ok": True, "stage": "accepted", "detail": "Gmail accepted the message", "message_id": result.get("message_id"), "thread_id": result.get("thread_id"), "method": client_method(account), "provider_response": _clip(str(result.get("raw_response", ""))), "elapsed_ms": elapsed} # provider refused — surface the REAL error, never fake success err = str(result.get("error", "") or result.get("detail", "")) await log_audit(db, user.id, "test_send.failed", "gmail_account", account.id, {"to": str(payload.to_email), "error": err[:200]}) return {"ok": False, "stage": "perm_failed", "detail": err[:400], "error_class": result.get("error_class", "unknown"), "method": client_method(account), "provider_response": _clip(str(result.get("raw_response", ""))), "elapsed_ms": elapsed} def _clip(s: str) -> str: return s[:300] def client_method(account: GmailAccount) -> str: return account.sending_method or "api" @router.get("/accounts") async def test_accounts(db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)): """Authorized accounts available for test sends.""" rows = (await db.execute( select(GmailAccount).where(GmailAccount.user_id == user.id, GmailAccount.status == "authorized", GmailAccount.is_active == True))).scalars().all() # noqa: E712 return {"accounts": [{"id": a.id, "email": a.email, "sending_method": a.sending_method} for a in rows]} @router.get("/templates") async def test_templates(db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)): rows = (await db.execute( select(EmailTemplate).where(EmailTemplate.user_id == user.id))).scalars().all() return {"templates": [{"id": t.id, "name": t.name} for t in rows]}