""" Publisher ViewSet Phase 5: Sites Renderer & Publishing """ from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.response import Response from igny8_core.api.base import SiteSectorModelViewSet from igny8_core.api.permissions import IsAuthenticatedAndActive, IsEditorOrAbove from igny8_core.api.response import success_response, error_response from igny8_core.api.throttles import DebugScopedRateThrottle from igny8_core.business.publishing.models import PublishingRecord, DeploymentRecord from igny8_core.business.publishing.services.publisher_service import PublisherService from igny8_core.business.site_building.models import SiteBlueprint class PublishingRecordViewSet(SiteSectorModelViewSet): """ ViewSet for PublishingRecord model. """ queryset = PublishingRecord.objects.select_related('content', 'site_blueprint') permission_classes = [IsAuthenticatedAndActive, IsEditorOrAbove] throttle_scope = 'publisher' throttle_classes = [DebugScopedRateThrottle] def get_serializer_class(self): # Will be created in next step from rest_framework import serializers class PublishingRecordSerializer(serializers.ModelSerializer): class Meta: model = PublishingRecord fields = '__all__' return PublishingRecordSerializer class DeploymentRecordViewSet(SiteSectorModelViewSet): """ ViewSet for DeploymentRecord model. """ queryset = DeploymentRecord.objects.select_related('site_blueprint') permission_classes = [IsAuthenticatedAndActive, IsEditorOrAbove] throttle_scope = 'publisher' throttle_classes = [DebugScopedRateThrottle] def get_serializer_class(self): # Will be created in next step from rest_framework import serializers class DeploymentRecordSerializer(serializers.ModelSerializer): class Meta: model = DeploymentRecord fields = '__all__' return DeploymentRecordSerializer class PublisherViewSet(viewsets.ViewSet): """ Publisher actions for publishing content and sites. """ permission_classes = [IsAuthenticatedAndActive, IsEditorOrAbove] throttle_scope = 'publisher' throttle_classes = [DebugScopedRateThrottle] def __init__(self, **kwargs): super().__init__(**kwargs) self.publisher_service = PublisherService() @action(detail=False, methods=['post'], url_path='publish') def publish(self, request): """ Publish content or site to destinations. Request body: { "content_id": 123, # Optional: content to publish "site_blueprint_id": 456, # Optional: site to publish "destinations": ["wordpress", "sites"] # Required: list of destinations } """ content_id = request.data.get('content_id') site_blueprint_id = request.data.get('site_blueprint_id') destinations = request.data.get('destinations', []) if not destinations: return error_response( 'destinations is required', status.HTTP_400_BAD_REQUEST, request ) account = request.account if site_blueprint_id: # Publish site try: blueprint = SiteBlueprint.objects.get(id=site_blueprint_id, account=account) except SiteBlueprint.DoesNotExist: return error_response( f'Site blueprint {site_blueprint_id} not found', status.HTTP_404_NOT_FOUND, request ) if 'sites' in destinations: result = self.publisher_service.publish_to_sites(blueprint) return success_response(result, request=request) else: return error_response( 'Site publishing only supports "sites" destination', status.HTTP_400_BAD_REQUEST, request ) elif content_id: # Publish content result = self.publisher_service.publish_content( content_id, destinations, account ) return success_response(result, request=request) else: return error_response( 'Either content_id or site_blueprint_id is required', status.HTTP_400_BAD_REQUEST, request ) @action(detail=False, methods=['post'], url_path='deploy/(?P[^/.]+)') def deploy(self, request, blueprint_id): """ Deploy site blueprint to Sites renderer. POST /api/v1/publisher/deploy/{blueprint_id}/ """ account = request.account try: blueprint = SiteBlueprint.objects.get(id=blueprint_id, account=account) except SiteBlueprint.DoesNotExist: return error_response( f'Site blueprint {blueprint_id} not found', status.HTTP_404_NOT_FOUND, request ) result = self.publisher_service.publish_to_sites(blueprint) response_status = status.HTTP_202_ACCEPTED if result.get('success') else status.HTTP_400_BAD_REQUEST return success_response(result, request=request, status_code=response_status) @action(detail=False, methods=['get'], url_path='status/(?P[^/.]+)') def get_status(self, request, id): """ Get publishing/deployment status. GET /api/v1/publisher/status/{id}/ """ account = request.account # Try deployment record first try: deployment = DeploymentRecord.objects.get(id=id, account=account) return success_response({ 'type': 'deployment', 'status': deployment.status, 'version': deployment.version, 'deployed_version': deployment.deployed_version, 'deployment_url': deployment.deployment_url, 'deployed_at': deployment.deployed_at, 'error_message': deployment.error_message, }, request=request) except DeploymentRecord.DoesNotExist: pass # Try publishing record try: publishing = PublishingRecord.objects.get(id=id, account=account) return success_response({ 'type': 'publishing', 'status': publishing.status, 'destination': publishing.destination, 'destination_url': publishing.destination_url, 'published_at': publishing.published_at, 'error_message': publishing.error_message, }, request=request) except PublishingRecord.DoesNotExist: return error_response( f'Record {id} not found', status.HTTP_404_NOT_FOUND, request )