Harden backlink lead campaign ownership
This commit is contained in:
@@ -22,7 +22,10 @@ from services.backlink_outreach_models import (
|
||||
SuppressionAddRequest,
|
||||
)
|
||||
from services.backlink_outreach_service import backlink_outreach_service
|
||||
from services.backlink_outreach_storage import BacklinkOutreachStorageService
|
||||
from services.backlink_outreach_storage import (
|
||||
BacklinkCampaignNotFoundError,
|
||||
BacklinkOutreachStorageService,
|
||||
)
|
||||
from services.backlink_outreach_sender import backlink_outreach_sender
|
||||
from services.backlink_outreach_reply_monitor import backlink_outreach_reply_monitor
|
||||
from services.backlink_outreach_template_generator import (
|
||||
@@ -87,9 +90,14 @@ async def discover_deep_backlink_opportunities(
|
||||
):
|
||||
"""Enhanced discovery using Exa neural search + DuckDuckGo with full-page scraping."""
|
||||
user_id = _resolve_user_id(current_user)
|
||||
result = await backlink_outreach_service.deep_discover(payload.keyword, payload.max_results)
|
||||
storage = None
|
||||
if payload.campaign_id:
|
||||
storage = BacklinkOutreachStorageService()
|
||||
if not storage.get_campaign(payload.campaign_id, user_id):
|
||||
raise HTTPException(status_code=404, detail="Campaign not found")
|
||||
|
||||
result = await backlink_outreach_service.deep_discover(payload.keyword, payload.max_results)
|
||||
if payload.campaign_id:
|
||||
saved = 0
|
||||
save_failed = 0
|
||||
for opp in result.get("opportunities", []):
|
||||
@@ -183,7 +191,9 @@ async def add_campaign_lead(
|
||||
notes=payload.notes,
|
||||
)
|
||||
return lead
|
||||
except Exception as e:
|
||||
except BacklinkCampaignNotFoundError:
|
||||
raise HTTPException(status_code=404, detail="Campaign not found")
|
||||
except Exception:
|
||||
raise HTTPException(status_code=500, detail="Failed to add lead")
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@ from models.backlink_outreach_models import (
|
||||
)
|
||||
|
||||
|
||||
class BacklinkCampaignNotFoundError(RuntimeError):
|
||||
"""Raised when a backlink campaign is missing or not owned by the user."""
|
||||
|
||||
|
||||
class BacklinkOutreachStorageService:
|
||||
_NEW_LEAD_COLUMNS = [
|
||||
"url", "page_title", "snippet", "confidence_score", "discovery_source", "notes"
|
||||
@@ -120,6 +124,14 @@ class BacklinkOutreachStorageService:
|
||||
|
||||
# -- Lead CRUD --
|
||||
|
||||
def _campaign_belongs_to_user(self, db, campaign_id: str, user_id: str) -> bool:
|
||||
return (
|
||||
db.query(BacklinkCampaign)
|
||||
.filter(BacklinkCampaign.id == campaign_id, BacklinkCampaign.user_id == user_id)
|
||||
.first()
|
||||
is not None
|
||||
)
|
||||
|
||||
def add_lead(
|
||||
self,
|
||||
campaign_id: str,
|
||||
@@ -138,6 +150,9 @@ class BacklinkOutreachStorageService:
|
||||
if not db:
|
||||
raise RuntimeError("Database session unavailable")
|
||||
try:
|
||||
if not self._campaign_belongs_to_user(db, campaign_id, user_id):
|
||||
raise BacklinkCampaignNotFoundError("Campaign not found")
|
||||
|
||||
lead = BacklinkLead(
|
||||
id=f"bl_{uuid4().hex[:16]}",
|
||||
campaign_id=campaign_id,
|
||||
@@ -164,6 +179,9 @@ class BacklinkOutreachStorageService:
|
||||
if not db:
|
||||
raise RuntimeError("Database session unavailable")
|
||||
try:
|
||||
if not self._campaign_belongs_to_user(db, campaign_id, user_id):
|
||||
raise BacklinkCampaignNotFoundError("Campaign not found")
|
||||
|
||||
added = []
|
||||
for data in leads_data:
|
||||
lead = BacklinkLead(
|
||||
|
||||
Reference in New Issue
Block a user