django-cfg 1.2.16__py3-none-any.whl → 1.2.18__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 (81) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/models/__init__.py +68 -0
  3. django_cfg/apps/accounts/models/activity.py +34 -0
  4. django_cfg/apps/accounts/models/auth.py +50 -0
  5. django_cfg/apps/accounts/models/base.py +8 -0
  6. django_cfg/apps/accounts/models/choices.py +32 -0
  7. django_cfg/apps/accounts/models/integrations.py +75 -0
  8. django_cfg/apps/accounts/models/registration.py +52 -0
  9. django_cfg/apps/accounts/models/user.py +80 -0
  10. django_cfg/apps/maintenance/__init__.py +53 -24
  11. django_cfg/apps/maintenance/admin/__init__.py +7 -18
  12. django_cfg/apps/maintenance/admin/api_key_admin.py +185 -0
  13. django_cfg/apps/maintenance/admin/log_admin.py +156 -0
  14. django_cfg/apps/maintenance/admin/scheduled_admin.py +390 -0
  15. django_cfg/apps/maintenance/admin/site_admin.py +448 -0
  16. django_cfg/apps/maintenance/apps.py +9 -96
  17. django_cfg/apps/maintenance/management/commands/maintenance.py +193 -307
  18. django_cfg/apps/maintenance/management/commands/process_scheduled_maintenance.py +241 -0
  19. django_cfg/apps/maintenance/management/commands/sync_cloudflare.py +152 -111
  20. django_cfg/apps/maintenance/managers/__init__.py +7 -12
  21. django_cfg/apps/maintenance/managers/cloudflare_site_manager.py +192 -0
  22. django_cfg/apps/maintenance/managers/maintenance_log_manager.py +151 -0
  23. django_cfg/apps/maintenance/migrations/0001_initial.py +145 -705
  24. django_cfg/apps/maintenance/migrations/0002_cloudflaresite_maintenance_url.py +21 -0
  25. django_cfg/apps/maintenance/models/__init__.py +23 -21
  26. django_cfg/apps/maintenance/models/cloudflare_api_key.py +109 -0
  27. django_cfg/apps/maintenance/models/cloudflare_site.py +125 -0
  28. django_cfg/apps/maintenance/models/maintenance_log.py +131 -0
  29. django_cfg/apps/maintenance/models/scheduled_maintenance.py +307 -0
  30. django_cfg/apps/maintenance/services/__init__.py +37 -16
  31. django_cfg/apps/maintenance/services/bulk_operations_service.py +400 -0
  32. django_cfg/apps/maintenance/services/maintenance_service.py +230 -0
  33. django_cfg/apps/maintenance/services/scheduled_maintenance_service.py +381 -0
  34. django_cfg/apps/maintenance/services/site_sync_service.py +390 -0
  35. django_cfg/apps/maintenance/utils/__init__.py +12 -0
  36. django_cfg/apps/maintenance/utils/retry_utils.py +109 -0
  37. django_cfg/config.py +3 -0
  38. django_cfg/core/config.py +4 -6
  39. django_cfg/modules/django_unfold/dashboard.py +4 -5
  40. {django_cfg-1.2.16.dist-info → django_cfg-1.2.18.dist-info}/METADATA +52 -1
  41. {django_cfg-1.2.16.dist-info → django_cfg-1.2.18.dist-info}/RECORD +45 -55
  42. django_cfg/apps/maintenance/README.md +0 -305
  43. django_cfg/apps/maintenance/admin/deployments_admin.py +0 -251
  44. django_cfg/apps/maintenance/admin/events_admin.py +0 -374
  45. django_cfg/apps/maintenance/admin/monitoring_admin.py +0 -215
  46. django_cfg/apps/maintenance/admin/sites_admin.py +0 -464
  47. django_cfg/apps/maintenance/managers/deployments.py +0 -287
  48. django_cfg/apps/maintenance/managers/events.py +0 -374
  49. django_cfg/apps/maintenance/managers/monitoring.py +0 -301
  50. django_cfg/apps/maintenance/managers/sites.py +0 -335
  51. django_cfg/apps/maintenance/models/cloudflare.py +0 -316
  52. django_cfg/apps/maintenance/models/maintenance.py +0 -334
  53. django_cfg/apps/maintenance/models/monitoring.py +0 -393
  54. django_cfg/apps/maintenance/models/sites.py +0 -419
  55. django_cfg/apps/maintenance/serializers/__init__.py +0 -60
  56. django_cfg/apps/maintenance/serializers/actions.py +0 -310
  57. django_cfg/apps/maintenance/serializers/base.py +0 -44
  58. django_cfg/apps/maintenance/serializers/deployments.py +0 -209
  59. django_cfg/apps/maintenance/serializers/events.py +0 -210
  60. django_cfg/apps/maintenance/serializers/monitoring.py +0 -278
  61. django_cfg/apps/maintenance/serializers/sites.py +0 -213
  62. django_cfg/apps/maintenance/services/README.md +0 -168
  63. django_cfg/apps/maintenance/services/cloudflare_client.py +0 -441
  64. django_cfg/apps/maintenance/services/dns_manager.py +0 -497
  65. django_cfg/apps/maintenance/services/maintenance_manager.py +0 -504
  66. django_cfg/apps/maintenance/services/site_sync.py +0 -448
  67. django_cfg/apps/maintenance/services/sync_command_service.py +0 -330
  68. django_cfg/apps/maintenance/services/worker_manager.py +0 -264
  69. django_cfg/apps/maintenance/signals.py +0 -38
  70. django_cfg/apps/maintenance/urls.py +0 -36
  71. django_cfg/apps/maintenance/views/__init__.py +0 -18
  72. django_cfg/apps/maintenance/views/base.py +0 -61
  73. django_cfg/apps/maintenance/views/deployments.py +0 -175
  74. django_cfg/apps/maintenance/views/events.py +0 -204
  75. django_cfg/apps/maintenance/views/monitoring.py +0 -213
  76. django_cfg/apps/maintenance/views/sites.py +0 -338
  77. django_cfg/models/cloudflare.py +0 -316
  78. /django_cfg/apps/accounts/{models.py → __models.py} +0 -0
  79. {django_cfg-1.2.16.dist-info → django_cfg-1.2.18.dist-info}/WHEEL +0 -0
  80. {django_cfg-1.2.16.dist-info → django_cfg-1.2.18.dist-info}/entry_points.txt +0 -0
  81. {django_cfg-1.2.16.dist-info → django_cfg-1.2.18.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,192 @@
1
+ """
2
+ CloudflareSite custom manager.
3
+
4
+ Simplified manager with useful query methods for sites.
5
+ """
6
+
7
+ from django.db import models
8
+ from django.utils import timezone
9
+ from typing import List
10
+ from datetime import timedelta
11
+
12
+
13
+ class CloudflareSiteQuerySet(models.QuerySet):
14
+ """Custom queryset for CloudflareSite with useful filters."""
15
+
16
+ def active(self):
17
+ """Get active sites."""
18
+ return self.filter(is_active=True)
19
+
20
+ def inactive(self):
21
+ """Get inactive sites."""
22
+ return self.filter(is_active=False)
23
+
24
+ def in_maintenance(self):
25
+ """Get sites currently in maintenance mode."""
26
+ return self.filter(maintenance_active=True)
27
+
28
+ def not_in_maintenance(self):
29
+ """Get sites not in maintenance mode."""
30
+ return self.filter(maintenance_active=False)
31
+
32
+ def by_domain(self, domain: str):
33
+ """Filter by domain (exact or contains)."""
34
+ if '.' in domain:
35
+ return self.filter(domain__iexact=domain)
36
+ else:
37
+ return self.filter(domain__icontains=domain)
38
+
39
+ def by_name(self, name: str):
40
+ """Filter by site name."""
41
+ return self.filter(name__icontains=name)
42
+
43
+ def recent(self, days: int = 7):
44
+ """Get recently created sites."""
45
+ cutoff = timezone.now() - timedelta(days=days)
46
+ return self.filter(created_at__gte=cutoff)
47
+
48
+ def recently_maintained(self, days: int = 7):
49
+ """Get sites that had maintenance recently."""
50
+ cutoff = timezone.now() - timedelta(days=days)
51
+ return self.filter(last_maintenance_at__gte=cutoff)
52
+
53
+ def with_logs(self):
54
+ """Include related logs in query."""
55
+ return self.prefetch_related('logs')
56
+
57
+ def with_recent_logs(self, limit: int = 5):
58
+ """Include recent logs in query."""
59
+ from django.db.models import Prefetch
60
+ return self.prefetch_related(
61
+ Prefetch(
62
+ 'logs',
63
+ queryset=self.model.logs.rel.related_model.objects.order_by('-created_at')[:limit],
64
+ to_attr='recent_logs'
65
+ )
66
+ )
67
+
68
+ def search(self, query: str):
69
+ """Search sites by name or domain."""
70
+ return self.filter(
71
+ models.Q(name__icontains=query) |
72
+ models.Q(domain__icontains=query)
73
+ )
74
+
75
+
76
+ class CloudflareSiteManager(models.Manager):
77
+ """Custom manager for CloudflareSite."""
78
+
79
+ def get_queryset(self):
80
+ """Return custom queryset."""
81
+ return CloudflareSiteQuerySet(self.model, using=self._db)
82
+
83
+ def active(self):
84
+ """Get active sites."""
85
+ return self.get_queryset().active()
86
+
87
+ def inactive(self):
88
+ """Get inactive sites."""
89
+ return self.get_queryset().inactive()
90
+
91
+ def in_maintenance(self):
92
+ """Get sites in maintenance mode."""
93
+ return self.get_queryset().in_maintenance()
94
+
95
+ def not_in_maintenance(self):
96
+ """Get sites not in maintenance mode."""
97
+ return self.get_queryset().not_in_maintenance()
98
+
99
+ def by_domain(self, domain: str):
100
+ """Find site by domain."""
101
+ return self.get_queryset().by_domain(domain)
102
+
103
+ def search(self, query: str):
104
+ """Search sites."""
105
+ return self.get_queryset().search(query)
106
+
107
+ def recent(self, days: int = 7):
108
+ """Get recent sites."""
109
+ return self.get_queryset().recent(days)
110
+
111
+ def recently_maintained(self, days: int = 7):
112
+ """Get recently maintained sites."""
113
+ return self.get_queryset().recently_maintained(days)
114
+
115
+ def get_stats(self):
116
+ """Get site statistics."""
117
+ total = self.count()
118
+ active = self.active().count()
119
+ in_maintenance = self.in_maintenance().count()
120
+
121
+ return {
122
+ 'total': total,
123
+ 'active': active,
124
+ 'inactive': total - active,
125
+ 'in_maintenance': in_maintenance,
126
+ 'normal': active - in_maintenance,
127
+ }
128
+
129
+ def bulk_sync_all(self):
130
+ """Bulk sync all sites with Cloudflare."""
131
+ from ..services import SiteSyncService
132
+ from ..models import CloudflareApiKey
133
+ import logging
134
+
135
+ logger = logging.getLogger(__name__)
136
+ synced = 0
137
+ errors = 0
138
+ error_details = []
139
+
140
+ # Get all active API keys
141
+ api_keys = CloudflareApiKey.objects.filter(is_active=True)
142
+
143
+ if not api_keys.exists():
144
+ return {
145
+ 'synced': 0,
146
+ 'errors': 1,
147
+ 'error_details': ['No active API keys found']
148
+ }
149
+
150
+ for api_key in api_keys:
151
+ try:
152
+ sync_service = SiteSyncService(api_key)
153
+ result = sync_service.sync_zones()
154
+ # Count created + updated as synced
155
+ sync_count = result.get('created', 0) + result.get('updated', 0)
156
+ synced += sync_count
157
+ logger.info(f"Synced {sync_count} sites for API key: {api_key.name}")
158
+ except Exception as e:
159
+ errors += 1
160
+ error_msg = f"API key '{api_key.name}': {str(e)}"
161
+ error_details.append(error_msg)
162
+ logger.error(f"Sync failed for API key {api_key.name}: {e}")
163
+
164
+ return {
165
+ 'synced': synced,
166
+ 'errors': errors,
167
+ 'error_details': error_details
168
+ }
169
+
170
+ def discover_all_sites(self):
171
+ """Discover new sites from all API keys."""
172
+ from ..services import SiteSyncService
173
+ from ..models import CloudflareApiKey
174
+
175
+ discovered = 0
176
+ errors = 0
177
+
178
+ # Get all active API keys
179
+ api_keys = CloudflareApiKey.objects.filter(is_active=True)
180
+
181
+ for api_key in api_keys:
182
+ try:
183
+ sync_service = SiteSyncService(api_key)
184
+ result = sync_service.sync_zones(dry_run=False)
185
+ discovered += result.get('created', 0) # Count only newly created sites
186
+ except Exception:
187
+ errors += 1
188
+
189
+ return {
190
+ 'discovered': discovered,
191
+ 'errors': errors
192
+ }
@@ -0,0 +1,151 @@
1
+ """
2
+ MaintenanceLog custom manager.
3
+
4
+ Simplified manager with useful query methods for logs.
5
+ """
6
+
7
+ from django.db import models
8
+ from django.utils import timezone
9
+ from datetime import timedelta
10
+
11
+
12
+ class MaintenanceLogQuerySet(models.QuerySet):
13
+ """Custom queryset for MaintenanceLog with useful filters."""
14
+
15
+ def successful(self):
16
+ """Get successful operations."""
17
+ return self.filter(status='success')
18
+
19
+ def failed(self):
20
+ """Get failed operations."""
21
+ return self.filter(status='failed')
22
+
23
+ def pending(self):
24
+ """Get pending operations."""
25
+ return self.filter(status='pending')
26
+
27
+ def by_action(self, action: str):
28
+ """Filter by action type."""
29
+ return self.filter(action=action)
30
+
31
+ def enable_actions(self):
32
+ """Get enable maintenance actions."""
33
+ return self.filter(action='enable')
34
+
35
+ def disable_actions(self):
36
+ """Get disable maintenance actions."""
37
+ return self.filter(action='disable')
38
+
39
+ def sync_actions(self):
40
+ """Get sync actions."""
41
+ return self.filter(action='sync')
42
+
43
+ def error_actions(self):
44
+ """Get error actions."""
45
+ return self.filter(action='error')
46
+
47
+ def for_site(self, site):
48
+ """Get logs for specific site."""
49
+ return self.filter(site=site)
50
+
51
+ def for_domain(self, domain: str):
52
+ """Get logs for specific domain."""
53
+ return self.filter(site__domain=domain)
54
+
55
+ def recent(self, days: int = 7):
56
+ """Get recent logs."""
57
+ cutoff = timezone.now() - timedelta(days=days)
58
+ return self.filter(created_at__gte=cutoff)
59
+
60
+ def today(self):
61
+ """Get today's logs."""
62
+ today = timezone.now().date()
63
+ return self.filter(created_at__date=today)
64
+
65
+ def with_errors(self):
66
+ """Get logs that have error messages."""
67
+ return self.filter(error_message__isnull=False).exclude(error_message='')
68
+
69
+ def with_cloudflare_response(self):
70
+ """Get logs that have Cloudflare response data."""
71
+ return self.filter(cloudflare_response__isnull=False)
72
+
73
+ def fast_operations(self, max_seconds: int = 5):
74
+ """Get operations that completed quickly."""
75
+ return self.filter(duration_seconds__lte=max_seconds)
76
+
77
+ def slow_operations(self, min_seconds: int = 30):
78
+ """Get operations that took a long time."""
79
+ return self.filter(duration_seconds__gte=min_seconds)
80
+
81
+ def search(self, query: str):
82
+ """Search logs by reason or error message."""
83
+ return self.filter(
84
+ models.Q(reason__icontains=query) |
85
+ models.Q(error_message__icontains=query) |
86
+ models.Q(site__name__icontains=query) |
87
+ models.Q(site__domain__icontains=query)
88
+ )
89
+
90
+
91
+ class MaintenanceLogManager(models.Manager):
92
+ """Custom manager for MaintenanceLog."""
93
+
94
+ def get_queryset(self):
95
+ """Return custom queryset."""
96
+ return MaintenanceLogQuerySet(self.model, using=self._db)
97
+
98
+ def successful(self):
99
+ """Get successful operations."""
100
+ return self.get_queryset().successful()
101
+
102
+ def failed(self):
103
+ """Get failed operations."""
104
+ return self.get_queryset().failed()
105
+
106
+ def pending(self):
107
+ """Get pending operations."""
108
+ return self.get_queryset().pending()
109
+
110
+ def recent(self, days: int = 7):
111
+ """Get recent logs."""
112
+ return self.get_queryset().recent(days)
113
+
114
+ def today(self):
115
+ """Get today's logs."""
116
+ return self.get_queryset().today()
117
+
118
+ def for_site(self, site):
119
+ """Get logs for site."""
120
+ return self.get_queryset().for_site(site)
121
+
122
+ def for_domain(self, domain: str):
123
+ """Get logs for domain."""
124
+ return self.get_queryset().for_domain(domain)
125
+
126
+ def with_errors(self):
127
+ """Get logs with errors."""
128
+ return self.get_queryset().with_errors()
129
+
130
+ def search(self, query: str):
131
+ """Search logs."""
132
+ return self.get_queryset().search(query)
133
+
134
+ def get_stats(self):
135
+ """Get log statistics."""
136
+ total = self.count()
137
+ successful = self.successful().count()
138
+ failed = self.failed().count()
139
+ pending = self.pending().count()
140
+
141
+ return {
142
+ 'total': total,
143
+ 'successful': successful,
144
+ 'failed': failed,
145
+ 'pending': pending,
146
+ 'success_rate': (successful / total * 100) if total > 0 else 0,
147
+ }
148
+
149
+ def get_recent_activity(self, days: int = 7, limit: int = 10):
150
+ """Get recent activity summary."""
151
+ return self.recent(days).order_by('-created_at')[:limit]