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.
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 +4 -0
  38. django_cfg/core/config.py +4 -6
  39. django_cfg/modules/django_unfold/dashboard.py +4 -5
  40. {django_cfg-1.2.17.dist-info → django_cfg-1.2.19.dist-info}/METADATA +52 -1
  41. {django_cfg-1.2.17.dist-info → django_cfg-1.2.19.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.17.dist-info → django_cfg-1.2.19.dist-info}/WHEEL +0 -0
  80. {django_cfg-1.2.17.dist-info → django_cfg-1.2.19.dist-info}/entry_points.txt +0 -0
  81. {django_cfg-1.2.17.dist-info → django_cfg-1.2.19.dist-info}/licenses/LICENSE +0 -0
@@ -1,393 +0,0 @@
1
- """
2
- Monitoring models for external health checks.
3
-
4
- Models for tracking health checks and automatic maintenance triggers.
5
- Following CRITICAL_REQUIREMENTS - proper typing, no raw Dict usage.
6
- """
7
-
8
- from django.db import models
9
- from django.utils import timezone
10
- from django.core.exceptions import ValidationError
11
- from typing import Optional, Dict, Any
12
- from datetime import timedelta
13
- import json
14
-
15
-
16
- class MonitoringTarget(models.Model):
17
- """
18
- External monitoring target configuration.
19
-
20
- Defines what to monitor and how to trigger maintenance mode
21
- when health checks fail.
22
- """
23
-
24
- class Status(models.TextChoices):
25
- """Monitoring status choices."""
26
- ACTIVE = "active", "Active"
27
- PAUSED = "paused", "Paused"
28
- DISABLED = "disabled", "Disabled"
29
- ERROR = "error", "Error"
30
-
31
- # === Target Configuration ===
32
- site = models.OneToOneField(
33
- 'django_cfg_maintenance.CloudflareSite',
34
- on_delete=models.CASCADE,
35
- related_name='monitoring_target',
36
- help_text="Site being monitored"
37
- )
38
-
39
- # === Health Check Settings ===
40
- check_url = models.URLField(
41
- help_text="URL to check for health status"
42
- )
43
- check_interval = models.PositiveIntegerField(
44
- default=60,
45
- help_text="Check interval in seconds"
46
- )
47
- timeout = models.PositiveIntegerField(
48
- default=10,
49
- help_text="Request timeout in seconds"
50
- )
51
-
52
- # === Expected Response ===
53
- expected_status_codes = models.JSONField(
54
- default=list,
55
- help_text="Expected HTTP status codes (e.g., [200, 201])"
56
- )
57
- expected_response_time_ms = models.PositiveIntegerField(
58
- null=True,
59
- blank=True,
60
- help_text="Maximum expected response time in milliseconds"
61
- )
62
- expected_content = models.TextField(
63
- blank=True,
64
- help_text="Expected content in response body (substring match)"
65
- )
66
-
67
- # === Failure Detection ===
68
- failure_threshold = models.PositiveIntegerField(
69
- default=3,
70
- help_text="Consecutive failures before triggering maintenance"
71
- )
72
- recovery_threshold = models.PositiveIntegerField(
73
- default=2,
74
- help_text="Consecutive successes before disabling maintenance"
75
- )
76
-
77
- # === Current State ===
78
- status = models.CharField(
79
- max_length=20,
80
- choices=Status.choices,
81
- default=Status.ACTIVE,
82
- help_text="Current monitoring status"
83
- )
84
- consecutive_failures = models.PositiveIntegerField(
85
- default=0,
86
- help_text="Current consecutive failure count"
87
- )
88
- consecutive_successes = models.PositiveIntegerField(
89
- default=0,
90
- help_text="Current consecutive success count"
91
- )
92
-
93
- # === Last Check Results ===
94
- last_check_at = models.DateTimeField(
95
- null=True,
96
- blank=True,
97
- help_text="When last health check was performed"
98
- )
99
- last_check_success = models.BooleanField(
100
- default=True,
101
- help_text="Whether last check was successful"
102
- )
103
- last_response_time_ms = models.PositiveIntegerField(
104
- null=True,
105
- blank=True,
106
- help_text="Response time of last check in milliseconds"
107
- )
108
-
109
- # === Maintenance Triggers ===
110
- auto_enable_maintenance = models.BooleanField(
111
- default=True,
112
- help_text="Automatically enable maintenance on failure threshold"
113
- )
114
- auto_disable_maintenance = models.BooleanField(
115
- default=True,
116
- help_text="Automatically disable maintenance on recovery threshold"
117
- )
118
- maintenance_triggered_at = models.DateTimeField(
119
- null=True,
120
- blank=True,
121
- help_text="When maintenance was last auto-triggered"
122
- )
123
-
124
- # === Advanced Settings ===
125
- user_agent = models.CharField(
126
- max_length=200,
127
- default="Django-CFG-Monitor/1.0",
128
- help_text="User agent for health checks"
129
- )
130
- follow_redirects = models.BooleanField(
131
- default=True,
132
- help_text="Follow HTTP redirects during checks"
133
- )
134
- verify_ssl = models.BooleanField(
135
- default=True,
136
- help_text="Verify SSL certificates"
137
- )
138
- custom_headers = models.JSONField(
139
- default=dict,
140
- blank=True,
141
- help_text="Custom headers for health check requests"
142
- )
143
-
144
- # === Metadata ===
145
- created_at = models.DateTimeField(auto_now_add=True)
146
- updated_at = models.DateTimeField(auto_now=True)
147
-
148
- # === Custom Manager ===
149
- from ..managers.monitoring import MonitoringTargetManager
150
- objects = MonitoringTargetManager()
151
-
152
- class Meta:
153
- verbose_name = "Monitoring Target"
154
- verbose_name_plural = "Monitoring Targets"
155
- indexes = [
156
- models.Index(fields=['status', 'last_check_at']),
157
- models.Index(fields=['site', '-last_check_at']),
158
- ]
159
-
160
- def __str__(self) -> str:
161
- return f"Monitor: {self.site.name} ({self.get_status_display()})"
162
-
163
- @property
164
- def is_healthy(self) -> bool:
165
- """Check if target is currently healthy."""
166
- return self.last_check_success and self.consecutive_failures == 0
167
-
168
- @property
169
- def should_trigger_maintenance(self) -> bool:
170
- """Check if maintenance should be triggered."""
171
- return (
172
- self.auto_enable_maintenance and
173
- self.consecutive_failures >= self.failure_threshold and
174
- not self.site.maintenance_active
175
- )
176
-
177
- @property
178
- def should_disable_maintenance(self) -> bool:
179
- """Check if maintenance should be disabled."""
180
- return (
181
- self.auto_disable_maintenance and
182
- self.consecutive_successes >= self.recovery_threshold and
183
- self.site.maintenance_active
184
- )
185
-
186
- @property
187
- def next_check_at(self) -> Optional[timezone.datetime]:
188
- """Calculate when next check should occur."""
189
- if self.last_check_at:
190
- return self.last_check_at + timedelta(seconds=self.check_interval)
191
- return timezone.now()
192
-
193
- def record_success(self, response_time_ms: int) -> None:
194
- """Record successful health check."""
195
- self.last_check_at = timezone.now()
196
- self.last_check_success = True
197
- self.last_response_time_ms = response_time_ms
198
- self.consecutive_failures = 0
199
- self.consecutive_successes += 1
200
-
201
- self.save(update_fields=[
202
- 'last_check_at',
203
- 'last_check_success',
204
- 'last_response_time_ms',
205
- 'consecutive_failures',
206
- 'consecutive_successes',
207
- 'updated_at'
208
- ])
209
-
210
- def record_failure(self, error_message: str = "") -> None:
211
- """Record failed health check."""
212
- self.last_check_at = timezone.now()
213
- self.last_check_success = False
214
- self.consecutive_successes = 0
215
- self.consecutive_failures += 1
216
-
217
- self.save(update_fields=[
218
- 'last_check_at',
219
- 'last_check_success',
220
- 'consecutive_successes',
221
- 'consecutive_failures',
222
- 'updated_at'
223
- ])
224
-
225
- # Create health check result record
226
- HealthCheckResult.objects.create(
227
- target=self,
228
- success=False,
229
- error_message=error_message
230
- )
231
-
232
- def trigger_maintenance(self) -> bool:
233
- """Trigger maintenance mode for this target."""
234
- if self.should_trigger_maintenance:
235
- self.site.enable_maintenance()
236
- self.maintenance_triggered_at = timezone.now()
237
- self.save(update_fields=['maintenance_triggered_at', 'updated_at'])
238
- return True
239
- return False
240
-
241
- def disable_maintenance_if_recovered(self) -> bool:
242
- """Disable maintenance if recovery threshold is met."""
243
- if self.should_disable_maintenance:
244
- self.site.disable_maintenance()
245
- return True
246
- return False
247
-
248
- def clean(self) -> None:
249
- """Validate model data."""
250
- super().clean()
251
-
252
- # Validate thresholds
253
- if self.failure_threshold < 1:
254
- raise ValidationError({'failure_threshold': 'Must be at least 1'})
255
-
256
- if self.recovery_threshold < 1:
257
- raise ValidationError({'recovery_threshold': 'Must be at least 1'})
258
-
259
- # Validate intervals
260
- if self.check_interval < 10:
261
- raise ValidationError({'check_interval': 'Must be at least 10 seconds'})
262
-
263
- if self.timeout >= self.check_interval:
264
- raise ValidationError({'timeout': 'Timeout must be less than check interval'})
265
-
266
- # Validate expected status codes
267
- if self.expected_status_codes:
268
- for code in self.expected_status_codes:
269
- if not isinstance(code, int) or code < 100 or code > 599:
270
- raise ValidationError({
271
- 'expected_status_codes': 'Must contain valid HTTP status codes (100-599)'
272
- })
273
-
274
-
275
- class HealthCheckResult(models.Model):
276
- """
277
- Individual health check result record.
278
-
279
- Stores detailed results of each health check for analysis and debugging.
280
- """
281
-
282
- # === Relationships ===
283
- target = models.ForeignKey(
284
- MonitoringTarget,
285
- on_delete=models.CASCADE,
286
- related_name='results',
287
- help_text="Monitoring target this result belongs to"
288
- )
289
-
290
- # === Check Results ===
291
- timestamp = models.DateTimeField(
292
- default=timezone.now,
293
- help_text="When the check was performed"
294
- )
295
- success = models.BooleanField(
296
- help_text="Whether the check was successful"
297
- )
298
-
299
- # === Response Details ===
300
- status_code = models.PositiveIntegerField(
301
- null=True,
302
- blank=True,
303
- help_text="HTTP response status code"
304
- )
305
- response_time_ms = models.PositiveIntegerField(
306
- null=True,
307
- blank=True,
308
- help_text="Response time in milliseconds"
309
- )
310
- response_size_bytes = models.PositiveIntegerField(
311
- null=True,
312
- blank=True,
313
- help_text="Response size in bytes"
314
- )
315
-
316
- # === Error Information ===
317
- error_message = models.TextField(
318
- blank=True,
319
- help_text="Error message if check failed"
320
- )
321
- error_type = models.CharField(
322
- max_length=100,
323
- blank=True,
324
- help_text="Type of error (timeout, connection, etc.)"
325
- )
326
-
327
- # === Additional Data ===
328
- details = models.JSONField(
329
- default=dict,
330
- blank=True,
331
- help_text="Additional check details (headers, content, etc.)"
332
- )
333
-
334
- class Meta:
335
- ordering = ['-timestamp']
336
- verbose_name = "Health Check Result"
337
- verbose_name_plural = "Health Check Results"
338
- indexes = [
339
- models.Index(fields=['target', '-timestamp']),
340
- models.Index(fields=['success', '-timestamp']),
341
- models.Index(fields=['-timestamp']),
342
- ]
343
-
344
- def __str__(self) -> str:
345
- status = "✅ Success" if self.success else "❌ Failed"
346
- return f"{status}: {self.target.site.name} at {self.timestamp}"
347
-
348
- @property
349
- def is_slow_response(self) -> bool:
350
- """Check if response was slower than expected."""
351
- if not self.response_time_ms or not self.target.expected_response_time_ms:
352
- return False
353
- return self.response_time_ms > self.target.expected_response_time_ms
354
-
355
- @property
356
- def duration_seconds(self) -> Optional[float]:
357
- """Get response time in seconds."""
358
- if self.response_time_ms is not None:
359
- return self.response_time_ms / 1000.0
360
- return None
361
-
362
- def get_error_summary(self) -> str:
363
- """Get concise error summary."""
364
- if self.success:
365
- return "Success"
366
-
367
- if self.error_type and self.error_message:
368
- return f"{self.error_type}: {self.error_message[:100]}"
369
- elif self.error_message:
370
- return self.error_message[:100]
371
- elif self.status_code:
372
- return f"HTTP {self.status_code}"
373
- else:
374
- return "Unknown error"
375
-
376
- @classmethod
377
- def create_success(cls, target: MonitoringTarget, **kwargs) -> 'HealthCheckResult':
378
- """Create successful health check result."""
379
- return cls.objects.create(
380
- target=target,
381
- success=True,
382
- **kwargs
383
- )
384
-
385
- @classmethod
386
- def create_failure(cls, target: MonitoringTarget, error_message: str, **kwargs) -> 'HealthCheckResult':
387
- """Create failed health check result."""
388
- return cls.objects.create(
389
- target=target,
390
- success=False,
391
- error_message=error_message,
392
- **kwargs
393
- )