django-cfg 1.2.17__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.17.dist-info → django_cfg-1.2.18.dist-info}/METADATA +52 -1
  41. {django_cfg-1.2.17.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.17.dist-info → django_cfg-1.2.18.dist-info}/WHEEL +0 -0
  80. {django_cfg-1.2.17.dist-info → django_cfg-1.2.18.dist-info}/entry_points.txt +0 -0
  81. {django_cfg-1.2.17.dist-info → django_cfg-1.2.18.dist-info}/licenses/LICENSE +0 -0
@@ -1,330 +0,0 @@
1
- """
2
- Cloudflare synchronization command service.
3
-
4
- Handles the business logic for synchronizing sites with Cloudflare zones.
5
- Extracted from management command for better testability and reusability.
6
- """
7
-
8
- import logging
9
- from typing import Dict, List, Any, Optional
10
- from django.db import transaction
11
- from django.utils import timezone
12
- from django.contrib.auth import get_user_model
13
-
14
- from .site_sync import SiteSyncService
15
- from ..models import CloudflareSite
16
- from django_cfg.models.cloudflare import CloudflareConfig
17
-
18
- User = get_user_model()
19
- logger = logging.getLogger(__name__)
20
-
21
-
22
- class SyncCommandService:
23
- """
24
- Service for handling Cloudflare synchronization command logic.
25
-
26
- Provides clean separation between command interface and business logic.
27
- """
28
-
29
- def __init__(self, config: CloudflareConfig):
30
- """Initialize sync command service."""
31
- self.config = config
32
- self.sync_service = SiteSyncService(config)
33
-
34
- def sync_user_sites_command(
35
- self,
36
- user: User,
37
- dry_run: bool = False,
38
- force_update: bool = False,
39
- environment: str = 'production',
40
- project: str = '',
41
- tags: List[str] = None,
42
- verbose: bool = False
43
- ) -> Dict[str, Any]:
44
- """
45
- Execute site synchronization with command-specific logic.
46
-
47
- Args:
48
- user: User to sync sites for
49
- dry_run: If True, only show what would be changed
50
- force_update: If True, update existing sites
51
- environment: Default environment for new sites
52
- project: Default project for new sites
53
- tags: Default tags for new sites
54
- verbose: Enable verbose logging
55
-
56
- Returns:
57
- Dict with detailed sync results
58
- """
59
- logger.info(f"Starting command sync for user {user.username}")
60
-
61
- if verbose:
62
- logging.getLogger().setLevel(logging.DEBUG)
63
-
64
- try:
65
- # Use the core sync service
66
- core_stats = self.sync_service.sync_user_sites(
67
- user=user,
68
- dry_run=dry_run,
69
- force_update=force_update,
70
- environment=environment,
71
- project=project,
72
- tags=tags or []
73
- )
74
-
75
- # Transform results for command output
76
- command_stats = self._transform_stats_for_command(core_stats)
77
-
78
- logger.info(f"Command sync completed: {command_stats}")
79
- return {
80
- 'success': True,
81
- 'stats': command_stats,
82
- 'core_stats': core_stats,
83
- 'dry_run': dry_run
84
- }
85
-
86
- except Exception as e:
87
- logger.error(f"Command sync failed: {e}")
88
- return {
89
- 'success': False,
90
- 'error': str(e),
91
- 'stats': {'created': 0, 'updated': 0, 'skipped': 0, 'errors': 1}
92
- }
93
-
94
- def get_user_by_identifier(self, user_identifier: str) -> User:
95
- """
96
- Get user by username or email.
97
-
98
- Args:
99
- user_identifier: Username or email address
100
-
101
- Returns:
102
- User instance
103
-
104
- Raises:
105
- User.DoesNotExist: If user not found
106
- """
107
- try:
108
- # Try by email first
109
- if '@' in user_identifier:
110
- return User.objects.get(email=user_identifier)
111
- else:
112
- return User.objects.get(username=user_identifier)
113
- except User.DoesNotExist:
114
- raise User.DoesNotExist(f"User not found: {user_identifier}")
115
-
116
- def get_api_token_for_user(self, user: User, provided_token: Optional[str] = None) -> str:
117
- """
118
- Get Cloudflare API token for user.
119
-
120
- Args:
121
- user: User to get token for
122
- provided_token: Explicitly provided token
123
-
124
- Returns:
125
- API token string
126
-
127
- Raises:
128
- ValueError: If no token available
129
- """
130
- if provided_token:
131
- return provided_token
132
-
133
- # Try to get from user's existing sites
134
- existing_site = CloudflareSite.objects.filter(owner=user).first()
135
- if existing_site and existing_site.api_token:
136
- return existing_site.api_token
137
-
138
- raise ValueError(
139
- "No API token available. Provide --api-token or ensure user has existing sites with tokens."
140
- )
141
-
142
- def validate_sync_parameters(
143
- self,
144
- user_identifier: str,
145
- api_token: Optional[str] = None,
146
- environment: str = 'production',
147
- **kwargs
148
- ) -> Dict[str, Any]:
149
- """
150
- Validate synchronization parameters.
151
-
152
- Args:
153
- user_identifier: Username or email
154
- api_token: Cloudflare API token
155
- environment: Target environment
156
- **kwargs: Additional parameters
157
-
158
- Returns:
159
- Dict with validation results
160
- """
161
- validation_result = {
162
- 'valid': True,
163
- 'errors': [],
164
- 'warnings': []
165
- }
166
-
167
- # Validate user
168
- try:
169
- user = self.get_user_by_identifier(user_identifier)
170
- validation_result['user'] = user
171
- except User.DoesNotExist as e:
172
- validation_result['valid'] = False
173
- validation_result['errors'].append(str(e))
174
- return validation_result
175
-
176
- # Validate API token
177
- try:
178
- token = self.get_api_token_for_user(user, api_token)
179
- validation_result['api_token'] = token
180
- except ValueError as e:
181
- validation_result['valid'] = False
182
- validation_result['errors'].append(str(e))
183
-
184
- # Validate environment
185
- valid_environments = ['production', 'staging', 'development', 'testing']
186
- if environment not in valid_environments:
187
- validation_result['valid'] = False
188
- validation_result['errors'].append(
189
- f"Invalid environment '{environment}'. Must be one of: {valid_environments}"
190
- )
191
-
192
- # Check if user has existing sites
193
- existing_sites_count = CloudflareSite.objects.filter(owner=user).count()
194
- if existing_sites_count == 0:
195
- validation_result['warnings'].append(
196
- f"User {user.username} has no existing sites. All zones will be created as new sites."
197
- )
198
- else:
199
- validation_result['warnings'].append(
200
- f"User {user.username} has {existing_sites_count} existing sites."
201
- )
202
-
203
- return validation_result
204
-
205
- def get_sync_preview(
206
- self,
207
- user: User,
208
- verbose: bool = False
209
- ) -> Dict[str, Any]:
210
- """
211
- Get a preview of what would be synchronized without making changes.
212
-
213
- Args:
214
- user: User to preview sync for
215
- verbose: Include detailed information
216
-
217
- Returns:
218
- Dict with preview information
219
- """
220
- try:
221
- # Get current sync status
222
- sync_status = self.sync_service.get_sync_status(user)
223
-
224
- # Get zones from Cloudflare (this requires API call)
225
- cf_zones = self.sync_service.client.list_zones()
226
-
227
- # Get existing sites
228
- existing_sites = CloudflareSite.objects.for_user(user)
229
- existing_domains = set(existing_sites.values_list('domain', flat=True))
230
-
231
- # Analyze what would happen
232
- cf_domains = {zone.name for zone in cf_zones}
233
-
234
- preview = {
235
- 'cloudflare_zones': len(cf_zones),
236
- 'existing_sites': existing_sites.count(),
237
- 'would_create': len(cf_domains - existing_domains),
238
- 'would_update': len(cf_domains & existing_domains),
239
- 'would_skip': len(existing_domains - cf_domains),
240
- 'sync_status': sync_status
241
- }
242
-
243
- if verbose:
244
- preview.update({
245
- 'new_domains': list(cf_domains - existing_domains),
246
- 'existing_domains': list(cf_domains & existing_domains),
247
- 'orphaned_domains': list(existing_domains - cf_domains),
248
- 'cloudflare_zones_details': [
249
- {
250
- 'name': zone.name,
251
- 'id': zone.id,
252
- 'status': zone.status
253
- }
254
- for zone in cf_zones
255
- ]
256
- })
257
-
258
- return {
259
- 'success': True,
260
- 'preview': preview
261
- }
262
-
263
- except Exception as e:
264
- logger.error(f"Failed to get sync preview: {e}")
265
- return {
266
- 'success': False,
267
- 'error': str(e)
268
- }
269
-
270
- def _transform_stats_for_command(self, core_stats: Dict[str, Any]) -> Dict[str, int]:
271
- """
272
- Transform core sync stats to command-compatible format.
273
-
274
- Args:
275
- core_stats: Stats from SiteSyncService
276
-
277
- Returns:
278
- Dict with command-compatible stats
279
- """
280
- # Map core stats to command stats
281
- command_stats = {
282
- 'created': core_stats.get('created', 0),
283
- 'updated': core_stats.get('updated', 0),
284
- 'skipped': core_stats.get('skipped', 0),
285
- 'errors': core_stats.get('errors', 0)
286
- }
287
-
288
- return command_stats
289
-
290
- def create_config_from_params(
291
- self,
292
- api_token: str,
293
- domain: str = "dummy.com"
294
- ) -> CloudflareConfig:
295
- """
296
- Create CloudflareConfig from parameters.
297
-
298
- Args:
299
- api_token: Cloudflare API token
300
- domain: Domain (not used for zone listing)
301
-
302
- Returns:
303
- CloudflareConfig instance
304
- """
305
- return CloudflareConfig(
306
- api_token=api_token,
307
- domain=domain
308
- )
309
-
310
- @classmethod
311
- def create_from_params(
312
- cls,
313
- api_token: str,
314
- domain: str = "dummy.com"
315
- ) -> 'SyncCommandService':
316
- """
317
- Create service instance from parameters.
318
-
319
- Args:
320
- api_token: Cloudflare API token
321
- domain: Domain (not used for zone listing)
322
-
323
- Returns:
324
- SyncCommandService instance
325
- """
326
- config = CloudflareConfig(
327
- api_token=api_token,
328
- domain=domain
329
- )
330
- return cls(config)
@@ -1,264 +0,0 @@
1
- """
2
- Cloudflare Workers management using official library.
3
-
4
- Provides clean interface for managing Workers scripts and routes.
5
- """
6
-
7
- import logging
8
- from typing import Dict, List, Optional, Any
9
- from datetime import datetime
10
-
11
- from .cloudflare_client import CloudflareClient
12
- from django_cfg.models.cloudflare import CloudflareConfig
13
-
14
- logger = logging.getLogger(__name__)
15
-
16
-
17
- class WorkerManager:
18
- """
19
- Cloudflare Workers management service.
20
-
21
- Provides high-level interface for Workers operations.
22
- """
23
-
24
- def __init__(self, config: CloudflareConfig):
25
- """Initialize worker manager."""
26
- self.config = config
27
- self.client = CloudflareClient(config)
28
-
29
- def deploy_worker(
30
- self,
31
- account_id: str,
32
- script_name: str,
33
- script_content: str,
34
- routes: Optional[List[Dict[str, str]]] = None
35
- ) -> Dict[str, Any]:
36
- """
37
- Deploy a Worker script with optional routes.
38
-
39
- Args:
40
- account_id: Cloudflare account ID
41
- script_name: Name for the Worker script
42
- script_content: JavaScript code for the Worker
43
- routes: List of route configurations [{'zone_id': '...', 'pattern': '...'}]
44
-
45
- Returns:
46
- Dict with deployment results
47
- """
48
- logger.info(f"Deploying Worker script: {script_name}")
49
-
50
- try:
51
- # Deploy the Worker script
52
- worker_result = self.client.create_worker(
53
- account_id=account_id,
54
- script_name=script_name,
55
- script_content=script_content
56
- )
57
-
58
- deployed_routes = []
59
-
60
- # Create routes if provided
61
- if routes:
62
- for route_config in routes:
63
- try:
64
- route_result = self.client.create_worker_route(
65
- zone_id=route_config['zone_id'],
66
- pattern=route_config['pattern'],
67
- script_name=script_name
68
- )
69
- deployed_routes.append({
70
- 'zone_id': route_config['zone_id'],
71
- 'pattern': route_config['pattern'],
72
- 'route_id': route_result.get('id'),
73
- 'success': True
74
- })
75
- except Exception as e:
76
- logger.error(f"Failed to create route {route_config['pattern']}: {e}")
77
- deployed_routes.append({
78
- 'zone_id': route_config['zone_id'],
79
- 'pattern': route_config['pattern'],
80
- 'success': False,
81
- 'error': str(e)
82
- })
83
-
84
- logger.info(f"Successfully deployed Worker: {script_name}")
85
-
86
- return {
87
- 'success': True,
88
- 'worker_id': worker_result.id,
89
- 'script_name': script_name,
90
- 'routes': deployed_routes,
91
- 'deployed_at': datetime.now().isoformat()
92
- }
93
-
94
- except Exception as e:
95
- logger.error(f"Failed to deploy Worker {script_name}: {e}")
96
- return {
97
- 'success': False,
98
- 'error': str(e),
99
- 'script_name': script_name
100
- }
101
-
102
- def update_worker(
103
- self,
104
- account_id: str,
105
- script_name: str,
106
- script_content: str
107
- ) -> Dict[str, Any]:
108
- """
109
- Update an existing Worker script.
110
-
111
- Args:
112
- account_id: Cloudflare account ID
113
- script_name: Name of the Worker script to update
114
- script_content: New JavaScript code
115
-
116
- Returns:
117
- Dict with update results
118
- """
119
- logger.info(f"Updating Worker script: {script_name}")
120
-
121
- try:
122
- worker_result = self.client.create_worker(
123
- account_id=account_id,
124
- script_name=script_name,
125
- script_content=script_content
126
- )
127
-
128
- logger.info(f"Successfully updated Worker: {script_name}")
129
-
130
- return {
131
- 'success': True,
132
- 'worker_id': worker_result.id,
133
- 'script_name': script_name,
134
- 'updated_at': datetime.now().isoformat()
135
- }
136
-
137
- except Exception as e:
138
- logger.error(f"Failed to update Worker {script_name}: {e}")
139
- return {
140
- 'success': False,
141
- 'error': str(e),
142
- 'script_name': script_name
143
- }
144
-
145
- def delete_worker(
146
- self,
147
- account_id: str,
148
- script_name: str,
149
- cleanup_routes: bool = True
150
- ) -> Dict[str, Any]:
151
- """
152
- Delete a Worker script and optionally its routes.
153
-
154
- Args:
155
- account_id: Cloudflare account ID
156
- script_name: Name of the Worker script to delete
157
- cleanup_routes: Whether to delete associated routes
158
-
159
- Returns:
160
- Dict with deletion results
161
- """
162
- logger.info(f"Deleting Worker script: {script_name}")
163
-
164
- try:
165
- # TODO: If cleanup_routes is True, we'd need to find and delete routes
166
- # This would require additional API calls to list routes and filter by script
167
-
168
- success = self.client.delete_worker(account_id, script_name)
169
-
170
- if success:
171
- logger.info(f"Successfully deleted Worker: {script_name}")
172
- return {
173
- 'success': True,
174
- 'script_name': script_name,
175
- 'deleted_at': datetime.now().isoformat()
176
- }
177
- else:
178
- return {
179
- 'success': False,
180
- 'error': 'Delete operation returned False',
181
- 'script_name': script_name
182
- }
183
-
184
- except Exception as e:
185
- logger.error(f"Failed to delete Worker {script_name}: {e}")
186
- return {
187
- 'success': False,
188
- 'error': str(e),
189
- 'script_name': script_name
190
- }
191
-
192
- def list_workers(self, account_id: str) -> Dict[str, Any]:
193
- """
194
- List all Workers in an account.
195
-
196
- Args:
197
- account_id: Cloudflare account ID
198
-
199
- Returns:
200
- Dict with workers list
201
- """
202
- logger.info("Listing Workers scripts")
203
-
204
- try:
205
- workers = self.client.list_workers(account_id)
206
-
207
- workers_data = []
208
- for worker in workers:
209
- workers_data.append({
210
- 'id': worker.id,
211
- 'script_name': getattr(worker, 'script_name', ''),
212
- 'created_on': getattr(worker, 'created_on', ''),
213
- 'modified_on': getattr(worker, 'modified_on', ''),
214
- 'size': getattr(worker, 'size', 0)
215
- })
216
-
217
- logger.info(f"Found {len(workers_data)} Workers")
218
-
219
- return {
220
- 'success': True,
221
- 'workers': workers_data,
222
- 'count': len(workers_data)
223
- }
224
-
225
- except Exception as e:
226
- logger.error(f"Failed to list Workers: {e}")
227
- return {
228
- 'success': False,
229
- 'error': str(e),
230
- 'workers': [],
231
- 'count': 0
232
- }
233
-
234
- def get_worker_analytics(
235
- self,
236
- account_id: str,
237
- script_name: str,
238
- since: Optional[datetime] = None,
239
- until: Optional[datetime] = None
240
- ) -> Dict[str, Any]:
241
- """
242
- Get analytics for a Worker script.
243
-
244
- Note: This would require additional API calls to Cloudflare Analytics API
245
- which might not be available in the basic cloudflare library.
246
-
247
- Args:
248
- account_id: Cloudflare account ID
249
- script_name: Worker script name
250
- since: Start date for analytics
251
- until: End date for analytics
252
-
253
- Returns:
254
- Dict with analytics data
255
- """
256
- logger.info(f"Getting analytics for Worker: {script_name}")
257
-
258
- # This is a placeholder - actual implementation would require
259
- # additional API calls to Cloudflare Analytics API
260
- return {
261
- 'success': False,
262
- 'error': 'Analytics API not implemented yet',
263
- 'script_name': script_name
264
- }
@@ -1,38 +0,0 @@
1
- """
2
- Maintenance app signals.
3
-
4
- Signal handlers for maintenance events and site management.
5
- """
6
-
7
- from django.db.models.signals import post_save, pre_delete
8
- from django.dispatch import receiver
9
- import logging
10
-
11
- from .models import MaintenanceEvent, CloudflareSite
12
-
13
- logger = logging.getLogger(__name__)
14
-
15
-
16
- @receiver(post_save, sender=MaintenanceEvent)
17
- def maintenance_event_created(sender, instance, created, **kwargs):
18
- """Handle maintenance event creation."""
19
- if created:
20
- logger.info(f"Maintenance event created: {instance.title}")
21
-
22
-
23
- @receiver(post_save, sender=CloudflareSite)
24
- def cloudflare_site_updated(sender, instance, created, **kwargs):
25
- """Handle Cloudflare site updates."""
26
- if created:
27
- logger.info(f"New Cloudflare site added: {instance.domain}")
28
- else:
29
- logger.debug(f"Cloudflare site updated: {instance.domain}")
30
-
31
-
32
- @receiver(pre_delete, sender=CloudflareSite)
33
- def cloudflare_site_cleanup(sender, instance, **kwargs):
34
- """Clean up before deleting Cloudflare site."""
35
- logger.info(f"Cleaning up Cloudflare site: {instance.domain}")
36
-
37
- # Note: In a full implementation, this would clean up
38
- # any active Workers or Page Rules in Cloudflare
@@ -1,36 +0,0 @@
1
- """
2
- URL configuration for maintenance app.
3
-
4
- RESTful API endpoints using nested routers for multi-site maintenance management.
5
- """
6
-
7
- from django.urls import path, include
8
- from rest_framework_nested import routers
9
- from .views import (
10
- CloudflareSiteViewSet, SiteGroupViewSet, MaintenanceEventViewSet,
11
- MonitoringTargetViewSet, CloudflareDeploymentViewSet
12
- )
13
-
14
- app_name = 'maintenance'
15
-
16
- # Main router for primary resources
17
- router = routers.SimpleRouter()
18
- router.register(r'sites', CloudflareSiteViewSet, basename='site')
19
- router.register(r'groups', SiteGroupViewSet, basename='group')
20
- router.register(r'events', MaintenanceEventViewSet, basename='event')
21
- router.register(r'deployments', CloudflareDeploymentViewSet, basename='deployment')
22
-
23
- # Nested routers for related resources
24
- sites_router = routers.NestedSimpleRouter(router, r'sites', lookup='site')
25
- sites_router.register(r'monitoring', MonitoringTargetViewSet, basename='site-monitoring')
26
-
27
- events_router = routers.NestedSimpleRouter(router, r'events', lookup='event')
28
- # events_router.register(r'logs', MaintenanceLogViewSet, basename='event-logs') # Future
29
-
30
- # API endpoints
31
- urlpatterns = [
32
- # RESTful API routes
33
- path('api/', include(router.urls)),
34
- path('api/', include(sites_router.urls)),
35
- path('api/', include(events_router.urls)),
36
- ]
@@ -1,18 +0,0 @@
1
- """
2
- Maintenance app views.
3
-
4
- RESTful API views for multi-site maintenance management.
5
- """
6
-
7
- from .sites import CloudflareSiteViewSet, SiteGroupViewSet
8
- from .events import MaintenanceEventViewSet
9
- from .monitoring import MonitoringTargetViewSet
10
- from .deployments import CloudflareDeploymentViewSet
11
-
12
- __all__ = [
13
- 'CloudflareSiteViewSet',
14
- 'SiteGroupViewSet',
15
- 'MaintenanceEventViewSet',
16
- 'MonitoringTargetViewSet',
17
- 'CloudflareDeploymentViewSet'
18
- ]