django-cfg 1.2.17__py3-none-any.whl → 1.2.19__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/accounts/models/__init__.py +68 -0
- django_cfg/apps/accounts/models/activity.py +34 -0
- django_cfg/apps/accounts/models/auth.py +50 -0
- django_cfg/apps/accounts/models/base.py +8 -0
- django_cfg/apps/accounts/models/choices.py +32 -0
- django_cfg/apps/accounts/models/integrations.py +75 -0
- django_cfg/apps/accounts/models/registration.py +52 -0
- django_cfg/apps/accounts/models/user.py +80 -0
- django_cfg/apps/maintenance/__init__.py +53 -24
- django_cfg/apps/maintenance/admin/__init__.py +7 -18
- django_cfg/apps/maintenance/admin/api_key_admin.py +185 -0
- django_cfg/apps/maintenance/admin/log_admin.py +156 -0
- django_cfg/apps/maintenance/admin/scheduled_admin.py +390 -0
- django_cfg/apps/maintenance/admin/site_admin.py +448 -0
- django_cfg/apps/maintenance/apps.py +9 -96
- django_cfg/apps/maintenance/management/commands/maintenance.py +193 -307
- django_cfg/apps/maintenance/management/commands/process_scheduled_maintenance.py +241 -0
- django_cfg/apps/maintenance/management/commands/sync_cloudflare.py +152 -111
- django_cfg/apps/maintenance/managers/__init__.py +7 -12
- django_cfg/apps/maintenance/managers/cloudflare_site_manager.py +192 -0
- django_cfg/apps/maintenance/managers/maintenance_log_manager.py +151 -0
- django_cfg/apps/maintenance/migrations/0001_initial.py +145 -705
- django_cfg/apps/maintenance/migrations/0002_cloudflaresite_maintenance_url.py +21 -0
- django_cfg/apps/maintenance/models/__init__.py +23 -21
- django_cfg/apps/maintenance/models/cloudflare_api_key.py +109 -0
- django_cfg/apps/maintenance/models/cloudflare_site.py +125 -0
- django_cfg/apps/maintenance/models/maintenance_log.py +131 -0
- django_cfg/apps/maintenance/models/scheduled_maintenance.py +307 -0
- django_cfg/apps/maintenance/services/__init__.py +37 -16
- django_cfg/apps/maintenance/services/bulk_operations_service.py +400 -0
- django_cfg/apps/maintenance/services/maintenance_service.py +230 -0
- django_cfg/apps/maintenance/services/scheduled_maintenance_service.py +381 -0
- django_cfg/apps/maintenance/services/site_sync_service.py +390 -0
- django_cfg/apps/maintenance/utils/__init__.py +12 -0
- django_cfg/apps/maintenance/utils/retry_utils.py +109 -0
- django_cfg/config.py +4 -0
- django_cfg/core/config.py +4 -6
- django_cfg/modules/django_unfold/dashboard.py +4 -5
- {django_cfg-1.2.17.dist-info → django_cfg-1.2.19.dist-info}/METADATA +52 -1
- {django_cfg-1.2.17.dist-info → django_cfg-1.2.19.dist-info}/RECORD +45 -55
- django_cfg/apps/maintenance/README.md +0 -305
- django_cfg/apps/maintenance/admin/deployments_admin.py +0 -251
- django_cfg/apps/maintenance/admin/events_admin.py +0 -374
- django_cfg/apps/maintenance/admin/monitoring_admin.py +0 -215
- django_cfg/apps/maintenance/admin/sites_admin.py +0 -464
- django_cfg/apps/maintenance/managers/deployments.py +0 -287
- django_cfg/apps/maintenance/managers/events.py +0 -374
- django_cfg/apps/maintenance/managers/monitoring.py +0 -301
- django_cfg/apps/maintenance/managers/sites.py +0 -335
- django_cfg/apps/maintenance/models/cloudflare.py +0 -316
- django_cfg/apps/maintenance/models/maintenance.py +0 -334
- django_cfg/apps/maintenance/models/monitoring.py +0 -393
- django_cfg/apps/maintenance/models/sites.py +0 -419
- django_cfg/apps/maintenance/serializers/__init__.py +0 -60
- django_cfg/apps/maintenance/serializers/actions.py +0 -310
- django_cfg/apps/maintenance/serializers/base.py +0 -44
- django_cfg/apps/maintenance/serializers/deployments.py +0 -209
- django_cfg/apps/maintenance/serializers/events.py +0 -210
- django_cfg/apps/maintenance/serializers/monitoring.py +0 -278
- django_cfg/apps/maintenance/serializers/sites.py +0 -213
- django_cfg/apps/maintenance/services/README.md +0 -168
- django_cfg/apps/maintenance/services/cloudflare_client.py +0 -441
- django_cfg/apps/maintenance/services/dns_manager.py +0 -497
- django_cfg/apps/maintenance/services/maintenance_manager.py +0 -504
- django_cfg/apps/maintenance/services/site_sync.py +0 -448
- django_cfg/apps/maintenance/services/sync_command_service.py +0 -330
- django_cfg/apps/maintenance/services/worker_manager.py +0 -264
- django_cfg/apps/maintenance/signals.py +0 -38
- django_cfg/apps/maintenance/urls.py +0 -36
- django_cfg/apps/maintenance/views/__init__.py +0 -18
- django_cfg/apps/maintenance/views/base.py +0 -61
- django_cfg/apps/maintenance/views/deployments.py +0 -175
- django_cfg/apps/maintenance/views/events.py +0 -204
- django_cfg/apps/maintenance/views/monitoring.py +0 -213
- django_cfg/apps/maintenance/views/sites.py +0 -338
- django_cfg/models/cloudflare.py +0 -316
- /django_cfg/apps/accounts/{models.py → __models.py} +0 -0
- {django_cfg-1.2.17.dist-info → django_cfg-1.2.19.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.17.dist-info → django_cfg-1.2.19.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.17.dist-info → django_cfg-1.2.19.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]
|