django-cfg 1.2.29__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 -9
- 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 +600 -108
- django_cfg/apps/payments/admin/filters.py +306 -199
- django_cfg/apps/payments/admin/payments_admin.py +470 -64
- 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 +381 -0
- django_cfg/apps/payments/management/commands/manage_providers.py +408 -0
- 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 +343 -163
- django_cfg/apps/payments/middleware/usage_tracking.py +250 -238
- django_cfg/apps/payments/migrations/0001_initial.py +708 -536
- django_cfg/apps/payments/models/__init__.py +16 -20
- 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 +207 -67
- 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 -284
- 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 +344 -468
- django_cfg/apps/payments/services/core/subscription_service.py +425 -484
- 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 +232 -71
- django_cfg/apps/payments/services/providers/nowpayments.py +404 -219
- django_cfg/apps/payments/services/providers/registry.py +429 -80
- 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 +211 -130
- django_cfg/apps/payments/signals/balance_signals.py +174 -0
- django_cfg/apps/payments/signals/payment_signals.py +129 -98
- django_cfg/apps/payments/signals/subscription_signals.py +195 -143
- 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 +46 -47
- django_cfg/apps/payments/urls_admin.py +49 -0
- 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/apps/tasks/urls.py +0 -2
- django_cfg/apps/tasks/urls_admin.py +14 -0
- django_cfg/apps/urls.py +4 -4
- django_cfg/config.py +1 -1
- django_cfg/core/config.py +75 -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 -498
- django_cfg/modules/django_currency/__init__.py +16 -11
- django_cfg/modules/django_currency/clients/__init__.py +4 -4
- django_cfg/modules/django_currency/clients/coinpaprika_client.py +289 -0
- django_cfg/modules/django_currency/clients/yahoo_client.py +157 -0
- django_cfg/modules/django_currency/core/__init__.py +1 -7
- django_cfg/modules/django_currency/core/converter.py +18 -23
- django_cfg/modules/django_currency/core/models.py +122 -11
- django_cfg/modules/django_currency/database/__init__.py +4 -4
- django_cfg/modules/django_currency/database/database_loader.py +190 -309
- django_cfg/modules/django_logger.py +160 -146
- django_cfg/modules/django_unfold/dashboard.py +65 -12
- django_cfg/registry/core.py +1 -0
- django_cfg/template_archive/django_sample.zip +0 -0
- django_cfg/templates/admin/components/action_grid.html +9 -9
- django_cfg/templates/admin/components/metric_card.html +5 -5
- django_cfg/templates/admin/components/status_badge.html +2 -2
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +152 -24
- django_cfg/templates/admin/snippets/components/quick_actions.html +3 -3
- django_cfg/templates/admin/snippets/components/system_health.html +1 -1
- django_cfg/templates/admin/snippets/tabs/overview_tab.html +49 -52
- django_cfg/utils/smart_defaults.py +222 -571
- django_cfg/utils/toolkit.py +51 -11
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/METADATA +5 -4
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/RECORD +172 -182
- 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 -178
- django_cfg/apps/payments/management/commands/currency_stats.py +0 -323
- django_cfg/apps/payments/management/commands/populate_currencies.py +0 -246
- django_cfg/apps/payments/management/commands/update_currencies.py +0 -336
- django_cfg/apps/payments/managers/__init__.py +0 -22
- 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 -83
- django_cfg/apps/payments/managers/payment_manager.py +0 -44
- django_cfg/apps/payments/managers/subscription_manager.py +0 -37
- django_cfg/apps/payments/managers/tariff_manager.py +0 -29
- django_cfg/apps/payments/models/events.py +0 -73
- django_cfg/apps/payments/serializers/__init__.py +0 -56
- 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 -55
- 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 -297
- 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 -222
- django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
- django_cfg/apps/payments/services/providers/cryptapi.py +0 -273
- django_cfg/apps/payments/services/providers/cryptomus.py +0 -311
- django_cfg/apps/payments/services/security/__init__.py +0 -34
- django_cfg/apps/payments/services/security/error_handler.py +0 -637
- django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
- django_cfg/apps/payments/services/security/webhook_validator.py +0 -475
- django_cfg/apps/payments/services/validators/__init__.py +0 -8
- 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/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 -36
- django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
- django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -27
- django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -144
- django_cfg/apps/payments/templates/payments/dashboard.html +0 -346
- django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
- django_cfg/apps/payments/urls_templates.py +0 -52
- django_cfg/apps/payments/utils/__init__.py +0 -45
- django_cfg/apps/payments/utils/billing_utils.py +0 -342
- django_cfg/apps/payments/utils/config_utils.py +0 -245
- 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 -62
- 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 -111
- 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 -312
- django_cfg/apps/payments/views/templates/base.py +0 -204
- 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 -164
- django_cfg/apps/payments/views/templates/qr_code.py +0 -174
- django_cfg/apps/payments/views/templates/stats.py +0 -240
- 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 -65
- django_cfg/core/integration.py +0 -160
- django_cfg/modules/django_currency/clients/coingecko_client.py +0 -257
- django_cfg/modules/django_currency/clients/yfinance_client.py +0 -246
- 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.29.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,291 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Decorators for API access control and endpoint registration.
|
3
|
-
"""
|
4
|
-
|
5
|
-
import functools
|
6
|
-
import logging
|
7
|
-
from typing import Optional, List, Callable, Any
|
8
|
-
from django.http import JsonResponse
|
9
|
-
from django.conf import settings
|
10
|
-
from django.utils import timezone
|
11
|
-
from .models import EndpointGroup, Subscription
|
12
|
-
|
13
|
-
logger = logging.getLogger(__name__)
|
14
|
-
|
15
|
-
|
16
|
-
def require_api_key(func: Callable) -> Callable:
|
17
|
-
"""
|
18
|
-
Decorator to require valid API key for function-based views.
|
19
|
-
Works with APIAccessMiddleware.
|
20
|
-
"""
|
21
|
-
@functools.wraps(func)
|
22
|
-
def wrapper(request, *args, **kwargs):
|
23
|
-
if not hasattr(request, 'payment_api_key'):
|
24
|
-
return JsonResponse({
|
25
|
-
'error': {
|
26
|
-
'code': 'MISSING_API_KEY',
|
27
|
-
'message': 'Valid API key required',
|
28
|
-
'timestamp': timezone.now().isoformat(),
|
29
|
-
}
|
30
|
-
}, status=401)
|
31
|
-
|
32
|
-
return func(request, *args, **kwargs)
|
33
|
-
|
34
|
-
return wrapper
|
35
|
-
|
36
|
-
|
37
|
-
def require_subscription(endpoint_group_name: str):
|
38
|
-
"""
|
39
|
-
Decorator to require active subscription for specific endpoint group.
|
40
|
-
|
41
|
-
Args:
|
42
|
-
endpoint_group_name: Name of the endpoint group to check
|
43
|
-
"""
|
44
|
-
def decorator(func: Callable) -> Callable:
|
45
|
-
@functools.wraps(func)
|
46
|
-
def wrapper(request, *args, **kwargs):
|
47
|
-
# Check if middleware already validated subscription
|
48
|
-
if hasattr(request, 'payment_subscription'):
|
49
|
-
subscription = request.payment_subscription
|
50
|
-
if subscription.endpoint_group.name == endpoint_group_name:
|
51
|
-
return func(request, *args, **kwargs)
|
52
|
-
|
53
|
-
# If not validated by middleware, check manually
|
54
|
-
if not hasattr(request, 'payment_api_key'):
|
55
|
-
return JsonResponse({
|
56
|
-
'error': {
|
57
|
-
'code': 'MISSING_API_KEY',
|
58
|
-
'message': 'Valid API key required',
|
59
|
-
'timestamp': timezone.now().isoformat(),
|
60
|
-
}
|
61
|
-
}, status=401)
|
62
|
-
|
63
|
-
try:
|
64
|
-
endpoint_group = EndpointGroup.objects.get(
|
65
|
-
name=endpoint_group_name,
|
66
|
-
is_active=True
|
67
|
-
)
|
68
|
-
|
69
|
-
subscription = Subscription.objects.filter(
|
70
|
-
user=request.payment_api_key.user,
|
71
|
-
endpoint_group=endpoint_group,
|
72
|
-
status='active',
|
73
|
-
expires_at__gt=timezone.now()
|
74
|
-
).first()
|
75
|
-
|
76
|
-
if not subscription:
|
77
|
-
return JsonResponse({
|
78
|
-
'error': {
|
79
|
-
'code': 'NO_SUBSCRIPTION',
|
80
|
-
'message': f'Active subscription required for {endpoint_group.display_name}',
|
81
|
-
'timestamp': timezone.now().isoformat(),
|
82
|
-
}
|
83
|
-
}, status=403)
|
84
|
-
|
85
|
-
# Store subscription in request
|
86
|
-
request.payment_subscription = subscription
|
87
|
-
|
88
|
-
return func(request, *args, **kwargs)
|
89
|
-
|
90
|
-
except EndpointGroup.DoesNotExist:
|
91
|
-
logger.error(f"Endpoint group '{endpoint_group_name}' not found")
|
92
|
-
return JsonResponse({
|
93
|
-
'error': {
|
94
|
-
'code': 'INVALID_ENDPOINT_GROUP',
|
95
|
-
'message': 'Invalid endpoint group',
|
96
|
-
'timestamp': timezone.now().isoformat(),
|
97
|
-
}
|
98
|
-
}, status=500)
|
99
|
-
|
100
|
-
return wrapper
|
101
|
-
return decorator
|
102
|
-
|
103
|
-
|
104
|
-
def require_tier(minimum_tier: str):
|
105
|
-
"""
|
106
|
-
Decorator to require minimum subscription tier.
|
107
|
-
|
108
|
-
Args:
|
109
|
-
minimum_tier: Minimum required tier (basic, premium, enterprise)
|
110
|
-
"""
|
111
|
-
tier_hierarchy = {
|
112
|
-
'basic': 1,
|
113
|
-
'premium': 2,
|
114
|
-
'enterprise': 3,
|
115
|
-
}
|
116
|
-
|
117
|
-
def decorator(func: Callable) -> Callable:
|
118
|
-
@functools.wraps(func)
|
119
|
-
def wrapper(request, *args, **kwargs):
|
120
|
-
if not hasattr(request, 'payment_subscription'):
|
121
|
-
return JsonResponse({
|
122
|
-
'error': {
|
123
|
-
'code': 'NO_SUBSCRIPTION',
|
124
|
-
'message': 'Active subscription required',
|
125
|
-
'timestamp': timezone.now().isoformat(),
|
126
|
-
}
|
127
|
-
}, status=403)
|
128
|
-
|
129
|
-
subscription = request.payment_subscription
|
130
|
-
current_tier_level = tier_hierarchy.get(subscription.tier, 0)
|
131
|
-
required_tier_level = tier_hierarchy.get(minimum_tier, 999)
|
132
|
-
|
133
|
-
if current_tier_level < required_tier_level:
|
134
|
-
return JsonResponse({
|
135
|
-
'error': {
|
136
|
-
'code': 'INSUFFICIENT_TIER',
|
137
|
-
'message': f'Tier {minimum_tier} or higher required',
|
138
|
-
'current_tier': subscription.tier,
|
139
|
-
'required_tier': minimum_tier,
|
140
|
-
'timestamp': timezone.now().isoformat(),
|
141
|
-
}
|
142
|
-
}, status=403)
|
143
|
-
|
144
|
-
return func(request, *args, **kwargs)
|
145
|
-
|
146
|
-
return wrapper
|
147
|
-
return decorator
|
148
|
-
|
149
|
-
|
150
|
-
def track_usage(cost_per_request: float = 0.0):
|
151
|
-
"""
|
152
|
-
Decorator to track API usage and deduct costs.
|
153
|
-
|
154
|
-
Args:
|
155
|
-
cost_per_request: Cost to deduct per successful request
|
156
|
-
"""
|
157
|
-
def decorator(func: Callable) -> Callable:
|
158
|
-
@functools.wraps(func)
|
159
|
-
def wrapper(request, *args, **kwargs):
|
160
|
-
# Execute the function
|
161
|
-
response = func(request, *args, **kwargs)
|
162
|
-
|
163
|
-
# Track usage if successful and we have subscription
|
164
|
-
if (hasattr(request, 'payment_subscription') and
|
165
|
-
hasattr(response, 'status_code') and
|
166
|
-
200 <= response.status_code < 300 and
|
167
|
-
cost_per_request > 0):
|
168
|
-
|
169
|
-
try:
|
170
|
-
from .models import Transaction
|
171
|
-
|
172
|
-
subscription = request.payment_subscription
|
173
|
-
|
174
|
-
# Create billing transaction
|
175
|
-
Transaction.objects.create(
|
176
|
-
user=subscription.user,
|
177
|
-
subscription=subscription,
|
178
|
-
transaction_type='debit',
|
179
|
-
amount_usd=-cost_per_request,
|
180
|
-
description=f"API usage: {request.method} {request.path}",
|
181
|
-
metadata={
|
182
|
-
'endpoint': request.path,
|
183
|
-
'method': request.method,
|
184
|
-
'cost_per_request': cost_per_request,
|
185
|
-
}
|
186
|
-
)
|
187
|
-
|
188
|
-
except Exception as e:
|
189
|
-
logger.error(f"Error tracking usage: {e}")
|
190
|
-
|
191
|
-
return response
|
192
|
-
|
193
|
-
return wrapper
|
194
|
-
return decorator
|
195
|
-
|
196
|
-
|
197
|
-
def register_endpoint(endpoint_group_name: str,
|
198
|
-
display_name: Optional[str] = None,
|
199
|
-
description: Optional[str] = None,
|
200
|
-
require_api_key: bool = True):
|
201
|
-
"""
|
202
|
-
Decorator to automatically register endpoint with the system.
|
203
|
-
This creates or updates EndpointGroup records.
|
204
|
-
|
205
|
-
Args:
|
206
|
-
endpoint_group_name: Internal name for the endpoint group
|
207
|
-
display_name: Human-readable name
|
208
|
-
description: Description of the endpoint functionality
|
209
|
-
require_api_key: Whether this endpoint requires API key
|
210
|
-
"""
|
211
|
-
def decorator(func: Callable) -> Callable:
|
212
|
-
@functools.wraps(func)
|
213
|
-
def wrapper(*args, **kwargs):
|
214
|
-
# Auto-register endpoint group if it doesn't exist
|
215
|
-
try:
|
216
|
-
endpoint_group, created = EndpointGroup.objects.get_or_create(
|
217
|
-
name=endpoint_group_name,
|
218
|
-
defaults={
|
219
|
-
'display_name': display_name or endpoint_group_name.replace('_', ' ').title(),
|
220
|
-
'description': description or f'Auto-registered endpoint group: {endpoint_group_name}',
|
221
|
-
'require_api_key': require_api_key,
|
222
|
-
'is_active': True,
|
223
|
-
}
|
224
|
-
)
|
225
|
-
|
226
|
-
if created:
|
227
|
-
logger.info(f"Auto-registered endpoint group: {endpoint_group_name}")
|
228
|
-
|
229
|
-
except Exception as e:
|
230
|
-
logger.error(f"Error auto-registering endpoint group: {e}")
|
231
|
-
|
232
|
-
return func(*args, **kwargs)
|
233
|
-
|
234
|
-
return wrapper
|
235
|
-
return decorator
|
236
|
-
|
237
|
-
|
238
|
-
def check_usage_limit(func: Callable) -> Callable:
|
239
|
-
"""
|
240
|
-
Decorator to check subscription usage limits before processing request.
|
241
|
-
"""
|
242
|
-
@functools.wraps(func)
|
243
|
-
def wrapper(request, *args, **kwargs):
|
244
|
-
if hasattr(request, 'payment_subscription'):
|
245
|
-
subscription = request.payment_subscription
|
246
|
-
|
247
|
-
# Check if usage limit exceeded
|
248
|
-
if (subscription.usage_limit > 0 and
|
249
|
-
subscription.usage_current >= subscription.usage_limit):
|
250
|
-
|
251
|
-
return JsonResponse({
|
252
|
-
'error': {
|
253
|
-
'code': 'USAGE_EXCEEDED',
|
254
|
-
'message': 'Monthly usage limit exceeded',
|
255
|
-
'current_usage': subscription.usage_current,
|
256
|
-
'usage_limit': subscription.usage_limit,
|
257
|
-
'reset_date': subscription.next_billing.isoformat() if subscription.next_billing else None,
|
258
|
-
'timestamp': timezone.now().isoformat(),
|
259
|
-
}
|
260
|
-
}, status=429)
|
261
|
-
|
262
|
-
return func(request, *args, **kwargs)
|
263
|
-
|
264
|
-
return wrapper
|
265
|
-
|
266
|
-
|
267
|
-
# Utility decorator combinations
|
268
|
-
def api_endpoint(endpoint_group_name: str,
|
269
|
-
minimum_tier: str = 'basic',
|
270
|
-
cost_per_request: float = 0.0):
|
271
|
-
"""
|
272
|
-
Combination decorator for typical API endpoint protection.
|
273
|
-
|
274
|
-
Args:
|
275
|
-
endpoint_group_name: Name of the endpoint group
|
276
|
-
minimum_tier: Minimum subscription tier required
|
277
|
-
cost_per_request: Cost to charge per successful request
|
278
|
-
"""
|
279
|
-
def decorator(func: Callable) -> Callable:
|
280
|
-
# Apply decorators in reverse order (they wrap from inside out)
|
281
|
-
decorated_func = func
|
282
|
-
decorated_func = track_usage(cost_per_request)(decorated_func)
|
283
|
-
decorated_func = check_usage_limit(decorated_func)
|
284
|
-
decorated_func = require_tier(minimum_tier)(decorated_func)
|
285
|
-
decorated_func = require_subscription(endpoint_group_name)(decorated_func)
|
286
|
-
decorated_func = require_api_key(decorated_func)
|
287
|
-
decorated_func = register_endpoint(endpoint_group_name)(decorated_func)
|
288
|
-
|
289
|
-
return decorated_func
|
290
|
-
|
291
|
-
return decorator
|
@@ -1,178 +0,0 @@
|
|
1
|
-
# Currency Management Commands
|
2
|
-
|
3
|
-
Management команды для работы с валютами в Universal Payments System.
|
4
|
-
|
5
|
-
## Команды
|
6
|
-
|
7
|
-
### 🪙 `populate_currencies` - Первоначальное заполнение
|
8
|
-
|
9
|
-
Заполняет пустую базу данных валютами из внешних API (CoinGecko, YFinance).
|
10
|
-
|
11
|
-
```bash
|
12
|
-
# Быстрое заполнение (50 криптовалют + 20 фиатных)
|
13
|
-
python manage.py populate_currencies --quick
|
14
|
-
|
15
|
-
# Стандартное заполнение (200 криптовалют + 30 фиатных)
|
16
|
-
python manage.py populate_currencies
|
17
|
-
|
18
|
-
# Только криптовалюты
|
19
|
-
python manage.py populate_currencies --crypto-only
|
20
|
-
|
21
|
-
# Только фиатные валюты
|
22
|
-
python manage.py populate_currencies --fiat-only
|
23
|
-
|
24
|
-
# Пропустить существующие валюты
|
25
|
-
python manage.py populate_currencies --skip-existing
|
26
|
-
```
|
27
|
-
|
28
|
-
### 🔄 `update_currencies` - Обновление курсов
|
29
|
-
|
30
|
-
Обновляет курсы валют с внешних API.
|
31
|
-
|
32
|
-
```bash
|
33
|
-
# Обновить устаревшие курсы (старше 6 часов)
|
34
|
-
python manage.py update_currencies
|
35
|
-
|
36
|
-
# Принудительно обновить все валюты
|
37
|
-
python manage.py update_currencies --force-update
|
38
|
-
|
39
|
-
# Сухой прогон (показать что будет обновлено)
|
40
|
-
python manage.py update_currencies --dry-run
|
41
|
-
|
42
|
-
# Кастомные лимиты
|
43
|
-
python manage.py update_currencies --max-crypto 100 --max-fiat 30
|
44
|
-
|
45
|
-
# Исключить стейблкоины
|
46
|
-
python manage.py update_currencies --exclude-stablecoins
|
47
|
-
|
48
|
-
# Подробный вывод
|
49
|
-
python manage.py update_currencies --verbose
|
50
|
-
```
|
51
|
-
|
52
|
-
### 📊 `currency_stats` - Статистика валют
|
53
|
-
|
54
|
-
Показывает статистику базы данных валют.
|
55
|
-
|
56
|
-
```bash
|
57
|
-
# Базовая статистика
|
58
|
-
python manage.py currency_stats
|
59
|
-
|
60
|
-
# Детальная статистика
|
61
|
-
python manage.py currency_stats --detailed
|
62
|
-
|
63
|
-
# Показать топ-10 валют
|
64
|
-
python manage.py currency_stats --top 10
|
65
|
-
|
66
|
-
# Проверить устаревшие курсы
|
67
|
-
python manage.py currency_stats --check-rates
|
68
|
-
|
69
|
-
# Экспортировать в CSV
|
70
|
-
python manage.py currency_stats --export-csv currencies.csv
|
71
|
-
```
|
72
|
-
|
73
|
-
## Автоматизация
|
74
|
-
|
75
|
-
### Cron для регулярного обновления
|
76
|
-
|
77
|
-
```bash
|
78
|
-
# Обновлять курсы каждые 6 часов
|
79
|
-
0 */6 * * * cd /path/to/project && poetry run python manage.py update_currencies
|
80
|
-
|
81
|
-
# Ежедневная проверка статистики
|
82
|
-
0 9 * * * cd /path/to/project && poetry run python manage.py currency_stats --check-rates
|
83
|
-
```
|
84
|
-
|
85
|
-
### Docker
|
86
|
-
|
87
|
-
```bash
|
88
|
-
# В Docker контейнере
|
89
|
-
docker exec -it container_name poetry run python manage.py populate_currencies --quick
|
90
|
-
docker exec -it container_name poetry run python manage.py update_currencies
|
91
|
-
```
|
92
|
-
|
93
|
-
## Примеры использования
|
94
|
-
|
95
|
-
### Первоначальная настройка
|
96
|
-
|
97
|
-
```bash
|
98
|
-
# 1. Заполнить базу данных валютами
|
99
|
-
python manage.py populate_currencies --quick
|
100
|
-
|
101
|
-
# 2. Проверить результат
|
102
|
-
python manage.py currency_stats
|
103
|
-
```
|
104
|
-
|
105
|
-
### Регулярное обслуживание
|
106
|
-
|
107
|
-
```bash
|
108
|
-
# 1. Обновить курсы
|
109
|
-
python manage.py update_currencies --verbose
|
110
|
-
|
111
|
-
# 2. Проверить устаревшие курсы
|
112
|
-
python manage.py currency_stats --check-rates
|
113
|
-
|
114
|
-
# 3. При необходимости - принудительное обновление
|
115
|
-
python manage.py update_currencies --force-update
|
116
|
-
```
|
117
|
-
|
118
|
-
### Production обслуживание
|
119
|
-
|
120
|
-
```bash
|
121
|
-
# Обновление с минимальными API запросами
|
122
|
-
python manage.py update_currencies --max-crypto 50 --max-fiat 20
|
123
|
-
|
124
|
-
# Мониторинг состояния
|
125
|
-
python manage.py currency_stats --detailed --export-csv daily_report.csv
|
126
|
-
```
|
127
|
-
|
128
|
-
## Интеграция с django_currency
|
129
|
-
|
130
|
-
Команды используют модуль `django_currency.database.database_loader` для:
|
131
|
-
|
132
|
-
- ✅ Получения списка валют с CoinGecko
|
133
|
-
- ✅ Загрузки курсов фиатных валют с YFinance
|
134
|
-
- ✅ Rate limiting для защиты от API throttling
|
135
|
-
- ✅ Кэширования для оптимизации производительности
|
136
|
-
- ✅ Pydantic валидации для типобезопасности
|
137
|
-
|
138
|
-
## Конфигурация
|
139
|
-
|
140
|
-
Настройки в `DatabaseLoaderConfig`:
|
141
|
-
|
142
|
-
```python
|
143
|
-
config = DatabaseLoaderConfig(
|
144
|
-
max_cryptocurrencies=500, # Максимум криптовалют
|
145
|
-
max_fiat_currencies=50, # Максимум фиатных валют
|
146
|
-
min_market_cap_usd=1_000_000, # Минимальная капитализация
|
147
|
-
coingecko_delay=1.5, # Задержка между запросами
|
148
|
-
yfinance_delay=0.5, # Задержка для YFinance
|
149
|
-
exclude_stablecoins=False, # Исключить стейблкоины
|
150
|
-
cache_ttl_hours=24 # TTL кэша в часах
|
151
|
-
)
|
152
|
-
```
|
153
|
-
|
154
|
-
## Мониторинг
|
155
|
-
|
156
|
-
### Логи
|
157
|
-
|
158
|
-
Команды логируют в `django_cfg.apps.payments.management.commands`:
|
159
|
-
|
160
|
-
```python
|
161
|
-
import logging
|
162
|
-
logger = logging.getLogger('django_cfg.apps.payments.management.commands')
|
163
|
-
```
|
164
|
-
|
165
|
-
### Метрики
|
166
|
-
|
167
|
-
- Количество созданных/обновленных валют
|
168
|
-
- Время выполнения API запросов
|
169
|
-
- Ошибки валидации и API
|
170
|
-
- Статистика курсов валют
|
171
|
-
|
172
|
-
## Безопасность
|
173
|
-
|
174
|
-
- ✅ Rate limiting для API запросов
|
175
|
-
- ✅ Atomic транзакции для консистентности
|
176
|
-
- ✅ Graceful handling ошибок API
|
177
|
-
- ✅ Валидация данных с Pydantic
|
178
|
-
- ✅ Rollback при критических ошибках
|