django-cfg 1.2.31__py3-none-any.whl → 1.3.3__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 (264) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/api/health/views.py +4 -2
  3. django_cfg/apps/knowbase/config/settings.py +16 -15
  4. django_cfg/apps/payments/README.md +326 -0
  5. django_cfg/apps/payments/admin/__init__.py +20 -10
  6. django_cfg/apps/payments/admin/api_keys_admin.py +521 -237
  7. django_cfg/apps/payments/admin/balance_admin.py +592 -297
  8. django_cfg/apps/payments/admin/currencies_admin.py +526 -222
  9. django_cfg/apps/payments/admin/filters.py +306 -199
  10. django_cfg/apps/payments/admin/payments_admin.py +465 -70
  11. django_cfg/apps/payments/admin/subscriptions_admin.py +578 -128
  12. django_cfg/apps/payments/admin_interface/__init__.py +18 -0
  13. django_cfg/apps/payments/admin_interface/templates/payments/base.html +162 -0
  14. django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +38 -0
  15. django_cfg/apps/payments/admin_interface/templates/payments/components/loading_spinner.html +16 -0
  16. django_cfg/apps/payments/admin_interface/templates/payments/components/notification.html +27 -0
  17. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_card.html +86 -0
  18. django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +39 -0
  19. django_cfg/apps/payments/admin_interface/templates/payments/currency_converter.html +382 -0
  20. django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +300 -0
  21. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +303 -0
  22. django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +382 -0
  23. django_cfg/apps/payments/admin_interface/templates/payments/payment_status.html +500 -0
  24. django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +594 -0
  25. django_cfg/apps/payments/admin_interface/views/__init__.py +23 -0
  26. django_cfg/apps/payments/admin_interface/views/payment_views.py +259 -0
  27. django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +37 -0
  28. django_cfg/apps/payments/apps.py +34 -9
  29. django_cfg/apps/payments/config/__init__.py +28 -51
  30. django_cfg/apps/payments/config/constance/__init__.py +22 -0
  31. django_cfg/apps/payments/config/constance/config_service.py +123 -0
  32. django_cfg/apps/payments/config/constance/fields.py +69 -0
  33. django_cfg/apps/payments/config/constance/settings.py +160 -0
  34. django_cfg/apps/payments/config/django_cfg_integration.py +202 -0
  35. django_cfg/apps/payments/config/helpers.py +130 -0
  36. django_cfg/apps/payments/management/__init__.py +1 -3
  37. django_cfg/apps/payments/management/commands/__init__.py +1 -3
  38. django_cfg/apps/payments/management/commands/cleanup_expired_data.py +419 -0
  39. django_cfg/apps/payments/management/commands/currency_stats.py +297 -225
  40. django_cfg/apps/payments/management/commands/manage_currencies.py +303 -151
  41. django_cfg/apps/payments/management/commands/manage_providers.py +333 -160
  42. django_cfg/apps/payments/management/commands/process_pending_payments.py +357 -0
  43. django_cfg/apps/payments/management/commands/test_providers.py +434 -0
  44. django_cfg/apps/payments/middleware/__init__.py +3 -1
  45. django_cfg/apps/payments/middleware/api_access.py +329 -222
  46. django_cfg/apps/payments/middleware/rate_limiting.py +342 -152
  47. django_cfg/apps/payments/middleware/usage_tracking.py +249 -240
  48. django_cfg/apps/payments/migrations/0001_initial.py +708 -536
  49. django_cfg/apps/payments/models/__init__.py +13 -18
  50. django_cfg/apps/payments/models/api_keys.py +121 -43
  51. django_cfg/apps/payments/models/balance.py +153 -115
  52. django_cfg/apps/payments/models/base.py +68 -15
  53. django_cfg/apps/payments/models/currencies.py +172 -148
  54. django_cfg/apps/payments/models/managers/__init__.py +44 -0
  55. django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
  56. django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
  57. django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
  58. django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
  59. django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
  60. django_cfg/apps/payments/models/payments.py +235 -285
  61. django_cfg/apps/payments/models/subscriptions.py +257 -177
  62. django_cfg/apps/payments/models/tariffs.py +147 -40
  63. django_cfg/apps/payments/services/__init__.py +209 -56
  64. django_cfg/apps/payments/services/cache/__init__.py +6 -6
  65. django_cfg/apps/payments/services/cache_service/__init__.py +143 -0
  66. django_cfg/apps/payments/services/cache_service/api_key_cache.py +37 -0
  67. django_cfg/apps/payments/services/{cache/base.py → cache_service/interfaces.py} +3 -1
  68. django_cfg/apps/payments/services/cache_service/keys.py +49 -0
  69. django_cfg/apps/payments/services/cache_service/rate_limit_cache.py +47 -0
  70. django_cfg/apps/payments/services/cache_service/simple_cache.py +101 -0
  71. django_cfg/apps/payments/services/core/__init__.py +10 -6
  72. django_cfg/apps/payments/services/core/balance_service.py +435 -360
  73. django_cfg/apps/payments/services/core/base.py +166 -0
  74. django_cfg/apps/payments/services/core/currency_service.py +478 -0
  75. django_cfg/apps/payments/services/core/payment_service.py +371 -465
  76. django_cfg/apps/payments/services/core/subscription_service.py +425 -481
  77. django_cfg/apps/payments/services/core/webhook_service.py +410 -0
  78. django_cfg/apps/payments/services/integrations/__init__.py +29 -0
  79. django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
  80. django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
  81. django_cfg/apps/payments/services/providers/__init__.py +9 -14
  82. django_cfg/apps/payments/services/providers/base.py +234 -174
  83. django_cfg/apps/payments/services/providers/nowpayments.py +478 -0
  84. django_cfg/apps/payments/services/providers/registry.py +367 -301
  85. django_cfg/apps/payments/services/types/__init__.py +78 -0
  86. django_cfg/apps/payments/services/types/data.py +177 -0
  87. django_cfg/apps/payments/services/types/requests.py +150 -0
  88. django_cfg/apps/payments/services/types/responses.py +156 -0
  89. django_cfg/apps/payments/services/types/webhooks.py +232 -0
  90. django_cfg/apps/payments/signals/__init__.py +33 -8
  91. django_cfg/apps/payments/signals/api_key_signals.py +210 -129
  92. django_cfg/apps/payments/signals/balance_signals.py +174 -0
  93. django_cfg/apps/payments/signals/payment_signals.py +128 -103
  94. django_cfg/apps/payments/signals/subscription_signals.py +194 -142
  95. django_cfg/apps/payments/static/payments/css/components.css +380 -0
  96. django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
  97. django_cfg/apps/payments/static/payments/js/components.js +545 -0
  98. django_cfg/apps/payments/static/payments/js/utils.js +412 -0
  99. django_cfg/apps/payments/templatetags/__init__.py +1 -1
  100. django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
  101. django_cfg/apps/payments/urls.py +45 -48
  102. django_cfg/apps/payments/urls_admin.py +33 -42
  103. django_cfg/apps/payments/views/api/__init__.py +101 -0
  104. django_cfg/apps/payments/views/api/api_keys.py +387 -0
  105. django_cfg/apps/payments/views/api/balances.py +381 -0
  106. django_cfg/apps/payments/views/api/base.py +298 -0
  107. django_cfg/apps/payments/views/api/currencies.py +402 -0
  108. django_cfg/apps/payments/views/api/payments.py +415 -0
  109. django_cfg/apps/payments/views/api/subscriptions.py +475 -0
  110. django_cfg/apps/payments/views/api/webhooks.py +476 -0
  111. django_cfg/apps/payments/views/serializers/__init__.py +99 -0
  112. django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
  113. django_cfg/apps/payments/views/serializers/balances.py +300 -0
  114. django_cfg/apps/payments/views/serializers/currencies.py +335 -0
  115. django_cfg/apps/payments/views/serializers/payments.py +387 -0
  116. django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
  117. django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
  118. django_cfg/config.py +1 -1
  119. django_cfg/core/config.py +40 -4
  120. django_cfg/core/generation.py +25 -4
  121. django_cfg/core/integration/README.md +363 -0
  122. django_cfg/core/integration/__init__.py +47 -0
  123. django_cfg/core/integration/commands_collector.py +239 -0
  124. django_cfg/core/integration/display/__init__.py +15 -0
  125. django_cfg/core/integration/display/base.py +157 -0
  126. django_cfg/core/integration/display/ngrok.py +164 -0
  127. django_cfg/core/integration/display/startup.py +815 -0
  128. django_cfg/core/integration/url_integration.py +123 -0
  129. django_cfg/core/integration/version_checker.py +160 -0
  130. django_cfg/management/commands/auto_generate.py +4 -0
  131. django_cfg/management/commands/check_settings.py +6 -0
  132. django_cfg/management/commands/clear_constance.py +5 -2
  133. django_cfg/management/commands/create_token.py +6 -0
  134. django_cfg/management/commands/list_urls.py +6 -0
  135. django_cfg/management/commands/migrate_all.py +6 -0
  136. django_cfg/management/commands/migrator.py +3 -0
  137. django_cfg/management/commands/rundramatiq.py +6 -0
  138. django_cfg/management/commands/runserver_ngrok.py +51 -29
  139. django_cfg/management/commands/script.py +6 -0
  140. django_cfg/management/commands/show_config.py +12 -2
  141. django_cfg/management/commands/show_urls.py +4 -0
  142. django_cfg/management/commands/superuser.py +6 -0
  143. django_cfg/management/commands/task_clear.py +4 -1
  144. django_cfg/management/commands/task_status.py +3 -1
  145. django_cfg/management/commands/test_email.py +3 -0
  146. django_cfg/management/commands/test_telegram.py +6 -0
  147. django_cfg/management/commands/test_twilio.py +6 -0
  148. django_cfg/management/commands/tree.py +6 -0
  149. django_cfg/management/commands/validate_config.py +155 -149
  150. django_cfg/models/constance.py +31 -11
  151. django_cfg/models/payments.py +175 -492
  152. django_cfg/modules/django_logger.py +160 -146
  153. django_cfg/modules/django_unfold/dashboard.py +64 -16
  154. django_cfg/registry/core.py +1 -0
  155. django_cfg/template_archive/django_sample.zip +0 -0
  156. django_cfg/utils/smart_defaults.py +227 -570
  157. django_cfg/utils/toolkit.py +51 -11
  158. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/METADATA +4 -1
  159. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/RECORD +162 -185
  160. django_cfg/apps/payments/__init__.py +0 -8
  161. django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
  162. django_cfg/apps/payments/config/module.py +0 -70
  163. django_cfg/apps/payments/config/providers.py +0 -105
  164. django_cfg/apps/payments/config/settings.py +0 -96
  165. django_cfg/apps/payments/config/utils.py +0 -52
  166. django_cfg/apps/payments/decorators.py +0 -291
  167. django_cfg/apps/payments/management/commands/README.md +0 -146
  168. django_cfg/apps/payments/managers/__init__.py +0 -23
  169. django_cfg/apps/payments/managers/api_key_manager.py +0 -35
  170. django_cfg/apps/payments/managers/balance_manager.py +0 -361
  171. django_cfg/apps/payments/managers/currency_manager.py +0 -306
  172. django_cfg/apps/payments/managers/payment_manager.py +0 -192
  173. django_cfg/apps/payments/managers/subscription_manager.py +0 -37
  174. django_cfg/apps/payments/managers/tariff_manager.py +0 -29
  175. django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +0 -241
  176. django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +0 -30
  177. django_cfg/apps/payments/models/events.py +0 -73
  178. django_cfg/apps/payments/serializers/__init__.py +0 -57
  179. django_cfg/apps/payments/serializers/api_keys.py +0 -51
  180. django_cfg/apps/payments/serializers/balance.py +0 -59
  181. django_cfg/apps/payments/serializers/currencies.py +0 -63
  182. django_cfg/apps/payments/serializers/payments.py +0 -62
  183. django_cfg/apps/payments/serializers/subscriptions.py +0 -71
  184. django_cfg/apps/payments/serializers/tariffs.py +0 -56
  185. django_cfg/apps/payments/services/billing/__init__.py +0 -8
  186. django_cfg/apps/payments/services/cache/simple_cache.py +0 -135
  187. django_cfg/apps/payments/services/core/fallback_service.py +0 -432
  188. django_cfg/apps/payments/services/internal_types.py +0 -461
  189. django_cfg/apps/payments/services/middleware/__init__.py +0 -8
  190. django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
  191. django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -76
  192. django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
  193. django_cfg/apps/payments/services/providers/cryptapi/__init__.py +0 -4
  194. django_cfg/apps/payments/services/providers/cryptapi/config.py +0 -8
  195. django_cfg/apps/payments/services/providers/cryptapi/models.py +0 -192
  196. django_cfg/apps/payments/services/providers/cryptapi/provider.py +0 -439
  197. django_cfg/apps/payments/services/providers/cryptomus/__init__.py +0 -4
  198. django_cfg/apps/payments/services/providers/cryptomus/models.py +0 -176
  199. django_cfg/apps/payments/services/providers/cryptomus/provider.py +0 -429
  200. django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +0 -564
  201. django_cfg/apps/payments/services/providers/models/__init__.py +0 -34
  202. django_cfg/apps/payments/services/providers/models/currencies.py +0 -190
  203. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +0 -4
  204. django_cfg/apps/payments/services/providers/nowpayments/models.py +0 -196
  205. django_cfg/apps/payments/services/providers/nowpayments/provider.py +0 -380
  206. django_cfg/apps/payments/services/providers/stripe/__init__.py +0 -4
  207. django_cfg/apps/payments/services/providers/stripe/models.py +0 -184
  208. django_cfg/apps/payments/services/providers/stripe/provider.py +0 -109
  209. django_cfg/apps/payments/services/security/__init__.py +0 -34
  210. django_cfg/apps/payments/services/security/error_handler.py +0 -635
  211. django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
  212. django_cfg/apps/payments/services/security/webhook_validator.py +0 -474
  213. django_cfg/apps/payments/static/payments/css/payments.css +0 -340
  214. django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
  215. django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
  216. django_cfg/apps/payments/static/payments/js/theme.js +0 -86
  217. django_cfg/apps/payments/tasks/__init__.py +0 -12
  218. django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
  219. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +0 -50
  220. django_cfg/apps/payments/templates/payments/base.html +0 -182
  221. django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
  222. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
  223. django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -43
  224. django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
  225. django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -34
  226. django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -148
  227. django_cfg/apps/payments/templates/payments/dashboard.html +0 -258
  228. django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +0 -35
  229. django_cfg/apps/payments/templates/payments/payment_create.html +0 -579
  230. django_cfg/apps/payments/templates/payments/payment_detail.html +0 -373
  231. django_cfg/apps/payments/templates/payments/payment_list.html +0 -354
  232. django_cfg/apps/payments/templates/payments/stats.html +0 -261
  233. django_cfg/apps/payments/templates/payments/test.html +0 -213
  234. django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
  235. django_cfg/apps/payments/utils/__init__.py +0 -43
  236. django_cfg/apps/payments/utils/billing_utils.py +0 -342
  237. django_cfg/apps/payments/utils/config_utils.py +0 -239
  238. django_cfg/apps/payments/utils/middleware_utils.py +0 -228
  239. django_cfg/apps/payments/utils/validation_utils.py +0 -94
  240. django_cfg/apps/payments/views/__init__.py +0 -63
  241. django_cfg/apps/payments/views/api_key_views.py +0 -164
  242. django_cfg/apps/payments/views/balance_views.py +0 -75
  243. django_cfg/apps/payments/views/currency_views.py +0 -122
  244. django_cfg/apps/payments/views/payment_views.py +0 -149
  245. django_cfg/apps/payments/views/subscription_views.py +0 -135
  246. django_cfg/apps/payments/views/tariff_views.py +0 -131
  247. django_cfg/apps/payments/views/templates/__init__.py +0 -25
  248. django_cfg/apps/payments/views/templates/ajax.py +0 -451
  249. django_cfg/apps/payments/views/templates/base.py +0 -212
  250. django_cfg/apps/payments/views/templates/dashboard.py +0 -60
  251. django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
  252. django_cfg/apps/payments/views/templates/payment_management.py +0 -158
  253. django_cfg/apps/payments/views/templates/qr_code.py +0 -174
  254. django_cfg/apps/payments/views/templates/stats.py +0 -244
  255. django_cfg/apps/payments/views/templates/utils.py +0 -181
  256. django_cfg/apps/payments/views/webhook_views.py +0 -266
  257. django_cfg/apps/payments/viewsets.py +0 -66
  258. django_cfg/core/integration.py +0 -160
  259. django_cfg/template_archive/.gitignore +0 -1
  260. django_cfg/template_archive/__init__.py +0 -0
  261. django_cfg/urls.py +0 -33
  262. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/WHEEL +0 -0
  263. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/entry_points.txt +0 -0
  264. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,329 @@
