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
@@ -0,0 +1,448 @@
1
+ """
2
+ CloudflareSite admin with Unfold styling and action buttons.
3
+
4
+ Beautiful admin interface inspired by the old complex system but simplified.
5
+ """
6
+
7
+ from django.contrib import admin
8
+ from django.utils.html import format_html
9
+ from django.urls import reverse
10
+ from django.utils.safestring import mark_safe
11
+ from django.contrib import messages
12
+ from django.http import HttpRequest
13
+ from django.shortcuts import redirect
14
+ from typing import Any
15
+
16
+ from unfold.admin import ModelAdmin, TabularInline
17
+ from unfold.decorators import display, action
18
+ from unfold.enums import ActionVariant
19
+
20
+ from ..models import CloudflareSite, MaintenanceLog
21
+ from ..services import MaintenanceService
22
+
23
+
24
+ class MaintenanceLogInline(TabularInline):
25
+ """Inline for recent maintenance logs."""
26
+
27
+ model = MaintenanceLog
28
+ verbose_name = "Recent Log"
29
+ verbose_name_plural = "📋 Recent Maintenance Logs"
30
+ extra = 0
31
+ max_num = 5
32
+ can_delete = False
33
+ show_change_link = True
34
+
35
+ fields = ['status_display', 'action', 'created_at', 'duration_seconds', 'error_preview']
36
+ readonly_fields = ['status_display', 'action', 'created_at', 'duration_seconds', 'error_preview']
37
+
38
+ def has_add_permission(self, request, obj=None):
39
+ return False
40
+
41
+ def has_change_permission(self, request, obj=None):
42
+ return False
43
+
44
+ @display(description="Status")
45
+ def status_display(self, obj):
46
+ """Display status with emoji."""
47
+ status_emoji = {
48
+ MaintenanceLog.Status.SUCCESS: "✅",
49
+ MaintenanceLog.Status.FAILED: "❌",
50
+ MaintenanceLog.Status.PENDING: "⏳"
51
+ }.get(obj.status, "❓")
52
+
53
+ return format_html('{} {}', status_emoji, obj.get_status_display())
54
+
55
+ @display(description="Error")
56
+ def error_preview(self, obj):
57
+ """Show error message preview."""
58
+ if not obj.error_message:
59
+ return "-"
60
+
61
+ preview = obj.error_message[:50]
62
+ if len(obj.error_message) > 50:
63
+ preview += "..."
64
+
65
+ return format_html('<span style="color: red; font-family: monospace;">{}</span>', preview)
66
+
67
+
68
+ @admin.register(CloudflareSite)
69
+ class CloudflareSiteAdmin(ModelAdmin):
70
+ """Admin for CloudflareSite with Unfold styling and action buttons."""
71
+
72
+ list_display = [
73
+ 'status_display',
74
+ 'name',
75
+ 'domain',
76
+ 'maintenance_badge',
77
+ 'active_badge',
78
+ 'last_maintenance_at',
79
+ 'logs_count',
80
+ 'action_buttons'
81
+ ]
82
+
83
+ list_display_links = ['name', 'domain']
84
+
85
+ search_fields = ['name', 'domain', 'zone_id']
86
+
87
+ list_filter = [
88
+ 'maintenance_active',
89
+ 'is_active',
90
+ 'created_at',
91
+ 'last_maintenance_at'
92
+ ]
93
+
94
+ readonly_fields = [
95
+ 'created_at',
96
+ 'updated_at',
97
+ 'last_maintenance_at',
98
+ 'logs_preview'
99
+ ]
100
+
101
+ fieldsets = [
102
+ ('Basic Information', {
103
+ 'fields': ['name', 'domain']
104
+ }),
105
+ ('Cloudflare Configuration', {
106
+ 'fields': ['zone_id', 'account_id', 'api_key'],
107
+ 'classes': ['collapse']
108
+ }),
109
+ ('Status', {
110
+ 'fields': ['maintenance_active', 'maintenance_url', 'is_active']
111
+ }),
112
+ ('Timestamps', {
113
+ 'fields': ['created_at', 'updated_at', 'last_maintenance_at'],
114
+ 'classes': ['collapse']
115
+ }),
116
+ ('Recent Activity', {
117
+ 'fields': ['logs_preview'],
118
+ 'classes': ['collapse']
119
+ })
120
+ ]
121
+
122
+ inlines = [MaintenanceLogInline]
123
+
124
+ actions = [
125
+ 'enable_maintenance_action',
126
+ 'disable_maintenance_action',
127
+ 'sync_from_cloudflare_action'
128
+ ]
129
+
130
+ # Unfold action buttons
131
+ actions_detail = [
132
+ 'sync_with_cloudflare_detail',
133
+ 'enable_maintenance_detail',
134
+ 'disable_maintenance_detail'
135
+ ]
136
+ actions_list = [
137
+ 'bulk_sync_sites',
138
+ 'bulk_discover_sites'
139
+ ]
140
+
141
+ # Display methods
142
+
143
+ @display(description="Status")
144
+ def status_display(self, obj: CloudflareSite) -> str:
145
+ """Display status with emoji."""
146
+ if obj.maintenance_active:
147
+ return format_html('<span style="color: orange;">🔧 {}</span>', obj.name)
148
+ elif obj.is_active:
149
+ return format_html('<span style="color: green;">🟢 {}</span>', obj.name)
150
+ else:
151
+ return format_html('<span style="color: red;">🔴 {}</span>', obj.name)
152
+
153
+ @display(description="Maintenance")
154
+ def maintenance_badge(self, obj: CloudflareSite) -> str:
155
+ """Display maintenance status badge."""
156
+ if obj.maintenance_active:
157
+ return format_html('<span class="badge badge-warning">🔧 Active</span>')
158
+ else:
159
+ return format_html('<span class="badge badge-success">✅ Normal</span>')
160
+
161
+ @display(description="Site Active")
162
+ def active_badge(self, obj: CloudflareSite) -> str:
163
+ """Display active status badge."""
164
+ if obj.is_active:
165
+ return format_html('<span class="badge badge-success">Active</span>')
166
+ else:
167
+ return format_html('<span class="badge badge-secondary">Inactive</span>')
168
+
169
+ @display(description="Logs")
170
+ def logs_count(self, obj: CloudflareSite) -> str:
171
+ """Display count of logs with link."""
172
+ count = obj.logs.count()
173
+ if count > 0:
174
+ url = reverse('admin:maintenance_maintenancelog_changelist')
175
+ return format_html(
176
+ '<a href="{}?site__id__exact={}">{} logs</a>',
177
+ url, obj.id, count
178
+ )
179
+ return "No logs"
180
+
181
+ @display(description="Actions")
182
+ def action_buttons(self, obj: CloudflareSite) -> str:
183
+ """Display action buttons."""
184
+ buttons = []
185
+
186
+ if obj.maintenance_active:
187
+ buttons.append(
188
+ f'<a href="#" onclick="disableMaintenance({obj.id}, \'{obj.domain}\')" '
189
+ f'class="button" style="background: green; color: white; margin: 2px;">Disable</a>'
190
+ )
191
+ else:
192
+ buttons.append(
193
+ f'<a href="#" onclick="enableMaintenance({obj.id}, \'{obj.domain}\')" '
194
+ f'class="button" style="background: orange; color: white; margin: 2px;">Enable</a>'
195
+ )
196
+
197
+ buttons.append(
198
+ f'<a href="#" onclick="syncSite({obj.id}, \'{obj.domain}\')" '
199
+ f'class="button" style="background: blue; color: white; margin: 2px;">Sync</a>'
200
+ )
201
+
202
+ return mark_safe(' '.join(buttons))
203
+
204
+ def logs_preview(self, obj: CloudflareSite) -> str:
205
+ """Show recent logs preview."""
206
+ recent_logs = obj.logs.all()[:5]
207
+ if not recent_logs:
208
+ return "No logs yet"
209
+
210
+ html = "<ul>"
211
+ for log in recent_logs:
212
+ status_emoji = {
213
+ MaintenanceLog.Status.SUCCESS: "✅",
214
+ MaintenanceLog.Status.FAILED: "❌",
215
+ MaintenanceLog.Status.PENDING: "⏳"
216
+ }.get(log.status, "❓")
217
+
218
+ html += f"<li>{status_emoji} {log.get_action_display()} - {log.created_at.strftime('%Y-%m-%d %H:%M')}"
219
+ if log.error_message:
220
+ html += f" <em>({log.error_message[:50]}...)</em>"
221
+ html += "</li>"
222
+
223
+ html += "</ul>"
224
+
225
+ if obj.logs.count() > 5:
226
+ url = reverse('admin:maintenance_maintenancelog_changelist')
227
+ html += f'<a href="{url}?site__id__exact={obj.id}">View all logs →</a>'
228
+
229
+ return mark_safe(html)
230
+
231
+ logs_preview.short_description = "Recent Activity"
232
+
233
+ # Admin Actions
234
+
235
+ @action(description="🔧 Enable maintenance mode")
236
+ def enable_maintenance_action(self, request: HttpRequest, queryset) -> None:
237
+ """Enable maintenance for selected sites."""
238
+ success_count = 0
239
+ error_count = 0
240
+
241
+ for site in queryset:
242
+ try:
243
+ service = MaintenanceService(site)
244
+ service.enable_maintenance("Enabled via admin interface")
245
+ success_count += 1
246
+ except Exception as e:
247
+ error_count += 1
248
+ messages.error(request, f"Failed to enable maintenance for {site.domain}: {str(e)}")
249
+
250
+ if success_count:
251
+ messages.success(request, f"Successfully enabled maintenance for {success_count} sites")
252
+
253
+ if error_count:
254
+ messages.error(request, f"Failed to enable maintenance for {error_count} sites")
255
+
256
+ @action(description="🟢 Disable maintenance mode")
257
+ def disable_maintenance_action(self, request: HttpRequest, queryset) -> None:
258
+ """Disable maintenance for selected sites."""
259
+ success_count = 0
260
+ error_count = 0
261
+
262
+ for site in queryset:
263
+ try:
264
+ service = MaintenanceService(site)
265
+ service.disable_maintenance()
266
+ success_count += 1
267
+ except Exception as e:
268
+ error_count += 1
269
+ messages.error(request, f"Failed to disable maintenance for {site.domain}: {str(e)}")
270
+
271
+ if success_count:
272
+ messages.success(request, f"Successfully disabled maintenance for {success_count} sites")
273
+
274
+ if error_count:
275
+ messages.error(request, f"Failed to disable maintenance for {error_count} sites")
276
+
277
+ @action(description="🔄 Sync from Cloudflare")
278
+ def sync_from_cloudflare_action(self, request: HttpRequest, queryset) -> None:
279
+ """Sync selected sites from Cloudflare."""
280
+ success_count = 0
281
+ error_count = 0
282
+
283
+ for site in queryset:
284
+ try:
285
+ service = MaintenanceService(site)
286
+ service.sync_site_from_cloudflare()
287
+ success_count += 1
288
+ except Exception as e:
289
+ error_count += 1
290
+ messages.error(request, f"Failed to sync {site.domain}: {str(e)}")
291
+
292
+ if success_count:
293
+ messages.success(request, f"Successfully synced {success_count} sites from Cloudflare")
294
+
295
+ if error_count:
296
+ messages.error(request, f"Failed to sync {error_count} sites")
297
+
298
+ # Unfold detail actions (кнопки на странице отдельного объекта)
299
+
300
+ @action(
301
+ description="🔄 Sync with Cloudflare",
302
+ icon="refresh",
303
+ variant=ActionVariant.INFO
304
+ )
305
+ def sync_with_cloudflare_detail(self, request, object_id):
306
+ """Sync site with Cloudflare zones."""
307
+ try:
308
+ site = self.get_object(request, object_id)
309
+ if not site:
310
+ messages.error(request, "Site not found.")
311
+ return redirect(request.META.get('HTTP_REFERER', '/admin/'))
312
+
313
+ # Use SiteSyncService for site synchronization
314
+ from ..services import SiteSyncService
315
+ sync_service = SiteSyncService(site.api_key)
316
+ sync_service.sync_zones()
317
+
318
+ messages.success(
319
+ request,
320
+ f"Site '{site.name}' has been synchronized with Cloudflare."
321
+ )
322
+
323
+ except Exception as e:
324
+ messages.error(request, f"Failed to sync site: {str(e)}")
325
+
326
+ return redirect(request.META.get('HTTP_REFERER', '/admin/'))
327
+
328
+ @action(
329
+ description="🔧 Enable Maintenance",
330
+ icon="build",
331
+ variant=ActionVariant.WARNING
332
+ )
333
+ def enable_maintenance_detail(self, request, object_id):
334
+ """Enable maintenance mode for a site."""
335
+ try:
336
+ site = self.get_object(request, object_id)
337
+ if not site:
338
+ messages.error(request, "Site not found.")
339
+ return redirect(request.META.get('HTTP_REFERER', '/admin/'))
340
+
341
+ service = MaintenanceService(site)
342
+ service.enable_maintenance("Enabled via admin interface")
343
+
344
+ messages.success(request, f"Maintenance mode enabled for {site.name}.")
345
+
346
+ except Exception as e:
347
+ messages.error(request, f"Failed to enable maintenance: {str(e)}")
348
+
349
+ return redirect(request.META.get('HTTP_REFERER', '/admin/'))
350
+
351
+ @action(
352
+ description="✅ Disable Maintenance",
353
+ icon="check_circle",
354
+ variant=ActionVariant.SUCCESS
355
+ )
356
+ def disable_maintenance_detail(self, request, object_id):
357
+ """Disable maintenance mode for a site."""
358
+ try:
359
+ site = self.get_object(request, object_id)
360
+ if not site:
361
+ messages.error(request, "Site not found.")
362
+ return redirect(request.META.get('HTTP_REFERER', '/admin/'))
363
+
364
+ service = MaintenanceService(site)
365
+ service.disable_maintenance()
366
+
367
+ messages.success(request, f"Maintenance mode disabled for {site.name}.")
368
+
369
+ except Exception as e:
370
+ messages.error(request, f"Failed to disable maintenance: {str(e)}")
371
+
372
+ return redirect(request.META.get('HTTP_REFERER', '/admin/'))
373
+
374
+ # Unfold list actions (кнопки над списком)
375
+
376
+ @action(
377
+ description="🔄 Sync All Sites with Cloudflare",
378
+ icon="sync",
379
+ variant=ActionVariant.INFO,
380
+ url_path="bulk-sync-sites"
381
+ )
382
+ def bulk_sync_sites(self, request):
383
+ """Bulk sync all sites with Cloudflare."""
384
+ try:
385
+ from ..models import CloudflareSite
386
+
387
+ # Use manager method for bulk sync
388
+ result = CloudflareSite.objects.bulk_sync_all()
389
+
390
+ if result.get('synced', 0) > 0:
391
+ messages.success(
392
+ request,
393
+ f"Successfully synchronized {result['synced']} sites with Cloudflare."
394
+ )
395
+
396
+ if result.get('errors', 0) > 0:
397
+ # Show detailed error messages
398
+ error_details = result.get('error_details', [])
399
+ for error in error_details:
400
+ messages.error(request, f"Sync error: {error}")
401
+
402
+ messages.warning(
403
+ request,
404
+ f"Synchronization completed with {result['errors']} errors."
405
+ )
406
+
407
+ if result.get('synced', 0) == 0 and result.get('errors', 0) == 0:
408
+ messages.info(request, "No sites to synchronize.")
409
+
410
+ except Exception as e:
411
+ messages.error(request, f"Bulk sync failed: {str(e)}")
412
+
413
+ return redirect(request.META.get('HTTP_REFERER', '/admin/'))
414
+
415
+ @action(
416
+ description="🔍 Discover New Sites",
417
+ icon="search",
418
+ variant=ActionVariant.SUCCESS,
419
+ url_path="bulk-discover-sites"
420
+ )
421
+ def bulk_discover_sites(self, request):
422
+ """Discover new sites from Cloudflare."""
423
+ try:
424
+ from ..models import CloudflareSite, CloudflareApiKey
425
+
426
+ # Check if we have active API keys
427
+ if not CloudflareApiKey.objects.filter(is_active=True).exists():
428
+ messages.error(request, "No active API keys found. Please add Cloudflare API keys first.")
429
+ return redirect(request.META.get('HTTP_REFERER', '/admin/'))
430
+
431
+ # Use manager method for discovery
432
+ result = CloudflareSite.objects.discover_all_sites()
433
+
434
+ if result.get('discovered', 0) > 0:
435
+ messages.success(
436
+ request,
437
+ f"Successfully discovered {result['discovered']} new sites from Cloudflare."
438
+ )
439
+ else:
440
+ messages.info(request, "No new sites discovered.")
441
+
442
+ if result.get('errors', 0) > 0:
443
+ messages.warning(request, f"Discovery completed with {result['errors']} API key errors.")
444
+
445
+ except Exception as e:
446
+ messages.error(request, f"Site discovery failed: {str(e)}")
447
+
448
+ return redirect(request.META.get('HTTP_REFERER', '/admin/'))
@@ -1,105 +1,18 @@
1
1
  """
2
- Maintenance Application Configuration
3
-
4
- Follows django-cfg patterns for app configuration with automatic setup.
2
+ Maintenance app configuration.
5
3
  """
