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,196 +1,248 @@
|
|
1
1
|
"""
|
2
|
-
|
2
|
+
Subscription Signals for the Universal Payment System v2.0.
|
3
3
|
|
4
|
-
|
4
|
+
Minimal signals focused on cache invalidation and notifications.
|
5
|
+
Business logic stays in SubscriptionManager.
|
5
6
|
"""
|
6
7
|
|
7
|
-
from django.db.models.signals import post_save,
|
8
|
+
from django.db.models.signals import post_save, post_delete, pre_save
|
8
9
|
from django.dispatch import receiver
|
9
|
-
from django.
|
10
|
+
from django.core.cache import cache
|
10
11
|
from django.utils import timezone
|
11
|
-
from datetime import timedelta
|
12
|
-
import logging
|
13
12
|
|
14
|
-
from ..models import Subscription
|
15
|
-
from
|
13
|
+
from ..models import Subscription
|
14
|
+
from django_cfg.modules.django_logger import get_logger
|
16
15
|
|
17
|
-
logger =
|
16
|
+
logger = get_logger("subscription_signals")
|
18
17
|
|
19
18
|
|
20
19
|
@receiver(pre_save, sender=Subscription)
|
21
|
-
def
|
22
|
-
"""Store original subscription
|
20
|
+
def store_original_subscription_data(sender, instance: Subscription, **kwargs):
|
21
|
+
"""Store original subscription data for change detection."""
|
23
22
|
if instance.pk:
|
24
23
|
try:
|
25
|
-
|
26
|
-
instance._original_status =
|
27
|
-
instance.
|
24
|
+
original = Subscription.objects.get(pk=instance.pk)
|
25
|
+
instance._original_status = original.status
|
26
|
+
instance._original_tier = original.tier
|
27
|
+
instance._original_expires_at = original.expires_at
|
28
28
|
except Subscription.DoesNotExist:
|
29
29
|
instance._original_status = None
|
30
|
+
instance._original_tier = None
|
30
31
|
instance._original_expires_at = None
|
32
|
+
else:
|
33
|
+
instance._original_status = None
|
34
|
+
instance._original_tier = None
|
35
|
+
instance._original_expires_at = None
|
31
36
|
|
32
37
|
|
33
38
|
@receiver(post_save, sender=Subscription)
|
34
|
-
def
|
35
|
-
"""
|
36
|
-
|
37
|
-
logger.info(
|
38
|
-
f"New subscription created: {instance.endpoint_group.name} "
|
39
|
-
f"for user {instance.user.email} (expires: {instance.expires_at})"
|
40
|
-
)
|
41
|
-
_clear_user_cache(instance.user.id)
|
42
|
-
return
|
39
|
+
def handle_subscription_changes(sender, instance: Subscription, created: bool, **kwargs):
|
40
|
+
"""
|
41
|
+
Handle subscription changes - only cache clearing and notifications.
|
43
42
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
43
|
+
Business logic (API key management, etc.) stays in managers.
|
44
|
+
"""
|
45
|
+
if created:
|
46
|
+
logger.info(f"New subscription created", extra={
|
47
|
+
'subscription_id': str(instance.id),
|
48
|
+
'user_id': instance.user.id,
|
49
|
+
'tier': instance.tier,
|
50
|
+
'status': instance.status
|
51
|
+
})
|
52
|
+
|
53
|
+
# Create default API key for new subscription (delegate to manager)
|
54
|
+
_create_default_api_key(instance)
|
55
|
+
|
56
|
+
else:
|
57
|
+
# Check for status changes
|
58
|
+
if hasattr(instance, '_original_status'):
|
59
|
+
old_status = instance._original_status
|
60
|
+
new_status = instance.status
|
54
61
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
+
if old_status != new_status:
|
63
|
+
logger.info(f"Subscription status changed", extra={
|
64
|
+
'subscription_id': str(instance.id),
|
65
|
+
'user_id': instance.user.id,
|
66
|
+
'old_status': old_status,
|
67
|
+
'new_status': new_status
|
68
|
+
})
|
69
|
+
|
70
|
+
# Handle activation
|
71
|
+
if new_status == 'active' and old_status != 'active':
|
72
|
+
_handle_subscription_activated(instance)
|
73
|
+
|
74
|
+
# Handle suspension/cancellation
|
75
|
+
elif new_status in ['suspended', 'cancelled'] and old_status not in ['suspended', 'cancelled']:
|
76
|
+
_handle_subscription_deactivated(instance)
|
77
|
+
|
78
|
+
# Check for tier changes
|
79
|
+
if hasattr(instance, '_original_tier'):
|
80
|
+
old_tier = instance._original_tier
|
81
|
+
new_tier = instance.tier
|
82
|
+
|
83
|
+
if old_tier != new_tier:
|
84
|
+
logger.info(f"Subscription tier changed", extra={
|
85
|
+
'subscription_id': str(instance.id),
|
86
|
+
'user_id': instance.user.id,
|
87
|
+
'old_tier': old_tier,
|
88
|
+
'new_tier': new_tier
|
89
|
+
})
|
90
|
+
|
91
|
+
_handle_tier_change(instance, old_tier, new_tier)
|
92
|
+
|
93
|
+
# Check for expiration changes
|
94
|
+
if hasattr(instance, '_original_expires_at'):
|
95
|
+
old_expires = instance._original_expires_at
|
96
|
+
new_expires = instance.expires_at
|
62
97
|
|
63
|
-
|
98
|
+
if old_expires != new_expires:
|
99
|
+
logger.info(f"Subscription expiration changed", extra={
|
100
|
+
'subscription_id': str(instance.id),
|
101
|
+
'user_id': instance.user.id,
|
102
|
+
'old_expires': old_expires.isoformat() if old_expires else None,
|
103
|
+
'new_expires': new_expires.isoformat() if new_expires else None
|
104
|
+
})
|
105
|
+
|
106
|
+
# Clear subscription-related caches
|
107
|
+
_clear_subscription_caches(instance)
|
64
108
|
|
65
109
|
|
66
|
-
@receiver(
|
67
|
-
def
|
68
|
-
"""Handle subscription
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
110
|
+
@receiver(post_delete, sender=Subscription)
|
111
|
+
def handle_subscription_deletion(sender, instance: Subscription, **kwargs):
|
112
|
+
"""Handle subscription deletion."""
|
113
|
+
logger.warning(f"Subscription deleted", extra={
|
114
|
+
'subscription_id': str(instance.id),
|
115
|
+
'user_id': instance.user.id,
|
116
|
+
'tier': instance.tier,
|
117
|
+
'status': instance.status,
|
118
|
+
'deletion_timestamp': timezone.now().isoformat()
|
119
|
+
})
|
74
120
|
|
75
|
-
#
|
76
|
-
|
77
|
-
logger.info(
|
78
|
-
f"Subscription renewed: {instance.endpoint_group.name} "
|
79
|
-
f"for user {instance.user.email} - extended to {new_expires_at}"
|
80
|
-
)
|
81
|
-
_clear_user_cache(instance.user.id)
|
121
|
+
# Clear caches
|
122
|
+
_clear_subscription_caches(instance)
|
82
123
|
|
83
124
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
f"Subscription deleted: {instance.endpoint_group.name} "
|
89
|
-
f"for user {instance.user.email} - Status was: {instance.status}"
|
90
|
-
)
|
91
|
-
_clear_user_cache(instance.user.id)
|
92
|
-
|
93
|
-
|
94
|
-
@receiver(post_save, sender=EndpointGroup)
|
95
|
-
def log_endpoint_group_changes(sender, instance, created, **kwargs):
|
96
|
-
"""Log endpoint group changes that may affect subscriptions."""
|
97
|
-
if created:
|
98
|
-
logger.info(f"New endpoint group created: {instance.name}")
|
99
|
-
else:
|
100
|
-
# Check if important fields changed
|
101
|
-
if instance.tracker.has_changed('is_active'):
|
102
|
-
logger.warning(
|
103
|
-
f"Endpoint group activity changed: {instance.name} "
|
104
|
-
f"- active: {instance.is_active}"
|
105
|
-
)
|
106
|
-
# Clear cache for all users with subscriptions to this group
|
107
|
-
_clear_endpoint_group_cache(instance)
|
108
|
-
|
109
|
-
|
110
|
-
def _handle_subscription_activation(subscription: Subscription):
|
111
|
-
"""Handle subscription activation logic."""
|
125
|
+
# Helper functions (notifications and delegations only)
|
126
|
+
|
127
|
+
def _create_default_api_key(subscription: Subscription):
|
128
|
+
"""Create default API key for new subscription (delegate to manager)."""
|
112
129
|
try:
|
113
|
-
|
114
|
-
subscription.usage_current = 0
|
130
|
+
from ..models import APIKey
|
115
131
|
|
116
|
-
#
|
117
|
-
|
118
|
-
subscription.
|
119
|
-
|
120
|
-
|
132
|
+
# Use manager method which has all the business logic
|
133
|
+
api_key = APIKey.objects.create_api_key_for_user(
|
134
|
+
user=subscription.user,
|
135
|
+
name=f"Default API Key ({subscription.tier})",
|
136
|
+
expires_in_days=None # No expiration for default key
|
137
|
+
)
|
121
138
|
|
122
|
-
logger.info(f"
|
139
|
+
logger.info(f"Created default API key for subscription", extra={
|
140
|
+
'subscription_id': str(subscription.id),
|
141
|
+
'user_id': subscription.user.id,
|
142
|
+
'api_key_id': str(api_key.id)
|
143
|
+
})
|
123
144
|
|
124
145
|
except Exception as e:
|
125
|
-
logger.error(f"
|
146
|
+
logger.error(f"Failed to create default API key", extra={
|
147
|
+
'subscription_id': str(subscription.id),
|
148
|
+
'user_id': subscription.user.id,
|
149
|
+
'error': str(e)
|
150
|
+
})
|
126
151
|
|
127
152
|
|
128
|
-
def
|
129
|
-
"""Handle subscription
|
153
|
+
def _handle_subscription_activated(subscription: Subscription):
|
154
|
+
"""Handle subscription activation (notification only)."""
|
130
155
|
try:
|
131
|
-
|
132
|
-
|
133
|
-
|
156
|
+
logger.info(f"Subscription activated", extra={
|
157
|
+
'subscription_id': str(subscription.id),
|
158
|
+
'user_id': subscription.user.id,
|
159
|
+
'tier': subscription.tier
|
160
|
+
})
|
134
161
|
|
135
|
-
|
162
|
+
# Set activation notification in cache
|
163
|
+
cache.set(
|
164
|
+
f"subscription_activated:{subscription.user.id}",
|
165
|
+
{
|
166
|
+
'subscription_id': str(subscription.id),
|
167
|
+
'tier': subscription.tier,
|
168
|
+
'timestamp': timezone.now().isoformat()
|
169
|
+
},
|
170
|
+
timeout=86400 # 24 hours
|
171
|
+
)
|
136
172
|
|
137
173
|
except Exception as e:
|
138
|
-
logger.error(f"
|
174
|
+
logger.error(f"Failed to handle subscription activation: {e}")
|
139
175
|
|
140
176
|
|
141
|
-
def
|
142
|
-
"""Handle subscription
|
177
|
+
def _handle_subscription_deactivated(subscription: Subscription):
|
178
|
+
"""Handle subscription deactivation (notification only)."""
|
143
179
|
try:
|
144
|
-
|
145
|
-
|
146
|
-
|
180
|
+
logger.warning(f"Subscription deactivated", extra={
|
181
|
+
'subscription_id': str(subscription.id),
|
182
|
+
'user_id': subscription.user.id,
|
183
|
+
'status': subscription.status,
|
184
|
+
'tier': subscription.tier
|
185
|
+
})
|
147
186
|
|
148
|
-
|
187
|
+
# Set deactivation notification in cache
|
188
|
+
cache.set(
|
189
|
+
f"subscription_deactivated:{subscription.user.id}",
|
190
|
+
{
|
191
|
+
'subscription_id': str(subscription.id),
|
192
|
+
'status': subscription.status,
|
193
|
+
'tier': subscription.tier,
|
194
|
+
'timestamp': timezone.now().isoformat()
|
195
|
+
},
|
196
|
+
timeout=86400 * 7 # 7 days
|
197
|
+
)
|
149
198
|
|
150
199
|
except Exception as e:
|
151
|
-
logger.error(f"
|
200
|
+
logger.error(f"Failed to handle subscription deactivation: {e}")
|
152
201
|
|
153
202
|
|
154
|
-
def
|
155
|
-
"""
|
203
|
+
def _handle_tier_change(subscription: Subscription, old_tier: str, new_tier: str):
|
204
|
+
"""Handle subscription tier change (notification only)."""
|
156
205
|
try:
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
206
|
+
logger.info(f"Subscription tier changed", extra={
|
207
|
+
'subscription_id': str(subscription.id),
|
208
|
+
'user_id': subscription.user.id,
|
209
|
+
'old_tier': old_tier,
|
210
|
+
'new_tier': new_tier
|
211
|
+
})
|
212
|
+
|
213
|
+
# Set tier change notification in cache
|
214
|
+
cache.set(
|
215
|
+
f"tier_changed:{subscription.user.id}",
|
216
|
+
{
|
217
|
+
'subscription_id': str(subscription.id),
|
218
|
+
'old_tier': old_tier,
|
219
|
+
'new_tier': new_tier,
|
220
|
+
'timestamp': timezone.now().isoformat()
|
221
|
+
},
|
222
|
+
timeout=86400 # 24 hours
|
223
|
+
)
|
163
224
|
|
164
|
-
for key in cache_keys:
|
165
|
-
cache.delete(key)
|
166
|
-
|
167
225
|
except Exception as e:
|
168
|
-
logger.
|
226
|
+
logger.error(f"Failed to handle tier change: {e}")
|
169
227
|
|
170
228
|
|
171
|
-
def
|
172
|
-
"""Clear cache
|
229
|
+
def _clear_subscription_caches(subscription: Subscription):
|
230
|
+
"""Clear subscription-related cache entries."""
|
173
231
|
try:
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
232
|
+
cache_keys = [
|
233
|
+
f"user_subscription:{subscription.user.id}",
|
234
|
+
f"subscription_access:{subscription.user.id}",
|
235
|
+
f"subscription_stats:{subscription.user.id}",
|
236
|
+
f"tier_limits:{subscription.tier}",
|
237
|
+
]
|
238
|
+
|
239
|
+
cache.delete_many(cache_keys)
|
240
|
+
|
241
|
+
logger.debug(f"Cleared subscription caches", extra={
|
242
|
+
'subscription_id': str(subscription.id),
|
243
|
+
'user_id': subscription.user.id,
|
244
|
+
'cache_keys_cleared': len(cache_keys)
|
245
|
+
})
|
246
|
+
|
183
247
|
except Exception as e:
|
184
|
-
logger.warning(f"Failed to clear
|
185
|
-
|
186
|
-
|
187
|
-
@receiver(post_save, sender=Subscription)
|
188
|
-
def update_usage_statistics(sender, instance, created, **kwargs):
|
189
|
-
"""Update usage statistics when subscription is modified."""
|
190
|
-
if not created and hasattr(instance, '_original_status'):
|
191
|
-
# Only update stats if usage-related fields might have changed
|
192
|
-
if instance.usage_current != getattr(instance, '_original_usage_current', instance.usage_current):
|
193
|
-
logger.debug(
|
194
|
-
f"Usage updated for subscription {instance.endpoint_group.name}: "
|
195
|
-
f"{instance.usage_current} requests"
|
196
|
-
)
|
248
|
+
logger.warning(f"Failed to clear subscription caches: {e}")
|