django-cfg 1.2.31__py3-none-any.whl → 1.3.1__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/__init__.py +1 -1
- django_cfg/apps/api/health/views.py +4 -2
- django_cfg/apps/knowbase/config/settings.py +16 -15
- django_cfg/apps/payments/README.md +326 -0
- django_cfg/apps/payments/admin/__init__.py +20 -10
- django_cfg/apps/payments/admin/api_keys_admin.py +521 -237
- django_cfg/apps/payments/admin/balance_admin.py +592 -297
- django_cfg/apps/payments/admin/currencies_admin.py +526 -222
- django_cfg/apps/payments/admin/filters.py +306 -199
- django_cfg/apps/payments/admin/payments_admin.py +465 -70
- django_cfg/apps/payments/admin/subscriptions_admin.py +578 -128
- django_cfg/apps/payments/admin_interface/__init__.py +18 -0
- django_cfg/apps/payments/admin_interface/templates/payments/base.html +162 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +38 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/loading_spinner.html +16 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/notification.html +27 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/provider_card.html +86 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +39 -0
- django_cfg/apps/payments/admin_interface/templates/payments/currency_converter.html +382 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +300 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +303 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +382 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_status.html +500 -0
- django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +594 -0
- django_cfg/apps/payments/admin_interface/views/__init__.py +23 -0
- django_cfg/apps/payments/admin_interface/views/payment_views.py +259 -0
- django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +37 -0
- django_cfg/apps/payments/apps.py +34 -9
- django_cfg/apps/payments/config/__init__.py +28 -51
- django_cfg/apps/payments/config/constance/__init__.py +22 -0
- django_cfg/apps/payments/config/constance/config_service.py +123 -0
- django_cfg/apps/payments/config/constance/fields.py +69 -0
- django_cfg/apps/payments/config/constance/settings.py +160 -0
- django_cfg/apps/payments/config/django_cfg_integration.py +202 -0
- django_cfg/apps/payments/config/helpers.py +130 -0
- django_cfg/apps/payments/management/__init__.py +1 -3
- django_cfg/apps/payments/management/commands/__init__.py +1 -3
- django_cfg/apps/payments/management/commands/manage_currencies.py +303 -151
- django_cfg/apps/payments/management/commands/manage_providers.py +333 -160
- django_cfg/apps/payments/middleware/__init__.py +3 -1
- django_cfg/apps/payments/middleware/api_access.py +329 -222
- django_cfg/apps/payments/middleware/rate_limiting.py +342 -152
- django_cfg/apps/payments/middleware/usage_tracking.py +249 -240
- django_cfg/apps/payments/migrations/0001_initial.py +708 -536
- django_cfg/apps/payments/models/__init__.py +13 -18
- django_cfg/apps/payments/models/api_keys.py +121 -43
- django_cfg/apps/payments/models/balance.py +150 -115
- django_cfg/apps/payments/models/base.py +68 -15
- django_cfg/apps/payments/models/currencies.py +172 -148
- django_cfg/apps/payments/models/managers/__init__.py +44 -0
- django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
- django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
- django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
- django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
- django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
- django_cfg/apps/payments/models/payments.py +235 -285
- django_cfg/apps/payments/models/subscriptions.py +257 -177
- django_cfg/apps/payments/models/tariffs.py +147 -40
- django_cfg/apps/payments/services/__init__.py +209 -56
- django_cfg/apps/payments/services/cache/__init__.py +6 -6
- django_cfg/apps/payments/services/cache/{simple_cache.py → cache_service.py} +112 -12
- django_cfg/apps/payments/services/core/__init__.py +10 -6
- django_cfg/apps/payments/services/core/balance_service.py +435 -360
- django_cfg/apps/payments/services/core/base.py +166 -0
- django_cfg/apps/payments/services/core/currency_service.py +478 -0
- django_cfg/apps/payments/services/core/payment_service.py +346 -467
- django_cfg/apps/payments/services/core/subscription_service.py +425 -481
- django_cfg/apps/payments/services/core/webhook_service.py +410 -0
- django_cfg/apps/payments/services/integrations/__init__.py +29 -0
- django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
- django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
- django_cfg/apps/payments/services/providers/__init__.py +9 -14
- django_cfg/apps/payments/services/providers/base.py +234 -174
- django_cfg/apps/payments/services/providers/nowpayments.py +478 -0
- django_cfg/apps/payments/services/providers/registry.py +367 -301
- django_cfg/apps/payments/services/types/__init__.py +78 -0
- django_cfg/apps/payments/services/types/data.py +177 -0
- django_cfg/apps/payments/services/types/requests.py +150 -0
- django_cfg/apps/payments/services/types/responses.py +156 -0
- django_cfg/apps/payments/services/types/webhooks.py +232 -0
- django_cfg/apps/payments/signals/__init__.py +33 -8
- django_cfg/apps/payments/signals/api_key_signals.py +210 -129
- django_cfg/apps/payments/signals/balance_signals.py +174 -0
- django_cfg/apps/payments/signals/payment_signals.py +128 -103
- django_cfg/apps/payments/signals/subscription_signals.py +194 -142
- django_cfg/apps/payments/static/payments/css/components.css +380 -0
- django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
- django_cfg/apps/payments/static/payments/js/components.js +545 -0
- django_cfg/apps/payments/static/payments/js/utils.js +412 -0
- django_cfg/apps/payments/templatetags/__init__.py +1 -1
- django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
- django_cfg/apps/payments/urls.py +45 -48
- django_cfg/apps/payments/urls_admin.py +33 -42
- django_cfg/apps/payments/views/api/__init__.py +101 -0
- django_cfg/apps/payments/views/api/api_keys.py +387 -0
- django_cfg/apps/payments/views/api/balances.py +381 -0
- django_cfg/apps/payments/views/api/base.py +298 -0
- django_cfg/apps/payments/views/api/currencies.py +402 -0
- django_cfg/apps/payments/views/api/payments.py +415 -0
- django_cfg/apps/payments/views/api/subscriptions.py +475 -0
- django_cfg/apps/payments/views/api/webhooks.py +476 -0
- django_cfg/apps/payments/views/serializers/__init__.py +99 -0
- django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
- django_cfg/apps/payments/views/serializers/balances.py +300 -0
- django_cfg/apps/payments/views/serializers/currencies.py +335 -0
- django_cfg/apps/payments/views/serializers/payments.py +387 -0
- django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
- django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
- django_cfg/config.py +1 -1
- django_cfg/core/config.py +40 -4
- django_cfg/core/generation.py +25 -4
- django_cfg/core/integration/README.md +363 -0
- django_cfg/core/integration/__init__.py +47 -0
- django_cfg/core/integration/commands_collector.py +239 -0
- django_cfg/core/integration/display/__init__.py +15 -0
- django_cfg/core/integration/display/base.py +157 -0
- django_cfg/core/integration/display/ngrok.py +164 -0
- django_cfg/core/integration/display/startup.py +815 -0
- django_cfg/core/integration/url_integration.py +123 -0
- django_cfg/core/integration/version_checker.py +160 -0
- django_cfg/management/commands/auto_generate.py +4 -0
- django_cfg/management/commands/check_settings.py +6 -0
- django_cfg/management/commands/clear_constance.py +5 -2
- django_cfg/management/commands/create_token.py +6 -0
- django_cfg/management/commands/list_urls.py +6 -0
- django_cfg/management/commands/migrate_all.py +6 -0
- django_cfg/management/commands/migrator.py +3 -0
- django_cfg/management/commands/rundramatiq.py +6 -0
- django_cfg/management/commands/runserver_ngrok.py +51 -29
- django_cfg/management/commands/script.py +6 -0
- django_cfg/management/commands/show_config.py +12 -2
- django_cfg/management/commands/show_urls.py +4 -0
- django_cfg/management/commands/superuser.py +6 -0
- django_cfg/management/commands/task_clear.py +4 -1
- django_cfg/management/commands/task_status.py +3 -1
- django_cfg/management/commands/test_email.py +3 -0
- django_cfg/management/commands/test_telegram.py +6 -0
- django_cfg/management/commands/test_twilio.py +6 -0
- django_cfg/management/commands/tree.py +6 -0
- django_cfg/management/commands/validate_config.py +155 -149
- django_cfg/models/constance.py +31 -11
- django_cfg/models/payments.py +175 -492
- django_cfg/modules/django_logger.py +160 -146
- django_cfg/modules/django_unfold/dashboard.py +64 -16
- django_cfg/registry/core.py +1 -0
- django_cfg/template_archive/django_sample.zip +0 -0
- django_cfg/utils/smart_defaults.py +222 -571
- django_cfg/utils/toolkit.py +51 -11
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/METADATA +4 -1
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/RECORD +153 -185
- django_cfg/apps/payments/__init__.py +0 -8
- django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
- django_cfg/apps/payments/config/module.py +0 -70
- django_cfg/apps/payments/config/providers.py +0 -105
- django_cfg/apps/payments/config/settings.py +0 -96
- django_cfg/apps/payments/config/utils.py +0 -52
- django_cfg/apps/payments/decorators.py +0 -291
- django_cfg/apps/payments/management/commands/README.md +0 -146
- django_cfg/apps/payments/management/commands/currency_stats.py +0 -304
- django_cfg/apps/payments/managers/__init__.py +0 -23
- django_cfg/apps/payments/managers/api_key_manager.py +0 -35
- django_cfg/apps/payments/managers/balance_manager.py +0 -361
- django_cfg/apps/payments/managers/currency_manager.py +0 -306
- django_cfg/apps/payments/managers/payment_manager.py +0 -192
- django_cfg/apps/payments/managers/subscription_manager.py +0 -37
- django_cfg/apps/payments/managers/tariff_manager.py +0 -29
- django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +0 -241
- django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +0 -30
- django_cfg/apps/payments/models/events.py +0 -73
- django_cfg/apps/payments/serializers/__init__.py +0 -57
- django_cfg/apps/payments/serializers/api_keys.py +0 -51
- django_cfg/apps/payments/serializers/balance.py +0 -59
- django_cfg/apps/payments/serializers/currencies.py +0 -63
- django_cfg/apps/payments/serializers/payments.py +0 -62
- django_cfg/apps/payments/serializers/subscriptions.py +0 -71
- django_cfg/apps/payments/serializers/tariffs.py +0 -56
- django_cfg/apps/payments/services/billing/__init__.py +0 -8
- django_cfg/apps/payments/services/cache/base.py +0 -30
- django_cfg/apps/payments/services/core/fallback_service.py +0 -432
- django_cfg/apps/payments/services/internal_types.py +0 -461
- django_cfg/apps/payments/services/middleware/__init__.py +0 -8
- django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
- django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -76
- django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
- django_cfg/apps/payments/services/providers/cryptapi/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/cryptapi/config.py +0 -8
- django_cfg/apps/payments/services/providers/cryptapi/models.py +0 -192
- django_cfg/apps/payments/services/providers/cryptapi/provider.py +0 -439
- django_cfg/apps/payments/services/providers/cryptomus/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/cryptomus/models.py +0 -176
- django_cfg/apps/payments/services/providers/cryptomus/provider.py +0 -429
- django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +0 -564
- django_cfg/apps/payments/services/providers/models/__init__.py +0 -34
- django_cfg/apps/payments/services/providers/models/currencies.py +0 -190
- django_cfg/apps/payments/services/providers/nowpayments/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/nowpayments/models.py +0 -196
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +0 -380
- django_cfg/apps/payments/services/providers/stripe/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/stripe/models.py +0 -184
- django_cfg/apps/payments/services/providers/stripe/provider.py +0 -109
- django_cfg/apps/payments/services/security/__init__.py +0 -34
- django_cfg/apps/payments/services/security/error_handler.py +0 -635
- django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
- django_cfg/apps/payments/services/security/webhook_validator.py +0 -474
- django_cfg/apps/payments/static/payments/css/payments.css +0 -340
- django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
- django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
- django_cfg/apps/payments/static/payments/js/theme.js +0 -86
- django_cfg/apps/payments/tasks/__init__.py +0 -12
- django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
- django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +0 -50
- django_cfg/apps/payments/templates/payments/base.html +0 -182
- django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
- django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
- django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -43
- django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
- django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -34
- django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -148
- django_cfg/apps/payments/templates/payments/dashboard.html +0 -258
- django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +0 -35
- django_cfg/apps/payments/templates/payments/payment_create.html +0 -579
- django_cfg/apps/payments/templates/payments/payment_detail.html +0 -373
- django_cfg/apps/payments/templates/payments/payment_list.html +0 -354
- django_cfg/apps/payments/templates/payments/stats.html +0 -261
- django_cfg/apps/payments/templates/payments/test.html +0 -213
- django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
- django_cfg/apps/payments/utils/__init__.py +0 -43
- django_cfg/apps/payments/utils/billing_utils.py +0 -342
- django_cfg/apps/payments/utils/config_utils.py +0 -239
- django_cfg/apps/payments/utils/middleware_utils.py +0 -228
- django_cfg/apps/payments/utils/validation_utils.py +0 -94
- django_cfg/apps/payments/views/__init__.py +0 -63
- django_cfg/apps/payments/views/api_key_views.py +0 -164
- django_cfg/apps/payments/views/balance_views.py +0 -75
- django_cfg/apps/payments/views/currency_views.py +0 -122
- django_cfg/apps/payments/views/payment_views.py +0 -149
- django_cfg/apps/payments/views/subscription_views.py +0 -135
- django_cfg/apps/payments/views/tariff_views.py +0 -131
- django_cfg/apps/payments/views/templates/__init__.py +0 -25
- django_cfg/apps/payments/views/templates/ajax.py +0 -451
- django_cfg/apps/payments/views/templates/base.py +0 -212
- django_cfg/apps/payments/views/templates/dashboard.py +0 -60
- django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
- django_cfg/apps/payments/views/templates/payment_management.py +0 -158
- django_cfg/apps/payments/views/templates/qr_code.py +0 -174
- django_cfg/apps/payments/views/templates/stats.py +0 -244
- django_cfg/apps/payments/views/templates/utils.py +0 -181
- django_cfg/apps/payments/views/webhook_views.py +0 -266
- django_cfg/apps/payments/viewsets.py +0 -66
- django_cfg/core/integration.py +0 -160
- django_cfg/template_archive/.gitignore +0 -1
- django_cfg/template_archive/__init__.py +0 -0
- django_cfg/urls.py +0 -33
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,101 @@
|
|
1
|
+
"""
|
2
|
+
API ViewSets for the Universal Payment System v2.0.
|
3
|
+
|
4
|
+
Django REST Framework ViewSets with service layer integration and nested routing.
|
5
|
+
"""
|
6
|
+
|
7
|
+
# Base ViewSets
|
8
|
+
from .base import PaymentBaseViewSet
|
9
|
+
|
10
|
+
# Payment ViewSets
|
11
|
+
from .payments import (
|
12
|
+
PaymentViewSet,
|
13
|
+
UserPaymentViewSet,
|
14
|
+
PaymentCreateView,
|
15
|
+
PaymentStatusView,
|
16
|
+
)
|
17
|
+
|
18
|
+
# Balance ViewSets
|
19
|
+
from .balances import (
|
20
|
+
UserBalanceViewSet,
|
21
|
+
TransactionViewSet,
|
22
|
+
UserTransactionViewSet,
|
23
|
+
)
|
24
|
+
|
25
|
+
# Subscription ViewSets
|
26
|
+
from .subscriptions import (
|
27
|
+
SubscriptionViewSet,
|
28
|
+
UserSubscriptionViewSet,
|
29
|
+
EndpointGroupViewSet,
|
30
|
+
TariffViewSet,
|
31
|
+
)
|
32
|
+
|
33
|
+
# Currency ViewSets
|
34
|
+
from .currencies import (
|
35
|
+
CurrencyViewSet,
|
36
|
+
NetworkViewSet,
|
37
|
+
ProviderCurrencyViewSet,
|
38
|
+
CurrencyConversionView,
|
39
|
+
CurrencyRatesView,
|
40
|
+
SupportedCurrenciesView,
|
41
|
+
)
|
42
|
+
|
43
|
+
# API Key ViewSets
|
44
|
+
from .api_keys import (
|
45
|
+
APIKeyViewSet,
|
46
|
+
UserAPIKeyViewSet,
|
47
|
+
APIKeyCreateView,
|
48
|
+
APIKeyValidateView,
|
49
|
+
)
|
50
|
+
|
51
|
+
# Webhook ViewSets
|
52
|
+
from .webhooks import (
|
53
|
+
UniversalWebhookView,
|
54
|
+
webhook_handler,
|
55
|
+
webhook_health_check,
|
56
|
+
webhook_stats,
|
57
|
+
supported_providers,
|
58
|
+
)
|
59
|
+
|
60
|
+
__all__ = [
|
61
|
+
# Base
|
62
|
+
'PaymentBaseViewSet',
|
63
|
+
|
64
|
+
# Payment ViewSets
|
65
|
+
'PaymentViewSet',
|
66
|
+
'UserPaymentViewSet',
|
67
|
+
'PaymentCreateView',
|
68
|
+
'PaymentStatusView',
|
69
|
+
|
70
|
+
# Balance ViewSets
|
71
|
+
'UserBalanceViewSet',
|
72
|
+
'TransactionViewSet',
|
73
|
+
'UserTransactionViewSet',
|
74
|
+
|
75
|
+
# Subscription ViewSets
|
76
|
+
'SubscriptionViewSet',
|
77
|
+
'UserSubscriptionViewSet',
|
78
|
+
'EndpointGroupViewSet',
|
79
|
+
'TariffViewSet',
|
80
|
+
|
81
|
+
# Currency ViewSets
|
82
|
+
'CurrencyViewSet',
|
83
|
+
'NetworkViewSet',
|
84
|
+
'ProviderCurrencyViewSet',
|
85
|
+
'CurrencyConversionView',
|
86
|
+
'CurrencyRatesView',
|
87
|
+
'SupportedCurrenciesView',
|
88
|
+
|
89
|
+
# API Key ViewSets
|
90
|
+
'APIKeyViewSet',
|
91
|
+
'UserAPIKeyViewSet',
|
92
|
+
'APIKeyCreateView',
|
93
|
+
'APIKeyValidateView',
|
94
|
+
|
95
|
+
# Webhook ViewSets
|
96
|
+
'UniversalWebhookView',
|
97
|
+
'webhook_handler',
|
98
|
+
'webhook_health_check',
|
99
|
+
'webhook_stats',
|
100
|
+
'supported_providers',
|
101
|
+
]
|
@@ -0,0 +1,387 @@
|
|
1
|
+
"""
|
2
|
+
API Key ViewSets for the Universal Payment System v2.0.
|
3
|
+
|
4
|
+
DRF ViewSets for API key management with service integration.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from rest_framework import viewsets, permissions, status, generics
|
8
|
+
from rest_framework.decorators import action
|
9
|
+
from rest_framework.response import Response
|
10
|
+
from django_filters.rest_framework import DjangoFilterBackend
|
11
|
+
from django.contrib.auth import get_user_model
|
12
|
+
from django.db import models
|
13
|
+
from django.utils import timezone
|
14
|
+
|
15
|
+
from .base import PaymentBaseViewSet, NestedPaymentViewSet, ReadOnlyPaymentViewSet
|
16
|
+
from ...models import APIKey
|
17
|
+
from ..serializers.api_keys import (
|
18
|
+
APIKeySerializer,
|
19
|
+
APIKeyCreateSerializer,
|
20
|
+
APIKeyListSerializer,
|
21
|
+
APIKeyUpdateSerializer,
|
22
|
+
APIKeyActionSerializer,
|
23
|
+
APIKeyValidationSerializer,
|
24
|
+
APIKeyStatsSerializer,
|
25
|
+
)
|
26
|
+
from django_cfg.modules.django_logger import get_logger
|
27
|
+
|
28
|
+
User = get_user_model()
|
29
|
+
logger = get_logger("api_key_viewsets")
|
30
|
+
|
31
|
+
|
32
|
+
class APIKeyViewSet(PaymentBaseViewSet):
|
33
|
+
"""
|
34
|
+
Global API Key ViewSet: /api/api-keys/
|
35
|
+
|
36
|
+
Provides admin-level access to all API keys with filtering and stats.
|
37
|
+
"""
|
38
|
+
|
39
|
+
queryset = APIKey.objects.all()
|
40
|
+
serializer_class = APIKeySerializer
|
41
|
+
permission_classes = [permissions.IsAdminUser] # Admin only for global access
|
42
|
+
filterset_fields = ['is_active', 'user']
|
43
|
+
search_fields = ['name', 'user__username', 'user__email']
|
44
|
+
ordering_fields = ['created_at', 'updated_at', 'last_used_at', 'expires_at', 'total_requests']
|
45
|
+
|
46
|
+
serializer_classes = {
|
47
|
+
'list': APIKeyListSerializer,
|
48
|
+
'create': APIKeyCreateSerializer,
|
49
|
+
'retrieve': APIKeySerializer,
|
50
|
+
'update': APIKeyUpdateSerializer,
|
51
|
+
'partial_update': APIKeyUpdateSerializer,
|
52
|
+
}
|
53
|
+
|
54
|
+
def get_queryset(self):
|
55
|
+
"""Optimized queryset with related objects."""
|
56
|
+
return super().get_queryset().select_related('user')
|
57
|
+
|
58
|
+
@action(detail=True, methods=['post'])
|
59
|
+
def perform_action(self, request, pk=None):
|
60
|
+
"""
|
61
|
+
Perform action on API key.
|
62
|
+
|
63
|
+
POST /api/api-keys/{id}/perform_action/
|
64
|
+
"""
|
65
|
+
api_key = self.get_object()
|
66
|
+
|
67
|
+
serializer = APIKeyActionSerializer(
|
68
|
+
data=request.data,
|
69
|
+
context={
|
70
|
+
**self.get_serializer_context(),
|
71
|
+
'api_key_id': str(api_key.id)
|
72
|
+
}
|
73
|
+
)
|
74
|
+
|
75
|
+
if serializer.is_valid():
|
76
|
+
result = serializer.save()
|
77
|
+
return Response(result)
|
78
|
+
|
79
|
+
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
80
|
+
|
81
|
+
@action(detail=False, methods=['post'])
|
82
|
+
def validate_key(self, request):
|
83
|
+
"""
|
84
|
+
Validate API key.
|
85
|
+
|
86
|
+
POST /api/api-keys/validate_key/
|
87
|
+
"""
|
88
|
+
serializer = APIKeyValidationSerializer(
|
89
|
+
data=request.data,
|
90
|
+
context=self.get_serializer_context()
|
91
|
+
)
|
92
|
+
|
93
|
+
if serializer.is_valid():
|
94
|
+
result = serializer.save()
|
95
|
+
return Response(result)
|
96
|
+
|
97
|
+
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
98
|
+
|
99
|
+
@action(detail=False, methods=['get'])
|
100
|
+
def analytics(self, request):
|
101
|
+
"""
|
102
|
+
Get API key analytics.
|
103
|
+
|
104
|
+
GET /api/api-keys/analytics/?days=30
|
105
|
+
"""
|
106
|
+
serializer = APIKeyStatsSerializer(data=request.query_params)
|
107
|
+
|
108
|
+
if serializer.is_valid():
|
109
|
+
result = serializer.save()
|
110
|
+
return Response(result)
|
111
|
+
|
112
|
+
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
113
|
+
|
114
|
+
@action(detail=False, methods=['get'])
|
115
|
+
def by_user(self, request):
|
116
|
+
"""
|
117
|
+
Get API keys grouped by user.
|
118
|
+
|
119
|
+
GET /api/api-keys/by_user/
|
120
|
+
"""
|
121
|
+
try:
|
122
|
+
queryset = self.filter_queryset(self.get_queryset())
|
123
|
+
|
124
|
+
user_stats = {}
|
125
|
+
for api_key in queryset.select_related('user'):
|
126
|
+
user_id = api_key.user.id
|
127
|
+
username = api_key.user.username
|
128
|
+
|
129
|
+
if user_id not in user_stats:
|
130
|
+
user_stats[user_id] = {
|
131
|
+
'user_id': user_id,
|
132
|
+
'username': username,
|
133
|
+
'total_keys': 0,
|
134
|
+
'active_keys': 0,
|
135
|
+
'expired_keys': 0,
|
136
|
+
'total_requests': 0,
|
137
|
+
}
|
138
|
+
|
139
|
+
user_stats[user_id]['total_keys'] += 1
|
140
|
+
if api_key.is_active:
|
141
|
+
user_stats[user_id]['active_keys'] += 1
|
142
|
+
if api_key.is_expired():
|
143
|
+
user_stats[user_id]['expired_keys'] += 1
|
144
|
+
user_stats[user_id]['total_requests'] += api_key.total_requests or 0
|
145
|
+
|
146
|
+
return Response({
|
147
|
+
'user_stats': list(user_stats.values()),
|
148
|
+
'total_users': len(user_stats),
|
149
|
+
'generated_at': timezone.now().isoformat()
|
150
|
+
})
|
151
|
+
|
152
|
+
except Exception as e:
|
153
|
+
logger.error(f"API key user stats failed: {e}")
|
154
|
+
return Response(
|
155
|
+
{'error': f'User stats failed: {e}'},
|
156
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
157
|
+
)
|
158
|
+
|
159
|
+
@action(detail=False, methods=['get'])
|
160
|
+
def expiring_soon(self, request):
|
161
|
+
"""
|
162
|
+
Get API keys expiring soon.
|
163
|
+
|
164
|
+
GET /api/api-keys/expiring_soon/?days=7
|
165
|
+
"""
|
166
|
+
try:
|
167
|
+
days = int(request.query_params.get('days', 7))
|
168
|
+
from datetime import timedelta
|
169
|
+
|
170
|
+
expiry_threshold = timezone.now() + timedelta(days=days)
|
171
|
+
|
172
|
+
queryset = self.filter_queryset(self.get_queryset())
|
173
|
+
expiring_keys = queryset.filter(
|
174
|
+
expires_at__lte=expiry_threshold,
|
175
|
+
expires_at__gte=timezone.now(),
|
176
|
+
is_active=True
|
177
|
+
).select_related('user')
|
178
|
+
|
179
|
+
serializer = APIKeyListSerializer(expiring_keys, many=True)
|
180
|
+
|
181
|
+
return Response({
|
182
|
+
'expiring_keys': serializer.data,
|
183
|
+
'count': len(serializer.data),
|
184
|
+
'threshold_days': days,
|
185
|
+
'generated_at': timezone.now().isoformat()
|
186
|
+
})
|
187
|
+
|
188
|
+
except Exception as e:
|
189
|
+
logger.error(f"Expiring API keys failed: {e}")
|
190
|
+
return Response(
|
191
|
+
{'error': f'Expiring keys lookup failed: {e}'},
|
192
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
193
|
+
)
|
194
|
+
|
195
|
+
|
196
|
+
class UserAPIKeyViewSet(NestedPaymentViewSet):
|
197
|
+
"""
|
198
|
+
User-specific API Key ViewSet: /api/users/{user_id}/api-keys/
|
199
|
+
|
200
|
+
Provides user-scoped access to API keys with full CRUD operations.
|
201
|
+
"""
|
202
|
+
|
203
|
+
queryset = APIKey.objects.all()
|
204
|
+
serializer_class = APIKeySerializer
|
205
|
+
permission_classes = [permissions.IsAuthenticated]
|
206
|
+
filterset_fields = ['is_active']
|
207
|
+
search_fields = ['name']
|
208
|
+
ordering_fields = ['created_at', 'updated_at', 'last_used_at', 'expires_at']
|
209
|
+
|
210
|
+
# Nested ViewSet configuration
|
211
|
+
parent_lookup_field = 'user_pk'
|
212
|
+
parent_model_field = 'user'
|
213
|
+
|
214
|
+
serializer_classes = {
|
215
|
+
'list': APIKeyListSerializer,
|
216
|
+
'create': APIKeyCreateSerializer,
|
217
|
+
'retrieve': APIKeySerializer,
|
218
|
+
'update': APIKeyUpdateSerializer,
|
219
|
+
'partial_update': APIKeyUpdateSerializer,
|
220
|
+
}
|
221
|
+
|
222
|
+
def get_queryset(self):
|
223
|
+
"""Filter by user and optimize queryset."""
|
224
|
+
queryset = super().get_queryset()
|
225
|
+
|
226
|
+
# Additional permission check: users can only see their own API keys
|
227
|
+
if not self.request.user.is_staff:
|
228
|
+
user_id = self.kwargs.get('user_pk')
|
229
|
+
if str(self.request.user.id) != str(user_id):
|
230
|
+
return queryset.none()
|
231
|
+
|
232
|
+
return queryset
|
233
|
+
|
234
|
+
@action(detail=True, methods=['post'])
|
235
|
+
def perform_action(self, request, user_pk=None, pk=None):
|
236
|
+
"""
|
237
|
+
Perform action on API key.
|
238
|
+
|
239
|
+
POST /api/users/{user_id}/api-keys/{id}/perform_action/
|
240
|
+
"""
|
241
|
+
api_key = self.get_object()
|
242
|
+
|
243
|
+
serializer = APIKeyActionSerializer(
|
244
|
+
data=request.data,
|
245
|
+
context={
|
246
|
+
**self.get_serializer_context(),
|
247
|
+
'api_key_id': str(api_key.id)
|
248
|
+
}
|
249
|
+
)
|
250
|
+
|
251
|
+
if serializer.is_valid():
|
252
|
+
result = serializer.save()
|
253
|
+
return Response(result)
|
254
|
+
|
255
|
+
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
256
|
+
|
257
|
+
@action(detail=False, methods=['get'])
|
258
|
+
def active(self, request, user_pk=None):
|
259
|
+
"""
|
260
|
+
Get user's active API keys.
|
261
|
+
|
262
|
+
GET /api/users/{user_id}/api-keys/active/
|
263
|
+
"""
|
264
|
+
try:
|
265
|
+
queryset = self.filter_queryset(self.get_queryset())
|
266
|
+
active_keys = queryset.filter(is_active=True)
|
267
|
+
|
268
|
+
serializer = self.get_serializer(active_keys, many=True)
|
269
|
+
|
270
|
+
return Response({
|
271
|
+
'api_keys': serializer.data,
|
272
|
+
'count': len(serializer.data),
|
273
|
+
'user_id': user_pk,
|
274
|
+
'generated_at': timezone.now().isoformat()
|
275
|
+
})
|
276
|
+
|
277
|
+
except Exception as e:
|
278
|
+
logger.error(f"Active API keys lookup failed: {e}")
|
279
|
+
return Response(
|
280
|
+
{'error': f'Active API keys lookup failed: {e}'},
|
281
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
282
|
+
)
|
283
|
+
|
284
|
+
@action(detail=False, methods=['get'])
|
285
|
+
def summary(self, request, user_pk=None):
|
286
|
+
"""
|
287
|
+
Get user API key summary.
|
288
|
+
|
289
|
+
GET /api/users/{user_id}/api-keys/summary/
|
290
|
+
"""
|
291
|
+
try:
|
292
|
+
queryset = self.filter_queryset(self.get_queryset())
|
293
|
+
|
294
|
+
summary = queryset.aggregate(
|
295
|
+
total_keys=models.Count('id'),
|
296
|
+
active_keys=models.Count('id', filter=models.Q(is_active=True)),
|
297
|
+
expired_keys=models.Count('id', filter=models.Q(expires_at__lt=timezone.now())),
|
298
|
+
total_requests=models.Sum('total_requests'),
|
299
|
+
last_used=models.Max('last_used_at'),
|
300
|
+
)
|
301
|
+
|
302
|
+
return Response({
|
303
|
+
'user_id': user_pk,
|
304
|
+
'summary': {
|
305
|
+
**summary,
|
306
|
+
'total_requests': summary['total_requests'] or 0,
|
307
|
+
'inactive_keys': summary['total_keys'] - summary['active_keys'],
|
308
|
+
'last_used_formatted': summary['last_used'].isoformat() if summary['last_used'] else None,
|
309
|
+
},
|
310
|
+
'generated_at': timezone.now().isoformat()
|
311
|
+
})
|
312
|
+
|
313
|
+
except Exception as e:
|
314
|
+
logger.error(f"User API key summary failed: {e}")
|
315
|
+
return Response(
|
316
|
+
{'error': f'Summary generation failed: {e}'},
|
317
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
318
|
+
)
|
319
|
+
|
320
|
+
|
321
|
+
# Standalone views for common operations
|
322
|
+
class APIKeyCreateView(generics.CreateAPIView):
|
323
|
+
"""
|
324
|
+
Standalone API key creation endpoint: /api/api-keys/create/
|
325
|
+
|
326
|
+
Simplified endpoint for API key creation.
|
327
|
+
"""
|
328
|
+
|
329
|
+
serializer_class = APIKeyCreateSerializer
|
330
|
+
permission_classes = [permissions.IsAuthenticated]
|
331
|
+
|
332
|
+
def create(self, request, *args, **kwargs):
|
333
|
+
"""Create API key with enhanced response."""
|
334
|
+
serializer = self.get_serializer(data=request.data)
|
335
|
+
|
336
|
+
if serializer.is_valid():
|
337
|
+
try:
|
338
|
+
api_key = serializer.save()
|
339
|
+
|
340
|
+
response_data = {
|
341
|
+
'success': True,
|
342
|
+
'message': 'API key created successfully',
|
343
|
+
'api_key': serializer.to_representation(api_key)
|
344
|
+
}
|
345
|
+
|
346
|
+
return Response(response_data, status=status.HTTP_201_CREATED)
|
347
|
+
|
348
|
+
except Exception as e:
|
349
|
+
logger.error(f"API key creation failed: {e}")
|
350
|
+
return Response(
|
351
|
+
{
|
352
|
+
'success': False,
|
353
|
+
'error': f'API key creation failed: {e}',
|
354
|
+
'error_code': 'creation_failed'
|
355
|
+
},
|
356
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
357
|
+
)
|
358
|
+
|
359
|
+
return Response(
|
360
|
+
{
|
361
|
+
'success': False,
|
362
|
+
'error': 'Invalid API key data',
|
363
|
+
'errors': serializer.errors
|
364
|
+
},
|
365
|
+
status=status.HTTP_400_BAD_REQUEST
|
366
|
+
)
|
367
|
+
|
368
|
+
|
369
|
+
class APIKeyValidateView(generics.GenericAPIView):
|
370
|
+
"""
|
371
|
+
Standalone API key validation endpoint: /api/api-keys/validate/
|
372
|
+
|
373
|
+
Quick validation without full ViewSet overhead.
|
374
|
+
"""
|
375
|
+
|
376
|
+
serializer_class = APIKeyValidationSerializer
|
377
|
+
permission_classes = [permissions.IsAuthenticated]
|
378
|
+
|
379
|
+
def post(self, request, *args, **kwargs):
|
380
|
+
"""Validate API key."""
|
381
|
+
serializer = self.get_serializer(data=request.data)
|
382
|
+
|
383
|
+
if serializer.is_valid():
|
384
|
+
result = serializer.save()
|
385
|
+
return Response(result)
|
386
|
+
|
387
|
+
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|