/workspace/bulkmailer/static/gmail-accounts.html-33-
/workspace/bulkmailer/static/gmail-accounts.html-34- Upload the client_secret_*.json file from Google Cloud Console → APIs & Services → Credentials. /workspace/bulkmailer/static/gmail-accounts.html-35- The file is validated, encrypted, and stored locally; the secret is never echoed back. /workspace/bulkmailer/static/gmail-accounts.html:36: Redirect URI for this app: /auth/google/callback /workspace/bulkmailer/static/gmail-accounts.html-37-
/workspace/bulkmailer/static/gmail-accounts.html-38-
/workspace/bulkmailer/static/gmail-accounts.html-39- /workspace/bulkmailer/static/gmail-accounts.html-40- /workspace/bulkmailer/static/gmail-accounts.html-41-
/workspace/bulkmailer/static/gmail-accounts.html-42- -- /workspace/bulkmailer/static/js/pages/gmail-accounts.js-80- const fd = new FormData(); fd.append("file", f); /workspace/bulkmailer/static/js/pages/gmail-accounts.js-81- try { /workspace/bulkmailer/static/js/pages/gmail-accounts.js-82- const d = await api("/gmail/accounts/import-config", { method: "POST", body: fd }); /workspace/bulkmailer/static/js/pages/gmail-accounts.js:83: $("#cfgRedirect").textContent = d.redirect_uri; /workspace/bulkmailer/static/js/pages/gmail-accounts.js-84- toast(`Configuration imported (project: ${d.project_id}). You can now connect accounts.`, "ok"); /workspace/bulkmailer/static/js/pages/gmail-accounts.js-85- $("#cfgForm").reset(); /workspace/bulkmailer/static/js/pages/gmail-accounts.js-86- } catch (err) { toast("Import failed: " + err.message, "err"); } /workspace/bulkmailer/static/js/pages/gmail-accounts.js-87- }); /workspace/bulkmailer/static/js/pages/gmail-accounts.js-88- /workspace/bulkmailer/static/js/pages/gmail-accounts.js-89- // ---- Clear all ---- ---endpoint--- 192- {"count": len(rows), "revoked_at_google": revoked}, 193- request.client.host if request else None) 194- return {"ok": True, "removed": len(rows), "revoked_at_google": revoked} 195- 196- 197:@router.post("/accounts/import-config") 198-async def import_client_config(file: UploadFile = File(...), 199- user: User = Depends(get_current_user_csrf), 200- db: AsyncSession = Depends(get_db), request: Request = None): 201- """Import & validate a Google OAuth client_secret JSON (never echoed back).""" 202- from app.core.config import settings 203- content = await file.read() 204- if len(content) > 1024 * 512: 205- raise HTTPException(413, "File too large (max 512 KB)") 206- if not file.filename.lower().endswith(".json"): 207- raise HTTPException(400, "Only .json configuration files are accepted") 208- try: 209- parsed = gmail_service.parse_client_json(content) 210- except gmail_service.GmailOauthError as e: 211- raise HTTPException(400, str(e)) 212- row = OAuthClientConfig( 213- name=parsed["project_id"] or file.filename[:100], 214- user_id=user.id, 215- client_id=parsed["client_id"], 216- client_secret=parsed["client_secret"], # encrypted via property setter 217- project_id=parsed["project_id"], 218- redirect_uri=f"{settings.BASE_URL}/auth/google/callback", 219- ) 220- db.add(row) 221- await db.commit() 222- await log_audit(db, user.id, "oauth_config.imported", "oauth_client_config", row.id, 223- {"project": parsed["project_id"]}, request.client.host if request else None) 224- return {"ok": True, "id": row.id, "project_id": parsed["project_id"], 225- "client_id_preview": parsed["client_id"][:12] + "…", 226- "redirect_uri": row.redirect_uri} 227-