This commit is contained in:
debu-2 2026-10-12 23:19:09 +05:30
parent 8726c931fa
commit 8c46d091ff
49 changed files with 1607 additions and 712 deletions

View file

@ -8,6 +8,7 @@ addressed by another tenant.
from __future__ import annotations
import logging
import re
import secrets
import uuid
from datetime import datetime, timezone
@ -101,11 +102,13 @@ def _quality_label(value: str | None) -> str | None:
def _graph_error_message(data: Any, fallback: str) -> str:
message = fallback
if isinstance(data, dict):
error = data.get("error")
if isinstance(error, dict) and error.get("message"):
return str(error["message"])[:500]
return fallback
message = str(error["message"])
message = re.sub(r"(access_token|token|secret)=([^&\s]+)", r"\1=redacted", message, flags=re.I)
return message[:500]
async def _graph(
@ -607,6 +610,78 @@ async def complete_signup(db: Session, user: UserAccount, payload: dict[str, Any
return {"status": overall, "message": message, "accounts": [serialize_account(account) for account in accounts]}
_META_ID = re.compile(r"^\d{6,32}$")
def _clean_token(value: str) -> str:
token = "".join(str(value or "").split())
if token.lower().startswith("bearer"):
token = token[6:]
return token
async def connect_with_token(
db: Session,
user: UserAccount,
access_token: str,
waba_id: str,
phone_number_id: str | None = None,
) -> dict[str, Any]:
"""Store a customer-supplied WhatsApp token and provision that account.
This is the non-embedded alternative to Embedded Signup. The token is
kept on the connection row and is not returned to the browser.
"""
token = _clean_token(access_token)
account_id = "".join(str(waba_id or "").split())
phone_id = "".join(str(phone_number_id or "").split()) or None
if len(token) < 20:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Paste the access token from Meta.")
if not _META_ID.match(account_id):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Enter the WhatsApp Business account ID from Meta. It is a number.",
)
if phone_id and not _META_ID.match(phone_id):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Phone number ID should be the number Meta shows for that phone.",
)
connection = WhatsAppMetaConnection(
user_id=user.id,
status="pending",
granted_scopes="manual",
access_token=token,
)
db.add(connection)
db.flush()
_audit(
db,
user,
"token_saved",
"Customer saved a WhatsApp access token",
connection_id=connection.id,
waba_id=account_id,
)
account = await _provision_waba(db, user, connection, token, account_id, phone_id)
db.commit()
db.refresh(account)
if account.status == "connected":
overall = "connected"
message = "WhatsApp Business is connected."
elif account.status == "incomplete":
overall = "incomplete"
message = account.last_error or "The account was saved, but the phone number still needs verification."
elif account.status == "expired":
overall = "expired"
message = account.last_error or "Meta did not accept this access token."
else:
overall = "failed"
message = account.last_error or "Urmi could not finish WhatsApp setup."
return {"status": overall, "message": message, "accounts": [serialize_account(account)]}
async def refresh_account(db: Session, user: UserAccount, account_id: Any) -> dict[str, Any]:
account = _owned_account(db, user, account_id)
connection = account.connection