django-cfg 1.4.9__py3-none-any.whl → 1.4.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_cfg/apps/agents/management/commands/create_agent.py +1 -1
- django_cfg/apps/agents/management/commands/orchestrator_status.py +3 -3
- django_cfg/apps/newsletter/serializers.py +40 -3
- django_cfg/apps/newsletter/views/campaigns.py +12 -3
- django_cfg/apps/newsletter/views/emails.py +14 -3
- django_cfg/apps/newsletter/views/subscriptions.py +12 -2
- django_cfg/apps/payments/middleware/api_access.py +6 -2
- django_cfg/apps/payments/middleware/rate_limiting.py +2 -1
- django_cfg/apps/payments/middleware/usage_tracking.py +5 -1
- django_cfg/apps/payments/models/managers/api_key_managers.py +0 -1
- django_cfg/apps/payments/models/managers/subscription_managers.py +0 -1
- django_cfg/apps/payments/services/core/balance_service.py +5 -5
- django_cfg/apps/payments/services/core/subscription_service.py +1 -2
- django_cfg/apps/payments/views/api/balances.py +8 -7
- django_cfg/apps/payments/views/api/base.py +10 -6
- django_cfg/apps/payments/views/api/currencies.py +53 -10
- django_cfg/apps/payments/views/api/payments.py +3 -1
- django_cfg/apps/payments/views/api/subscriptions.py +2 -5
- django_cfg/apps/payments/views/api/webhooks.py +72 -7
- django_cfg/apps/payments/views/overview/serializers.py +34 -1
- django_cfg/apps/payments/views/overview/views.py +2 -1
- django_cfg/apps/payments/views/serializers/payments.py +6 -6
- django_cfg/apps/urls.py +106 -45
- django_cfg/core/base/config_model.py +2 -2
- django_cfg/core/constants.py +1 -1
- django_cfg/core/generation/integration_generators/__init__.py +1 -1
- django_cfg/core/generation/integration_generators/api.py +82 -41
- django_cfg/core/integration/display/startup.py +30 -22
- django_cfg/core/integration/url_integration.py +15 -16
- django_cfg/dashboard/sections/documentation.py +391 -0
- django_cfg/management/commands/check_endpoints.py +11 -160
- django_cfg/management/commands/check_settings.py +13 -265
- django_cfg/management/commands/clear_constance.py +13 -201
- django_cfg/management/commands/create_token.py +13 -321
- django_cfg/management/commands/generate_clients.py +23 -0
- django_cfg/management/commands/list_urls.py +13 -306
- django_cfg/management/commands/migrate_all.py +13 -126
- django_cfg/management/commands/migrator.py +13 -396
- django_cfg/management/commands/rundramatiq.py +15 -247
- django_cfg/management/commands/rundramatiq_simulator.py +12 -429
- django_cfg/management/commands/runserver_ngrok.py +15 -160
- django_cfg/management/commands/script.py +12 -488
- django_cfg/management/commands/show_config.py +12 -215
- django_cfg/management/commands/show_urls.py +12 -342
- django_cfg/management/commands/superuser.py +15 -295
- django_cfg/management/commands/task_clear.py +14 -217
- django_cfg/management/commands/task_status.py +13 -248
- django_cfg/management/commands/test_email.py +15 -86
- django_cfg/management/commands/test_telegram.py +14 -61
- django_cfg/management/commands/test_twilio.py +15 -105
- django_cfg/management/commands/tree.py +13 -383
- django_cfg/management/commands/validate_openapi.py +10 -0
- django_cfg/middleware/README.md +1 -1
- django_cfg/middleware/user_activity.py +3 -3
- django_cfg/models/__init__.py +2 -2
- django_cfg/models/api/drf/spectacular.py +6 -6
- django_cfg/models/django/__init__.py +2 -2
- django_cfg/models/django/openapi.py +238 -0
- django_cfg/models/django/{revolution.py → revolution_legacy.py} +8 -0
- django_cfg/modules/django_admin/management/__init__.py +0 -0
- django_cfg/modules/django_admin/management/commands/__init__.py +0 -0
- django_cfg/modules/django_admin/management/commands/check_endpoints.py +169 -0
- django_cfg/modules/django_admin/management/commands/check_settings.py +355 -0
- django_cfg/modules/django_admin/management/commands/clear_constance.py +208 -0
- django_cfg/modules/django_admin/management/commands/create_token.py +328 -0
- django_cfg/modules/django_admin/management/commands/list_urls.py +313 -0
- django_cfg/modules/django_admin/management/commands/migrate_all.py +133 -0
- django_cfg/modules/django_admin/management/commands/migrator.py +403 -0
- django_cfg/modules/django_admin/management/commands/script.py +496 -0
- django_cfg/modules/django_admin/management/commands/show_config.py +225 -0
- django_cfg/modules/django_admin/management/commands/show_urls.py +361 -0
- django_cfg/modules/django_admin/management/commands/superuser.py +302 -0
- django_cfg/modules/django_admin/management/commands/tree.py +390 -0
- django_cfg/modules/django_client/__init__.py +20 -0
- django_cfg/modules/django_client/apps.py +35 -0
- django_cfg/modules/django_client/core/__init__.py +56 -0
- django_cfg/modules/django_client/core/archive/__init__.py +11 -0
- django_cfg/modules/django_client/core/archive/manager.py +134 -0
- django_cfg/modules/django_client/core/cli/__init__.py +12 -0
- django_cfg/modules/django_client/core/cli/main.py +235 -0
- django_cfg/modules/django_client/core/config/__init__.py +18 -0
- django_cfg/modules/django_client/core/config/config.py +188 -0
- django_cfg/modules/django_client/core/config/group.py +101 -0
- django_cfg/modules/django_client/core/config/service.py +209 -0
- django_cfg/modules/django_client/core/generator/__init__.py +115 -0
- django_cfg/modules/django_client/core/generator/base.py +767 -0
- django_cfg/modules/django_client/core/generator/python.py +751 -0
- django_cfg/modules/django_client/core/generator/templates/python/__init__.py.jinja +9 -0
- django_cfg/modules/django_client/core/generator/templates/python/api_wrapper.py.jinja +130 -0
- django_cfg/modules/django_client/core/generator/templates/python/app_init.py.jinja +6 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/app_client.py.jinja +18 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/flat_client.py.jinja +38 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/main_client.py.jinja +50 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/main_client_file.py.jinja +13 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/operation_method.py.jinja +7 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/sub_client.py.jinja +11 -0
- django_cfg/modules/django_client/core/generator/templates/python/client_file.py.jinja +13 -0
- django_cfg/modules/django_client/core/generator/templates/python/main_init.py.jinja +50 -0
- django_cfg/modules/django_client/core/generator/templates/python/models/app_models.py.jinja +17 -0
- django_cfg/modules/django_client/core/generator/templates/python/models/enum_class.py.jinja +15 -0
- django_cfg/modules/django_client/core/generator/templates/python/models/enums.py.jinja +8 -0
- django_cfg/modules/django_client/core/generator/templates/python/models/models.py.jinja +17 -0
- django_cfg/modules/django_client/core/generator/templates/python/models/schema_class.py.jinja +19 -0
- django_cfg/modules/django_client/core/generator/templates/python/utils/logger.py.jinja +255 -0
- django_cfg/modules/django_client/core/generator/templates/python/utils/schema.py.jinja +12 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/app_index.ts.jinja +2 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/app_client.ts.jinja +18 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/client.ts.jinja +327 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/flat_client.ts.jinja +109 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/main_client_file.ts.jinja +9 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/operation.ts.jinja +61 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/sub_client.ts.jinja +15 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client_file.ts.jinja +9 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/index.ts.jinja +5 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/main_index.ts.jinja +206 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/models/app_models.ts.jinja +8 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/models/enums.ts.jinja +4 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/models/models.ts.jinja +8 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/utils/errors.ts.jinja +114 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/utils/http.ts.jinja +98 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/utils/logger.ts.jinja +251 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/utils/schema.ts.jinja +7 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/utils/storage.ts.jinja +114 -0
- django_cfg/modules/django_client/core/generator/typescript.py +872 -0
- django_cfg/modules/django_client/core/groups/__init__.py +13 -0
- django_cfg/modules/django_client/core/groups/detector.py +178 -0
- django_cfg/modules/django_client/core/groups/manager.py +314 -0
- django_cfg/modules/django_client/core/ir/__init__.py +57 -0
- django_cfg/modules/django_client/core/ir/context.py +387 -0
- django_cfg/modules/django_client/core/ir/operation.py +518 -0
- django_cfg/modules/django_client/core/ir/schema.py +353 -0
- django_cfg/modules/django_client/core/parser/__init__.py +74 -0
- django_cfg/modules/django_client/core/parser/base.py +648 -0
- django_cfg/modules/django_client/core/parser/models/__init__.py +74 -0
- django_cfg/modules/django_client/core/parser/models/base.py +212 -0
- django_cfg/modules/django_client/core/parser/models/components.py +160 -0
- django_cfg/modules/django_client/core/parser/models/openapi.py +203 -0
- django_cfg/modules/django_client/core/parser/models/operation.py +207 -0
- django_cfg/modules/django_client/core/parser/models/schema.py +266 -0
- django_cfg/modules/django_client/core/parser/openapi30.py +56 -0
- django_cfg/modules/django_client/core/parser/openapi31.py +64 -0
- django_cfg/modules/django_client/core/validation/__init__.py +22 -0
- django_cfg/modules/django_client/core/validation/checker.py +134 -0
- django_cfg/modules/django_client/core/validation/fixer.py +216 -0
- django_cfg/modules/django_client/core/validation/reporter.py +480 -0
- django_cfg/modules/django_client/core/validation/rules/__init__.py +11 -0
- django_cfg/modules/django_client/core/validation/rules/base.py +96 -0
- django_cfg/modules/django_client/core/validation/rules/type_hints.py +288 -0
- django_cfg/modules/django_client/core/validation/safety.py +266 -0
- django_cfg/modules/django_client/management/__init__.py +3 -0
- django_cfg/modules/django_client/management/commands/__init__.py +3 -0
- django_cfg/modules/django_client/management/commands/generate_client.py +422 -0
- django_cfg/modules/django_client/management/commands/validate_openapi.py +343 -0
- django_cfg/modules/django_client/spectacular/__init__.py +9 -0
- django_cfg/modules/django_client/spectacular/enum_naming.py +192 -0
- django_cfg/modules/django_client/urls.py +72 -0
- django_cfg/modules/django_email/management/__init__.py +0 -0
- django_cfg/modules/django_email/management/commands/__init__.py +0 -0
- django_cfg/modules/django_email/management/commands/test_email.py +93 -0
- django_cfg/modules/django_logging/django_logger.py +6 -6
- django_cfg/modules/django_ngrok/management/__init__.py +0 -0
- django_cfg/modules/django_ngrok/management/commands/__init__.py +0 -0
- django_cfg/modules/django_ngrok/management/commands/runserver_ngrok.py +167 -0
- django_cfg/modules/django_tasks/management/__init__.py +0 -0
- django_cfg/modules/django_tasks/management/commands/__init__.py +0 -0
- django_cfg/modules/django_tasks/management/commands/rundramatiq.py +254 -0
- django_cfg/modules/django_tasks/management/commands/rundramatiq_simulator.py +437 -0
- django_cfg/modules/django_tasks/management/commands/task_clear.py +226 -0
- django_cfg/modules/django_tasks/management/commands/task_status.py +257 -0
- django_cfg/modules/django_telegram/management/__init__.py +0 -0
- django_cfg/modules/django_telegram/management/commands/__init__.py +0 -0
- django_cfg/modules/django_telegram/management/commands/test_telegram.py +68 -0
- django_cfg/modules/django_twilio/management/__init__.py +0 -0
- django_cfg/modules/django_twilio/management/commands/__init__.py +0 -0
- django_cfg/modules/django_twilio/management/commands/test_twilio.py +112 -0
- django_cfg/modules/django_unfold/callbacks/main.py +16 -5
- django_cfg/modules/django_unfold/callbacks/revolution.py +41 -36
- django_cfg/modules/django_unfold/dashboard.py +1 -1
- django_cfg/pyproject.toml +2 -6
- django_cfg/registry/third_party.py +5 -7
- django_cfg/routing/callbacks.py +1 -1
- django_cfg/static/admin/css/prose-unfold.css +666 -0
- django_cfg/templates/admin/index.html +8 -0
- django_cfg/templates/admin/index_new.html +13 -0
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +15 -3
- django_cfg/templates/admin/sections/documentation_section.html +172 -0
- django_cfg/templates/admin/snippets/tabs/documentation_tab.html +231 -0
- {django_cfg-1.4.9.dist-info → django_cfg-1.4.11.dist-info}/METADATA +2 -2
- {django_cfg-1.4.9.dist-info → django_cfg-1.4.11.dist-info}/RECORD +192 -71
- django_cfg/management/commands/generate.py +0 -107
- {django_cfg-1.4.9.dist-info → django_cfg-1.4.11.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.9.dist-info → django_cfg-1.4.11.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.9.dist-info → django_cfg-1.4.11.dist-info}/licenses/LICENSE +0 -0
@@ -17,9 +17,16 @@ from rest_framework.decorators import api_view, permission_classes
|
|
17
17
|
from rest_framework.permissions import AllowAny
|
18
18
|
from rest_framework.response import Response
|
19
19
|
from rest_framework.views import APIView
|
20
|
+
from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiTypes
|
20
21
|
|
21
22
|
from ...services.core.webhook_service import WebhookService
|
22
23
|
from ...services.types import WebhookValidationRequest, WebhookProcessingResult
|
24
|
+
from ..serializers.webhooks import (
|
25
|
+
WebhookResponseSerializer,
|
26
|
+
WebhookHealthSerializer,
|
27
|
+
WebhookStatsSerializer,
|
28
|
+
SupportedProvidersSerializer,
|
29
|
+
)
|
23
30
|
from django_cfg.modules.django_logging import get_logger
|
24
31
|
|
25
32
|
logger = get_logger("webhook_views")
|
@@ -29,7 +36,7 @@ logger = get_logger("webhook_views")
|
|
29
36
|
class UniversalWebhookView(APIView):
|
30
37
|
"""
|
31
38
|
Universal webhook handler for all payment providers.
|
32
|
-
|
39
|
+
|
33
40
|
Features:
|
34
41
|
- Provider-agnostic webhook processing
|
35
42
|
- Signature validation and security
|
@@ -37,13 +44,29 @@ class UniversalWebhookView(APIView):
|
|
37
44
|
- Comprehensive logging and monitoring
|
38
45
|
- Integration with ngrok for development
|
39
46
|
"""
|
40
|
-
|
47
|
+
|
41
48
|
permission_classes = [AllowAny] # Webhooks don't use standard auth
|
49
|
+
serializer_class = WebhookResponseSerializer # For OpenAPI schema generation
|
42
50
|
|
43
51
|
def __init__(self, **kwargs):
|
44
52
|
super().__init__(**kwargs)
|
45
53
|
self.webhook_service = WebhookService()
|
46
|
-
|
54
|
+
|
55
|
+
@extend_schema(
|
56
|
+
summary="Process Webhook",
|
57
|
+
description="Process incoming webhook from payment provider",
|
58
|
+
parameters=[
|
59
|
+
OpenApiParameter(
|
60
|
+
name='provider',
|
61
|
+
description='Payment provider name (nowpayments, stripe, etc.)',
|
62
|
+
required=True,
|
63
|
+
type=OpenApiTypes.STR,
|
64
|
+
location=OpenApiParameter.PATH
|
65
|
+
)
|
66
|
+
],
|
67
|
+
responses={200: WebhookResponseSerializer},
|
68
|
+
tags=["Webhooks"]
|
69
|
+
)
|
47
70
|
def post(self, request: HttpRequest, provider: str) -> JsonResponse:
|
48
71
|
"""
|
49
72
|
Handle incoming webhook from any payment provider.
|
@@ -144,10 +167,25 @@ class UniversalWebhookView(APIView):
|
|
144
167
|
request_id
|
145
168
|
)
|
146
169
|
|
170
|
+
@extend_schema(
|
171
|
+
summary="Webhook Endpoint Info",
|
172
|
+
description="Get webhook endpoint information for debugging and configuration",
|
173
|
+
parameters=[
|
174
|
+
OpenApiParameter(
|
175
|
+
name='provider',
|
176
|
+
description='Payment provider name',
|
177
|
+
required=True,
|
178
|
+
type=OpenApiTypes.STR,
|
179
|
+
location=OpenApiParameter.PATH
|
180
|
+
)
|
181
|
+
],
|
182
|
+
responses={200: WebhookResponseSerializer},
|
183
|
+
tags=["Webhooks"]
|
184
|
+
)
|
147
185
|
def get(self, request: HttpRequest, provider: str) -> JsonResponse:
|
148
186
|
"""
|
149
187
|
Handle GET requests for webhook endpoint info.
|
150
|
-
|
188
|
+
|
151
189
|
Useful for debugging and provider configuration verification.
|
152
190
|
"""
|
153
191
|
|
@@ -314,12 +352,18 @@ class UniversalWebhookView(APIView):
|
|
314
352
|
return JsonResponse(response_data, status=status_code)
|
315
353
|
|
316
354
|
|
355
|
+
@extend_schema(
|
356
|
+
summary="Webhook Health Check",
|
357
|
+
description="Check webhook service health status and recent activity metrics",
|
358
|
+
responses={200: WebhookHealthSerializer},
|
359
|
+
tags=["Webhooks"]
|
360
|
+
)
|
317
361
|
@api_view(['GET'])
|
318
362
|
@permission_classes([AllowAny])
|
319
363
|
def webhook_health_check(request):
|
320
364
|
"""
|
321
365
|
Health check endpoint for webhook service.
|
322
|
-
|
366
|
+
|
323
367
|
Returns service status and recent activity metrics.
|
324
368
|
"""
|
325
369
|
|
@@ -362,12 +406,27 @@ def webhook_health_check(request):
|
|
362
406
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
363
407
|
|
364
408
|
|
409
|
+
@extend_schema(
|
410
|
+
summary="Webhook Statistics",
|
411
|
+
description="Get webhook processing statistics for a given time period",
|
412
|
+
parameters=[
|
413
|
+
OpenApiParameter(
|
414
|
+
name='days',
|
415
|
+
description='Number of days to analyze (1-365)',
|
416
|
+
required=False,
|
417
|
+
type=OpenApiTypes.INT,
|
418
|
+
default=30
|
419
|
+
)
|
420
|
+
],
|
421
|
+
responses={200: WebhookStatsSerializer},
|
422
|
+
tags=["Webhooks"]
|
423
|
+
)
|
365
424
|
@api_view(['GET'])
|
366
425
|
@permission_classes([AllowAny])
|
367
426
|
def webhook_stats(request):
|
368
427
|
"""
|
369
428
|
Get webhook processing statistics.
|
370
|
-
|
429
|
+
|
371
430
|
Query parameters:
|
372
431
|
- days: Number of days to analyze (default: 30)
|
373
432
|
"""
|
@@ -408,12 +467,18 @@ def webhook_stats(request):
|
|
408
467
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
409
468
|
|
410
469
|
|
470
|
+
@extend_schema(
|
471
|
+
summary="Supported Webhook Providers",
|
472
|
+
description="Get list of supported webhook providers with configuration details",
|
473
|
+
responses={200: SupportedProvidersSerializer},
|
474
|
+
tags=["Webhooks"]
|
475
|
+
)
|
411
476
|
@api_view(['GET'])
|
412
477
|
@permission_classes([AllowAny])
|
413
478
|
def supported_providers(request):
|
414
479
|
"""
|
415
480
|
Get list of supported webhook providers.
|
416
|
-
|
481
|
+
|
417
482
|
Returns provider information and webhook URLs.
|
418
483
|
"""
|
419
484
|
|
@@ -201,5 +201,38 @@ class TimePeriodSerializer(serializers.Serializer):
|
|
201
201
|
('90d', 'Last 90 days'),
|
202
202
|
('1y', 'Last year'),
|
203
203
|
]
|
204
|
-
|
204
|
+
|
205
205
|
period = serializers.ChoiceField(choices=PERIOD_CHOICES, default='30d')
|
206
|
+
|
207
|
+
|
208
|
+
class CurrencyAnalyticsItemSerializer(serializers.Serializer):
|
209
|
+
"""
|
210
|
+
Analytics data for a single currency
|
211
|
+
"""
|
212
|
+
currency_code = serializers.CharField(help_text="Currency code (e.g., BTC)")
|
213
|
+
currency_name = serializers.CharField(help_text="Currency name (e.g., Bitcoin)")
|
214
|
+
total_payments = serializers.IntegerField(help_text="Total number of payments")
|
215
|
+
total_amount = serializers.FloatField(help_text="Total amount in USD")
|
216
|
+
completed_payments = serializers.IntegerField(help_text="Number of completed payments")
|
217
|
+
average_amount = serializers.FloatField(help_text="Average payment amount in USD")
|
218
|
+
success_rate = serializers.FloatField(help_text="Success rate percentage")
|
219
|
+
|
220
|
+
|
221
|
+
class ProviderAnalyticsItemSerializer(serializers.Serializer):
|
222
|
+
"""
|
223
|
+
Analytics data for a single payment provider
|
224
|
+
"""
|
225
|
+
provider = serializers.CharField(help_text="Provider code")
|
226
|
+
provider_display = serializers.CharField(help_text="Provider display name")
|
227
|
+
total_payments = serializers.IntegerField(help_text="Total number of payments")
|
228
|
+
total_amount = serializers.FloatField(help_text="Total amount in USD")
|
229
|
+
completed_payments = serializers.IntegerField(help_text="Number of completed payments")
|
230
|
+
success_rate = serializers.FloatField(help_text="Success rate percentage")
|
231
|
+
|
232
|
+
|
233
|
+
class PaymentAnalyticsResponseSerializer(serializers.Serializer):
|
234
|
+
"""
|
235
|
+
Payment analytics response with currency and provider breakdown
|
236
|
+
"""
|
237
|
+
currency_analytics = CurrencyAnalyticsItemSerializer(many=True, help_text="Analytics by currency")
|
238
|
+
provider_analytics = ProviderAnalyticsItemSerializer(many=True, help_text="Analytics by provider")
|
@@ -26,6 +26,7 @@ from .serializers import (
|
|
26
26
|
BalanceOverviewSerializer,
|
27
27
|
SubscriptionOverviewSerializer,
|
28
28
|
APIKeysOverviewSerializer,
|
29
|
+
PaymentAnalyticsResponseSerializer,
|
29
30
|
)
|
30
31
|
|
31
32
|
|
@@ -95,7 +96,7 @@ from .serializers import (
|
|
95
96
|
default=10
|
96
97
|
)
|
97
98
|
],
|
98
|
-
responses={200:
|
99
|
+
responses={200: PaymentAnalyticsResponseSerializer}
|
99
100
|
),
|
100
101
|
balance_overview=extend_schema(
|
101
102
|
summary="Balance Overview",
|
@@ -27,7 +27,7 @@ class PaymentListSerializer(serializers.ModelSerializer):
|
|
27
27
|
status_display = serializers.CharField(source='get_status_display', read_only=True)
|
28
28
|
amount_display = serializers.SerializerMethodField(read_only=True)
|
29
29
|
|
30
|
-
def get_amount_display(self, obj):
|
30
|
+
def get_amount_display(self, obj) -> str:
|
31
31
|
"""Get formatted amount display."""
|
32
32
|
return f"${obj.amount_usd:.2f}"
|
33
33
|
|
@@ -64,23 +64,23 @@ class PaymentSerializer(serializers.ModelSerializer):
|
|
64
64
|
is_failed = serializers.SerializerMethodField(read_only=True)
|
65
65
|
is_expired = serializers.SerializerMethodField(read_only=True)
|
66
66
|
|
67
|
-
def get_amount_display(self, obj):
|
67
|
+
def get_amount_display(self, obj) -> str:
|
68
68
|
"""Get formatted amount display."""
|
69
69
|
return f"${obj.amount_usd:.2f}"
|
70
70
|
|
71
|
-
def get_is_pending(self, obj):
|
71
|
+
def get_is_pending(self, obj) -> bool:
|
72
72
|
"""Check if payment is pending."""
|
73
73
|
return obj.status == obj.PaymentStatus.PENDING
|
74
74
|
|
75
|
-
def get_is_completed(self, obj):
|
75
|
+
def get_is_completed(self, obj) -> bool:
|
76
76
|
"""Check if payment is completed."""
|
77
77
|
return obj.status == obj.PaymentStatus.COMPLETED
|
78
78
|
|
79
|
-
def get_is_failed(self, obj):
|
79
|
+
def get_is_failed(self, obj) -> bool:
|
80
80
|
"""Check if payment is failed."""
|
81
81
|
return obj.status == obj.PaymentStatus.FAILED
|
82
82
|
|
83
|
-
def get_is_expired(self, obj):
|
83
|
+
def get_is_expired(self, obj) -> bool:
|
84
84
|
"""Check if payment is expired."""
|
85
85
|
return obj.status == obj.PaymentStatus.EXPIRED
|
86
86
|
|
django_cfg/apps/urls.py
CHANGED
@@ -5,67 +5,128 @@ Built-in API endpoints for django_cfg functionality.
|
|
5
5
|
"""
|
6
6
|
|
7
7
|
from django.urls import path, include
|
8
|
-
from typing import List
|
8
|
+
from typing import List, Dict
|
9
9
|
from django.urls import URLPattern
|
10
10
|
|
11
11
|
|
12
|
+
def _register_group_urls(patterns: List[URLPattern], groups: Dict) -> None:
|
13
|
+
"""
|
14
|
+
Auto-register URLs from OpenAPI groups using convention.
|
15
|
+
|
16
|
+
Convention: cfg_{app} → /cfg/{app}/
|
17
|
+
|
18
|
+
Args:
|
19
|
+
patterns: URL patterns list to append to
|
20
|
+
groups: OpenAPI groups dict
|
21
|
+
"""
|
22
|
+
for group_name in groups.keys():
|
23
|
+
# Only django-cfg apps (convention: cfg_*)
|
24
|
+
if not group_name.startswith('cfg_'):
|
25
|
+
continue
|
26
|
+
|
27
|
+
# Extract app name: cfg_payments → payments
|
28
|
+
app_name = group_name[4:]
|
29
|
+
|
30
|
+
# Register main URLs: /cfg/{app}/
|
31
|
+
try:
|
32
|
+
patterns.append(
|
33
|
+
path(f'cfg/{app_name}/', include(f'django_cfg.apps.{app_name}.urls'))
|
34
|
+
)
|
35
|
+
except ImportError:
|
36
|
+
pass # URL module doesn't exist
|
37
|
+
|
38
|
+
# Register admin URLs: /cfg/{app}/admin/ (if exists)
|
39
|
+
try:
|
40
|
+
patterns.append(
|
41
|
+
path(f'cfg/{app_name}/admin/', include(f'django_cfg.apps.{app_name}.urls_admin'))
|
42
|
+
)
|
43
|
+
except ImportError:
|
44
|
+
pass # Admin URL module doesn't exist
|
45
|
+
|
46
|
+
|
47
|
+
def _register_apps_fallback(patterns: List[URLPattern]) -> None:
|
48
|
+
"""
|
49
|
+
Fallback: Register apps when OpenAPI is disabled.
|
50
|
+
|
51
|
+
Uses BaseCfgModule checks to determine which apps are enabled.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
patterns: URL patterns list to append to
|
55
|
+
"""
|
56
|
+
from django_cfg.modules.base import BaseCfgModule
|
57
|
+
base_module = BaseCfgModule()
|
58
|
+
|
59
|
+
# Business logic apps
|
60
|
+
if base_module.is_support_enabled():
|
61
|
+
patterns.append(path('cfg/support/', include('django_cfg.apps.support.urls')))
|
62
|
+
|
63
|
+
if base_module.is_accounts_enabled():
|
64
|
+
patterns.append(path('cfg/accounts/', include('django_cfg.apps.accounts.urls')))
|
65
|
+
|
66
|
+
if base_module.is_newsletter_enabled():
|
67
|
+
patterns.append(path('cfg/newsletter/', include('django_cfg.apps.newsletter.urls')))
|
68
|
+
|
69
|
+
if base_module.is_leads_enabled():
|
70
|
+
patterns.append(path('cfg/leads/', include('django_cfg.apps.leads.urls')))
|
71
|
+
|
72
|
+
if base_module.is_knowbase_enabled():
|
73
|
+
patterns.append(path('cfg/knowbase/', include('django_cfg.apps.knowbase.urls')))
|
74
|
+
|
75
|
+
if base_module.is_agents_enabled():
|
76
|
+
patterns.append(path('cfg/agents/', include('django_cfg.apps.agents.urls')))
|
77
|
+
|
78
|
+
if base_module.should_enable_tasks():
|
79
|
+
patterns.append(path('cfg/tasks/', include('django_cfg.apps.tasks.urls')))
|
80
|
+
patterns.append(path('cfg/tasks/admin/', include('django_cfg.apps.tasks.urls_admin')))
|
81
|
+
|
82
|
+
if base_module.is_payments_enabled():
|
83
|
+
patterns.append(path('cfg/payments/', include('django_cfg.apps.payments.urls')))
|
84
|
+
patterns.append(path('cfg/payments/admin/', include('django_cfg.apps.payments.urls_admin')))
|
85
|
+
|
86
|
+
# Standalone apps
|
87
|
+
if base_module.is_maintenance_enabled():
|
88
|
+
patterns.append(
|
89
|
+
path('admin/django_cfg_maintenance/', include('django_cfg.apps.maintenance.urls_admin'))
|
90
|
+
)
|
91
|
+
|
92
|
+
if base_module.is_rpc_enabled():
|
93
|
+
patterns.append(path('rpc/', include('django_cfg.modules.django_ipc_client.dashboard.urls')))
|
94
|
+
patterns.append(path('admin/rpc/', include('django_cfg.modules.django_ipc_client.dashboard.urls_admin')))
|
95
|
+
|
96
|
+
|
12
97
|
def get_django_cfg_urlpatterns() -> List[URLPattern]:
|
13
98
|
"""
|
14
|
-
Get Django CFG URL patterns based on
|
15
|
-
|
99
|
+
Get Django CFG URL patterns based on OpenAPI groups.
|
100
|
+
|
16
101
|
Returns:
|
17
|
-
List of URL patterns for
|
102
|
+
List of URL patterns for django_cfg
|
18
103
|
"""
|
19
|
-
from django_cfg.modules.base import BaseCfgModule
|
20
|
-
|
21
104
|
patterns = [
|
22
105
|
# Core APIs (always enabled)
|
23
106
|
path('health/', include('django_cfg.apps.api.health.urls')),
|
24
107
|
path('endpoints/', include('django_cfg.apps.api.endpoints.urls')),
|
25
108
|
path('commands/', include('django_cfg.apps.api.commands.urls')),
|
26
|
-
|
109
|
+
|
110
|
+
# OpenAPI schemas (if enabled)
|
111
|
+
# Provides /openapi/{group}/schema/
|
112
|
+
path('openapi/', include('django_cfg.modules.django_client.urls')),
|
27
113
|
]
|
28
|
-
|
114
|
+
|
29
115
|
try:
|
30
|
-
#
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
# patterns.append(path('accounts/', include('django_cfg.apps.accounts.urls')))
|
40
|
-
|
41
|
-
# Newsletter and Leads are handled by Django Revolution zones
|
42
|
-
# to avoid URL namespace conflicts and enable client generation
|
43
|
-
# if base_module.is_newsletter_enabled():
|
44
|
-
# patterns.append(path('newsletter/', include('django_cfg.apps.newsletter.urls')))
|
45
|
-
#
|
46
|
-
# if base_module.is_leads_enabled():
|
47
|
-
# patterns.append(path('leads/', include('django_cfg.apps.leads.urls')))
|
48
|
-
|
49
|
-
# Tasks app - enabled when knowbase or agents are enabled
|
50
|
-
if base_module.should_enable_tasks():
|
51
|
-
patterns.append(path('admin/django_cfg_tasks/admin/', include('django_cfg.apps.tasks.urls_admin')))
|
52
|
-
|
53
|
-
# Maintenance app - multi-site maintenance mode with Cloudflare
|
54
|
-
# if base_module.is_maintenance_enabled():
|
55
|
-
# patterns.append(path('admin/django_cfg_maintenance/', include('django_cfg.apps.maintenance.urls_admin')))
|
56
|
-
|
57
|
-
if base_module.is_payments_enabled():
|
58
|
-
patterns.append(path('admin/django_cfg_payments/admin/', include('django_cfg.apps.payments.urls_admin')))
|
59
|
-
|
60
|
-
# RPC Dashboard - WebSocket & RPC activity monitoring
|
61
|
-
if base_module.is_rpc_enabled():
|
62
|
-
patterns.append(path('admin/rpc/', include('django_cfg.modules.django_ipc_client.dashboard.urls_admin')))
|
116
|
+
# Auto-register from OpenAPI groups (preferred)
|
117
|
+
from django_cfg.modules.django_client.core import get_openapi_service
|
118
|
+
service = get_openapi_service()
|
119
|
+
|
120
|
+
if service and service.is_enabled():
|
121
|
+
_register_group_urls(patterns, service.get_groups())
|
122
|
+
else:
|
123
|
+
# Fallback: Use BaseCfgModule when OpenAPI disabled
|
124
|
+
_register_apps_fallback(patterns)
|
63
125
|
|
64
126
|
except Exception:
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
|
127
|
+
# Last resort fallback
|
128
|
+
_register_apps_fallback(patterns)
|
129
|
+
|
69
130
|
return patterns
|
70
131
|
|
71
132
|
|
@@ -313,12 +313,12 @@ class DjangoConfig(BaseModel):
|
|
313
313
|
# === API Configuration ===
|
314
314
|
drf: Optional[DRFConfig] = Field(
|
315
315
|
default=None,
|
316
|
-
description="Extended Django REST Framework configuration (supplements
|
316
|
+
description="Extended Django REST Framework configuration (supplements OpenAPI Client)",
|
317
317
|
)
|
318
318
|
|
319
319
|
spectacular: Optional[SpectacularConfig] = Field(
|
320
320
|
default=None,
|
321
|
-
description="Extended DRF Spectacular configuration (supplements
|
321
|
+
description="Extended DRF Spectacular configuration (supplements OpenAPI Client)",
|
322
322
|
)
|
323
323
|
|
324
324
|
# === Limits Configuration ===
|
django_cfg/core/constants.py
CHANGED
@@ -4,7 +4,7 @@ Integration generators module.
|
|
4
4
|
Contains generators for third-party integrations and frameworks:
|
5
5
|
- Session configuration
|
6
6
|
- External services (Telegram, Unfold, Constance)
|
7
|
-
- API frameworks (JWT, DRF, Spectacular,
|
7
|
+
- API frameworks (JWT, DRF, Spectacular, OpenAPI Client)
|
8
8
|
- Background tasks (Dramatiq)
|
9
9
|
"""
|
10
10
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
"""
|
2
2
|
API frameworks generator.
|
3
3
|
|
4
|
-
Handles JWT, DRF, Spectacular, and Django
|
4
|
+
Handles JWT, DRF, Spectacular, and Django Client (OpenAPI) configuration.
|
5
5
|
Size: ~250 lines (focused on API frameworks)
|
6
6
|
"""
|
7
7
|
|
@@ -20,7 +20,7 @@ class APIFrameworksGenerator:
|
|
20
20
|
|
21
21
|
Responsibilities:
|
22
22
|
- JWT authentication configuration
|
23
|
-
- Django
|
23
|
+
- Django Client (OpenAPI) framework
|
24
24
|
- Django REST Framework (DRF)
|
25
25
|
- DRF Spectacular (OpenAPI/Swagger)
|
26
26
|
- Auto-configuration and extensions
|
@@ -56,7 +56,7 @@ class APIFrameworksGenerator:
|
|
56
56
|
|
57
57
|
# Generate settings for each API framework
|
58
58
|
settings.update(self._generate_jwt_settings())
|
59
|
-
settings.update(self.
|
59
|
+
settings.update(self._generate_openapi_client_settings())
|
60
60
|
settings.update(self._apply_drf_spectacular_extensions())
|
61
61
|
|
62
62
|
return settings
|
@@ -74,71 +74,104 @@ class APIFrameworksGenerator:
|
|
74
74
|
jwt_settings = self.config.jwt.to_django_settings(self.config.secret_key)
|
75
75
|
return jwt_settings
|
76
76
|
|
77
|
-
def
|
77
|
+
def _generate_openapi_client_settings(self) -> Dict[str, Any]:
|
78
78
|
"""
|
79
|
-
Generate Django
|
79
|
+
Generate Django Client (OpenAPI) framework settings.
|
80
80
|
|
81
81
|
Returns:
|
82
|
-
Dictionary with
|
82
|
+
Dictionary with OpenAPI configuration and auto-generated DRF configuration
|
83
83
|
"""
|
84
|
-
if not hasattr(self.config, "
|
84
|
+
if not hasattr(self.config, "openapi_client") or not self.config.openapi_client:
|
85
85
|
return {}
|
86
86
|
|
87
87
|
settings = {}
|
88
88
|
|
89
|
-
#
|
90
|
-
|
91
|
-
"
|
92
|
-
"api_prefix": self.config.revolution.api_prefix,
|
93
|
-
"debug": getattr(self.config.revolution, "debug", self.config.debug),
|
94
|
-
"auto_install_deps": getattr(self.config.revolution, "auto_install_deps", True),
|
95
|
-
"zones": {
|
96
|
-
zone_name: zone_config.model_dump()
|
97
|
-
for zone_name, zone_config in self.config.revolution.get_zones_with_defaults().items()
|
98
|
-
},
|
99
|
-
}
|
89
|
+
# OpenAPI Client configuration
|
90
|
+
openapi_settings = {
|
91
|
+
"OPENAPI_CLIENT": self.config.openapi_client.model_dump(),
|
100
92
|
}
|
101
|
-
settings.update(
|
93
|
+
settings.update(openapi_settings)
|
102
94
|
|
103
|
-
# Auto-generate DRF configuration
|
104
|
-
drf_settings = self.
|
95
|
+
# Auto-generate DRF configuration from OpenAPIClientConfig
|
96
|
+
drf_settings = self._generate_drf_from_openapi()
|
105
97
|
if drf_settings:
|
106
98
|
settings.update(drf_settings)
|
107
99
|
|
108
100
|
return settings
|
109
101
|
|
110
|
-
def
|
102
|
+
def _generate_drf_from_openapi(self) -> Dict[str, Any]:
|
111
103
|
"""
|
112
|
-
Generate DRF + Spectacular settings from
|
104
|
+
Generate DRF + Spectacular settings from OpenAPIClientConfig.
|
113
105
|
|
114
106
|
Returns:
|
115
107
|
Dictionary with DRF and Spectacular configuration
|
116
108
|
"""
|
117
109
|
try:
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
"
|
124
|
-
"
|
125
|
-
"
|
126
|
-
"
|
127
|
-
|
110
|
+
# Extract DRF parameters from OpenAPIClientConfig
|
111
|
+
openapi_config = self.config.openapi_client
|
112
|
+
|
113
|
+
# Build REST_FRAMEWORK settings
|
114
|
+
rest_framework = {
|
115
|
+
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
|
116
|
+
"DEFAULT_PAGINATION_CLASS": "django_cfg.middleware.pagination.DefaultPagination",
|
117
|
+
"PAGE_SIZE": 100,
|
118
|
+
"DEFAULT_RENDERER_CLASSES": [
|
119
|
+
"rest_framework.renderers.JSONRenderer",
|
120
|
+
"django_cfg.modules.django_drf_theme.renderers.TailwindBrowsableAPIRenderer",
|
121
|
+
],
|
122
|
+
}
|
123
|
+
|
124
|
+
# Add authentication classes if not browsable
|
125
|
+
if not openapi_config.drf_enable_browsable_api:
|
126
|
+
rest_framework["DEFAULT_RENDERER_CLASSES"] = [
|
127
|
+
"rest_framework.renderers.JSONRenderer",
|
128
|
+
]
|
129
|
+
|
130
|
+
# Add throttling if enabled
|
131
|
+
if openapi_config.drf_enable_throttling:
|
132
|
+
rest_framework["DEFAULT_THROTTLE_CLASSES"] = [
|
133
|
+
"rest_framework.throttling.AnonRateThrottle",
|
134
|
+
"rest_framework.throttling.UserRateThrottle",
|
135
|
+
]
|
136
|
+
rest_framework["DEFAULT_THROTTLE_RATES"] = {
|
137
|
+
"anon": "100/day",
|
138
|
+
"user": "1000/day",
|
139
|
+
}
|
140
|
+
|
141
|
+
# Build SPECTACULAR_SETTINGS
|
142
|
+
spectacular_settings = {
|
143
|
+
"TITLE": openapi_config.drf_title,
|
144
|
+
"DESCRIPTION": openapi_config.drf_description,
|
145
|
+
"VERSION": openapi_config.drf_version,
|
146
|
+
"SERVE_INCLUDE_SCHEMA": openapi_config.drf_serve_include_schema,
|
147
|
+
"SCHEMA_PATH_PREFIX": openapi_config.get_drf_schema_path_prefix(),
|
148
|
+
"SWAGGER_UI_SETTINGS": {
|
149
|
+
"deepLinking": True,
|
150
|
+
"persistAuthorization": True,
|
151
|
+
"displayOperationId": True,
|
152
|
+
},
|
153
|
+
"COMPONENT_SPLIT_REQUEST": True,
|
154
|
+
"COMPONENT_SPLIT_PATCH": True,
|
155
|
+
# Auto-fix enum naming collisions
|
156
|
+
"POSTPROCESSING_HOOKS": [
|
157
|
+
"django_cfg.modules.django_client.spectacular.auto_fix_enum_names",
|
158
|
+
],
|
128
159
|
}
|
129
160
|
|
130
|
-
|
131
|
-
|
161
|
+
drf_settings = {
|
162
|
+
"REST_FRAMEWORK": rest_framework,
|
163
|
+
"SPECTACULAR_SETTINGS": spectacular_settings,
|
164
|
+
}
|
132
165
|
|
133
|
-
logger.info("🚀 Generated DRF + Spectacular settings
|
166
|
+
logger.info("🚀 Generated DRF + Spectacular settings from OpenAPIClientConfig")
|
167
|
+
logger.info(" - Pagination: django_cfg.middleware.pagination.DefaultPagination")
|
168
|
+
logger.info(" - Renderer: TailwindBrowsableAPIRenderer")
|
169
|
+
logger.info(f" - API: {openapi_config.drf_title} v{openapi_config.drf_version}")
|
134
170
|
|
135
171
|
return drf_settings
|
136
172
|
|
137
|
-
except ImportError as e:
|
138
|
-
logger.warning(f"Could not import django_revolution.create_drf_spectacular_config: {e}")
|
139
|
-
return {}
|
140
173
|
except Exception as e:
|
141
|
-
logger.warning(f"Could not generate DRF config from
|
174
|
+
logger.warning(f"Could not generate DRF config from OpenAPIClientConfig: {e}")
|
142
175
|
return {}
|
143
176
|
|
144
177
|
def _apply_drf_spectacular_extensions(self) -> Dict[str, Any]:
|
@@ -175,7 +208,7 @@ class APIFrameworksGenerator:
|
|
175
208
|
Returns:
|
176
209
|
Dictionary with Spectacular settings
|
177
210
|
"""
|
178
|
-
# Check if Spectacular settings exist (from
|
211
|
+
# Check if Spectacular settings exist (from OpenAPI Client or elsewhere)
|
179
212
|
if not hasattr(self, '_has_spectacular_settings'):
|
180
213
|
return {}
|
181
214
|
|
@@ -205,9 +238,17 @@ class APIFrameworksGenerator:
|
|
205
238
|
"""
|
206
239
|
Apply DRF settings extensions.
|
207
240
|
|
241
|
+
Note: This method should NOT overwrite existing REST_FRAMEWORK settings.
|
242
|
+
It should only add missing settings or extend existing ones.
|
243
|
+
|
208
244
|
Returns:
|
209
245
|
Dictionary with DRF settings
|
210
246
|
"""
|
247
|
+
# Don't override if OpenAPIClientConfig already created full DRF config
|
248
|
+
if hasattr(self.config, 'openapi_client') and self.config.openapi_client:
|
249
|
+
logger.info("🔧 DRF settings already configured by OpenAPIClientConfig, skipping django-cfg extensions")
|
250
|
+
return {}
|
251
|
+
|
211
252
|
settings = {}
|
212
253
|
|
213
254
|
if self.config.drf:
|