fix: resolve remaining 5 QA audit findings (#3, #8, #10, #11, #12)

#3 — Duplicate prospect handling: add_lead now checks (campaign_id, url)
     before insert; bulk_add_leads skips existing URLs.
#8 — Atomic rate limiting: try_increment_* methods atomically check cap
     and increment in a single session; router uses these before send.
#10 — Reply matching via Message-ID: sender generates Message-ID header,
     stored on OutreachAttempt; reply monitor parses In-Reply-To/References;
     poll_replies matches by message_id first, falls back to from_email.
#11 — Save-to-campaign uses existing store results instead of
      re-running expensive deepDiscover.
#12 — Lead status Literal type: Pydantic models enforce valid status
      values; backend validates via LEAD_VALID_STATUSES frozenset;
      frontend API typed as LeadStatus union.
This commit is contained in:
ajaysi
2026-06-03 20:06:11 +05:30
parent 259194c289
commit 8699ffc27d
9 changed files with 226 additions and 103 deletions

View File

@@ -368,26 +368,40 @@ async def send_outreach(
lead_email = (lead.get("email") or "") if lead else ""
if result.status == "approved" and result.policy_allowed and not result.duplicate and lead_email:
send_result = await backlink_outreach_sender.send_email(
to_email=lead_email,
subject=subject,
body=body,
from_email=payload.sender_email,
)
if send_result.success:
storage.update_attempt_status(result.attempt_id, "sent", user_id=user_id)
result.status = "sent"
result.effective_sender_email = send_result.effective_sender_email or result.effective_sender_email
storage.mark_idempotency(payload.idempotency_key, user_id)
storage.increment_user_send_counter(user_id)
domain = lead_email.split("@")[-1] if "@" in lead_email else "unknown"
storage.increment_domain_send_counter(domain, user_id=user_id)
domain = lead_email.split("@")[-1] if "@" in lead_email else "unknown"
user_within_cap, _ = storage.try_increment_user_send_counter(user_id)
domain_within_cap, _ = storage.try_increment_domain_send_counter(domain, user_id=user_id)
if not (user_within_cap and domain_within_cap):
reasons = []
if not user_within_cap:
reasons.append("user_daily_cap_exceeded")
if not domain_within_cap:
reasons.append("domain_daily_cap_exceeded")
reason_str = f"rate_limit_hit; retry_policy={backlink_outreach_service.SMTP_RETRY_POLICY}"
storage.update_attempt_status(result.attempt_id, "blocked", decision_reason=reason_str, user_id=user_id)
result.status = "blocked"
result.policy_reasons = reasons
else:
reason = f"smtp_send_failed; retry_policy={backlink_outreach_service.SMTP_RETRY_POLICY}"
storage.update_attempt_status(result.attempt_id, "failed", decision_reason=reason, user_id=user_id)
result.status = "failed"
result.policy_reasons = ["smtp_send_failed"]
result.retry_policy = backlink_outreach_service.SMTP_RETRY_POLICY
send_result = await backlink_outreach_sender.send_email(
to_email=lead_email,
subject=subject,
body=body,
from_email=payload.sender_email,
)
if send_result.success:
storage.update_attempt_status(result.attempt_id, "sent", user_id=user_id)
result.status = "sent"
result.effective_sender_email = send_result.effective_sender_email or result.effective_sender_email
if send_result.message_id:
storage.update_attempt_message_id(result.attempt_id, send_result.message_id, user_id=user_id)
storage.mark_idempotency(payload.idempotency_key, user_id)
else:
reason = f"smtp_send_failed; retry_policy={backlink_outreach_service.SMTP_RETRY_POLICY}"
storage.update_attempt_status(result.attempt_id, "failed", decision_reason=reason, user_id=user_id)
result.status = "failed"
result.policy_reasons = ["smtp_send_failed"]
result.retry_policy = backlink_outreach_service.SMTP_RETRY_POLICY
elif result.status == "approved" and result.policy_allowed and not result.duplicate and not lead_email:
reason = f"lead_has_no_email; retry_policy={backlink_outreach_service.SMTP_RETRY_POLICY}"
storage.update_attempt_status(result.attempt_id, "failed", decision_reason=reason, user_id=user_id)
@@ -448,7 +462,18 @@ async def poll_replies(
if storage.reply_exists(from_email, subject, user_id=user_id):
skipped += 1
continue
attempt_id = storage.find_attempt_by_from_email(from_email, user_id=user_id) or ""
attempt_id = ""
in_reply_to = raw.get("in_reply_to", "")
references = raw.get("references", "")
if in_reply_to:
attempt_id = storage.find_attempt_by_message_id(in_reply_to, user_id=user_id) or ""
if not attempt_id and references:
mid = references.split()[-1]
attempt_id = storage.find_attempt_by_message_id(mid, user_id=user_id) or ""
if not attempt_id:
attempt_id = storage.find_attempt_by_from_email(from_email, user_id=user_id) or ""
reply = storage.add_reply(
attempt_id=attempt_id,
from_email=from_email,