django bacekdn opeartioanl fixes and site wp integration api fixes
This commit is contained in:
@@ -5,6 +5,12 @@ from django.contrib import admin
|
||||
from django.utils.html import format_html
|
||||
from django.contrib import messages
|
||||
from unfold.admin import ModelAdmin
|
||||
from unfold.contrib.filters.admin import (
|
||||
RelatedDropdownFilter,
|
||||
ChoicesDropdownFilter,
|
||||
DropdownFilter,
|
||||
RangeDateFilter,
|
||||
)
|
||||
from simple_history.admin import SimpleHistoryAdmin
|
||||
from igny8_core.admin.base import AccountAdminMixin, Igny8ModelAdmin
|
||||
from igny8_core.business.billing.models import (
|
||||
@@ -20,7 +26,6 @@ from igny8_core.business.billing.models import (
|
||||
from .models import CreditTransaction, CreditUsageLog, AccountPaymentMethod
|
||||
from import_export.admin import ExportMixin, ImportExportMixin
|
||||
from import_export import resources
|
||||
from rangefilter.filters import DateRangeFilter
|
||||
|
||||
|
||||
class CreditTransactionResource(resources.ModelResource):
|
||||
@@ -36,7 +41,11 @@ class CreditTransactionResource(resources.ModelResource):
|
||||
class CreditTransactionAdmin(ExportMixin, AccountAdminMixin, Igny8ModelAdmin):
|
||||
resource_class = CreditTransactionResource
|
||||
list_display = ['id', 'account', 'transaction_type', 'amount', 'balance_after', 'description', 'created_at']
|
||||
list_filter = ['transaction_type', ('created_at', DateRangeFilter), 'account']
|
||||
list_filter = [
|
||||
('transaction_type', ChoicesDropdownFilter),
|
||||
('created_at', RangeDateFilter),
|
||||
('account', RelatedDropdownFilter),
|
||||
]
|
||||
search_fields = ['description', 'account__name']
|
||||
readonly_fields = ['created_at']
|
||||
date_hierarchy = 'created_at'
|
||||
@@ -188,7 +197,13 @@ class PaymentAdmin(ExportMixin, AccountAdminMixin, SimpleHistoryAdmin, Igny8Mode
|
||||
'approved_by',
|
||||
'processed_at',
|
||||
]
|
||||
list_filter = ['status', 'payment_method', 'currency', ('created_at', DateRangeFilter), ('processed_at', DateRangeFilter)]
|
||||
list_filter = [
|
||||
('status', ChoicesDropdownFilter),
|
||||
('payment_method', ChoicesDropdownFilter),
|
||||
('currency', ChoicesDropdownFilter),
|
||||
('created_at', RangeDateFilter),
|
||||
('processed_at', RangeDateFilter),
|
||||
]
|
||||
search_fields = [
|
||||
'invoice__invoice_number',
|
||||
'account__name',
|
||||
@@ -654,10 +669,10 @@ class PlanLimitUsageAdmin(ExportMixin, AccountAdminMixin, Igny8ModelAdmin):
|
||||
'created_at',
|
||||
]
|
||||
list_filter = [
|
||||
'limit_type',
|
||||
('period_start', DateRangeFilter),
|
||||
('period_end', DateRangeFilter),
|
||||
'account',
|
||||
('limit_type', ChoicesDropdownFilter),
|
||||
('period_start', RangeDateFilter),
|
||||
('period_end', RangeDateFilter),
|
||||
('account', RelatedDropdownFilter),
|
||||
]
|
||||
search_fields = ['account__name']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
@@ -67,25 +67,23 @@ class IntegrationViewSet(SiteSectorModelViewSet):
|
||||
api_key = serializers.SerializerMethodField()
|
||||
|
||||
def get_api_key(self, obj):
|
||||
"""Return the API key from encrypted credentials"""
|
||||
credentials = obj.get_credentials()
|
||||
return credentials.get('api_key', '')
|
||||
"""Return the API key from Site.wp_api_key (SINGLE source of truth)"""
|
||||
# API key is stored on Site model, not in SiteIntegration credentials
|
||||
return obj.site.wp_api_key or ''
|
||||
|
||||
def validate(self, data):
|
||||
"""
|
||||
Custom validation for WordPress integrations.
|
||||
API key is the only required authentication method.
|
||||
API key is stored on Site model, not in SiteIntegration.
|
||||
"""
|
||||
validated_data = super().validate(data)
|
||||
|
||||
# For WordPress platform, require API key only
|
||||
# For WordPress platform, check API key exists on Site (not in credentials_json)
|
||||
if validated_data.get('platform') == 'wordpress':
|
||||
credentials = validated_data.get('credentials_json', {})
|
||||
|
||||
# API key is required for all WordPress integrations
|
||||
if not credentials.get('api_key'):
|
||||
site = validated_data.get('site') or getattr(self.instance, 'site', None)
|
||||
if site and not site.wp_api_key:
|
||||
raise serializers.ValidationError({
|
||||
'credentials_json': 'API key is required for WordPress integration.'
|
||||
'site': 'Site must have an API key generated before creating WordPress integration.'
|
||||
})
|
||||
|
||||
return validated_data
|
||||
@@ -198,7 +196,7 @@ class IntegrationViewSet(SiteSectorModelViewSet):
|
||||
# Try to find an existing integration for this site+platform
|
||||
integration = SiteIntegration.objects.filter(site=site, platform='wordpress').first()
|
||||
|
||||
# If not found, create and save the integration to database
|
||||
# If not found, create and save the integration to database (for status tracking, not credentials)
|
||||
integration_created = False
|
||||
if not integration:
|
||||
integration = SiteIntegration.objects.create(
|
||||
@@ -207,7 +205,7 @@ class IntegrationViewSet(SiteSectorModelViewSet):
|
||||
platform='wordpress',
|
||||
platform_type='cms',
|
||||
config_json={'site_url': site_url} if site_url else {},
|
||||
credentials_json={'api_key': api_key} if api_key else {},
|
||||
credentials_json={}, # API key is stored in Site.wp_api_key, not here
|
||||
is_active=True,
|
||||
sync_enabled=True
|
||||
)
|
||||
@@ -805,27 +803,38 @@ class IntegrationViewSet(SiteSectorModelViewSet):
|
||||
random_suffix = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10))
|
||||
api_key = f"igny8_site_{site_id}_{timestamp}_{random_suffix}"
|
||||
|
||||
# Get or create SiteIntegration
|
||||
# SINGLE SOURCE OF TRUTH: Store API key ONLY in Site.wp_api_key
|
||||
# This is where APIKeyAuthentication validates against
|
||||
site.wp_api_key = api_key
|
||||
site.save(update_fields=['wp_api_key'])
|
||||
|
||||
# Get or create SiteIntegration (for integration status/config, NOT credentials)
|
||||
integration, created = SiteIntegration.objects.get_or_create(
|
||||
site=site,
|
||||
platform='wordpress',
|
||||
defaults={
|
||||
'integration_type': 'wordpress',
|
||||
'account': site.account,
|
||||
'platform': 'wordpress',
|
||||
'platform_type': 'cms',
|
||||
'is_active': True,
|
||||
'credentials_json': {'api_key': api_key},
|
||||
'sync_enabled': True,
|
||||
'credentials_json': {}, # Empty - API key is on Site model
|
||||
'config_json': {}
|
||||
}
|
||||
)
|
||||
|
||||
# If integration already exists, update the API key
|
||||
# If integration already exists, just ensure it's active
|
||||
if not created:
|
||||
credentials = integration.get_credentials()
|
||||
credentials['api_key'] = api_key
|
||||
integration.credentials_json = credentials
|
||||
integration.is_active = True
|
||||
integration.sync_enabled = True
|
||||
# Clear any old credentials_json API key (migrate to Site.wp_api_key)
|
||||
if integration.credentials_json.get('api_key'):
|
||||
integration.credentials_json = {}
|
||||
integration.save()
|
||||
|
||||
logger.info(
|
||||
f"Generated new API key for site {site.name} (ID: {site_id}), "
|
||||
f"integration {'created' if created else 'updated'}"
|
||||
f"stored in Site.wp_api_key (single source of truth)"
|
||||
)
|
||||
|
||||
# Serialize the integration with the new key
|
||||
|
||||
@@ -122,10 +122,10 @@ def wordpress_status_webhook(request):
|
||||
request=request
|
||||
)
|
||||
|
||||
# Verify API key matches integration
|
||||
stored_api_key = integration.credentials_json.get('api_key')
|
||||
# Verify API key matches Site.wp_api_key (SINGLE source of truth)
|
||||
stored_api_key = integration.site.wp_api_key
|
||||
if not stored_api_key or stored_api_key != api_key:
|
||||
logger.error(f"[wordpress_status_webhook] Invalid API key for integration {integration.id}")
|
||||
logger.error(f"[wordpress_status_webhook] Invalid API key for site {integration.site.id}")
|
||||
return error_response(
|
||||
error='Invalid API key',
|
||||
status_code=http_status.HTTP_401_UNAUTHORIZED,
|
||||
@@ -293,8 +293,8 @@ def wordpress_metadata_webhook(request):
|
||||
request=request
|
||||
)
|
||||
|
||||
# Verify API key
|
||||
stored_api_key = integration.credentials_json.get('api_key')
|
||||
# Verify API key against Site.wp_api_key (SINGLE source of truth)
|
||||
stored_api_key = integration.site.wp_api_key
|
||||
if not stored_api_key or stored_api_key != api_key:
|
||||
return error_response(
|
||||
error='Invalid API key',
|
||||
|
||||
@@ -114,7 +114,6 @@ class KeywordsAdmin(ImportExportMixin, SiteSectorAdminMixin, Igny8ModelAdmin):
|
||||
'bulk_assign_cluster',
|
||||
'bulk_set_status_active',
|
||||
'bulk_set_status_inactive',
|
||||
'bulk_soft_delete',
|
||||
]
|
||||
|
||||
@admin.display(description='Keyword')
|
||||
|
||||
Reference in New Issue
Block a user