django-cfg 1.2.15__py3-none-any.whl → 1.2.16__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 (61) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/maintenance/README.md +305 -0
  3. django_cfg/apps/maintenance/__init__.py +27 -0
  4. django_cfg/apps/maintenance/admin/__init__.py +28 -0
  5. django_cfg/apps/maintenance/admin/deployments_admin.py +251 -0
  6. django_cfg/apps/maintenance/admin/events_admin.py +374 -0
  7. django_cfg/apps/maintenance/admin/monitoring_admin.py +215 -0
  8. django_cfg/apps/maintenance/admin/sites_admin.py +464 -0
  9. django_cfg/apps/maintenance/apps.py +105 -0
  10. django_cfg/apps/maintenance/management/__init__.py +0 -0
  11. django_cfg/apps/maintenance/management/commands/__init__.py +0 -0
  12. django_cfg/apps/maintenance/management/commands/maintenance.py +375 -0
  13. django_cfg/apps/maintenance/management/commands/sync_cloudflare.py +168 -0
  14. django_cfg/apps/maintenance/managers/__init__.py +20 -0
  15. django_cfg/apps/maintenance/managers/deployments.py +287 -0
  16. django_cfg/apps/maintenance/managers/events.py +374 -0
  17. django_cfg/apps/maintenance/managers/monitoring.py +301 -0
  18. django_cfg/apps/maintenance/managers/sites.py +335 -0
  19. django_cfg/apps/maintenance/migrations/0001_initial.py +939 -0
  20. django_cfg/apps/maintenance/migrations/__init__.py +0 -0
  21. django_cfg/apps/maintenance/models/__init__.py +27 -0
  22. django_cfg/apps/maintenance/models/cloudflare.py +316 -0
  23. django_cfg/apps/maintenance/models/maintenance.py +334 -0
  24. django_cfg/apps/maintenance/models/monitoring.py +393 -0
  25. django_cfg/apps/maintenance/models/sites.py +419 -0
  26. django_cfg/apps/maintenance/serializers/__init__.py +60 -0
  27. django_cfg/apps/maintenance/serializers/actions.py +310 -0
  28. django_cfg/apps/maintenance/serializers/base.py +44 -0
  29. django_cfg/apps/maintenance/serializers/deployments.py +209 -0
  30. django_cfg/apps/maintenance/serializers/events.py +210 -0
  31. django_cfg/apps/maintenance/serializers/monitoring.py +278 -0
  32. django_cfg/apps/maintenance/serializers/sites.py +213 -0
  33. django_cfg/apps/maintenance/services/README.md +168 -0
  34. django_cfg/apps/maintenance/services/__init__.py +21 -0
  35. django_cfg/apps/maintenance/services/cloudflare_client.py +441 -0
  36. django_cfg/apps/maintenance/services/dns_manager.py +497 -0
  37. django_cfg/apps/maintenance/services/maintenance_manager.py +504 -0
  38. django_cfg/apps/maintenance/services/site_sync.py +448 -0
  39. django_cfg/apps/maintenance/services/sync_command_service.py +330 -0
  40. django_cfg/apps/maintenance/services/worker_manager.py +264 -0
  41. django_cfg/apps/maintenance/signals.py +38 -0
  42. django_cfg/apps/maintenance/urls.py +36 -0
  43. django_cfg/apps/maintenance/views/__init__.py +18 -0
  44. django_cfg/apps/maintenance/views/base.py +61 -0
  45. django_cfg/apps/maintenance/views/deployments.py +175 -0
  46. django_cfg/apps/maintenance/views/events.py +204 -0
  47. django_cfg/apps/maintenance/views/monitoring.py +213 -0
  48. django_cfg/apps/maintenance/views/sites.py +338 -0
  49. django_cfg/apps/urls.py +5 -1
  50. django_cfg/core/config.py +34 -3
  51. django_cfg/core/generation.py +15 -10
  52. django_cfg/models/cloudflare.py +316 -0
  53. django_cfg/models/revolution.py +1 -1
  54. django_cfg/models/tasks.py +1 -1
  55. django_cfg/modules/base.py +12 -5
  56. django_cfg/modules/django_unfold/dashboard.py +16 -1
  57. {django_cfg-1.2.15.dist-info → django_cfg-1.2.16.dist-info}/METADATA +2 -1
  58. {django_cfg-1.2.15.dist-info → django_cfg-1.2.16.dist-info}/RECORD +61 -13
  59. {django_cfg-1.2.15.dist-info → django_cfg-1.2.16.dist-info}/WHEEL +0 -0
  60. {django_cfg-1.2.15.dist-info → django_cfg-1.2.16.dist-info}/entry_points.txt +0 -0
  61. {django_cfg-1.2.15.dist-info → django_cfg-1.2.16.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,939 @@
1
+ # Generated by Django 5.2.6 on 2025-09-22 17:18
2
+
3
+ import django.db.models.deletion
4
+ import django.utils.timezone
5
+ from django.conf import settings
6
+ from django.db import migrations, models
7
+
8
+
9
+ class Migration(migrations.Migration):
10
+
11
+ initial = True
12
+
13
+ dependencies = [
14
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15
+ ]
16
+
17
+ operations = [
18
+ migrations.CreateModel(
19
+ name="CloudflareSite",
20
+ fields=[
21
+ (
22
+ "id",
23
+ models.BigAutoField(
24
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
25
+ ),
26
+ ),
27
+ (
28
+ "name",
29
+ models.CharField(
30
+ help_text="Friendly site name for identification", max_length=100
31
+ ),
32
+ ),
33
+ (
34
+ "domain",
35
+ models.CharField(
36
+ help_text="Domain name (e.g., example.com)", max_length=253, unique=True
37
+ ),
38
+ ),
39
+ (
40
+ "description",
41
+ models.TextField(blank=True, help_text="Site description or notes"),
42
+ ),
43
+ (
44
+ "zone_id",
45
+ models.CharField(help_text="Cloudflare Zone ID", max_length=32, unique=True),
46
+ ),
47
+ ("account_id", models.CharField(help_text="Cloudflare Account ID", max_length=32)),
48
+ (
49
+ "api_token",
50
+ models.CharField(
51
+ help_text="Site-specific or account API token", max_length=200
52
+ ),
53
+ ),
54
+ (
55
+ "environment",
56
+ models.CharField(
57
+ choices=[
58
+ ("production", "Production"),
59
+ ("staging", "Staging"),
60
+ ("development", "Development"),
61
+ ("testing", "Testing"),
62
+ ],
63
+ default="production",
64
+ help_text="Site environment type",
65
+ max_length=20,
66
+ ),
67
+ ),
68
+ (
69
+ "project",
70
+ models.CharField(
71
+ blank=True, help_text="Project or client name", max_length=100
72
+ ),
73
+ ),
74
+ (
75
+ "tags",
76
+ models.JSONField(
77
+ blank=True,
78
+ default=list,
79
+ help_text="Custom tags for filtering and organization",
80
+ ),
81
+ ),
82
+ (
83
+ "current_status",
84
+ models.CharField(
85
+ choices=[
86
+ ("active", "Active"),
87
+ ("maintenance", "Under Maintenance"),
88
+ ("offline", "Offline"),
89
+ ("unknown", "Unknown"),
90
+ ],
91
+ default="unknown",
92
+ help_text="Current operational status",
93
+ max_length=20,
94
+ ),
95
+ ),
96
+ (
97
+ "maintenance_active",
98
+ models.BooleanField(
99
+ default=False, help_text="Whether maintenance mode is currently active"
100
+ ),
101
+ ),
102
+ (
103
+ "last_status_check",
104
+ models.DateTimeField(
105
+ blank=True, help_text="When status was last checked", null=True
106
+ ),
107
+ ),
108
+ (
109
+ "worker_name",
110
+ models.CharField(
111
+ default="maintenance-mode",
112
+ help_text="Cloudflare Worker name for maintenance mode",
113
+ max_length=100,
114
+ ),
115
+ ),
116
+ (
117
+ "maintenance_template",
118
+ models.CharField(
119
+ default="modern", help_text="Maintenance page template", max_length=50
120
+ ),
121
+ ),
122
+ (
123
+ "custom_maintenance_message",
124
+ models.TextField(
125
+ blank=True, help_text="Custom maintenance message for this site"
126
+ ),
127
+ ),
128
+ (
129
+ "monitoring_enabled",
130
+ models.BooleanField(
131
+ default=True, help_text="Enable health monitoring for this site"
132
+ ),
133
+ ),
134
+ (
135
+ "health_check_url",
136
+ models.URLField(
137
+ blank=True, help_text="Custom health check URL (defaults to domain/health/)"
138
+ ),
139
+ ),
140
+ (
141
+ "check_interval",
142
+ models.PositiveIntegerField(
143
+ default=300, help_text="Health check interval in seconds"
144
+ ),
145
+ ),
146
+ ("created_at", models.DateTimeField(auto_now_add=True)),
147
+ ("updated_at", models.DateTimeField(auto_now=True)),
148
+ (
149
+ "last_maintenance_at",
150
+ models.DateTimeField(
151
+ blank=True, help_text="When maintenance was last activated", null=True
152
+ ),
153
+ ),
154
+ (
155
+ "allowed_users",
156
+ models.ManyToManyField(
157
+ blank=True,
158
+ help_text="Users with access to manage this site",
159
+ related_name="accessible_sites",
160
+ to=settings.AUTH_USER_MODEL,
161
+ ),
162
+ ),
163
+ (
164
+ "owner",
165
+ models.ForeignKey(
166
+ help_text="Site owner",
167
+ on_delete=django.db.models.deletion.CASCADE,
168
+ related_name="owned_sites",
169
+ to=settings.AUTH_USER_MODEL,
170
+ ),
171
+ ),
172
+ ],
173
+ options={
174
+ "verbose_name": "Cloudflare Site",
175
+ "verbose_name_plural": "Cloudflare Sites",
176
+ "ordering": ["name"],
177
+ },
178
+ ),
179
+ migrations.CreateModel(
180
+ name="MaintenanceEvent",
181
+ fields=[
182
+ (
183
+ "id",
184
+ models.BigAutoField(
185
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
186
+ ),
187
+ ),
188
+ (
189
+ "title",
190
+ models.CharField(
191
+ help_text="Human-readable maintenance event title", max_length=200
192
+ ),
193
+ ),
194
+ (
195
+ "description",
196
+ models.TextField(
197
+ blank=True, help_text="Detailed description of maintenance work"
198
+ ),
199
+ ),
200
+ (
201
+ "reason",
202
+ models.CharField(
203
+ choices=[
204
+ ("manual", "Manual Activation"),
205
+ ("server_down", "Server Unreachable"),
206
+ ("high_error_rate", "High Error Rate"),
207
+ ("database_issues", "Database Issues"),
208
+ ("deployment", "Deployment"),
209
+ ("security_update", "Security Update"),
210
+ ("scheduled", "Scheduled Maintenance"),
211
+ ("emergency", "Emergency Maintenance"),
212
+ ],
213
+ default="manual",
214
+ help_text="Reason for maintenance activation",
215
+ max_length=50,
216
+ ),
217
+ ),
218
+ (
219
+ "status",
220
+ models.CharField(
221
+ choices=[
222
+ ("scheduled", "Scheduled"),
223
+ ("active", "Active"),
224
+ ("completed", "Completed"),
225
+ ("failed", "Failed"),
226
+ ("cancelled", "Cancelled"),
227
+ ],
228
+ default="active",
229
+ help_text="Current maintenance status",
230
+ max_length=20,
231
+ ),
232
+ ),
233
+ (
234
+ "started_at",
235
+ models.DateTimeField(
236
+ default=django.utils.timezone.now, help_text="When maintenance was started"
237
+ ),
238
+ ),
239
+ (
240
+ "ended_at",
241
+ models.DateTimeField(
242
+ blank=True, help_text="When maintenance was completed", null=True
243
+ ),
244
+ ),
245
+ (
246
+ "estimated_duration",
247
+ models.DurationField(
248
+ blank=True, help_text="Estimated maintenance duration", null=True
249
+ ),
250
+ ),
251
+ (
252
+ "cloudflare_worker_deployed",
253
+ models.BooleanField(
254
+ default=False, help_text="Whether Cloudflare Worker was deployed"
255
+ ),
256
+ ),
257
+ (
258
+ "cloudflare_deployment_id",
259
+ models.CharField(
260
+ blank=True, help_text="Cloudflare deployment identifier", max_length=100
261
+ ),
262
+ ),
263
+ (
264
+ "worker_deployment_success",
265
+ models.BooleanField(
266
+ default=False, help_text="Whether Worker deployment was successful"
267
+ ),
268
+ ),
269
+ (
270
+ "affected_requests",
271
+ models.PositiveIntegerField(
272
+ default=0, help_text="Number of requests affected during maintenance"
273
+ ),
274
+ ),
275
+ (
276
+ "error_count_before",
277
+ models.PositiveIntegerField(
278
+ default=0, help_text="Error count before maintenance"
279
+ ),
280
+ ),
281
+ (
282
+ "error_count_during",
283
+ models.PositiveIntegerField(
284
+ default=0, help_text="Error count during maintenance"
285
+ ),
286
+ ),
287
+ ("created_at", models.DateTimeField(auto_now_add=True)),
288
+ ("updated_at", models.DateTimeField(auto_now=True)),
289
+ (
290
+ "completed_by",
291
+ models.ForeignKey(
292
+ blank=True,
293
+ help_text="User who completed maintenance",
294
+ null=True,
295
+ on_delete=django.db.models.deletion.SET_NULL,
296
+ related_name="completed_maintenance_events",
297
+ to=settings.AUTH_USER_MODEL,
298
+ ),
299
+ ),
300
+ (
301
+ "initiated_by",
302
+ models.ForeignKey(
303
+ blank=True,
304
+ help_text="User who initiated maintenance",
305
+ null=True,
306
+ on_delete=django.db.models.deletion.SET_NULL,
307
+ related_name="initiated_maintenance_events",
308
+ to=settings.AUTH_USER_MODEL,
309
+ ),
310
+ ),
311
+ (
312
+ "sites",
313
+ models.ManyToManyField(
314
+ blank=True,
315
+ help_text="Sites affected by this maintenance event",
316
+ related_name="maintenance_events",
317
+ to="django_cfg_maintenance.cloudflaresite",
318
+ ),
319
+ ),
320
+ ],
321
+ options={
322
+ "verbose_name": "Maintenance Event",
323
+ "verbose_name_plural": "Maintenance Events",
324
+ "ordering": ["-started_at"],
325
+ },
326
+ ),
327
+ migrations.CreateModel(
328
+ name="CloudflareDeployment",
329
+ fields=[
330
+ (
331
+ "id",
332
+ models.BigAutoField(
333
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
334
+ ),
335
+ ),
336
+ (
337
+ "deployment_type",
338
+ models.CharField(
339
+ choices=[
340
+ ("worker", "Cloudflare Worker"),
341
+ ("page_rule", "Page Rule"),
342
+ ("dns_record", "DNS Record"),
343
+ ("ssl_setting", "SSL Setting"),
344
+ ("custom_error_page", "Custom Error Page"),
345
+ ],
346
+ default="worker",
347
+ help_text="Type of Cloudflare resource being deployed",
348
+ max_length=50,
349
+ ),
350
+ ),
351
+ (
352
+ "resource_name",
353
+ models.CharField(
354
+ help_text="Name of the deployed resource (Worker name, Page Rule, etc.)",
355
+ max_length=200,
356
+ ),
357
+ ),
358
+ (
359
+ "resource_id",
360
+ models.CharField(
361
+ blank=True,
362
+ help_text="Cloudflare resource ID after deployment",
363
+ max_length=100,
364
+ ),
365
+ ),
366
+ (
367
+ "status",
368
+ models.CharField(
369
+ choices=[
370
+ ("pending", "Pending"),
371
+ ("deploying", "Deploying"),
372
+ ("deployed", "Deployed"),
373
+ ("failed", "Failed"),
374
+ ("rolled_back", "Rolled Back"),
375
+ ],
376
+ default="pending",
377
+ help_text="Current deployment status",
378
+ max_length=20,
379
+ ),
380
+ ),
381
+ (
382
+ "created_at",
383
+ models.DateTimeField(
384
+ auto_now_add=True, help_text="When deployment was initiated"
385
+ ),
386
+ ),
387
+ (
388
+ "deployed_at",
389
+ models.DateTimeField(
390
+ blank=True, help_text="When deployment completed successfully", null=True
391
+ ),
392
+ ),
393
+ (
394
+ "failed_at",
395
+ models.DateTimeField(blank=True, help_text="When deployment failed", null=True),
396
+ ),
397
+ (
398
+ "configuration",
399
+ models.JSONField(
400
+ default=dict,
401
+ help_text="Deployment configuration (Worker script, Page Rule settings, etc.)",
402
+ ),
403
+ ),
404
+ (
405
+ "cloudflare_response",
406
+ models.JSONField(
407
+ blank=True, default=dict, help_text="Full response from Cloudflare API"
408
+ ),
409
+ ),
410
+ (
411
+ "error_message",
412
+ models.TextField(blank=True, help_text="Error message if deployment failed"),
413
+ ),
414
+ (
415
+ "rollback_data",
416
+ models.JSONField(
417
+ blank=True,
418
+ default=dict,
419
+ help_text="Data needed for rollback (previous configuration, etc.)",
420
+ ),
421
+ ),
422
+ (
423
+ "previous_deployment",
424
+ models.ForeignKey(
425
+ blank=True,
426
+ help_text="Previous deployment for rollback",
427
+ null=True,
428
+ on_delete=django.db.models.deletion.SET_NULL,
429
+ related_name="rollback_deployments",
430
+ to="django_cfg_maintenance.cloudflaredeployment",
431
+ ),
432
+ ),
433
+ (
434
+ "site",
435
+ models.ForeignKey(
436
+ help_text="Site this deployment belongs to",
437
+ on_delete=django.db.models.deletion.CASCADE,
438
+ related_name="deployments",
439
+ to="django_cfg_maintenance.cloudflaresite",
440
+ ),
441
+ ),
442
+ (
443
+ "maintenance_event",
444
+ models.ForeignKey(
445
+ blank=True,
446
+ help_text="Related maintenance event",
447
+ null=True,
448
+ on_delete=django.db.models.deletion.SET_NULL,
449
+ related_name="cloudflare_deployments",
450
+ to="django_cfg_maintenance.maintenanceevent",
451
+ ),
452
+ ),
453
+ ],
454
+ options={
455
+ "verbose_name": "Cloudflare Deployment",
456
+ "verbose_name_plural": "Cloudflare Deployments",
457
+ "ordering": ["-created_at"],
458
+ },
459
+ ),
460
+ migrations.CreateModel(
461
+ name="MaintenanceLog",
462
+ fields=[
463
+ (
464
+ "id",
465
+ models.BigAutoField(
466
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
467
+ ),
468
+ ),
469
+ (
470
+ "level",
471
+ models.CharField(
472
+ choices=[
473
+ ("debug", "Debug"),
474
+ ("info", "Info"),
475
+ ("warning", "Warning"),
476
+ ("error", "Error"),
477
+ ("critical", "Critical"),
478
+ ],
479
+ default="info",
480
+ help_text="Log level",
481
+ max_length=20,
482
+ ),
483
+ ),
484
+ ("message", models.TextField(help_text="Log message")),
485
+ (
486
+ "details",
487
+ models.JSONField(
488
+ blank=True, default=dict, help_text="Additional log details (JSON)"
489
+ ),
490
+ ),
491
+ (
492
+ "component",
493
+ models.CharField(
494
+ blank=True,
495
+ help_text="Component that generated the log (e.g., 'cloudflare', 'monitoring')",
496
+ max_length=100,
497
+ ),
498
+ ),
499
+ (
500
+ "operation",
501
+ models.CharField(
502
+ blank=True,
503
+ help_text="Operation being performed (e.g., 'deploy_worker', 'health_check')",
504
+ max_length=100,
505
+ ),
506
+ ),
507
+ (
508
+ "timestamp",
509
+ models.DateTimeField(
510
+ default=django.utils.timezone.now,
511
+ help_text="When the log entry was created",
512
+ ),
513
+ ),
514
+ (
515
+ "event",
516
+ models.ForeignKey(
517
+ help_text="Related maintenance event",
518
+ on_delete=django.db.models.deletion.CASCADE,
519
+ related_name="logs",
520
+ to="django_cfg_maintenance.maintenanceevent",
521
+ ),
522
+ ),
523
+ (
524
+ "user",
525
+ models.ForeignKey(
526
+ blank=True,
527
+ help_text="User associated with this log entry",
528
+ null=True,
529
+ on_delete=django.db.models.deletion.SET_NULL,
530
+ to=settings.AUTH_USER_MODEL,
531
+ ),
532
+ ),
533
+ ],
534
+ options={
535
+ "verbose_name": "Maintenance Log",
536
+ "verbose_name_plural": "Maintenance Logs",
537
+ "ordering": ["-timestamp"],
538
+ },
539
+ ),
540
+ migrations.CreateModel(
541
+ name="MonitoringTarget",
542
+ fields=[
543
+ (
544
+ "id",
545
+ models.BigAutoField(
546
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
547
+ ),
548
+ ),
549
+ ("check_url", models.URLField(help_text="URL to check for health status")),
550
+ (
551
+ "check_interval",
552
+ models.PositiveIntegerField(default=60, help_text="Check interval in seconds"),
553
+ ),
554
+ (
555
+ "timeout",
556
+ models.PositiveIntegerField(default=10, help_text="Request timeout in seconds"),
557
+ ),
558
+ (
559
+ "expected_status_codes",
560
+ models.JSONField(
561
+ default=list, help_text="Expected HTTP status codes (e.g., [200, 201])"
562
+ ),
563
+ ),
564
+ (
565
+ "expected_response_time_ms",
566
+ models.PositiveIntegerField(
567
+ blank=True,
568
+ help_text="Maximum expected response time in milliseconds",
569
+ null=True,
570
+ ),
571
+ ),
572
+ (
573
+ "expected_content",
574
+ models.TextField(
575
+ blank=True, help_text="Expected content in response body (substring match)"
576
+ ),
577
+ ),
578
+ (
579
+ "failure_threshold",
580
+ models.PositiveIntegerField(
581
+ default=3, help_text="Consecutive failures before triggering maintenance"
582
+ ),
583
+ ),
584
+ (
585
+ "recovery_threshold",
586
+ models.PositiveIntegerField(
587
+ default=2, help_text="Consecutive successes before disabling maintenance"
588
+ ),
589
+ ),
590
+ (
591
+ "status",
592
+ models.CharField(
593
+ choices=[
594
+ ("active", "Active"),
595
+ ("paused", "Paused"),
596
+ ("disabled", "Disabled"),
597
+ ("error", "Error"),
598
+ ],
599
+ default="active",
600
+ help_text="Current monitoring status",
601
+ max_length=20,
602
+ ),
603
+ ),
604
+ (
605
+ "consecutive_failures",
606
+ models.PositiveIntegerField(
607
+ default=0, help_text="Current consecutive failure count"
608
+ ),
609
+ ),
610
+ (
611
+ "consecutive_successes",
612
+ models.PositiveIntegerField(
613
+ default=0, help_text="Current consecutive success count"
614
+ ),
615
+ ),
616
+ (
617
+ "last_check_at",
618
+ models.DateTimeField(
619
+ blank=True, help_text="When last health check was performed", null=True
620
+ ),
621
+ ),
622
+ (
623
+ "last_check_success",
624
+ models.BooleanField(
625
+ default=True, help_text="Whether last check was successful"
626
+ ),
627
+ ),
628
+ (
629
+ "last_response_time_ms",
630
+ models.PositiveIntegerField(
631
+ blank=True,
632
+ help_text="Response time of last check in milliseconds",
633
+ null=True,
634
+ ),
635
+ ),
636
+ (
637
+ "auto_enable_maintenance",
638
+ models.BooleanField(
639
+ default=True,
640
+ help_text="Automatically enable maintenance on failure threshold",
641
+ ),
642
+ ),
643
+ (
644
+ "auto_disable_maintenance",
645
+ models.BooleanField(
646
+ default=True,
647
+ help_text="Automatically disable maintenance on recovery threshold",
648
+ ),
649
+ ),
650
+ (
651
+ "maintenance_triggered_at",
652
+ models.DateTimeField(
653
+ blank=True, help_text="When maintenance was last auto-triggered", null=True
654
+ ),
655
+ ),
656
+ (
657
+ "user_agent",
658
+ models.CharField(
659
+ default="Django-CFG-Monitor/1.0",
660
+ help_text="User agent for health checks",
661
+ max_length=200,
662
+ ),
663
+ ),
664
+ (
665
+ "follow_redirects",
666
+ models.BooleanField(
667
+ default=True, help_text="Follow HTTP redirects during checks"
668
+ ),
669
+ ),
670
+ (
671
+ "verify_ssl",
672
+ models.BooleanField(default=True, help_text="Verify SSL certificates"),
673
+ ),
674
+ (
675
+ "custom_headers",
676
+ models.JSONField(
677
+ blank=True,
678
+ default=dict,
679
+ help_text="Custom headers for health check requests",
680
+ ),
681
+ ),
682
+ ("created_at", models.DateTimeField(auto_now_add=True)),
683
+ ("updated_at", models.DateTimeField(auto_now=True)),
684
+ (
685
+ "site",
686
+ models.OneToOneField(
687
+ help_text="Site being monitored",
688
+ on_delete=django.db.models.deletion.CASCADE,
689
+ related_name="monitoring_target",
690
+ to="django_cfg_maintenance.cloudflaresite",
691
+ ),
692
+ ),
693
+ ],
694
+ options={
695
+ "verbose_name": "Monitoring Target",
696
+ "verbose_name_plural": "Monitoring Targets",
697
+ },
698
+ ),
699
+ migrations.CreateModel(
700
+ name="HealthCheckResult",
701
+ fields=[
702
+ (
703
+ "id",
704
+ models.BigAutoField(
705
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
706
+ ),
707
+ ),
708
+ (
709
+ "timestamp",
710
+ models.DateTimeField(
711
+ default=django.utils.timezone.now, help_text="When the check was performed"
712
+ ),
713
+ ),
714
+ ("success", models.BooleanField(help_text="Whether the check was successful")),
715
+ (
716
+ "status_code",
717
+ models.PositiveIntegerField(
718
+ blank=True, help_text="HTTP response status code", null=True
719
+ ),
720
+ ),
721
+ (
722
+ "response_time_ms",
723
+ models.PositiveIntegerField(
724
+ blank=True, help_text="Response time in milliseconds", null=True
725
+ ),
726
+ ),
727
+ (
728
+ "response_size_bytes",
729
+ models.PositiveIntegerField(
730
+ blank=True, help_text="Response size in bytes", null=True
731
+ ),
732
+ ),
733
+ (
734
+ "error_message",
735
+ models.TextField(blank=True, help_text="Error message if check failed"),
736
+ ),
737
+ (
738
+ "error_type",
739
+ models.CharField(
740
+ blank=True,
741
+ help_text="Type of error (timeout, connection, etc.)",
742
+ max_length=100,
743
+ ),
744
+ ),
745
+ (
746
+ "details",
747
+ models.JSONField(
748
+ blank=True,
749
+ default=dict,
750
+ help_text="Additional check details (headers, content, etc.)",
751
+ ),
752
+ ),
753
+ (
754
+ "target",
755
+ models.ForeignKey(
756
+ help_text="Monitoring target this result belongs to",
757
+ on_delete=django.db.models.deletion.CASCADE,
758
+ related_name="results",
759
+ to="django_cfg_maintenance.monitoringtarget",
760
+ ),
761
+ ),
762
+ ],
763
+ options={
764
+ "verbose_name": "Health Check Result",
765
+ "verbose_name_plural": "Health Check Results",
766
+ "ordering": ["-timestamp"],
767
+ },
768
+ ),
769
+ migrations.CreateModel(
770
+ name="SiteGroup",
771
+ fields=[
772
+ (
773
+ "id",
774
+ models.BigAutoField(
775
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
776
+ ),
777
+ ),
778
+ ("name", models.CharField(help_text="Group name", max_length=100)),
779
+ ("description", models.TextField(blank=True, help_text="Group description")),
780
+ (
781
+ "auto_maintenance_rules",
782
+ models.JSONField(
783
+ blank=True,
784
+ default=dict,
785
+ help_text="Automatic maintenance rules for this group",
786
+ ),
787
+ ),
788
+ (
789
+ "notification_settings",
790
+ models.JSONField(
791
+ blank=True,
792
+ default=dict,
793
+ help_text="Notification preferences for group events",
794
+ ),
795
+ ),
796
+ ("created_at", models.DateTimeField(auto_now_add=True)),
797
+ ("updated_at", models.DateTimeField(auto_now=True)),
798
+ (
799
+ "owner",
800
+ models.ForeignKey(
801
+ help_text="Group owner",
802
+ on_delete=django.db.models.deletion.CASCADE,
803
+ to=settings.AUTH_USER_MODEL,
804
+ ),
805
+ ),
806
+ (
807
+ "sites",
808
+ models.ManyToManyField(
809
+ help_text="Sites in this group",
810
+ related_name="groups",
811
+ to="django_cfg_maintenance.cloudflaresite",
812
+ ),
813
+ ),
814
+ ],
815
+ options={
816
+ "verbose_name": "Site Group",
817
+ "verbose_name_plural": "Site Groups",
818
+ "ordering": ["name"],
819
+ },
820
+ ),
821
+ migrations.AddIndex(
822
+ model_name="cloudflaresite",
823
+ index=models.Index(
824
+ fields=["owner", "environment"], name="django_cfg__owner_i_5d4060_idx"
825
+ ),
826
+ ),
827
+ migrations.AddIndex(
828
+ model_name="cloudflaresite",
829
+ index=models.Index(
830
+ fields=["project", "environment"], name="django_cfg__project_9a07f1_idx"
831
+ ),
832
+ ),
833
+ migrations.AddIndex(
834
+ model_name="cloudflaresite",
835
+ index=models.Index(fields=["current_status"], name="django_cfg__current_fcca20_idx"),
836
+ ),
837
+ migrations.AddIndex(
838
+ model_name="cloudflaresite",
839
+ index=models.Index(
840
+ fields=["maintenance_active"], name="django_cfg__mainten_0c725d_idx"
841
+ ),
842
+ ),
843
+ migrations.AddIndex(
844
+ model_name="cloudflaresite",
845
+ index=models.Index(fields=["domain"], name="django_cfg__domain_179845_idx"),
846
+ ),
847
+ migrations.AddIndex(
848
+ model_name="maintenanceevent",
849
+ index=models.Index(
850
+ fields=["status", "-started_at"], name="django_cfg__status_a7d5f3_idx"
851
+ ),
852
+ ),
853
+ migrations.AddIndex(
854
+ model_name="maintenanceevent",
855
+ index=models.Index(
856
+ fields=["reason", "-started_at"], name="django_cfg__reason_214648_idx"
857
+ ),
858
+ ),
859
+ migrations.AddIndex(
860
+ model_name="maintenanceevent",
861
+ index=models.Index(
862
+ fields=["initiated_by", "-started_at"], name="django_cfg__initiat_7ad0ad_idx"
863
+ ),
864
+ ),
865
+ migrations.AddIndex(
866
+ model_name="cloudflaredeployment",
867
+ index=models.Index(
868
+ fields=["site", "-created_at"], name="django_cfg__site_id_7414e8_idx"
869
+ ),
870
+ ),
871
+ migrations.AddIndex(
872
+ model_name="cloudflaredeployment",
873
+ index=models.Index(
874
+ fields=["status", "-created_at"], name="django_cfg__status_ee1f80_idx"
875
+ ),
876
+ ),
877
+ migrations.AddIndex(
878
+ model_name="cloudflaredeployment",
879
+ index=models.Index(
880
+ fields=["deployment_type", "-created_at"], name="django_cfg__deploym_bc24ff_idx"
881
+ ),
882
+ ),
883
+ migrations.AddIndex(
884
+ model_name="cloudflaredeployment",
885
+ index=models.Index(fields=["resource_id"], name="django_cfg__resourc_fab8ca_idx"),
886
+ ),
887
+ migrations.AddIndex(
888
+ model_name="maintenancelog",
889
+ index=models.Index(
890
+ fields=["event", "-timestamp"], name="django_cfg__event_i_b03d81_idx"
891
+ ),
892
+ ),
893
+ migrations.AddIndex(
894
+ model_name="maintenancelog",
895
+ index=models.Index(fields=["level", "-timestamp"], name="django_cfg__level_b66e18_idx"),
896
+ ),
897
+ migrations.AddIndex(
898
+ model_name="maintenancelog",
899
+ index=models.Index(
900
+ fields=["component", "-timestamp"], name="django_cfg__compone_35dbe4_idx"
901
+ ),
902
+ ),
903
+ migrations.AddIndex(
904
+ model_name="monitoringtarget",
905
+ index=models.Index(
906
+ fields=["status", "last_check_at"], name="django_cfg__status_e57085_idx"
907
+ ),
908
+ ),
909
+ migrations.AddIndex(
910
+ model_name="monitoringtarget",
911
+ index=models.Index(
912
+ fields=["site", "-last_check_at"], name="django_cfg__site_id_37651c_idx"
913
+ ),
914
+ ),
915
+ migrations.AddIndex(
916
+ model_name="healthcheckresult",
917
+ index=models.Index(
918
+ fields=["target", "-timestamp"], name="django_cfg__target__1a3262_idx"
919
+ ),
920
+ ),
921
+ migrations.AddIndex(
922
+ model_name="healthcheckresult",
923
+ index=models.Index(
924
+ fields=["success", "-timestamp"], name="django_cfg__success_6181d8_idx"
925
+ ),
926
+ ),
927
+ migrations.AddIndex(
928
+ model_name="healthcheckresult",
929
+ index=models.Index(fields=["-timestamp"], name="django_cfg__timesta_ba6784_idx"),
930
+ ),
931
+ migrations.AddIndex(
932
+ model_name="sitegroup",
933
+ index=models.Index(fields=["owner", "name"], name="django_cfg__owner_i_6ba808_idx"),
934
+ ),
935
+ migrations.AlterUniqueTogether(
936
+ name="sitegroup",
937
+ unique_together={("name", "owner")},
938
+ ),
939
+ ]