django bacekdn opeartioanl fixes and site wp integration api fixes

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-11 21:54:08 +00:00
parent 00ef985a5f
commit 3925ddf894
16 changed files with 2045 additions and 89 deletions

View File

@@ -244,14 +244,15 @@ class IntegrationService:
}
}
# Get API key from site
# Get API key from Site.wp_api_key (SINGLE source of truth)
# API key is stored on Site model for authentication by APIKeyAuthentication
api_key = integration.site.wp_api_key
if not api_key:
return {
'success': False,
'message': 'API key not configured.',
'details': {}
'message': 'API key not configured. Generate an API key in Site Settings.',
'details': {'site_id': integration.site.id, 'site_name': integration.site.name}
}
# Initialize health check results

View File

@@ -39,8 +39,8 @@ class SyncMetadataService:
try:
# Get WordPress site URL and API key
site_url = integration.config_json.get('site_url', '')
credentials = integration.get_credentials()
api_key = credentials.get('api_key', '')
# API key is stored in Site.wp_api_key (SINGLE source of truth)
api_key = integration.site.wp_api_key or ''
if not site_url:
return {
@@ -51,7 +51,7 @@ class SyncMetadataService:
if not api_key:
return {
'success': False,
'error': 'Missing api_key in integration credentials'
'error': 'API key not configured for site. Generate one in Site Settings.'
}
# Call WordPress metadata endpoint

View File

@@ -142,44 +142,67 @@ class Keywords(SoftDeletableModel, SiteSectorBaseModel):
@property
def keyword(self):
"""Get keyword text from seed_keyword"""
return self.seed_keyword.keyword if self.seed_keyword else ''
try:
return self.seed_keyword.keyword if self.seed_keyword else ''
except self.__class__.seed_keyword.RelatedObjectDoesNotExist:
return ''
@property
def volume(self):
"""Get volume from override or seed_keyword"""
return self.volume_override if self.volume_override is not None else (self.seed_keyword.volume if self.seed_keyword else 0)
try:
seed_kw = self.seed_keyword
except self.__class__.seed_keyword.RelatedObjectDoesNotExist:
seed_kw = None
return self.volume_override if self.volume_override is not None else (seed_kw.volume if seed_kw else 0)
@property
def difficulty(self):
"""Get difficulty from override or seed_keyword"""
return self.difficulty_override if self.difficulty_override is not None else (self.seed_keyword.difficulty if self.seed_keyword else 0)
try:
seed_kw = self.seed_keyword
except self.__class__.seed_keyword.RelatedObjectDoesNotExist:
seed_kw = None
return self.difficulty_override if self.difficulty_override is not None else (seed_kw.difficulty if seed_kw else 0)
@property
def country(self):
"""Get country from seed_keyword"""
return self.seed_keyword.country if self.seed_keyword else 'US'
try:
return self.seed_keyword.country if self.seed_keyword else 'US'
except self.__class__.seed_keyword.RelatedObjectDoesNotExist:
return 'US'
def save(self, *args, **kwargs):
"""Validate that seed_keyword's industry/sector matches site's industry/sector"""
if self.seed_keyword and self.site and self.sector:
# Skip validation if seed_keyword is None (during soft delete or orphaned)
try:
seed_kw = self.seed_keyword
except self.__class__.seed_keyword.RelatedObjectDoesNotExist:
seed_kw = None
if seed_kw and self.site and self.sector:
# Validate industry match
if self.site.industry != self.seed_keyword.industry:
if self.site.industry != seed_kw.industry:
from django.core.exceptions import ValidationError
raise ValidationError(
f"SeedKeyword industry ({self.seed_keyword.industry.name}) must match site industry ({self.site.industry.name})"
f"SeedKeyword industry ({seed_kw.industry.name}) must match site industry ({self.site.industry.name})"
)
# Validate sector match (site sector's industry_sector must match seed_keyword's sector)
if self.sector.industry_sector != self.seed_keyword.sector:
if self.sector.industry_sector != seed_kw.sector:
from django.core.exceptions import ValidationError
raise ValidationError(
f"SeedKeyword sector ({self.seed_keyword.sector.name}) must match site sector's industry sector ({self.sector.industry_sector.name if self.sector.industry_sector else 'None'})"
f"SeedKeyword sector ({seed_kw.sector.name}) must match site sector's industry sector ({self.sector.industry_sector.name if self.sector.industry_sector else 'None'})"
)
super().save(*args, **kwargs)
def __str__(self):
return self.keyword
try:
return self.seed_keyword.keyword if self.seed_keyword else f'Keyword #{self.pk}'
except self.__class__.seed_keyword.RelatedObjectDoesNotExist:
return f'Keyword #{self.pk} (orphaned)'
class ContentIdeas(SoftDeletableModel, SiteSectorBaseModel):

View File

@@ -139,9 +139,13 @@ class PublisherService:
if integration:
logger.info(f"[PublisherService._publish_to_destination] ✅ Integration found: id={integration.id}")
# Merge config_json and credentials_json
# Merge config_json (site_url, etc.)
destination_config.update(integration.config_json or {})
destination_config.update(integration.get_credentials() or {})
# API key is stored in Site.wp_api_key (SINGLE source of truth)
if integration.site.wp_api_key:
destination_config['api_key'] = integration.site.wp_api_key
logger.info(f"[PublisherService._publish_to_destination] 🔑 Config merged: has_api_key={bool(destination_config.get('api_key'))}, has_site_url={bool(destination_config.get('site_url'))}")
# Ensure site_url is set (from config or from site model)
@@ -342,13 +346,16 @@ class PublisherService:
destinations = []
for integration in integrations:
config = integration.config_json.copy()
credentials = integration.get_credentials()
destination_config = {
'platform': integration.platform,
**config,
**credentials
}
# API key is stored in Site.wp_api_key (SINGLE source of truth)
if integration.site.wp_api_key:
destination_config['api_key'] = integration.site.wp_api_key
destinations.append(destination_config)
# Also add 'sites' destination if not in platforms filter or if platforms is None