1
+ """
2
+ API Key managers for the Universal Payment System v2.0.
3
+
4
+ Optimized querysets and managers for API key operations.
5
+ """
6
+
7
+ from django.db import models
8
+ from django.utils import timezone
9
+ from datetime import timedelta
10
+ from django_cfg.modules.django_logger import get_logger
11
+
12
+ logger = get_logger("api_key_managers")
13
+
14
+
15
+ class APIKeyQuerySet(models.QuerySet):
16
+ """
17
+ Optimized queryset for API key operations.
18
+
19
+ Provides efficient queries for API key management and validation.
20
+ """
21
+
22
+ def active(self):
23
+ """Get active API keys."""
24
+ return self.filter(is_active=True)
25
+
26
+ def expired(self):
27
+ """Get expired API keys."""
28
+ now = timezone.now()
29
+ return self.filter(expires_at__lte=now)
30
+
31
+ def expiring_soon(self, days=7):
32
+ """
33
+ Get API keys expiring in the next N days.
34
+
35
+ Args:
36
+ days: Number of days to look ahead (default: 7)
37
+ """
38
+ soon = timezone.now() + timedelta(days=days)
39
+ return self.filter(
40
+ expires_at__lte=soon,
41
+ expires_at__gt=timezone.now(),
42
+ is_active=True
43
+ )
44
+
45
+ def by_user(self, user):
46
+ """Filter API keys by user."""
47
+ return self.filter(user=user)
48
+
49
+ def valid(self):
50
+ """Get valid API keys (active and not expired)."""
51
+ now = timezone.now()
52
+ return self.filter(
53
+ is_active=True
54
+ ).filter(
55
+ models.Q(expires_at__isnull=True) | models.Q(expires_at__gt=now)
56
+ )
57
+
58
+ def recent_usage(self, hours=24):
59
+ """
60
+ Get API keys used in the last N hours.
61
+
62
+ Args:
63
+ hours: Number of hours to look back (default: 24)
64
+ """
65
+ since = timezone.now() - timedelta(hours=hours)
66
+ return self.filter(last_used_at__gte=since)
67
+
68
+
69
+ class APIKeyManager(models.Manager):
70
+ """
71
+ Manager for API key operations with business logic.
72
+
73
+ Provides high-level methods for API key management and validation.
74
+ """
75
+
76
+ def get_queryset(self):
77
+ """Return custom queryset."""
78
+ return APIKeyQuerySet(self.model, using=self._db)
79
+
80
+ def active(self):
81
+ """Get active API keys."""
82
+ return self.get_queryset().active()
83
+
84
+ def valid(self):
85
+ """Get valid API keys."""
86
+ return self.get_queryset().valid()
87
+
88
+ def expired(self):
89
+ """Get expired API keys."""
90
+ return self.get_queryset().expired()
91
+
92
+ def expiring_soon(self, days=7):
93
+ """Get API keys expiring soon."""
94
+ return self.get_queryset().expiring_soon(days)
95
+
96
+ # Business logic methods
97
+ def increment_api_key_usage(self, api_key_id, ip_address=None):
98
+ """
99
+ Increment API key usage counter (business logic in manager).
100
+
101
+ Args:
102
+ api_key_id: API key ID or instance
103
+ ip_address: IP address making the request (for logging)
104
+
105
+ Returns:
106
+ bool: True if usage was incremented successfully
107
+ """
108
+ try:
109
+ if isinstance(api_key_id, str):
110
+ api_key = self.get(id=api_key_id)
111
+ else:
112
+ api_key = api_key_id
113
+
114
+ api_key.total_requests += 1
115
+ api_key.last_used_at = timezone.now()
116
+ api_key.save(update_fields=['total_requests', 'last_used_at', 'updated_at'])
117
+
118
+ logger.debug(f"Incremented API key usage", extra={
119
+ 'api_key_id': str(api_key.id),
120
+ 'user_id': api_key.user.id,
121
+ 'total_requests': api_key.total_requests,
122
+ 'ip_address': ip_address
123
+ })
124
+
125
+ return True
126
+
127
+ except Exception as e:
128
+ logger.error(f"Failed to increment API key usage: {e}", extra={
129
+ 'api_key_id': str(api_key_id) if hasattr(api_key_id, 'id') else api_key_id,
130
+ 'ip_address': ip_address
131
+ })
132
+ return False
133
+
134
+ def deactivate_api_key(self, api_key_id, reason=None):
135
+ """
136
+ Deactivate API key (business logic in manager).
137
+
138
+ Args:
139
+ api_key_id: API key ID or instance
140
+ reason: Deactivation reason
141
+
142
+ Returns:
143
+ bool: True if API key was deactivated successfully
144
+ """
145
+ try:
146
+ if isinstance(api_key_id, str):
147
+ api_key = self.get(id=api_key_id)
148
+ else:
149
+ api_key = api_key_id
150
+
151
+ api_key.is_active = False
152
+ api_key.save(update_fields=['is_active', 'updated_at'])
153
+
154
+ logger.info(f"API key deactivated", extra={
155
+ 'api_key_id': str(api_key.id),
156
+ 'user_id': api_key.user.id,
157
+ 'reason': reason
158
+ })
159
+
160
+ return True
161
+
162
+ except Exception as e:
163
+ logger.error(f"Failed to deactivate API key: {e}", extra={
164
+ 'api_key_id': str(api_key_id) if hasattr(api_key_id, 'id') else api_key_id
165
+ })
166
+ return False
167
+
168
+ def extend_api_key_expiry(self, api_key_id, days):
169
+ """
170
+ Extend API key expiration (business logic in manager).
171
+
172
+ Args:
173
+ api_key_id: API key ID or instance
174
+ days: Number of days to extend
175
+
176
+ Returns:
177
+ bool: True if expiry was extended successfully
178
+ """
179
+ try:
180
+ if isinstance(api_key_id, str):
181
+ api_key = self.get(id=api_key_id)
182
+ else:
183
+ api_key = api_key_id
184
+
185
+ if api_key.expires_at:
186
+ api_key.expires_at += timedelta(days=days)
187
+ else:
188
+ api_key.expires_at = timezone.now() + timedelta(days=days)
189
+
190
+ api_key.save(update_fields=['expires_at', 'updated_at'])
191
+
192
+ logger.info(f"Extended API key expiry", extra={
193
+ 'api_key_id': str(api_key.id),
194
+ 'user_id': api_key.user.id,
195
+ 'days_extended': days,
196
+ 'new_expires_at': api_key.expires_at.isoformat()
197
+ })
198
+
199
+ return True
200
+
201
+ except Exception as e:
202
+ logger.error(f"Failed to extend API key expiry: {e}", extra={
203
+ 'api_key_id': str(api_key_id) if hasattr(api_key_id, 'id') else api_key_id
204
+ })
205
+ return False
206
+
207
+ def create_api_key_for_user(self, user, name="Default API Key", expires_in_days=None):
208
+ """
209
+ Create new API key for user (business logic in manager).
210
+
211
+ Args:
212
+ user: User instance
213
+ name: Name for the API key
214
+ expires_in_days: Days until expiration (None = never expires)
215
+
216
+ Returns:
217
+ APIKey: Created API key
218
+ """
219
+ try:
220
+ expires_at = None
221
+ if expires_in_days:
222
+ expires_at = timezone.now() + timedelta(days=expires_in_days)
223
+
224
+ api_key = self.create(
225
+ user=user,
226
+ name=name,
227
+ expires_at=expires_at
228
+ )
229
+
230
+ logger.info(f"Created API key for user", extra={
231
+ 'api_key_id': str(api_key.id),
232
+ 'user_id': user.id,
233
+ 'key_name': name,
234
+ 'expires_in_days': expires_in_days
235
+ })
236
+
237
+ return api_key
238
+
239
+ except Exception as e:
240
+ logger.error(f"Failed to create API key: {e}", extra={
241
+ 'user_id': user.id,
242
+ 'key_name': name
243
+ })
244
+ raise
245
+
246
+ def get_valid_api_key(self, key_value):
247
+ """
248
+ Get valid API key by key value (business logic in manager).
249
+
250
+ Args:
251
+ key_value: API key string
252
+
253
+ Returns:
254
+ APIKey or None: Valid API key if found
255
+ """
256
+ try:
257
+ api_key = self.get(key=key_value, is_active=True)
258
+
259
+ # Check if expired
260
+ if api_key.expires_at and timezone.now() > api_key.expires_at:
261
+ logger.debug(f"API key is expired", extra={
262
+ 'api_key_id': str(api_key.id),
263
+ 'expires_at': api_key.expires_at.isoformat()
264
+ })
265
+ return None
266
+
267
+ return api_key
268
+
269
+ except self.model.DoesNotExist:
270
+ logger.debug(f"API key not found or inactive", extra={
271
+ 'key_prefix': key_value[:8] if len(key_value) >= 8 else key_value
272
+ })
273
+ return None
274
+
275
+ def cleanup_expired_keys(self, dry_run=True):
276
+ """
277
+ Deactivate expired API keys.
278
+
279
+ Args:
280
+ dry_run: If True, only return count without making changes
281
+
282
+ Returns:
283
+ int: Number of API keys that would be/were deactivated
284
+ """
285
+ expired_keys = self.expired().filter(is_active=True)
286
+ count = expired_keys.count()
287
+
288
+ if not dry_run and count > 0:
289
+ expired_keys.update(is_active=False)
290
+ logger.info(f"Deactivated {count} expired API keys")
291
+
292
+ return count
293
+
294
+ def get_api_key_stats(self, days=30):
295
+ """
296
+ Get API key statistics.
297
+
298
+ Args:
299
+ days: Number of days to analyze (default: 30)
300
+
301
+ Returns:
302
+ dict: API key statistics
303
+ """
304
+ queryset = self.get_queryset()
305
+
306
+ stats = {
307
+ 'total_keys': queryset.count(),
308
+ 'active_keys': queryset.active().count(),
309
+ 'expired_keys': queryset.expired().count(),
310
+ 'expiring_soon': queryset.expiring_soon(7).count(),
311
+ 'recent_usage': queryset.recent_usage(24).count(),
312
+ 'valid_keys': queryset.valid().count(),
313
+ }
314
+
315
+ # Usage statistics
316
+ usage_stats = queryset.aggregate(
317
+ total_requests=models.Sum('total_requests'),
318
+ avg_requests=models.Avg('total_requests'),
319
+ max_requests=models.Max('total_requests')
320
+ )
321
+ stats.update(usage_stats)
322
+
323
+ logger.info(f"Generated API key stats", extra={
324
+ 'days': days,
325
+ 'total_keys': stats['total_keys'],
326
+ 'active_keys': stats['active_keys']
327
+ })
328
+
329
+ return stats