Harden backlink lead campaign ownership
This commit is contained in:
@@ -22,7 +22,10 @@ from services.backlink_outreach_models import (
|
|||||||
SuppressionAddRequest,
|
SuppressionAddRequest,
|
||||||
)
|
)
|
||||||
from services.backlink_outreach_service import backlink_outreach_service
|
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_sender import backlink_outreach_sender
|
||||||
from services.backlink_outreach_reply_monitor import backlink_outreach_reply_monitor
|
from services.backlink_outreach_reply_monitor import backlink_outreach_reply_monitor
|
||||||
from services.backlink_outreach_template_generator import (
|
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."""
|
"""Enhanced discovery using Exa neural search + DuckDuckGo with full-page scraping."""
|
||||||
user_id = _resolve_user_id(current_user)
|
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:
|
if payload.campaign_id:
|
||||||
storage = BacklinkOutreachStorageService()
|
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
|
saved = 0
|
||||||
save_failed = 0
|
save_failed = 0
|
||||||
for opp in result.get("opportunities", []):
|
for opp in result.get("opportunities", []):
|
||||||
@@ -183,7 +191,9 @@ async def add_campaign_lead(
|
|||||||
notes=payload.notes,
|
notes=payload.notes,
|
||||||
)
|
)
|
||||||
return lead
|
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")
|
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:
|
class BacklinkOutreachStorageService:
|
||||||
_NEW_LEAD_COLUMNS = [
|
_NEW_LEAD_COLUMNS = [
|
||||||
"url", "page_title", "snippet", "confidence_score", "discovery_source", "notes"
|
"url", "page_title", "snippet", "confidence_score", "discovery_source", "notes"
|
||||||
@@ -120,6 +124,14 @@ class BacklinkOutreachStorageService:
|
|||||||
|
|
||||||
# -- Lead CRUD --
|
# -- 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(
|
def add_lead(
|
||||||
self,
|
self,
|
||||||
campaign_id: str,
|
campaign_id: str,
|
||||||
@@ -138,6 +150,9 @@ class BacklinkOutreachStorageService:
|
|||||||
if not db:
|
if not db:
|
||||||
raise RuntimeError("Database session unavailable")
|
raise RuntimeError("Database session unavailable")
|
||||||
try:
|
try:
|
||||||
|
if not self._campaign_belongs_to_user(db, campaign_id, user_id):
|
||||||
|
raise BacklinkCampaignNotFoundError("Campaign not found")
|
||||||
|
|
||||||
lead = BacklinkLead(
|
lead = BacklinkLead(
|
||||||
id=f"bl_{uuid4().hex[:16]}",
|
id=f"bl_{uuid4().hex[:16]}",
|
||||||
campaign_id=campaign_id,
|
campaign_id=campaign_id,
|
||||||
@@ -164,6 +179,9 @@ class BacklinkOutreachStorageService:
|
|||||||
if not db:
|
if not db:
|
||||||
raise RuntimeError("Database session unavailable")
|
raise RuntimeError("Database session unavailable")
|
||||||
try:
|
try:
|
||||||
|
if not self._campaign_belongs_to_user(db, campaign_id, user_id):
|
||||||
|
raise BacklinkCampaignNotFoundError("Campaign not found")
|
||||||
|
|
||||||
added = []
|
added = []
|
||||||
for data in leads_data:
|
for data in leads_data:
|
||||||
lead = BacklinkLead(
|
lead = BacklinkLead(
|
||||||
|
|||||||
Reference in New Issue
Block a user