Refactor CreditBalanceViewSet for improved error handling and account retrieval. Update PublisherViewSet registration to root level for cleaner URL structure. Adjust siteBuilder.api.ts to reflect new endpoint for deploying blueprints.

This commit is contained in:
IGNY8 VPS (Salman)
2025-11-18 19:10:23 +00:00
parent c378e503d8
commit 49ac8f10c1
4 changed files with 67 additions and 36 deletions

Binary file not shown.

View File

@@ -41,41 +41,70 @@ 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"""
account = getattr(request, 'account', None) try:
if not account: account = getattr(request, 'account', None)
user = getattr(request, 'user', None) if not account:
if user: user = getattr(request, 'user', None)
account = getattr(user, 'account', None) if user and user.is_authenticated:
# Try to get account from user - refresh from DB to ensure we have latest
if not account: try:
return error_response( from igny8_core.auth.models import User as UserModel
error='Account not found', # Refresh user from DB to get account relationship
status_code=status.HTTP_400_BAD_REQUEST, user = UserModel.objects.select_related('account', 'account__plan').get(id=user.id)
request=request account = user.account
) # Also set it on request for future use
request.account = account
# Get plan credits per month (use get_effective_credits_per_month for Phase 0 compatibility) except (AttributeError, UserModel.DoesNotExist, Exception):
plan_credits_per_month = account.plan.get_effective_credits_per_month() if account.plan else 0 account = None
# Calculate credits used this month if not account:
now = timezone.now() # Return empty balance instead of error - frontend will show "no data" message
start_of_month = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0) return success_response(data={
credits_used_this_month = CreditUsageLog.objects.filter( 'credits': 0,
account=account, 'plan_credits_per_month': 0,
created_at__gte=start_of_month 'credits_used_this_month': 0,
).aggregate(total=Sum('credits_used'))['total'] or 0 'credits_remaining': 0,
}, request=request)
credits_remaining = account.credits
# Get plan credits per month (use get_effective_credits_per_month for Phase 0 compatibility)
data = { plan_credits_per_month = 0
'credits': account.credits, try:
'plan_credits_per_month': plan_credits_per_month, if account.plan:
'credits_used_this_month': credits_used_this_month, plan_credits_per_month = account.plan.get_effective_credits_per_month()
'credits_remaining': credits_remaining, except (AttributeError, Exception):
} # Plan might not have the method or there's an error accessing it
plan_credits_per_month = 0
serializer = CreditBalanceSerializer(data)
return success_response(data=serializer.data, request=request) # 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={
'credits': 0,
'plan_credits_per_month': 0,
'credits_used_this_month': 0,
'credits_remaining': 0,
}, request=request)
@extend_schema_view( @extend_schema_view(

View File

@@ -14,7 +14,8 @@ from igny8_core.modules.publisher.views import (
router = DefaultRouter() router = DefaultRouter()
router.register(r'publishing-records', PublishingRecordViewSet, basename='publishing-record') router.register(r'publishing-records', PublishingRecordViewSet, basename='publishing-record')
router.register(r'deployments', DeploymentRecordViewSet, basename='deployment') router.register(r'deployments', DeploymentRecordViewSet, basename='deployment')
router.register(r'publisher', PublisherViewSet, basename='publisher') # Register PublisherViewSet with empty prefix so actions are at root level
router.register(r'', PublisherViewSet, basename='publisher')
urlpatterns = [ urlpatterns = [
path('', include(router.urls)), path('', include(router.urls)),

View File

@@ -162,6 +162,7 @@ export const siteBuilderApi = {
* Deploy a blueprint to Sites renderer * Deploy a blueprint to Sites renderer
*/ */
async deployBlueprint(blueprintId: number): Promise<{ success: boolean; deployment_url?: string; deployment_id?: number }> { async deployBlueprint(blueprintId: number): Promise<{ success: boolean; deployment_url?: string; deployment_id?: number }> {
// PublisherViewSet is now registered with empty prefix, so URL is /publisher/deploy/{id}/
const response = await fetchAPI(`/v1/publisher/deploy/${blueprintId}/`, { const response = await fetchAPI(`/v1/publisher/deploy/${blueprintId}/`, {
method: 'POST', method: 'POST',
}); });