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,338 +0,0 @@
1
- """
2
- Site management views.
3
-
4
- ViewSets for CloudflareSite and SiteGroup management.
5
- """
6
-
7
- import logging
8
- from datetime import datetime
9
-
10
- from django.shortcuts import get_object_or_404
11
- from rest_framework import viewsets, status
12
- from rest_framework.decorators import action
13
- from drf_spectacular.utils import (
14
- extend_schema, extend_schema_view, OpenApiResponse, OpenApiParameter, OpenApiTypes
15
- )
16
-
17
- from ..models import CloudflareSite, SiteGroup
18
- from ..serializers import (
19
- CloudflareSiteSerializer, CloudflareSiteCreateSerializer, CloudflareSiteListSerializer,
20
- SiteGroupSerializer, SiteGroupCreateSerializer,
21
- BulkMaintenanceActionSerializer, APIResponseSerializer
22
- )
23
- from ..services import MaintenanceManager
24
- from .base import MaintenancePermissionMixin, MaintenanceResponseMixin
25
-
26
- logger = logging.getLogger(__name__)
27
-
28
-
29
- @extend_schema_view(
30
- list=extend_schema(
31
- summary="List Cloudflare sites",
32
- description="Get list of Cloudflare sites with filtering options",
33
- parameters=[
34
- OpenApiParameter('environment', OpenApiTypes.STR, description='Filter by environment'),
35
- OpenApiParameter('project', OpenApiTypes.STR, description='Filter by project'),
36
- OpenApiParameter('status', OpenApiTypes.STR, description='Filter by status'),
37
- OpenApiParameter('maintenance_active', OpenApiTypes.BOOL, description='Filter by maintenance status'),
38
- OpenApiParameter('search', OpenApiTypes.STR, description='Search in name and domain'),
39
- ]
40
- ),
41
- create=extend_schema(
42
- summary="Create Cloudflare site",
43
- description="Create a new Cloudflare site configuration"
44
- ),
45
- retrieve=extend_schema(
46
- summary="Get Cloudflare site",
47
- description="Get detailed information about a specific Cloudflare site"
48
- ),
49
- update=extend_schema(
50
- summary="Update Cloudflare site",
51
- description="Update Cloudflare site configuration"
52
- ),
53
- destroy=extend_schema(
54
- summary="Delete Cloudflare site",
55
- description="Delete a Cloudflare site configuration"
56
- )
57
- )
58
- class CloudflareSiteViewSet(MaintenancePermissionMixin, MaintenanceResponseMixin, viewsets.ModelViewSet):
59
- """ViewSet for managing Cloudflare sites."""
60
-
61
- serializer_class = CloudflareSiteSerializer
62
- lookup_field = 'id'
63
- filterset_fields = ['environment', 'project', 'current_status', 'maintenance_active']
64
- search_fields = ['name', 'domain', 'project']
65
- ordering_fields = ['name', 'domain', 'created_at', 'last_maintenance_at']
66
- ordering = ['-created_at']
67
-
68
- def get_queryset(self):
69
- """Get queryset filtered by user permissions."""
70
- return self.get_user_queryset(CloudflareSite)
71
-
72
- def get_serializer_class(self):
73
- """Return appropriate serializer based on action."""
74
- if self.action == 'create':
75
- return CloudflareSiteCreateSerializer
76
- elif self.action == 'list':
77
- return CloudflareSiteListSerializer
78
- elif self.action == 'bulk_action':
79
- return BulkMaintenanceActionSerializer
80
- return CloudflareSiteSerializer
81
-
82
- def perform_create(self, serializer):
83
- """Set owner when creating site."""
84
- serializer.save(owner=self.request.user)
85
-
86
- @action(detail=True, methods=['post'])
87
- @extend_schema(
88
- summary="Enable maintenance mode",
89
- description="Enable maintenance mode for this site",
90
- request=None,
91
- responses={
92
- 200: OpenApiResponse(response=APIResponseSerializer, description="Maintenance enabled successfully"),
93
- 400: OpenApiResponse(response=APIResponseSerializer, description="Bad request"),
94
- 500: OpenApiResponse(response=APIResponseSerializer, description="Internal server error")
95
- }
96
- )
97
- def enable_maintenance(self, request, id=None):
98
- """Enable maintenance mode for a site."""
99
- try:
100
- site = self.get_object()
101
- reason = request.data.get('reason', 'Manual maintenance')
102
- message = request.data.get('message', 'Site is under maintenance')
103
-
104
- # Use multi-site manager for consistency
105
- manager = MaintenanceManager(self.request.user)
106
- success = manager._enable_site_maintenance(site, reason, message)
107
-
108
- if success:
109
- return self.success_response(f'Maintenance enabled for {site.domain}')
110
- else:
111
- return self.error_response('Failed to enable maintenance mode')
112
-
113
- except Exception as e:
114
- return self.error_response(f"Enable maintenance error for site {id}: {e}")
115
-
116
- @action(detail=True, methods=['post'])
117
- @extend_schema(
118
- summary="Disable maintenance mode",
119
- description="Disable maintenance mode for this site",
120
- responses={
121
- 200: OpenApiResponse(response=APIResponseSerializer, description="Maintenance disabled successfully"),
122
- 500: OpenApiResponse(response=APIResponseSerializer, description="Internal server error")
123
- }
124
- )
125
- def disable_maintenance(self, request, id=None):
126
- """Disable maintenance mode for a site."""
127
- try:
128
- site = self.get_object()
129
-
130
- # Use multi-site manager for consistency
131
- manager = MaintenanceManager(self.request.user)
132
- success = manager._disable_site_maintenance(site)
133
-
134
- if success:
135
- return self.success_response(f'Maintenance disabled for {site.domain}')
136
- else:
137
- return self.error_response('Failed to disable maintenance mode')
138
-
139
- except Exception as e:
140
- return self.error_response(f"Disable maintenance error for site {id}: {e}")
141
-
142
- @action(detail=True, methods=['get'])
143
- @extend_schema(
144
- summary="Check site status",
145
- description="Check current status of the site",
146
- responses={
147
- 200: OpenApiResponse(response=APIResponseSerializer, description="Status checked successfully")
148
- }
149
- )
150
- def check_status(self, request, id=None):
151
- """Check current status of a site."""
152
- try:
153
- site = self.get_object()
154
-
155
- # Use multi-site manager for consistency
156
- manager = MaintenanceManager(self.request.user)
157
- site_status = manager._check_site_status(site)
158
-
159
- return self.success_response(
160
- 'Status checked successfully',
161
- data={
162
- 'site_id': site.id,
163
- 'domain': site.domain,
164
- 'status': site_status,
165
- 'maintenance_active': site.maintenance_active,
166
- 'last_check': datetime.now().isoformat()
167
- }
168
- )
169
-
170
- except Exception as e:
171
- return self.error_response(f"Status check error for site {id}: {e}")
172
-
173
- @action(detail=False, methods=['post'])
174
- @extend_schema(
175
- summary="Bulk maintenance actions",
176
- description="Perform bulk maintenance actions on multiple sites",
177
- request=BulkMaintenanceActionSerializer,
178
- responses={
179
- 200: OpenApiResponse(response=APIResponseSerializer, description="Bulk action completed"),
180
- 400: OpenApiResponse(response=APIResponseSerializer, description="Invalid request data")
181
- }
182
- )
183
- def bulk_action(self, request):
184
- """Perform bulk maintenance actions."""
185
- serializer = BulkMaintenanceActionSerializer(data=request.data)
186
- if not serializer.is_valid():
187
- return self.validation_error_response(serializer.errors)
188
-
189
- try:
190
- action_type = serializer.validated_data['action']
191
- site_ids = serializer.validated_data['site_ids']
192
- dry_run = serializer.validated_data.get('dry_run', False)
193
-
194
- # Get sites (filtered by user permissions)
195
- sites = self.get_queryset().filter(id__in=site_ids)
196
-
197
- if dry_run:
198
- return self.success_response(
199
- f'Dry run: would {action_type} maintenance for {sites.count()} sites',
200
- data={
201
- 'dry_run': True,
202
- 'would_affect': [site.domain for site in sites]
203
- }
204
- )
205
-
206
- # Use multi-site manager for bulk operations
207
- manager = MaintenanceManager(self.request.user).sites(request.user).filter(id__in=site_ids)
208
-
209
- if action_type == 'enable':
210
- reason = serializer.validated_data.get('reason', 'Bulk maintenance')
211
- result = manager.enable_maintenance(
212
- reason=reason,
213
- user=request.user,
214
- message=serializer.validated_data.get('maintenance_message')
215
- )
216
- elif action_type == 'disable':
217
- result = manager.disable_maintenance(user=request.user)
218
- elif action_type == 'status_check':
219
- result = manager.check_status()
220
- else:
221
- return self.error_response(f'Unknown action: {action_type}', status.HTTP_400_BAD_REQUEST)
222
-
223
- return self.success_response(f'Bulk {action_type} completed', data=result)
224
-
225
- except Exception as e:
226
- return self.error_response(f"Bulk action error: {e}")
227
-
228
-
229
- @extend_schema_view(
230
- list=extend_schema(
231
- summary="List site groups",
232
- description="Get list of site groups"
233
- ),
234
- create=extend_schema(
235
- summary="Create site group",
236
- description="Create a new site group"
237
- )
238
- )
239
- class SiteGroupViewSet(MaintenancePermissionMixin, MaintenanceResponseMixin, viewsets.ModelViewSet):
240
- """ViewSet for managing site groups."""
241
-
242
- serializer_class = SiteGroupSerializer
243
- lookup_field = 'id'
244
-
245
- def get_queryset(self):
246
- """Get queryset filtered by user permissions."""
247
- return self.get_user_queryset(SiteGroup)
248
-
249
- def get_serializer_class(self):
250
- """Return appropriate serializer based on action."""
251
- if self.action == 'create':
252
- return SiteGroupCreateSerializer
253
- return SiteGroupSerializer
254
-
255
- def perform_create(self, serializer):
256
- """Set owner when creating group."""
257
- serializer.save(owner=self.request.user)
258
-
259
- @action(detail=True, methods=['post'])
260
- @extend_schema(
261
- summary="Add sites to group",
262
- description="Add sites to this group"
263
- )
264
- def add_sites(self, request, id=None):
265
- """Add sites to group."""
266
- try:
267
- group = self.get_object()
268
- site_ids = request.data.get('site_ids', [])
269
-
270
- # Get sites (filtered by user permissions)
271
- sites = self.get_user_queryset(CloudflareSite).filter(id__in=site_ids)
272
- group.sites.add(*sites)
273
-
274
- return self.success_response(f'Added {sites.count()} sites to group {group.name}')
275
-
276
- except Exception as e:
277
- return self.error_response(f"Add sites to group error: {e}")
278
-
279
- @action(detail=True, methods=['post'])
280
- @extend_schema(
281
- summary="Remove sites from group",
282
- description="Remove sites from this group"
283
- )
284
- def remove_sites(self, request, id=None):
285
- """Remove sites from group."""
286
- try:
287
- group = self.get_object()
288
- site_ids = request.data.get('site_ids', [])
289
-
290
- # Get sites (filtered by user permissions)
291
- sites = self.get_user_queryset(CloudflareSite).filter(id__in=site_ids)
292
- group.sites.remove(*sites)
293
-
294
- return self.success_response(f'Removed {sites.count()} sites from group {group.name}')
295
-
296
- except Exception as e:
297
- return self.error_response(f"Remove sites from group error: {e}")
298
-
299
- @action(detail=True, methods=['post'])
300
- @extend_schema(
301
- summary="Enable maintenance for group",
302
- description="Enable maintenance mode for all sites in this group"
303
- )
304
- def enable_maintenance(self, request, id=None):
305
- """Enable maintenance for all sites in group."""
306
- try:
307
- group = self.get_object()
308
- reason = request.data.get('reason', f'Group maintenance: {group.name}')
309
-
310
- result = group.enable_maintenance_for_all(request.user, reason)
311
-
312
- return self.success_response(
313
- f'Group maintenance enabled for {group.name}',
314
- data=result
315
- )
316
-
317
- except Exception as e:
318
- return self.error_response(f"Group maintenance enable error: {e}")
319
-
320
- @action(detail=True, methods=['post'])
321
- @extend_schema(
322
- summary="Disable maintenance for group",
323
- description="Disable maintenance mode for all sites in this group"
324
- )
325
- def disable_maintenance(self, request, id=None):
326
- """Disable maintenance for all sites in group."""
327
- try:
328
- group = self.get_object()
329
-
330
- result = group.disable_maintenance_for_all()
331
-
332
- return self.success_response(
333
- f'Group maintenance disabled for {group.name}',
334
- data=result
335
- )
336
-
337
- except Exception as e:
338
- return self.error_response(f"Group maintenance disable error: {e}")
@@ -1,316 +0,0 @@
1
- """
2
- Cloudflare configuration models for django_cfg.
3
-
4
- Type-safe Cloudflare maintenance mode configuration following CRITICAL_REQUIREMENTS.
5
- No raw Dict/Any usage - everything through Pydantic v2 models.
6
- """
7
-
8
- from typing import Annotated, Optional, List, Dict, Any
9
- from pydantic import BaseModel, Field, SecretStr, field_validator, HttpUrl
10
- from enum import Enum
11
- from datetime import timedelta
12
-
13
-
14
- class MaintenanceTemplate(str, Enum):
15
- """Available maintenance page templates."""
16
- BASIC = "basic"
17
- MODERN = "modern"
18
- CUSTOM = "custom"
19
-
20
-
21
- class CloudflareConfig(BaseModel):
22
- """
23
- Zero-configuration Cloudflare maintenance mode setup.
24
-
25
- Following KISS principle - user provides only api_token and domain,
26
- everything else is auto-discovered and configured.
27
- """
28
-
29
- model_config = {
30
- "env_prefix": "CLOUDFLARE_",
31
- "case_sensitive": False,
32
- "validate_assignment": True,
33
- "extra": "forbid",
34
- "str_strip_whitespace": True,
35
- }
36
-
37
- # === Required Configuration (Zero-config approach) ===
38
- api_token: SecretStr = Field(
39
- description="Cloudflare API token with Zone:Edit permissions"
40
- )
41
- domain: Annotated[str, Field(
42
- min_length=3,
43
- max_length=253,
44
- pattern=r"^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$",
45
- description="Domain name (auto-discovers Zone ID)"
46
- )]
47
-
48
- # === Auto-discovered Fields (set by system) ===
49
- zone_id: Optional[str] = Field(
50
- default=None,
51
- description="Auto-discovered Cloudflare Zone ID"
52
- )
53
- account_id: Optional[str] = Field(
54
- default=None,
55
- description="Auto-discovered Cloudflare Account ID"
56
- )
57
-
58
- # === Optional Customization ===
59
- enabled: bool = Field(
60
- default=True,
61
- description="Enable Cloudflare maintenance mode integration"
62
- )
63
-
64
- template: MaintenanceTemplate = Field(
65
- default=MaintenanceTemplate.MODERN,
66
- description="Maintenance page template"
67
- )
68
-
69
- maintenance_title: str = Field(
70
- default="Site Under Maintenance",
71
- max_length=200,
72
- description="Title for maintenance page"
73
- )
74
-
75
- maintenance_message: str = Field(
76
- default="We're performing scheduled maintenance. Please try again shortly.",
77
- max_length=1000,
78
- description="Message for maintenance page"
79
- )
80
-
81
- # === Auto-configuration Flags ===
82
- auto_ssl: bool = Field(
83
- default=True,
84
- description="Automatically configure SSL/TLS settings"
85
- )
86
- auto_dns: bool = Field(
87
- default=True,
88
- description="Create missing DNS records automatically"
89
- )
90
- auto_monitoring: bool = Field(
91
- default=True,
92
- description="Enable external monitoring"
93
- )
94
-
95
- # === Advanced Settings (Smart Defaults) ===
96
- worker_name: str = Field(
97
- default="maintenance-mode",
98
- max_length=100,
99
- pattern=r"^[a-zA-Z0-9\-_]+$",
100
- description="Cloudflare Worker name"
101
- )
102
-
103
- monitoring_interval: Annotated[int, Field(
104
- ge=30,
105
- le=3600,
106
- description="Health check interval in seconds"
107
- )] = 60
108
-
109
- failure_threshold: Annotated[int, Field(
110
- ge=1,
111
- le=10,
112
- description="Consecutive failures before enabling maintenance"
113
- )] = 3
114
-
115
- recovery_threshold: Annotated[int, Field(
116
- ge=1,
117
- le=10,
118
- description="Consecutive successes before disabling maintenance"
119
- )] = 2
120
-
121
- # === Multi-site Support ===
122
- multi_site_enabled: bool = Field(
123
- default=False,
124
- description="Enable multi-site management features"
125
- )
126
-
127
- @field_validator('domain')
128
- @classmethod
129
- def validate_domain(cls, v: str) -> str:
130
- """Validate and normalize domain name."""
131
- domain = v.lower().strip()
132
-
133
- # Remove protocol if present
134
- if domain.startswith(('http://', 'https://')):
135
- raise ValueError('Domain should not include protocol (http:// or https://)')
136
-
137
- # Remove www prefix for consistency
138
- if domain.startswith('www.'):
139
- domain = domain[4:]
140
-
141
- # Basic domain validation
142
- if not domain or '.' not in domain:
143
- raise ValueError('Domain must be a valid domain name')
144
-
145
- return domain
146
-
147
- def get_api_token(self) -> str:
148
- """Get decrypted API token."""
149
- return self.api_token.get_secret_value()
150
-
151
- def is_configured(self) -> bool:
152
- """Check if Cloudflare is fully configured."""
153
- return bool(self.zone_id and self.account_id)
154
-
155
-
156
- class MultiSiteConfig(BaseModel):
157
- """
158
- Multi-site management configuration.
159
-
160
- Extends CloudflareConfig for managing multiple sites.
161
- """
162
-
163
- model_config = {
164
- "validate_assignment": True,
165
- "extra": "forbid"
166
- }
167
-
168
- # === Site Discovery ===
169
- auto_discover_sites: bool = Field(
170
- default=True,
171
- description="Automatically discover all sites in Cloudflare account"
172
- )
173
-
174
- site_filters: List[str] = Field(
175
- default_factory=list,
176
- description="Domain patterns to include (e.g., ['*.example.com', 'api.*.com'])"
177
- )
178
-
179
- excluded_domains: List[str] = Field(
180
- default_factory=list,
181
- description="Domains to exclude from management"
182
- )
183
-
184
- # === Default Site Settings ===
185
- default_environment: str = Field(
186
- default="production",
187
- pattern=r"^(production|staging|development|testing)$",
188
- description="Default environment for discovered sites"
189
- )
190
-
191
- default_project: str = Field(
192
- default="",
193
- max_length=100,
194
- description="Default project name for discovered sites"
195
- )
196
-
197
- default_tags: List[str] = Field(
198
- default_factory=list,
199
- description="Default tags for discovered sites"
200
- )
201
-
202
- # === Bulk Operations ===
203
- max_concurrent_operations: Annotated[int, Field(
204
- ge=1,
205
- le=50,
206
- description="Maximum concurrent Cloudflare API operations"
207
- )] = 10
208
-
209
- operation_timeout: Annotated[int, Field(
210
- ge=5,
211
- le=300,
212
- description="Timeout for individual operations in seconds"
213
- )] = 30
214
-
215
- # === Notifications ===
216
- notification_channels: List[str] = Field(
217
- default_factory=lambda: ["email"],
218
- description="Notification channels for maintenance events"
219
- )
220
-
221
- webhook_url: Optional[HttpUrl] = Field(
222
- default=None,
223
- description="Webhook URL for maintenance notifications"
224
- )
225
-
226
-
227
- class MonitoringConfig(BaseModel):
228
- """
229
- External monitoring configuration.
230
-
231
- Configures health checks and automatic maintenance triggers.
232
- """
233
-
234
- model_config = {
235
- "validate_assignment": True,
236
- "extra": "forbid"
237
- }
238
-
239
- # === Monitoring Settings ===
240
- enabled: bool = Field(
241
- default=True,
242
- description="Enable external monitoring"
243
- )
244
-
245
- check_interval: Annotated[int, Field(
246
- ge=10,
247
- le=3600,
248
- description="Health check interval in seconds"
249
- )] = 60
250
-
251
- timeout: Annotated[int, Field(
252
- ge=1,
253
- le=300,
254
- description="Health check timeout in seconds"
255
- )] = 10
256
-
257
- # === Health Check Configuration ===
258
- health_check_path: str = Field(
259
- default="/health/",
260
- description="Health check endpoint path"
261
- )
262
-
263
- expected_status_codes: List[int] = Field(
264
- default_factory=lambda: [200, 201, 204],
265
- description="Expected HTTP status codes for healthy response"
266
- )
267
-
268
- expected_response_time_ms: Optional[int] = Field(
269
- default=5000,
270
- ge=100,
271
- le=60000,
272
- description="Maximum expected response time in milliseconds"
273
- )
274
-
275
- # === Failure Detection ===
276
- failure_threshold: Annotated[int, Field(
277
- ge=1,
278
- le=20,
279
- description="Consecutive failures before triggering maintenance"
280
- )] = 3
281
-
282
- recovery_threshold: Annotated[int, Field(
283
- ge=1,
284
- le=20,
285
- description="Consecutive successes before disabling maintenance"
286
- )] = 2
287
-
288
- # === Advanced Settings ===
289
- user_agent: str = Field(
290
- default="Django-CFG-Monitor/1.0",
291
- description="User agent for health checks"
292
- )
293
-
294
- follow_redirects: bool = Field(
295
- default=True,
296
- description="Follow HTTP redirects during health checks"
297
- )
298
-
299
- verify_ssl: bool = Field(
300
- default=True,
301
- description="Verify SSL certificates during health checks"
302
- )
303
-
304
- custom_headers: Dict[str, str] = Field(
305
- default_factory=dict,
306
- description="Custom headers for health check requests"
307
- )
308
-
309
-
310
- # Export all models
311
- __all__ = [
312
- "CloudflareConfig",
313
- "MultiSiteConfig",
314
- "MonitoringConfig",
315
- "MaintenanceTemplate",
316
- ]
File without changes