Refactor CreditBalanceViewSet for improved readability and error handling. Update SiteDefinitionView to allow public access without authentication. Enhance Vite configuration for shared component paths and debugging logs. Remove inaccessible shared CSS imports in main.tsx.
This commit is contained in:
Binary file not shown.
@@ -41,64 +41,16 @@ class CreditBalanceViewSet(viewsets.ViewSet):
|
|||||||
@action(detail=False, methods=['get'])
|
@action(detail=False, methods=['get'])
|
||||||
def balance(self, request):
|
def balance(self, request):
|
||||||
"""Get current credit balance and usage"""
|
"""Get current credit balance and usage"""
|
||||||
try:
|
account = getattr(request, 'account', None)
|
||||||
account = getattr(request, 'account', None)
|
if not account:
|
||||||
if not account:
|
user = getattr(request, 'user', None)
|
||||||
user = getattr(request, 'user', None)
|
if user and user.is_authenticated:
|
||||||
if user and user.is_authenticated:
|
from igny8_core.auth.models import User as UserModel
|
||||||
# Try to get account from user - refresh from DB to ensure we have latest
|
user = UserModel.objects.select_related('account', 'account__plan').get(id=user.id)
|
||||||
try:
|
account = user.account
|
||||||
from igny8_core.auth.models import User as UserModel
|
request.account = account
|
||||||
# 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:
|
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
|
|
||||||
return success_response(data={
|
return success_response(data={
|
||||||
'credits': 0,
|
'credits': 0,
|
||||||
'plan_credits_per_month': 0,
|
'plan_credits_per_month': 0,
|
||||||
@@ -106,6 +58,32 @@ class CreditBalanceViewSet(viewsets.ViewSet):
|
|||||||
'credits_remaining': 0,
|
'credits_remaining': 0,
|
||||||
}, request=request)
|
}, 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(
|
@extend_schema_view(
|
||||||
list=extend_schema(tags=['Billing']),
|
list=extend_schema(tags=['Billing']),
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ from rest_framework import status, viewsets
|
|||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
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.base import SiteSectorModelViewSet
|
||||||
from igny8_core.api.permissions import IsAuthenticatedAndActive, IsEditorOrAbove
|
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):
|
class SiteDefinitionView(APIView):
|
||||||
"""
|
"""
|
||||||
Public endpoint to serve deployed site definitions.
|
Public endpoint to serve deployed site definitions.
|
||||||
@@ -208,6 +211,7 @@ class SiteDefinitionView(APIView):
|
|||||||
No authentication required for public sites.
|
No authentication required for public sites.
|
||||||
"""
|
"""
|
||||||
permission_classes = [] # Public endpoint
|
permission_classes = [] # Public endpoint
|
||||||
|
authentication_classes = [] # No authentication required
|
||||||
|
|
||||||
def get(self, request, site_id):
|
def get(self, request, site_id):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ import { StrictMode } from 'react';
|
|||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import App from './App.tsx';
|
import App from './App.tsx';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
// Import shared component styles - Vite alias @shared resolves to frontend/src/components/shared
|
|
||||||
import '@shared/blocks/blocks.css';
|
// Note: Shared component CSS imports removed - CSS files not accessible in container
|
||||||
import '@shared/layouts/layouts.css';
|
// 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(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
|
|||||||
@@ -10,14 +10,22 @@ const sharedPathCandidates = [
|
|||||||
path.resolve(__dirname, '../frontend/src/components/shared'),
|
path.resolve(__dirname, '../frontend/src/components/shared'),
|
||||||
path.resolve(__dirname, '../../frontend/src/components/shared'),
|
path.resolve(__dirname, '../../frontend/src/components/shared'),
|
||||||
'/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];
|
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({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@shared': sharedComponentsPath,
|
'@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: {
|
server: {
|
||||||
|
|||||||
Reference in New Issue
Block a user