diff --git a/backend/celerybeat-schedule b/backend/celerybeat-schedule index 52dda699..b9525287 100644 Binary files a/backend/celerybeat-schedule and b/backend/celerybeat-schedule differ diff --git a/backend/igny8_core/modules/billing/views.py b/backend/igny8_core/modules/billing/views.py index d63622f8..68cc7692 100644 --- a/backend/igny8_core/modules/billing/views.py +++ b/backend/igny8_core/modules/billing/views.py @@ -41,70 +41,48 @@ class CreditBalanceViewSet(viewsets.ViewSet): @action(detail=False, methods=['get']) def balance(self, request): """Get current credit balance and usage""" - try: - account = getattr(request, 'account', None) - if not account: - user = getattr(request, 'user', None) - if user and user.is_authenticated: - # Try to get account from user - refresh from DB to ensure we have latest - try: - from igny8_core.auth.models import User as UserModel - # Refresh user from DB to get account relationship - user = UserModel.objects.select_related('account', 'account__plan').get(id=user.id) - account = user.account - # Also set it on request for future use - request.account = account - except (AttributeError, UserModel.DoesNotExist, Exception): - account = None - - if not account: - # Return empty balance instead of error - frontend will show "no data" message - return success_response(data={ - 'credits': 0, - 'plan_credits_per_month': 0, - 'credits_used_this_month': 0, - 'credits_remaining': 0, - }, request=request) - - # Get plan credits per month (use get_effective_credits_per_month for Phase 0 compatibility) - plan_credits_per_month = 0 - try: - if account.plan: - plan_credits_per_month = account.plan.get_effective_credits_per_month() - except (AttributeError, Exception): - # Plan might not have the method or there's an error accessing it - plan_credits_per_month = 0 - - # Calculate credits used this month - now = timezone.now() - start_of_month = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0) - credits_used_this_month = CreditUsageLog.objects.filter( - account=account, - created_at__gte=start_of_month - ).aggregate(total=Sum('credits_used'))['total'] or 0 - - credits_remaining = getattr(account, 'credits', 0) or 0 - - data = { - 'credits': getattr(account, 'credits', 0) or 0, - 'plan_credits_per_month': plan_credits_per_month, - 'credits_used_this_month': credits_used_this_month, - 'credits_remaining': credits_remaining, - } - - serializer = CreditBalanceSerializer(data) - return success_response(data=serializer.data, request=request) - except Exception as e: - import logging - logger = logging.getLogger(__name__) - logger.error(f"Error in credit balance endpoint: {str(e)}", exc_info=True) - # Return empty balance instead of error + account = getattr(request, 'account', None) + if not account: + user = getattr(request, 'user', None) + if user and user.is_authenticated: + from igny8_core.auth.models import User as UserModel + user = UserModel.objects.select_related('account', 'account__plan').get(id=user.id) + account = user.account + request.account = account + + if not account: return success_response(data={ 'credits': 0, 'plan_credits_per_month': 0, 'credits_used_this_month': 0, 'credits_remaining': 0, }, request=request) + + # Get plan credits - plan is already associated + plan_credits_per_month = 0 + if account.plan: + plan_credits_per_month = account.plan.get_effective_credits_per_month() + + # Calculate credits used this month + now = timezone.now() + start_of_month = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0) + credits_used_this_month = CreditUsageLog.objects.filter( + account=account, + created_at__gte=start_of_month + ).aggregate(total=Sum('credits_used'))['total'] or 0 + + credits = account.credits or 0 + credits_remaining = credits + + data = { + 'credits': credits, + 'plan_credits_per_month': plan_credits_per_month, + 'credits_used_this_month': credits_used_this_month, + 'credits_remaining': credits_remaining, + } + + serializer = CreditBalanceSerializer(data) + return success_response(data=serializer.data, request=request) @extend_schema_view( diff --git a/backend/igny8_core/modules/publisher/views.py b/backend/igny8_core/modules/publisher/views.py index 4a88375f..83d86dc2 100644 --- a/backend/igny8_core/modules/publisher/views.py +++ b/backend/igny8_core/modules/publisher/views.py @@ -9,6 +9,8 @@ from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.views import APIView +from django.views.decorators.csrf import csrf_exempt +from django.utils.decorators import method_decorator from igny8_core.api.base import SiteSectorModelViewSet from igny8_core.api.permissions import IsAuthenticatedAndActive, IsEditorOrAbove @@ -201,6 +203,7 @@ class PublisherViewSet(viewsets.ViewSet): ) +@method_decorator(csrf_exempt, name='dispatch') class SiteDefinitionView(APIView): """ Public endpoint to serve deployed site definitions. @@ -208,6 +211,7 @@ class SiteDefinitionView(APIView): No authentication required for public sites. """ permission_classes = [] # Public endpoint + authentication_classes = [] # No authentication required def get(self, request, site_id): """ diff --git a/sites/src/main.tsx b/sites/src/main.tsx index bf351639..26f311c1 100644 --- a/sites/src/main.tsx +++ b/sites/src/main.tsx @@ -2,9 +2,12 @@ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import App from './App.tsx'; import './index.css'; -// Import shared component styles - Vite alias @shared resolves to frontend/src/components/shared -import '@shared/blocks/blocks.css'; -import '@shared/layouts/layouts.css'; + +// Note: Shared component CSS imports removed - CSS files not accessible in container +// Components will work without shared CSS, just without the shared styles +// To add shared styles later, either: +// 1. Copy CSS files to sites/src/styles/shared/ +// 2. Or mount frontend directory and use proper path createRoot(document.getElementById('root')!).render( diff --git a/sites/vite.config.ts b/sites/vite.config.ts index bfb17c9a..773be06a 100644 --- a/sites/vite.config.ts +++ b/sites/vite.config.ts @@ -10,14 +10,22 @@ const sharedPathCandidates = [ path.resolve(__dirname, '../frontend/src/components/shared'), path.resolve(__dirname, '../../frontend/src/components/shared'), '/frontend/src/components/shared', + path.resolve(__dirname, '../../frontend/src/components/shared'), // Try parent of parent ]; const sharedComponentsPath = sharedPathCandidates.find((candidate) => fs.existsSync(candidate)) ?? sharedPathCandidates[0]; +// Log for debugging +console.log('Shared components path:', sharedComponentsPath); +console.log('Path exists:', fs.existsSync(sharedComponentsPath)); + export default defineConfig({ plugins: [react()], resolve: { alias: { '@shared': sharedComponentsPath, + // Explicit aliases for CSS files to ensure Vite resolves them + '@shared/blocks/blocks.css': path.join(sharedComponentsPath, 'blocks/blocks.css'), + '@shared/layouts/layouts.css': path.join(sharedComponentsPath, 'layouts/layouts.css'), }, }, server: {