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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/accounts/models/__init__.py +68 -0
- django_cfg/apps/accounts/models/activity.py +34 -0
- django_cfg/apps/accounts/models/auth.py +50 -0
- django_cfg/apps/accounts/models/base.py +8 -0
- django_cfg/apps/accounts/models/choices.py +32 -0
- django_cfg/apps/accounts/models/integrations.py +75 -0
- django_cfg/apps/accounts/models/registration.py +52 -0
- django_cfg/apps/accounts/models/user.py +80 -0
- django_cfg/apps/maintenance/__init__.py +53 -24
- django_cfg/apps/maintenance/admin/__init__.py +7 -18
- django_cfg/apps/maintenance/admin/api_key_admin.py +185 -0
- django_cfg/apps/maintenance/admin/log_admin.py +156 -0
- django_cfg/apps/maintenance/admin/scheduled_admin.py +390 -0
- django_cfg/apps/maintenance/admin/site_admin.py +448 -0
- django_cfg/apps/maintenance/apps.py +9 -96
- django_cfg/apps/maintenance/management/commands/maintenance.py +193 -307
- django_cfg/apps/maintenance/management/commands/process_scheduled_maintenance.py +241 -0
- django_cfg/apps/maintenance/management/commands/sync_cloudflare.py +152 -111
- django_cfg/apps/maintenance/managers/__init__.py +7 -12
- django_cfg/apps/maintenance/managers/cloudflare_site_manager.py +192 -0
- django_cfg/apps/maintenance/managers/maintenance_log_manager.py +151 -0
- django_cfg/apps/maintenance/migrations/0001_initial.py +145 -705
- django_cfg/apps/maintenance/migrations/0002_cloudflaresite_maintenance_url.py +21 -0
- django_cfg/apps/maintenance/models/__init__.py +23 -21
- django_cfg/apps/maintenance/models/cloudflare_api_key.py +109 -0
- django_cfg/apps/maintenance/models/cloudflare_site.py +125 -0
- django_cfg/apps/maintenance/models/maintenance_log.py +131 -0
- django_cfg/apps/maintenance/models/scheduled_maintenance.py +307 -0
- django_cfg/apps/maintenance/services/__init__.py +37 -16
- django_cfg/apps/maintenance/services/bulk_operations_service.py +400 -0
- django_cfg/apps/maintenance/services/maintenance_service.py +230 -0
- django_cfg/apps/maintenance/services/scheduled_maintenance_service.py +381 -0
- django_cfg/apps/maintenance/services/site_sync_service.py +390 -0
- django_cfg/apps/maintenance/utils/__init__.py +12 -0
- django_cfg/apps/maintenance/utils/retry_utils.py +109 -0
- django_cfg/config.py +4 -0
- django_cfg/core/config.py +4 -6
- django_cfg/modules/django_unfold/dashboard.py +4 -5
- {django_cfg-1.2.17.dist-info → django_cfg-1.2.19.dist-info}/METADATA +52 -1
- {django_cfg-1.2.17.dist-info → django_cfg-1.2.19.dist-info}/RECORD +45 -55
- django_cfg/apps/maintenance/README.md +0 -305
- django_cfg/apps/maintenance/admin/deployments_admin.py +0 -251
- django_cfg/apps/maintenance/admin/events_admin.py +0 -374
- django_cfg/apps/maintenance/admin/monitoring_admin.py +0 -215
- django_cfg/apps/maintenance/admin/sites_admin.py +0 -464
- django_cfg/apps/maintenance/managers/deployments.py +0 -287
- django_cfg/apps/maintenance/managers/events.py +0 -374
- django_cfg/apps/maintenance/managers/monitoring.py +0 -301
- django_cfg/apps/maintenance/managers/sites.py +0 -335
- django_cfg/apps/maintenance/models/cloudflare.py +0 -316
- django_cfg/apps/maintenance/models/maintenance.py +0 -334
- django_cfg/apps/maintenance/models/monitoring.py +0 -393
- django_cfg/apps/maintenance/models/sites.py +0 -419
- django_cfg/apps/maintenance/serializers/__init__.py +0 -60
- django_cfg/apps/maintenance/serializers/actions.py +0 -310
- django_cfg/apps/maintenance/serializers/base.py +0 -44
- django_cfg/apps/maintenance/serializers/deployments.py +0 -209
- django_cfg/apps/maintenance/serializers/events.py +0 -210
- django_cfg/apps/maintenance/serializers/monitoring.py +0 -278
- django_cfg/apps/maintenance/serializers/sites.py +0 -213
- django_cfg/apps/maintenance/services/README.md +0 -168
- django_cfg/apps/maintenance/services/cloudflare_client.py +0 -441
- django_cfg/apps/maintenance/services/dns_manager.py +0 -497
- django_cfg/apps/maintenance/services/maintenance_manager.py +0 -504
- django_cfg/apps/maintenance/services/site_sync.py +0 -448
- django_cfg/apps/maintenance/services/sync_command_service.py +0 -330
- django_cfg/apps/maintenance/services/worker_manager.py +0 -264
- django_cfg/apps/maintenance/signals.py +0 -38
- django_cfg/apps/maintenance/urls.py +0 -36
- django_cfg/apps/maintenance/views/__init__.py +0 -18
- django_cfg/apps/maintenance/views/base.py +0 -61
- django_cfg/apps/maintenance/views/deployments.py +0 -175
- django_cfg/apps/maintenance/views/events.py +0 -204
- django_cfg/apps/maintenance/views/monitoring.py +0 -213
- django_cfg/apps/maintenance/views/sites.py +0 -338
- django_cfg/models/cloudflare.py +0 -316
- /django_cfg/apps/accounts/{models.py → __models.py} +0 -0
- {django_cfg-1.2.17.dist-info → django_cfg-1.2.19.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.17.dist-info → django_cfg-1.2.19.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.17.dist-info → django_cfg-1.2.19.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
|
-
]
|