6
4
 
7
5
  from django.apps import AppConfig
8
- from django.conf import settings
9
- import logging
10
-
11
- logger = logging.getLogger(__name__)
12
6
 
13
7
 
14
8
  class MaintenanceConfig(AppConfig):
15
- """Maintenance application configuration."""
16
-
17
- default_auto_field = "django.db.models.BigAutoField"
18
- name = "django_cfg.apps.maintenance"
19
- label = "django_cfg_maintenance"
20
- verbose_name = "Django CFG Maintenance"
21
-
22
- def ready(self):
23
- """Initialize the maintenance application."""
24
- # Import signal handlers
25
- try:
26
- import django_cfg.apps.maintenance.signals # noqa
27
- except ImportError:
28
- pass
29
-
30
- # Auto-setup Cloudflare if configured
31
- self._setup_cloudflare_auto_config()
32
-
33
- def _setup_cloudflare_auto_config(self):
34
- """Auto-setup Cloudflare configuration if API token and domain are provided."""
35
- try:
36
- # Check if basic Cloudflare config is available
37
- api_token = getattr(settings, 'CLOUDFLARE_API_TOKEN', None)
38
- domain = getattr(settings, 'CLOUDFLARE_DOMAIN', None)
39
-
40
- if api_token and domain:
41
- logger.info("Cloudflare maintenance mode auto-configuration detected")
42
-
43
- # Import here to avoid circular imports
44
- from django_cfg.apps.maintenance.services.auto_setup import CloudflareAutoSetup
45
- from django_cfg.models.cloudflare import CloudflareConfig
46
-
47
- # Create configuration
48
- config = CloudflareConfig(
49
- api_token=api_token,
50
- domain=domain
51
- )
52
-
53
- # Run auto-setup in background (non-blocking)
54
- import asyncio
55
- try:
56
- # Try to get existing event loop
57
- loop = asyncio.get_event_loop()
58
- if loop.is_running():
59
- # If loop is running, schedule the task
60
- asyncio.create_task(self._run_auto_setup(config))
61
- else:
62
- # If no loop is running, run in new thread
63
- import threading
64
- thread = threading.Thread(
65
- target=self._run_auto_setup_sync,
66
- args=(config,)
67
- )
68
- thread.daemon = True
69
- thread.start()
70
- except RuntimeError:
71
- # No event loop, run in thread
72
- import threading
73
- thread = threading.Thread(
74
- target=self._run_auto_setup_sync,
75
- args=(config,)
76
- )
77
- thread.daemon = True
78
- thread.start()
79
-
80
- except Exception as e:
81
- logger.warning(f"Cloudflare auto-setup skipped: {e}")
9
+ """Configuration for the simplified maintenance app."""
82
10
 
