74- 75- 76:@router.post("/accounts/{account_id}/test") 77:async def test_account(account_id: int, user: User = Depends(get_current_user_csrf), 78- db: AsyncSession = Depends(get_db)): 79- """Real connection test: verify OAuth creds and Gmail API profile.""" 80- account = await db.get(GmailAccount, account_id) 81- if not account or account.user_id != user.id: 82- raise HTTPException(404, "Account not found") 83- token = account.oauth_token 84- if not token: 85- return {"ok": False, "detail": "No OAuth token stored for this account"} 86- from app.db.session import SyncSession 87- with SyncSession() as s: 88- acc_sync = s.get(GmailAccount, account.id) 89- tok_sync = s.get(OAuthToken, token.id) 90- try: 91- client = gmail_service.build_gmail_client_from_account(acc_sync, tok_sync) 92- result = client.verify_connection() 93- except Exception as e: 94- result = {"ok": False, "detail": str(e)[:400]} 95- if result.get("ok"): 96- account.status = "authorized" 97- await db.commit() 98- else: 99- account.status = "expired" if "auth" in (result.get("detail") or "").lower() else account.status 100- account.last_error = (result.get("detail") or "")[:500] 101- await db.commit() 102- await log_audit(db, user.id, "gmail.test", "gmail_account", account.id, -- 105- 106- 107:@router.post("/accounts/{account_id}/reauthorize") 108:async def reauthorize(account_id: int, user: User = Depends(get_current_user_csrf), 109- db: AsyncSession = Depends(get_db)): 110- account = await db.get(GmailAccount, account_id) 111- if not account or account.user_id != user.id: 112- raise HTTPException(404, "Account not found") 113- try: 114- client = gmail_service.resolve_client_config_for_account(account) 115- except gmail_service.GmailOauthError as e: 116- raise HTTPException(400, str(e)) 117- state = gmail_service.generate_state() 118- import json as _json 119- import redis as _redis 120- from app.core.config import settings 121- try: 122- r = _redis.from_url(settings.REDIS_URL, socket_timeout=2) 123- r.setex(f"oauthstate:{state}", 600, _json.dumps({"user_id": user.id})) 124- except Exception: 125- raise HTTPException(503, "State store unavailable") 126- url = gmail_service.build_authorization_url(client, state) 127- return {"authorization_url": url} 128- 129- 130-@router.post("/accounts/{account_id}/revoke") 131-async def disconnect_account(account_id: int, user: User = Depends(get_current_user_csrf), 132- db: AsyncSession = Depends(get_db), request: Request = None): 133- """Disconnect: revoke token at Google and delete stored credentials."""