django-cfg 1.2.14__py3-none-any.whl → 1.2.16__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/maintenance/README.md +305 -0
- django_cfg/apps/maintenance/__init__.py +27 -0
- django_cfg/apps/maintenance/admin/__init__.py +28 -0
- django_cfg/apps/maintenance/admin/deployments_admin.py +251 -0
- django_cfg/apps/maintenance/admin/events_admin.py +374 -0
- django_cfg/apps/maintenance/admin/monitoring_admin.py +215 -0
- django_cfg/apps/maintenance/admin/sites_admin.py +464 -0
- django_cfg/apps/maintenance/apps.py +105 -0
- django_cfg/apps/maintenance/management/__init__.py +0 -0
- django_cfg/apps/maintenance/management/commands/__init__.py +0 -0
- django_cfg/apps/maintenance/management/commands/maintenance.py +375 -0
- django_cfg/apps/maintenance/management/commands/sync_cloudflare.py +168 -0
- django_cfg/apps/maintenance/managers/__init__.py +20 -0
- django_cfg/apps/maintenance/managers/deployments.py +287 -0
- django_cfg/apps/maintenance/managers/events.py +374 -0
- django_cfg/apps/maintenance/managers/monitoring.py +301 -0
- django_cfg/apps/maintenance/managers/sites.py +335 -0
- django_cfg/apps/maintenance/migrations/0001_initial.py +939 -0
- django_cfg/apps/maintenance/migrations/__init__.py +0 -0
- django_cfg/apps/maintenance/models/__init__.py +27 -0
- django_cfg/apps/maintenance/models/cloudflare.py +316 -0
- django_cfg/apps/maintenance/models/maintenance.py +334 -0
- django_cfg/apps/maintenance/models/monitoring.py +393 -0
- django_cfg/apps/maintenance/models/sites.py +419 -0
- django_cfg/apps/maintenance/serializers/__init__.py +60 -0
- django_cfg/apps/maintenance/serializers/actions.py +310 -0
- django_cfg/apps/maintenance/serializers/base.py +44 -0
- django_cfg/apps/maintenance/serializers/deployments.py +209 -0
- django_cfg/apps/maintenance/serializers/events.py +210 -0
- django_cfg/apps/maintenance/serializers/monitoring.py +278 -0
- django_cfg/apps/maintenance/serializers/sites.py +213 -0
- django_cfg/apps/maintenance/services/README.md +168 -0
- django_cfg/apps/maintenance/services/__init__.py +21 -0
- django_cfg/apps/maintenance/services/cloudflare_client.py +441 -0
- django_cfg/apps/maintenance/services/dns_manager.py +497 -0
- django_cfg/apps/maintenance/services/maintenance_manager.py +504 -0
- django_cfg/apps/maintenance/services/site_sync.py +448 -0
- django_cfg/apps/maintenance/services/sync_command_service.py +330 -0
- django_cfg/apps/maintenance/services/worker_manager.py +264 -0
- django_cfg/apps/maintenance/signals.py +38 -0
- django_cfg/apps/maintenance/urls.py +36 -0
- django_cfg/apps/maintenance/views/__init__.py +18 -0
- django_cfg/apps/maintenance/views/base.py +61 -0
- django_cfg/apps/maintenance/views/deployments.py +175 -0
- django_cfg/apps/maintenance/views/events.py +204 -0
- django_cfg/apps/maintenance/views/monitoring.py +213 -0
- django_cfg/apps/maintenance/views/sites.py +338 -0
- django_cfg/apps/urls.py +5 -1
- django_cfg/core/config.py +42 -3
- django_cfg/core/generation.py +16 -5
- django_cfg/models/cloudflare.py +316 -0
- django_cfg/models/revolution.py +1 -1
- django_cfg/models/tasks.py +55 -1
- django_cfg/modules/base.py +12 -5
- django_cfg/modules/django_tasks.py +41 -3
- django_cfg/modules/django_unfold/dashboard.py +16 -1
- {django_cfg-1.2.14.dist-info → django_cfg-1.2.16.dist-info}/METADATA +2 -1
- {django_cfg-1.2.14.dist-info → django_cfg-1.2.16.dist-info}/RECORD +62 -14
- {django_cfg-1.2.14.dist-info → django_cfg-1.2.16.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.14.dist-info → django_cfg-1.2.16.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.14.dist-info → django_cfg-1.2.16.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,338 @@
|
|
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}")
|
django_cfg/apps/urls.py
CHANGED
@@ -45,8 +45,12 @@ def get_django_cfg_urlpatterns() -> List[URLPattern]:
|
|
45
45
|
# patterns.append(path('leads/', include('django_cfg.apps.leads.urls')))
|
46
46
|
|
47
47
|
# Tasks app - enabled when knowbase or agents are enabled
|
48
|
-
if base_module.
|
48
|
+
if base_module.should_enable_tasks():
|
49
49
|
patterns.append(path('tasks/', include('django_cfg.apps.tasks.urls')))
|
50
|
+
|
51
|
+
# Maintenance app - multi-site maintenance mode with Cloudflare
|
52
|
+
if base_module.is_maintenance_enabled():
|
53
|
+
patterns.append(path('maintenance/', include('django_cfg.apps.maintenance.urls')))
|
50
54
|
|
51
55
|
except Exception:
|
52
56
|
# Fallback: include all URLs if config is not available
|
django_cfg/core/config.py
CHANGED
@@ -20,6 +20,7 @@ from django_cfg import (
|
|
20
20
|
DatabaseConfig, CacheConfig, EmailConfig, TelegramConfig,
|
21
21
|
UnfoldConfig, DRFConfig, SpectacularConfig, LimitsConfig
|
22
22
|
)
|
23
|
+
from django_cfg.models.tasks import TaskConfig
|
23
24
|
|
24
25
|
# Default apps
|
25
26
|
DEFAULT_APPS = [
|
@@ -57,7 +58,7 @@ DEFAULT_APPS = [
|
|
57
58
|
"django_extensions",
|
58
59
|
"constance",
|
59
60
|
"constance.backends.database",
|
60
|
-
|
61
|
+
# Note: django_dramatiq is added conditionally when tasks are enabled
|
61
62
|
# Django CFG
|
62
63
|
"django_cfg",
|
63
64
|
"django_revolution",
|
@@ -244,6 +245,12 @@ class DjangoConfig(BaseModel):
|
|
244
245
|
description="Unfold admin interface configuration",
|
245
246
|
)
|
246
247
|
|
248
|
+
# === Background Task Processing ===
|
249
|
+
tasks: Optional[TaskConfig] = Field(
|
250
|
+
default=None,
|
251
|
+
description="Background task processing configuration (Dramatiq)",
|
252
|
+
)
|
253
|
+
|
247
254
|
# === API Configuration ===
|
248
255
|
# Note: DRF base configuration is handled by django-revolution
|
249
256
|
# These fields provide additional/extended settings on top of Revolution
|
@@ -263,6 +270,12 @@ class DjangoConfig(BaseModel):
|
|
263
270
|
description="Application limits configuration (file uploads, requests, etc.)",
|
264
271
|
)
|
265
272
|
|
273
|
+
# === Maintenance Mode Configuration ===
|
274
|
+
enable_maintenance: bool = Field(
|
275
|
+
default=False,
|
276
|
+
description="Enable django-cfg Maintenance application (multi-site maintenance mode with Cloudflare)",
|
277
|
+
)
|
278
|
+
|
266
279
|
# === Middleware Configuration ===
|
267
280
|
custom_middleware: List[str] = Field(
|
268
281
|
default_factory=list,
|
@@ -353,6 +366,7 @@ class DjangoConfig(BaseModel):
|
|
353
366
|
if "default" not in self.databases:
|
354
367
|
raise ConfigurationError("'default' database is required", context={"available_databases": list(self.databases.keys())}, suggestions=["Add a database with alias 'default'"])
|
355
368
|
|
369
|
+
|
356
370
|
# Validate database routing consistency - check migrate_to references
|
357
371
|
referenced_databases = set()
|
358
372
|
for alias, db_config in self.databases.items():
|
@@ -510,6 +524,28 @@ class DjangoConfig(BaseModel):
|
|
510
524
|
code=otp_code
|
511
525
|
)
|
512
526
|
|
527
|
+
def should_enable_tasks(self) -> bool:
|
528
|
+
"""
|
529
|
+
Determine if background tasks should be enabled.
|
530
|
+
|
531
|
+
Tasks are enabled if:
|
532
|
+
1. Explicitly configured via tasks field
|
533
|
+
2. Knowledge base is enabled (requires background processing)
|
534
|
+
3. Agents are enabled (requires background processing)
|
535
|
+
|
536
|
+
Returns:
|
537
|
+
True if tasks should be enabled, False otherwise
|
538
|
+
"""
|
539
|
+
# Check if explicitly configured
|
540
|
+
if hasattr(self, 'tasks') and self.tasks and self.tasks.enabled:
|
541
|
+
return True
|
542
|
+
|
543
|
+
# Check if features that require tasks are enabled
|
544
|
+
if self.enable_knowbase or self.enable_agents:
|
545
|
+
return True
|
546
|
+
|
547
|
+
return False
|
548
|
+
|
513
549
|
def get_installed_apps(self) -> List[str]:
|
514
550
|
"""
|
515
551
|
Get complete list of installed Django apps.
|
@@ -539,9 +575,12 @@ class DjangoConfig(BaseModel):
|
|
539
575
|
apps.append("django_cfg.apps.knowbase")
|
540
576
|
if self.enable_agents:
|
541
577
|
apps.append("django_cfg.apps.agents")
|
578
|
+
if self.enable_maintenance:
|
579
|
+
apps.append("django_cfg.apps.maintenance")
|
542
580
|
|
543
|
-
# Auto-enable tasks if
|
544
|
-
if self.
|
581
|
+
# Auto-enable tasks if needed
|
582
|
+
if self.should_enable_tasks():
|
583
|
+
apps.append("django_dramatiq") # Add django_dramatiq first
|
545
584
|
apps.append("django_cfg.apps.tasks")
|
546
585
|
|
547
586
|
# Auto-detect dashboard apps from Unfold callback
|
django_cfg/core/generation.py
CHANGED
@@ -418,11 +418,22 @@ class SettingsGenerator:
|
|
418
418
|
|
419
419
|
# Check for Tasks/Dramatiq configuration
|
420
420
|
try:
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
421
|
+
# Use the config's computed method to determine if tasks should be enabled
|
422
|
+
if config.should_enable_tasks():
|
423
|
+
from django_cfg.models.tasks import TaskConfig
|
424
|
+
from django_cfg.modules.django_tasks import generate_dramatiq_settings_from_config
|
425
|
+
|
426
|
+
# Auto-initialize TaskConfig if needed and generate settings
|
427
|
+
task_config = TaskConfig.auto_initialize_if_needed()
|
428
|
+
if task_config is not None:
|
429
|
+
dramatiq_settings = generate_dramatiq_settings_from_config()
|
430
|
+
if dramatiq_settings:
|
431
|
+
settings.update(dramatiq_settings)
|
432
|
+
integrations.append("dramatiq")
|
433
|
+
logger.info("✅ Dramatiq enabled (tasks/knowbase/agents required)")
|
434
|
+
else:
|
435
|
+
logger.debug("⏭️ Dramatiq disabled (no tasks/knowbase/agents)")
|
436
|
+
|
426
437
|
except ImportError as e:
|
427
438
|
logger.warning(f"Failed to import django_tasks module: {e}")
|
428
439
|
except Exception as e:
|