83
- async def _run_auto_setup(self, config):
84
- """Run auto-setup asynchronously."""
85
- try:
86
- from django_cfg.apps.maintenance.services.auto_setup import CloudflareAutoSetup
87
-
88
- setup_service = CloudflareAutoSetup(config)
89
- result = await setup_service.setup_complete_infrastructure()
90
-
91
- if result.success:
92
- logger.info(f"✅ Cloudflare auto-setup completed in {result.get_duration_seconds():.2f}s")
93
- else:
94
- logger.warning(f"❌ Cloudflare auto-setup failed: {len(result.get_failed_steps())} errors")
95
-
96
- except Exception as e:
97
- logger.error(f"Cloudflare auto-setup error: {e}")
11
+ default_auto_field = 'django.db.models.BigAutoField'
12
+ name = 'django_cfg.apps.maintenance'
13
+ verbose_name = 'Maintenance Mode'
98
14
 
99
- def _run_auto_setup_sync(self, config):
100
- """Run auto-setup synchronously in thread."""
101
- try:
102
- import asyncio
103
- asyncio.run(self._run_auto_setup(config))
104
- except Exception as e:
105
- logger.error(f"Cloudflare auto-setup thread error: {e}")
15
+ def ready(self):
16
+ """Initialize app when Django starts."""
17
+ # No complex signals or initialization needed
18
+ pass