django-cfg 1.2.22__py3-none-any.whl → 1.2.25__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.
Files changed (125) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/knowbase/tasks/archive_tasks.py +6 -6
  3. django_cfg/apps/knowbase/tasks/document_processing.py +3 -3
  4. django_cfg/apps/knowbase/tasks/external_data_tasks.py +2 -2
  5. django_cfg/apps/knowbase/tasks/maintenance.py +3 -3
  6. django_cfg/apps/payments/admin/__init__.py +23 -0
  7. django_cfg/apps/payments/admin/api_keys_admin.py +347 -0
  8. django_cfg/apps/payments/admin/balance_admin.py +434 -0
  9. django_cfg/apps/payments/admin/currencies_admin.py +186 -0
  10. django_cfg/apps/payments/admin/filters.py +259 -0
  11. django_cfg/apps/payments/admin/payments_admin.py +142 -0
  12. django_cfg/apps/payments/admin/subscriptions_admin.py +227 -0
  13. django_cfg/apps/payments/admin/tariffs_admin.py +199 -0
  14. django_cfg/apps/payments/config/__init__.py +65 -0
  15. django_cfg/apps/payments/config/module.py +70 -0
  16. django_cfg/apps/payments/config/providers.py +115 -0
  17. django_cfg/apps/payments/config/settings.py +96 -0
  18. django_cfg/apps/payments/config/utils.py +52 -0
  19. django_cfg/apps/payments/decorators.py +291 -0
  20. django_cfg/apps/payments/management/__init__.py +3 -0
  21. django_cfg/apps/payments/management/commands/README.md +178 -0
  22. django_cfg/apps/payments/management/commands/__init__.py +3 -0
  23. django_cfg/apps/payments/management/commands/currency_stats.py +323 -0
  24. django_cfg/apps/payments/management/commands/populate_currencies.py +246 -0
  25. django_cfg/apps/payments/management/commands/update_currencies.py +336 -0
  26. django_cfg/apps/payments/managers/currency_manager.py +65 -14
  27. django_cfg/apps/payments/middleware/api_access.py +294 -0
  28. django_cfg/apps/payments/middleware/rate_limiting.py +216 -0
  29. django_cfg/apps/payments/middleware/usage_tracking.py +296 -0
  30. django_cfg/apps/payments/migrations/0001_initial.py +125 -11
  31. django_cfg/apps/payments/models/__init__.py +18 -0
  32. django_cfg/apps/payments/models/api_keys.py +2 -2
  33. django_cfg/apps/payments/models/balance.py +2 -2
  34. django_cfg/apps/payments/models/base.py +16 -0
  35. django_cfg/apps/payments/models/events.py +2 -2
  36. django_cfg/apps/payments/models/payments.py +112 -2
  37. django_cfg/apps/payments/models/subscriptions.py +2 -2
  38. django_cfg/apps/payments/services/__init__.py +64 -7
  39. django_cfg/apps/payments/services/billing/__init__.py +8 -0
  40. django_cfg/apps/payments/services/cache/__init__.py +15 -0
  41. django_cfg/apps/payments/services/cache/base.py +30 -0
  42. django_cfg/apps/payments/services/cache/simple_cache.py +135 -0
  43. django_cfg/apps/payments/services/core/__init__.py +17 -0
  44. django_cfg/apps/payments/services/core/balance_service.py +447 -0
  45. django_cfg/apps/payments/services/core/fallback_service.py +432 -0
  46. django_cfg/apps/payments/services/core/payment_service.py +576 -0
  47. django_cfg/apps/payments/services/core/subscription_service.py +614 -0
  48. django_cfg/apps/payments/services/internal_types.py +297 -0
  49. django_cfg/apps/payments/services/middleware/__init__.py +8 -0
  50. django_cfg/apps/payments/services/monitoring/__init__.py +22 -0
  51. django_cfg/apps/payments/services/monitoring/api_schemas.py +222 -0
  52. django_cfg/apps/payments/services/monitoring/provider_health.py +372 -0
  53. django_cfg/apps/payments/services/providers/__init__.py +22 -0
  54. django_cfg/apps/payments/services/providers/base.py +137 -0
  55. django_cfg/apps/payments/services/providers/cryptapi.py +273 -0
  56. django_cfg/apps/payments/services/providers/cryptomus.py +310 -0
  57. django_cfg/apps/payments/services/providers/nowpayments.py +293 -0
  58. django_cfg/apps/payments/services/providers/registry.py +103 -0
  59. django_cfg/apps/payments/services/security/__init__.py +34 -0
  60. django_cfg/apps/payments/services/security/error_handler.py +637 -0
  61. django_cfg/apps/payments/services/security/payment_notifications.py +342 -0
  62. django_cfg/apps/payments/services/security/webhook_validator.py +475 -0
  63. django_cfg/apps/payments/services/validators/__init__.py +8 -0
  64. django_cfg/apps/payments/signals/__init__.py +13 -0
  65. django_cfg/apps/payments/signals/api_key_signals.py +160 -0
  66. django_cfg/apps/payments/signals/payment_signals.py +128 -0
  67. django_cfg/apps/payments/signals/subscription_signals.py +196 -0
  68. django_cfg/apps/payments/tasks/__init__.py +12 -0
  69. django_cfg/apps/payments/tasks/webhook_processing.py +177 -0
  70. django_cfg/apps/payments/urls.py +5 -5
  71. django_cfg/apps/payments/utils/__init__.py +45 -0
  72. django_cfg/apps/payments/utils/billing_utils.py +342 -0
  73. django_cfg/apps/payments/utils/config_utils.py +245 -0
  74. django_cfg/apps/payments/utils/middleware_utils.py +228 -0
  75. django_cfg/apps/payments/utils/validation_utils.py +94 -0
  76. django_cfg/apps/payments/views/payment_views.py +40 -2
  77. django_cfg/apps/payments/views/webhook_views.py +266 -0
  78. django_cfg/apps/payments/viewsets.py +65 -0
  79. django_cfg/apps/support/signals.py +16 -4
  80. django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
  81. django_cfg/cli/README.md +2 -2
  82. django_cfg/cli/commands/create_project.py +1 -1
  83. django_cfg/cli/commands/info.py +1 -1
  84. django_cfg/cli/main.py +1 -1
  85. django_cfg/cli/utils.py +5 -5
  86. django_cfg/core/config.py +18 -4
  87. django_cfg/models/payments.py +546 -0
  88. django_cfg/models/revolution.py +1 -1
  89. django_cfg/models/tasks.py +51 -2
  90. django_cfg/modules/base.py +12 -6
  91. django_cfg/modules/django_currency/README.md +104 -269
  92. django_cfg/modules/django_currency/__init__.py +99 -41
  93. django_cfg/modules/django_currency/clients/__init__.py +11 -0
  94. django_cfg/modules/django_currency/clients/coingecko_client.py +257 -0
  95. django_cfg/modules/django_currency/clients/yfinance_client.py +246 -0
  96. django_cfg/modules/django_currency/core/__init__.py +42 -0
  97. django_cfg/modules/django_currency/core/converter.py +169 -0
  98. django_cfg/modules/django_currency/core/exceptions.py +28 -0
  99. django_cfg/modules/django_currency/core/models.py +54 -0
  100. django_cfg/modules/django_currency/database/__init__.py +25 -0
  101. django_cfg/modules/django_currency/database/database_loader.py +507 -0
  102. django_cfg/modules/django_currency/utils/__init__.py +9 -0
  103. django_cfg/modules/django_currency/utils/cache.py +92 -0
  104. django_cfg/modules/django_email.py +42 -4
  105. django_cfg/modules/django_unfold/dashboard.py +20 -0
  106. django_cfg/registry/core.py +10 -0
  107. django_cfg/template_archive/__init__.py +0 -0
  108. django_cfg/template_archive/django_sample.zip +0 -0
  109. {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/METADATA +11 -6
  110. {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/RECORD +113 -50
  111. django_cfg/apps/agents/examples/__init__.py +0 -3
  112. django_cfg/apps/agents/examples/simple_example.py +0 -161
  113. django_cfg/apps/knowbase/examples/__init__.py +0 -3
  114. django_cfg/apps/knowbase/examples/external_data_usage.py +0 -191
  115. django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +0 -199
  116. django_cfg/apps/payments/services/base.py +0 -68
  117. django_cfg/apps/payments/services/nowpayments.py +0 -78
  118. django_cfg/apps/payments/services/providers.py +0 -77
  119. django_cfg/apps/payments/services/redis_service.py +0 -215
  120. django_cfg/modules/django_currency/cache.py +0 -430
  121. django_cfg/modules/django_currency/converter.py +0 -324
  122. django_cfg/modules/django_currency/service.py +0 -277
  123. {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/WHEEL +0 -0
  124. {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/entry_points.txt +0 -0
  125. {django_cfg-1.2.22.dist-info → django_cfg-1.2.25.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,291 @@
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
@@ -0,0 +1,3 @@
1
+ """
2
+ Management commands for the payments app.
3
+ """
@@ -0,0 +1,178 @@
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 при критических ошибках
@@ -0,0 +1,3 @@
1
+ """
2
+ Management commands directory.
3
+ """