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
@@ -1,504 +0,0 @@
1
- """
2
- Modern maintenance mode manager using official Cloudflare library.
3
-
4
- Provides clean, reliable maintenance mode management with Workers and Page Rules.
5
- """
6
-
7
- import logging
8
- from typing import Dict, List, Optional, Any
9
- from datetime import datetime
10
- from django.utils import timezone
11
- from django.db import transaction
12
-
13
- from .cloudflare_client import CloudflareClient
14
- from ..models import CloudflareSite, MaintenanceEvent, CloudflareDeployment
15
- from django_cfg.models.cloudflare import CloudflareConfig
16
-
17
- logger = logging.getLogger(__name__)
18
-
19
-
20
- class MaintenanceManager:
21
- """
22
- Modern maintenance mode manager using Cloudflare Workers.
23
-
24
- Provides reliable maintenance mode with automatic rollback capabilities.
25
- """
26
-
27
- # Modern maintenance page template
28
- MAINTENANCE_WORKER_TEMPLATE = '''
29
- addEventListener('fetch', event => {
30
- event.respondWith(handleMaintenanceMode(event.request))
31
- })
32
-
33
- async function handleMaintenanceMode(request) {
34
- const maintenanceHtml = `
35
- <!DOCTYPE html>
36
- <html lang="en">
37
- <head>
38
- <meta charset="UTF-8">
39
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
40
- <title>Maintenance Mode - {site_name}</title>
41
- <style>
42
- * { margin: 0; padding: 0; box-sizing: border-box; }
43
- body {
44
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
45
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
46
- color: white;
47
- min-height: 100vh;
48
- display: flex;
49
- align-items: center;
50
- justify-content: center;
51
- text-align: center;
52
- padding: 20px;
53
- }
54
- .container {
55
- max-width: 600px;
56
- background: rgba(255, 255, 255, 0.1);
57
- backdrop-filter: blur(10px);
58
- border-radius: 20px;
59
- padding: 60px 40px;
60
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
61
- }
62
- h1 {
63
- font-size: 3rem;
64
- margin-bottom: 20px;
65
- font-weight: 300;
66
- }
67
- .icon {
68
- font-size: 4rem;
69
- margin-bottom: 30px;
70
- opacity: 0.8;
71
- }
72
- p {
73
- font-size: 1.2rem;
74
- line-height: 1.6;
75
- margin-bottom: 30px;
76
- opacity: 0.9;
77
- }
78
- .message {
79
- background: rgba(255, 255, 255, 0.1);
80
- border-radius: 10px;
81
- padding: 20px;
82
- margin: 20px 0;
83
- border-left: 4px solid #fff;
84
- }
85
- .footer {
86
- margin-top: 40px;
87
- font-size: 0.9rem;
88
- opacity: 0.7;
89
- }
90
- @media (max-width: 768px) {
91
- h1 { font-size: 2rem; }
92
- .container { padding: 40px 20px; }
93
- }
94
- </style>
95
- </head>
96
- <body>
97
- <div class="container">
98
- <div class="icon">🔧</div>
99
- <h1>Under Maintenance</h1>
100
- <p>We're currently performing scheduled maintenance to improve your experience.</p>
101
-
102
- <div class="message">
103
- <strong>{maintenance_message}</strong>
104
- </div>
105
-
106
- <p>We'll be back online shortly. Thank you for your patience!</p>
107
-
108
- <div class="footer">
109
- <p>Started: {started_at}<br>
110
- Estimated completion: {estimated_completion}</p>
111
- </div>
112
- </div>
113
- </body>
114
- </html>
115
- `
116
-
117
- return new Response(maintenanceHtml, {
118
- status: 503,
119
- headers: {
120
- 'Content-Type': 'text/html; charset=utf-8',
121
- 'Cache-Control': 'no-cache, no-store, must-revalidate',
122
- 'Pragma': 'no-cache',
123
- 'Expires': '0',
124
- 'Retry-After': '3600'
125
- }
126
- })
127
- }
128
- '''
129
-
130
- def __init__(self, config: CloudflareConfig):
131
- """Initialize maintenance manager."""
132
- self.config = config
133
- self.client = CloudflareClient(config)
134
-
135
- def enable_maintenance(
136
- self,
137
- site: CloudflareSite,
138
- maintenance_event: MaintenanceEvent,
139
- custom_message: Optional[str] = None
140
- ) -> Dict[str, Any]:
141
- """
142
- Enable maintenance mode for a site using Cloudflare Workers.
143
-
144
- Args:
145
- site: CloudflareSite to enable maintenance for
146
- maintenance_event: MaintenanceEvent record
147
- custom_message: Custom maintenance message
148
-
149
- Returns:
150
- Dict with deployment results
151
- """
152
- logger.info(f"Enabling maintenance mode for {site.domain}")
153
-
154
- try:
155
- with transaction.atomic():
156
- # Get account ID
157
- account_id = site.account_id or self.client.get_account_id()
158
- if not account_id:
159
- raise ValueError("No account ID available")
160
-
161
- # Prepare worker script
162
- worker_name = f"maintenance-{site.domain.replace('.', '-')}"
163
- worker_script = self._prepare_worker_script(
164
- site=site,
165
- maintenance_event=maintenance_event,
166
- custom_message=custom_message
167
- )
168
-
169
- # Deploy worker
170
- worker_result = self.client.create_worker(
171
- account_id=account_id,
172
- script_name=worker_name,
173
- script_content=worker_script
174
- )
175
-
176
- # Create worker route
177
- route_pattern = f"{site.domain}/*"
178
- route_result = self.client.create_worker_route(
179
- zone_id=site.zone_id,
180
- pattern=route_pattern,
181
- script_name=worker_name
182
- )
183
-
184
- # Create deployment record
185
- deployment = CloudflareDeployment.objects.create_deployment(
186
- site=site,
187
- deployment_type='worker',
188
- cloudflare_id=worker_result.id,
189
- config={
190
- 'worker_name': worker_name,
191
- 'route_pattern': route_pattern,
192
- 'route_id': route_result.get('id'),
193
- 'maintenance_event_id': maintenance_event.id,
194
- 'custom_message': custom_message
195
- },
196
- maintenance_event=maintenance_event
197
- )
198
-
199
- # Update site maintenance status
200
- site.maintenance_active = True
201
- site.last_maintenance_at = timezone.now()
202
- site.save()
203
-
204
- # Update maintenance event
205
- maintenance_event.status = MaintenanceEvent.Status.ACTIVE
206
- maintenance_event.started_at = timezone.now()
207
- maintenance_event.save()
208
-
209
- logger.info(f"Maintenance mode enabled for {site.domain}")
210
-
211
- return {
212
- 'success': True,
213
- 'deployment_id': deployment.id,
214
- 'worker_name': worker_name,
215
- 'route_pattern': route_pattern,
216
- 'message': f"Maintenance mode enabled for {site.domain}"
217
- }
218
-
219
- except Exception as e:
220
- logger.error(f"Failed to enable maintenance for {site.domain}: {e}")
221
-
222
- # Update maintenance event status
223
- maintenance_event.status = MaintenanceEvent.Status.FAILED
224
- maintenance_event.error_message = str(e)
225
- maintenance_event.save()
226
-
227
- return {
228
- 'success': False,
229
- 'error': str(e),
230
- 'message': f"Failed to enable maintenance for {site.domain}"
231
- }
232
-
233
- def disable_maintenance(
234
- self,
235
- site: CloudflareSite,
236
- maintenance_event: Optional[MaintenanceEvent] = None
237
- ) -> Dict[str, Any]:
238
- """
239
- Disable maintenance mode for a site.
240
-
241
- Args:
242
- site: CloudflareSite to disable maintenance for
243
- maintenance_event: Optional MaintenanceEvent to update
244
-
245
- Returns:
246
- Dict with results
247
- """
248
- logger.info(f"Disabling maintenance mode for {site.domain}")
249
-
250
- try:
251
- with transaction.atomic():
252
- # Find active maintenance deployments
253
- active_deployments = CloudflareDeployment.objects.filter(
254
- site=site,
255
- deployment_type='worker',
256
- status='active'
257
- )
258
-
259
- results = []
260
- account_id = site.account_id or self.client.get_account_id()
261
-
262
- for deployment in active_deployments:
263
- try:
264
- config = deployment.config or {}
265
- worker_name = config.get('worker_name')
266
- route_id = config.get('route_id')
267
-
268
- # Delete worker route
269
- if route_id:
270
- self.client.delete_worker_route(site.zone_id, route_id)
271
-
272
- # Delete worker script
273
- if worker_name and account_id:
274
- self.client.delete_worker(account_id, worker_name)
275
-
276
- # Update deployment status
277
- deployment.status = 'rolled_back'
278
- deployment.save()
279
-
280
- results.append({
281
- 'deployment_id': deployment.id,
282
- 'worker_name': worker_name,
283
- 'success': True
284
- })
285
-
286
- except Exception as e:
287
- logger.error(f"Failed to cleanup deployment {deployment.id}: {e}")
288
- results.append({
289
- 'deployment_id': deployment.id,
290
- 'success': False,
291
- 'error': str(e)
292
- })
293
-
294
- # Update site maintenance status
295
- site.maintenance_active = False
296
- site.save()
297
-
298
- # Update maintenance event if provided
299
- if maintenance_event:
300
- maintenance_event.status = MaintenanceEvent.Status.COMPLETED
301
- maintenance_event.ended_at = timezone.now()
302
- maintenance_event.save()
303
-
304
- logger.info(f"Maintenance mode disabled for {site.domain}")
305
-
306
- return {
307
- 'success': True,
308
- 'deployments_cleaned': len([r for r in results if r['success']]),
309
- 'cleanup_results': results,
310
- 'message': f"Maintenance mode disabled for {site.domain}"
311
- }
312
-
313
- except Exception as e:
314
- logger.error(f"Failed to disable maintenance for {site.domain}: {e}")
315
-
316
- if maintenance_event:
317
- maintenance_event.status = MaintenanceEvent.Status.FAILED
318
- maintenance_event.error_message = str(e)
319
- maintenance_event.save()
320
-
321
- return {
322
- 'success': False,
323
- 'error': str(e),
324
- 'message': f"Failed to disable maintenance for {site.domain}"
325
- }
326
-
327
- def bulk_enable_maintenance(
328
- self,
329
- sites: List[CloudflareSite],
330
- maintenance_event: MaintenanceEvent,
331
- custom_message: Optional[str] = None
332
- ) -> Dict[str, Any]:
333
- """
334
- Enable maintenance mode for multiple sites.
335
-
336
- Args:
337
- sites: List of CloudflareSite objects
338
- maintenance_event: MaintenanceEvent record
339
- custom_message: Custom maintenance message
340
-
341
- Returns:
342
- Dict with bulk operation results
343
- """
344
- logger.info(f"Enabling maintenance mode for {len(sites)} sites")
345
-
346
- results = {
347
- 'success': 0,
348
- 'failed': 0,
349
- 'sites': []
350
- }
351
-
352
- for site in sites:
353
- try:
354
- result = self.enable_maintenance(
355
- site=site,
356
- maintenance_event=maintenance_event,
357
- custom_message=custom_message
358
- )
359
-
360
- if result['success']:
361
- results['success'] += 1
362
- else:
363
- results['failed'] += 1
364
-
365
- results['sites'].append({
366
- 'site_id': site.id,
367
- 'domain': site.domain,
368
- 'result': result
369
- })
370
-
371
- except Exception as e:
372
- logger.error(f"Bulk maintenance failed for {site.domain}: {e}")
373
- results['failed'] += 1
374
- results['sites'].append({
375
- 'site_id': site.id,
376
- 'domain': site.domain,
377
- 'result': {
378
- 'success': False,
379
- 'error': str(e)
380
- }
381
- })
382
-
383
- logger.info(f"Bulk maintenance completed: {results['success']} success, {results['failed']} failed")
384
- return results
385
-
386
- def bulk_disable_maintenance(
387
- self,
388
- sites: List[CloudflareSite],
389
- maintenance_event: Optional[MaintenanceEvent] = None
390
- ) -> Dict[str, Any]:
391
- """
392
- Disable maintenance mode for multiple sites.
393
-
394
- Args:
395
- sites: List of CloudflareSite objects
396
- maintenance_event: Optional MaintenanceEvent to update
397
-
398
- Returns:
399
- Dict with bulk operation results
400
- """
401
- logger.info(f"Disabling maintenance mode for {len(sites)} sites")
402
-
403
- results = {
404
- 'success': 0,
405
- 'failed': 0,
406
- 'sites': []
407
- }
408
-
409
- for site in sites:
410
- try:
411
- result = self.disable_maintenance(
412
- site=site,
413
- maintenance_event=maintenance_event
414
- )
415
-
416
- if result['success']:
417
- results['success'] += 1
418
- else:
419
- results['failed'] += 1
420
-
421
- results['sites'].append({
422
- 'site_id': site.id,
423
- 'domain': site.domain,
424
- 'result': result
425
- })
426
-
427
- except Exception as e:
428
- logger.error(f"Bulk maintenance disable failed for {site.domain}: {e}")
429
- results['failed'] += 1
430
- results['sites'].append({
431
- 'site_id': site.id,
432
- 'domain': site.domain,
433
- 'result': {
434
- 'success': False,
435
- 'error': str(e)
436
- }
437
- })
438
-
439
- logger.info(f"Bulk maintenance disable completed: {results['success']} success, {results['failed']} failed")
440
- return results
441
-
442
- def _prepare_worker_script(
443
- self,
444
- site: CloudflareSite,
445
- maintenance_event: MaintenanceEvent,
446
- custom_message: Optional[str] = None
447
- ) -> str:
448
- """Prepare Worker script with site-specific content."""
449
- message = custom_message or maintenance_event.maintenance_message or "Scheduled maintenance in progress"
450
-
451
- started_at = maintenance_event.started_at or timezone.now()
452
- estimated_completion = "Soon"
453
-
454
- if maintenance_event.estimated_duration:
455
- completion_time = started_at + maintenance_event.estimated_duration
456
- estimated_completion = completion_time.strftime("%Y-%m-%d %H:%M UTC")
457
-
458
- return self.MAINTENANCE_WORKER_TEMPLATE.format(
459
- site_name=site.name or site.domain,
460
- maintenance_message=message,
461
- started_at=started_at.strftime("%Y-%m-%d %H:%M UTC"),
462
- estimated_completion=estimated_completion
463
- )
464
-
465
- def get_maintenance_status(self, site: CloudflareSite) -> Dict[str, Any]:
466
- """
467
- Get current maintenance status for a site.
468
-
469
- Args:
470
- site: CloudflareSite to check
471
-
472
- Returns:
473
- Dict with maintenance status
474
- """
475
- active_deployments = CloudflareDeployment.objects.filter(
476
- site=site,
477
- deployment_type='worker',
478
- status='active'
479
- ).with_maintenance_event()
480
-
481
- if not active_deployments.exists():
482
- return {
483
- 'maintenance_active': False,
484
- 'site_id': site.id,
485
- 'domain': site.domain
486
- }
487
-
488
- deployment = active_deployments.first()
489
- maintenance_event = deployment.maintenance_event
490
-
491
- return {
492
- 'maintenance_active': True,
493
- 'site_id': site.id,
494
- 'domain': site.domain,
495
- 'deployment_id': deployment.id,
496
- 'started_at': deployment.deployed_at.isoformat(),
497
- 'maintenance_event': {
498
- 'id': maintenance_event.id if maintenance_event else None,
499
- 'title': maintenance_event.title if maintenance_event else None,
500
- 'status': maintenance_event.status if maintenance_event else None,
501
- 'estimated_duration': str(maintenance_event.estimated_duration) if maintenance_event and maintenance_event.estimated_duration else None
502
- } if maintenance_event else None,
503
- 'worker_config': deployment.config
504
- }