django-cfg 1.4.120__py3-none-any.whl → 1.5.2__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.
Potentially problematic release.
This version of django-cfg might be problematic. Click here for more details.
- django_cfg/__init__.py +8 -4
- django_cfg/apps/centrifugo/admin/centrifugo_log.py +33 -71
- django_cfg/apps/dashboard/TRANSACTION_FIX.md +73 -0
- django_cfg/apps/dashboard/serializers/__init__.py +0 -12
- django_cfg/apps/dashboard/serializers/activity.py +1 -1
- django_cfg/apps/dashboard/services/__init__.py +0 -2
- django_cfg/apps/dashboard/services/charts_service.py +4 -3
- django_cfg/apps/dashboard/services/statistics_service.py +11 -2
- django_cfg/apps/dashboard/services/system_health_service.py +64 -106
- django_cfg/apps/dashboard/urls.py +0 -2
- django_cfg/apps/dashboard/views/__init__.py +0 -2
- django_cfg/apps/dashboard/views/commands_views.py +3 -6
- django_cfg/apps/dashboard/views/overview_views.py +14 -13
- django_cfg/apps/grpc/__init__.py +9 -0
- django_cfg/apps/grpc/admin/__init__.py +11 -0
- django_cfg/apps/{tasks → grpc}/admin/config.py +32 -41
- django_cfg/apps/grpc/admin/grpc_request_log.py +252 -0
- django_cfg/apps/grpc/apps.py +28 -0
- django_cfg/apps/grpc/auth/__init__.py +9 -0
- django_cfg/apps/grpc/auth/jwt_auth.py +295 -0
- django_cfg/apps/grpc/interceptors/__init__.py +19 -0
- django_cfg/apps/grpc/interceptors/errors.py +241 -0
- django_cfg/apps/grpc/interceptors/logging.py +270 -0
- django_cfg/apps/grpc/interceptors/metrics.py +306 -0
- django_cfg/apps/grpc/interceptors/request_logger.py +515 -0
- django_cfg/apps/grpc/management/__init__.py +1 -0
- django_cfg/apps/grpc/management/commands/rungrpc.py +302 -0
- django_cfg/apps/grpc/managers/__init__.py +10 -0
- django_cfg/apps/grpc/managers/grpc_request_log.py +310 -0
- django_cfg/apps/grpc/migrations/0001_initial.py +69 -0
- django_cfg/apps/grpc/migrations/0002_rename_django_cfg__service_4c4a8e_idx_django_cfg__service_584308_idx_and_more.py +38 -0
- django_cfg/apps/grpc/models/__init__.py +9 -0
- django_cfg/apps/grpc/models/grpc_request_log.py +219 -0
- django_cfg/apps/grpc/serializers/__init__.py +23 -0
- django_cfg/apps/grpc/serializers/health.py +18 -0
- django_cfg/apps/grpc/serializers/requests.py +18 -0
- django_cfg/apps/grpc/serializers/services.py +50 -0
- django_cfg/apps/grpc/serializers/stats.py +22 -0
- django_cfg/apps/grpc/services/__init__.py +16 -0
- django_cfg/apps/grpc/services/base.py +375 -0
- django_cfg/apps/grpc/services/discovery.py +415 -0
- django_cfg/apps/grpc/urls.py +23 -0
- django_cfg/apps/grpc/utils/__init__.py +13 -0
- django_cfg/apps/grpc/utils/proto_gen.py +423 -0
- django_cfg/apps/grpc/views/__init__.py +9 -0
- django_cfg/apps/grpc/views/monitoring.py +497 -0
- django_cfg/apps/knowbase/apps.py +2 -2
- django_cfg/apps/maintenance/admin/api_key_admin.py +7 -9
- django_cfg/apps/maintenance/admin/site_admin.py +5 -4
- django_cfg/apps/newsletter/admin/newsletter_admin.py +12 -11
- django_cfg/apps/payments/admin/balance_admin.py +26 -36
- django_cfg/apps/payments/admin/payment_admin.py +65 -85
- django_cfg/apps/payments/admin/withdrawal_admin.py +65 -100
- django_cfg/apps/rq/__init__.py +9 -0
- django_cfg/apps/rq/apps.py +80 -0
- django_cfg/apps/rq/management/__init__.py +1 -0
- django_cfg/apps/rq/management/commands/__init__.py +1 -0
- django_cfg/apps/rq/management/commands/rqscheduler.py +31 -0
- django_cfg/apps/rq/management/commands/rqstats.py +33 -0
- django_cfg/apps/rq/management/commands/rqworker.py +31 -0
- django_cfg/apps/rq/management/commands/rqworker_pool.py +27 -0
- django_cfg/apps/rq/serializers/__init__.py +40 -0
- django_cfg/apps/rq/serializers/health.py +60 -0
- django_cfg/apps/rq/serializers/job.py +100 -0
- django_cfg/apps/rq/serializers/queue.py +80 -0
- django_cfg/apps/rq/serializers/schedule.py +178 -0
- django_cfg/apps/rq/serializers/testing.py +139 -0
- django_cfg/apps/rq/serializers/worker.py +58 -0
- django_cfg/apps/rq/services/__init__.py +25 -0
- django_cfg/apps/rq/services/config_helper.py +233 -0
- django_cfg/apps/rq/services/models/README.md +417 -0
- django_cfg/apps/rq/services/models/__init__.py +30 -0
- django_cfg/apps/rq/services/models/event.py +123 -0
- django_cfg/apps/rq/services/models/job.py +99 -0
- django_cfg/apps/rq/services/models/queue.py +92 -0
- django_cfg/apps/rq/services/models/worker.py +104 -0
- django_cfg/apps/rq/services/rq_converters.py +183 -0
- django_cfg/apps/rq/tasks/__init__.py +23 -0
- django_cfg/apps/rq/tasks/demo_tasks.py +284 -0
- django_cfg/apps/rq/urls.py +54 -0
- django_cfg/apps/rq/views/__init__.py +19 -0
- django_cfg/apps/rq/views/jobs.py +882 -0
- django_cfg/apps/rq/views/monitoring.py +248 -0
- django_cfg/apps/rq/views/queues.py +261 -0
- django_cfg/apps/rq/views/schedule.py +400 -0
- django_cfg/apps/rq/views/testing.py +761 -0
- django_cfg/apps/rq/views/workers.py +195 -0
- django_cfg/apps/urls.py +13 -8
- django_cfg/config.py +106 -0
- django_cfg/core/base/config_model.py +16 -26
- django_cfg/core/builders/apps_builder.py +7 -11
- django_cfg/core/generation/integration_generators/__init__.py +3 -6
- django_cfg/core/generation/integration_generators/django_rq.py +80 -0
- django_cfg/core/generation/integration_generators/grpc_generator.py +318 -0
- django_cfg/core/generation/orchestrator.py +15 -15
- django_cfg/core/integration/display/startup.py +6 -20
- django_cfg/mixins/__init__.py +2 -0
- django_cfg/mixins/superadmin_api.py +59 -0
- django_cfg/models/__init__.py +3 -3
- django_cfg/models/api/grpc/__init__.py +59 -0
- django_cfg/models/api/grpc/config.py +364 -0
- django_cfg/models/django/__init__.py +3 -3
- django_cfg/models/django/django_rq.py +621 -0
- django_cfg/models/django/revolution_legacy.py +1 -1
- django_cfg/modules/base.py +19 -6
- django_cfg/modules/django_admin/base/pydantic_admin.py +2 -2
- django_cfg/modules/django_admin/config/background_task_config.py +4 -4
- django_cfg/modules/django_admin/utils/__init__.py +41 -3
- django_cfg/modules/django_admin/utils/badges/__init__.py +13 -0
- django_cfg/modules/django_admin/utils/{badges.py → badges/status_badges.py} +3 -3
- django_cfg/modules/django_admin/utils/displays/__init__.py +13 -0
- django_cfg/modules/django_admin/utils/{displays.py → displays/data_displays.py} +2 -2
- django_cfg/modules/django_admin/utils/html/__init__.py +26 -0
- django_cfg/modules/django_admin/utils/html/badges.py +47 -0
- django_cfg/modules/django_admin/utils/html/base.py +167 -0
- django_cfg/modules/django_admin/utils/html/code.py +87 -0
- django_cfg/modules/django_admin/utils/html/composition.py +205 -0
- django_cfg/modules/django_admin/utils/html/formatting.py +231 -0
- django_cfg/modules/django_admin/utils/html/keyvalue.py +219 -0
- django_cfg/modules/django_admin/utils/html/markdown_integration.py +108 -0
- django_cfg/modules/django_admin/utils/html/progress.py +127 -0
- django_cfg/modules/django_admin/utils/html_builder.py +55 -408
- django_cfg/modules/django_admin/utils/markdown/__init__.py +21 -0
- django_cfg/modules/django_unfold/navigation.py +21 -18
- django_cfg/pyproject.toml +4 -6
- django_cfg/registry/core.py +4 -7
- django_cfg/registry/modules.py +6 -0
- django_cfg/static/frontend/admin.zip +0 -0
- django_cfg/templates/admin/constance/includes/results_list.html +73 -0
- django_cfg/templates/admin/index.html +187 -62
- django_cfg/templatetags/django_cfg.py +61 -1
- {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/METADATA +12 -4
- {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/RECORD +140 -96
- django_cfg/apps/dashboard/permissions.py +0 -48
- django_cfg/apps/dashboard/serializers/django_q2.py +0 -50
- django_cfg/apps/dashboard/services/django_q2_service.py +0 -159
- django_cfg/apps/dashboard/views/django_q2_views.py +0 -79
- django_cfg/apps/tasks/__init__.py +0 -64
- django_cfg/apps/tasks/admin/__init__.py +0 -4
- django_cfg/apps/tasks/admin/task_log.py +0 -265
- django_cfg/apps/tasks/apps.py +0 -15
- django_cfg/apps/tasks/filters/__init__.py +0 -10
- django_cfg/apps/tasks/filters/task_log.py +0 -121
- django_cfg/apps/tasks/migrations/0001_initial.py +0 -196
- django_cfg/apps/tasks/migrations/0002_delete_tasklog.py +0 -16
- django_cfg/apps/tasks/models/__init__.py +0 -4
- django_cfg/apps/tasks/models/task_log.py +0 -246
- django_cfg/apps/tasks/serializers/__init__.py +0 -28
- django_cfg/apps/tasks/serializers/task_log.py +0 -249
- django_cfg/apps/tasks/services/__init__.py +0 -10
- django_cfg/apps/tasks/services/client/__init__.py +0 -7
- django_cfg/apps/tasks/services/client/client.py +0 -234
- django_cfg/apps/tasks/services/config_helper.py +0 -63
- django_cfg/apps/tasks/services/sync.py +0 -204
- django_cfg/apps/tasks/urls.py +0 -16
- django_cfg/apps/tasks/views/__init__.py +0 -10
- django_cfg/apps/tasks/views/task_log.py +0 -41
- django_cfg/apps/tasks/views/task_log_base.py +0 -41
- django_cfg/apps/tasks/views/task_log_overview.py +0 -100
- django_cfg/apps/tasks/views/task_log_related.py +0 -41
- django_cfg/apps/tasks/views/task_log_stats.py +0 -91
- django_cfg/apps/tasks/views/task_log_timeline.py +0 -81
- django_cfg/core/generation/integration_generators/django_q2.py +0 -133
- django_cfg/core/generation/integration_generators/tasks.py +0 -88
- django_cfg/models/django/django_q2.py +0 -514
- django_cfg/models/tasks/__init__.py +0 -49
- django_cfg/models/tasks/backends.py +0 -122
- django_cfg/models/tasks/config.py +0 -209
- django_cfg/models/tasks/utils.py +0 -162
- django_cfg/modules/django_admin/utils/CODE_BLOCK_DOCS.md +0 -396
- django_cfg/modules/django_q2/README.md +0 -140
- django_cfg/modules/django_q2/__init__.py +0 -8
- django_cfg/modules/django_q2/apps.py +0 -107
- django_cfg/modules/django_q2/management/commands/__init__.py +0 -0
- django_cfg/modules/django_q2/management/commands/sync_django_q_schedules.py +0 -74
- /django_cfg/apps/{tasks/migrations → grpc/management/commands}/__init__.py +0 -0
- /django_cfg/{modules/django_q2/management → apps/grpc/migrations}/__init__.py +0 -0
- /django_cfg/modules/django_admin/utils/{mermaid_plugin.py → markdown/mermaid_plugin.py} +0 -0
- /django_cfg/modules/django_admin/utils/{markdown_renderer.py → markdown/renderer.py} +0 -0
- {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"""
|
|
2
|
+
gRPC framework generator.
|
|
3
|
+
|
|
4
|
+
Handles gRPC server, authentication, and proto configuration.
|
|
5
|
+
Size: ~250 lines (focused on gRPC framework)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Dict, List
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from ...base.config_model import DjangoConfig
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class GRPCSettingsGenerator:
|
|
18
|
+
"""
|
|
19
|
+
Generates gRPC framework settings.
|
|
20
|
+
|
|
21
|
+
Responsibilities:
|
|
22
|
+
- Configure gRPC server settings
|
|
23
|
+
- Setup authentication and interceptors
|
|
24
|
+
- Configure proto generation
|
|
25
|
+
- Auto-detect if gRPC should be enabled
|
|
26
|
+
- Resolve handlers hook from ROOT_URLCONF
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
```python
|
|
30
|
+
generator = GRPCSettingsGenerator(config)
|
|
31
|
+
settings = generator.generate()
|
|
32
|
+
```
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, config: "DjangoConfig"):
|
|
36
|
+
"""
|
|
37
|
+
Initialize generator with configuration.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
config: DjangoConfig instance
|
|
41
|
+
"""
|
|
42
|
+
self.config = config
|
|
43
|
+
|
|
44
|
+
def generate(self) -> Dict[str, Any]:
|
|
45
|
+
"""
|
|
46
|
+
Generate gRPC framework settings.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Dictionary with gRPC configuration
|
|
50
|
+
|
|
51
|
+
Example:
|
|
52
|
+
>>> generator = GRPCSettingsGenerator(config)
|
|
53
|
+
>>> settings = generator.generate()
|
|
54
|
+
"""
|
|
55
|
+
# Check if gRPC should be enabled
|
|
56
|
+
if not self._should_enable_grpc():
|
|
57
|
+
logger.debug("⏭️ gRPC disabled")
|
|
58
|
+
return {}
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
return self._generate_grpc_settings()
|
|
62
|
+
except ImportError as e:
|
|
63
|
+
logger.warning(f"Failed to import gRPC dependencies: {e}")
|
|
64
|
+
logger.info("💡 Install with: pip install django-cfg[grpc]")
|
|
65
|
+
return {}
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.error(f"Failed to generate gRPC settings: {e}")
|
|
68
|
+
return {}
|
|
69
|
+
|
|
70
|
+
def _should_enable_grpc(self) -> bool:
|
|
71
|
+
"""
|
|
72
|
+
Check if gRPC should be enabled.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
True if gRPC should be enabled
|
|
76
|
+
"""
|
|
77
|
+
# Check if grpc config exists and is enabled
|
|
78
|
+
if not hasattr(self.config, "grpc") or not self.config.grpc:
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
if not self.config.grpc.enabled:
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
# Check if gRPC feature is available
|
|
85
|
+
from django_cfg.config import is_feature_available
|
|
86
|
+
if not is_feature_available("grpc"):
|
|
87
|
+
logger.warning("gRPC enabled but dependencies not installed")
|
|
88
|
+
logger.info("💡 Install with: pip install django-cfg[grpc]")
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
return True
|
|
92
|
+
|
|
93
|
+
def _generate_grpc_settings(self) -> Dict[str, Any]:
|
|
94
|
+
"""
|
|
95
|
+
Generate gRPC-specific settings.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Dictionary with gRPC configuration
|
|
99
|
+
"""
|
|
100
|
+
settings = {}
|
|
101
|
+
|
|
102
|
+
# Generate GRPC_FRAMEWORK settings
|
|
103
|
+
grpc_framework = self._build_grpc_framework_settings()
|
|
104
|
+
if grpc_framework:
|
|
105
|
+
settings["GRPC_FRAMEWORK"] = grpc_framework
|
|
106
|
+
|
|
107
|
+
# Generate server-specific settings
|
|
108
|
+
grpc_server = self._build_grpc_server_settings()
|
|
109
|
+
if grpc_server:
|
|
110
|
+
settings["GRPC_SERVER"] = grpc_server
|
|
111
|
+
|
|
112
|
+
# Generate auth-specific settings
|
|
113
|
+
grpc_auth = self._build_grpc_auth_settings()
|
|
114
|
+
if grpc_auth:
|
|
115
|
+
settings["GRPC_AUTH"] = grpc_auth
|
|
116
|
+
|
|
117
|
+
# Generate proto-specific settings
|
|
118
|
+
grpc_proto = self._build_grpc_proto_settings()
|
|
119
|
+
if grpc_proto:
|
|
120
|
+
settings["GRPC_PROTO"] = grpc_proto
|
|
121
|
+
|
|
122
|
+
logger.info("✅ gRPC framework enabled")
|
|
123
|
+
logger.info(f" - Server: {self.config.grpc.server.host}:{self.config.grpc.server.port}")
|
|
124
|
+
logger.info(f" - Workers: {self.config.grpc.server.max_workers}")
|
|
125
|
+
logger.info(f" - Auth: {'enabled' if self.config.grpc.auth.enabled else 'disabled'}")
|
|
126
|
+
logger.info(f" - Reflection: {'enabled' if self.config.grpc.server.enable_reflection else 'disabled'}")
|
|
127
|
+
|
|
128
|
+
return settings
|
|
129
|
+
|
|
130
|
+
def _build_grpc_framework_settings(self) -> Dict[str, Any]:
|
|
131
|
+
"""
|
|
132
|
+
Build GRPC_FRAMEWORK settings dictionary.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
Dictionary with framework-level gRPC settings
|
|
136
|
+
"""
|
|
137
|
+
grpc_config = self.config.grpc
|
|
138
|
+
|
|
139
|
+
# Resolve handlers hook (replace {ROOT_URLCONF} placeholder)
|
|
140
|
+
handlers_hook = self._resolve_handlers_hook(grpc_config.handlers_hook)
|
|
141
|
+
|
|
142
|
+
# Build interceptors list
|
|
143
|
+
interceptors = self._build_interceptors()
|
|
144
|
+
|
|
145
|
+
framework_settings = {
|
|
146
|
+
"ROOT_HANDLERS_HOOK": handlers_hook,
|
|
147
|
+
"SERVER_INTERCEPTORS": interceptors,
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# Add auto-registration settings
|
|
151
|
+
if grpc_config.auto_register_apps:
|
|
152
|
+
framework_settings["AUTO_REGISTER_APPS"] = grpc_config.enabled_apps
|
|
153
|
+
|
|
154
|
+
# Add custom services
|
|
155
|
+
if grpc_config.custom_services:
|
|
156
|
+
framework_settings["CUSTOM_SERVICES"] = grpc_config.custom_services
|
|
157
|
+
|
|
158
|
+
return framework_settings
|
|
159
|
+
|
|
160
|
+
def _build_grpc_server_settings(self) -> Dict[str, Any]:
|
|
161
|
+
"""
|
|
162
|
+
Build GRPC_SERVER settings dictionary.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Dictionary with server configuration
|
|
166
|
+
"""
|
|
167
|
+
server_config = self.config.grpc.server
|
|
168
|
+
|
|
169
|
+
server_settings = {
|
|
170
|
+
"host": server_config.host,
|
|
171
|
+
"port": server_config.port,
|
|
172
|
+
"max_workers": server_config.max_workers,
|
|
173
|
+
"enable_reflection": server_config.enable_reflection,
|
|
174
|
+
"enable_health_check": server_config.enable_health_check,
|
|
175
|
+
"max_send_message_length": server_config.max_send_message_length,
|
|
176
|
+
"max_receive_message_length": server_config.max_receive_message_length,
|
|
177
|
+
"keepalive_time_ms": server_config.keepalive_time_ms,
|
|
178
|
+
"keepalive_timeout_ms": server_config.keepalive_timeout_ms,
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# Add optional compression
|
|
182
|
+
if server_config.compression:
|
|
183
|
+
server_settings["compression"] = server_config.compression
|
|
184
|
+
|
|
185
|
+
# Add custom interceptors from config
|
|
186
|
+
if server_config.interceptors:
|
|
187
|
+
server_settings["custom_interceptors"] = server_config.interceptors
|
|
188
|
+
|
|
189
|
+
return server_settings
|
|
190
|
+
|
|
191
|
+
def _build_grpc_auth_settings(self) -> Dict[str, Any]:
|
|
192
|
+
"""
|
|
193
|
+
Build GRPC_AUTH settings dictionary.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Dictionary with authentication configuration
|
|
197
|
+
"""
|
|
198
|
+
auth_config = self.config.grpc.auth
|
|
199
|
+
|
|
200
|
+
auth_settings = {
|
|
201
|
+
"enabled": auth_config.enabled,
|
|
202
|
+
"require_auth": auth_config.require_auth,
|
|
203
|
+
"token_header": auth_config.token_header,
|
|
204
|
+
"token_prefix": auth_config.token_prefix,
|
|
205
|
+
"jwt_algorithm": auth_config.jwt_algorithm,
|
|
206
|
+
"jwt_verify_exp": auth_config.jwt_verify_exp,
|
|
207
|
+
"jwt_leeway": auth_config.jwt_leeway,
|
|
208
|
+
"public_methods": auth_config.public_methods,
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
# Use JWT secret from auth config or fall back to Django SECRET_KEY
|
|
212
|
+
if auth_config.jwt_secret_key:
|
|
213
|
+
auth_settings["jwt_secret_key"] = auth_config.jwt_secret_key
|
|
214
|
+
else:
|
|
215
|
+
# Will be resolved from Django settings at runtime
|
|
216
|
+
auth_settings["jwt_secret_key"] = None # Signal to use Django SECRET_KEY
|
|
217
|
+
|
|
218
|
+
return auth_settings
|
|
219
|
+
|
|
220
|
+
def _build_grpc_proto_settings(self) -> Dict[str, Any]:
|
|
221
|
+
"""
|
|
222
|
+
Build GRPC_PROTO settings dictionary.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
Dictionary with proto generation configuration
|
|
226
|
+
"""
|
|
227
|
+
proto_config = self.config.grpc.proto
|
|
228
|
+
|
|
229
|
+
proto_settings = {
|
|
230
|
+
"auto_generate": proto_config.auto_generate,
|
|
231
|
+
"output_dir": proto_config.output_dir,
|
|
232
|
+
"package_prefix": proto_config.package_prefix,
|
|
233
|
+
"include_services": proto_config.include_services,
|
|
234
|
+
"field_naming": proto_config.field_naming,
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return proto_settings
|
|
238
|
+
|
|
239
|
+
def _build_interceptors(self) -> List[str]:
|
|
240
|
+
"""
|
|
241
|
+
Build list of server interceptors.
|
|
242
|
+
|
|
243
|
+
Interceptors are added in order:
|
|
244
|
+
1. Request logger interceptor (always enabled for monitoring)
|
|
245
|
+
2. Logging interceptor (if dev mode)
|
|
246
|
+
3. Auth interceptor (if auth enabled)
|
|
247
|
+
4. Metrics interceptor (if dev mode)
|
|
248
|
+
5. Custom interceptors (from config)
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
List of interceptor class paths
|
|
252
|
+
"""
|
|
253
|
+
interceptors = []
|
|
254
|
+
|
|
255
|
+
# Check if we're in dev mode
|
|
256
|
+
is_dev = self.config.env_mode in ("local", "development", "dev")
|
|
257
|
+
|
|
258
|
+
# Add request logger interceptor (always enabled for DB logging)
|
|
259
|
+
interceptors.append(
|
|
260
|
+
"django_cfg.apps.grpc.interceptors.RequestLoggerInterceptor"
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Add logging interceptor in dev mode
|
|
264
|
+
if is_dev:
|
|
265
|
+
interceptors.append(
|
|
266
|
+
"django_cfg.apps.grpc.interceptors.LoggingInterceptor"
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# Add auth interceptor if enabled
|
|
270
|
+
if self.config.grpc.auth.enabled:
|
|
271
|
+
interceptors.append(
|
|
272
|
+
"django_cfg.apps.grpc.auth.JWTAuthInterceptor"
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
# Add metrics interceptor in dev mode
|
|
276
|
+
if is_dev:
|
|
277
|
+
interceptors.append(
|
|
278
|
+
"django_cfg.apps.grpc.interceptors.MetricsInterceptor"
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# Add custom interceptors from server config
|
|
282
|
+
if self.config.grpc.server.interceptors:
|
|
283
|
+
interceptors.extend(self.config.grpc.server.interceptors)
|
|
284
|
+
|
|
285
|
+
return interceptors
|
|
286
|
+
|
|
287
|
+
def _resolve_handlers_hook(self, handlers_hook: str) -> str:
|
|
288
|
+
"""
|
|
289
|
+
Resolve handlers hook path.
|
|
290
|
+
|
|
291
|
+
Replaces {ROOT_URLCONF} placeholder with actual ROOT_URLCONF value.
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
handlers_hook: Handler hook path (may contain {ROOT_URLCONF})
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
Resolved handler hook path
|
|
298
|
+
|
|
299
|
+
Example:
|
|
300
|
+
>>> self._resolve_handlers_hook("{ROOT_URLCONF}.grpc_handlers")
|
|
301
|
+
'myproject.urls.grpc_handlers'
|
|
302
|
+
"""
|
|
303
|
+
if "{ROOT_URLCONF}" in handlers_hook:
|
|
304
|
+
# Get ROOT_URLCONF from config
|
|
305
|
+
root_urlconf = getattr(self.config, "root_urlconf", None)
|
|
306
|
+
if not root_urlconf:
|
|
307
|
+
# Fall back to default Django pattern
|
|
308
|
+
root_urlconf = f"{self.config.project_name}.urls"
|
|
309
|
+
logger.debug(
|
|
310
|
+
f"ROOT_URLCONF not set, using default: {root_urlconf}"
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
handlers_hook = handlers_hook.replace("{ROOT_URLCONF}", root_urlconf)
|
|
314
|
+
|
|
315
|
+
return handlers_hook
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
__all__ = ["GRPCSettingsGenerator"]
|
|
@@ -77,8 +77,8 @@ class SettingsOrchestrator:
|
|
|
77
77
|
settings.update(self._generate_session_settings())
|
|
78
78
|
settings.update(self._generate_third_party_settings())
|
|
79
79
|
settings.update(self._generate_api_settings())
|
|
80
|
-
settings.update(self.
|
|
81
|
-
settings.update(self.
|
|
80
|
+
settings.update(self._generate_django_rq_settings())
|
|
81
|
+
settings.update(self._generate_grpc_settings())
|
|
82
82
|
settings.update(self._generate_tailwind_settings())
|
|
83
83
|
|
|
84
84
|
# Apply additional settings (user overrides)
|
|
@@ -215,26 +215,26 @@ class SettingsOrchestrator:
|
|
|
215
215
|
except Exception as e:
|
|
216
216
|
raise ConfigurationError(f"Failed to generate API settings: {e}") from e
|
|
217
217
|
|
|
218
|
-
def
|
|
219
|
-
"""Generate
|
|
218
|
+
def _generate_django_rq_settings(self) -> Dict[str, Any]:
|
|
219
|
+
"""Generate Django-RQ task queue and scheduler settings."""
|
|
220
|
+
if not hasattr(self.config, "django_rq") or not self.config.django_rq:
|
|
221
|
+
return {}
|
|
222
|
+
|
|
220
223
|
try:
|
|
221
|
-
from .integration_generators.
|
|
222
|
-
generator =
|
|
224
|
+
from .integration_generators.django_rq import DjangoRQSettingsGenerator
|
|
225
|
+
generator = DjangoRQSettingsGenerator(self.config.django_rq, parent_config=self.config)
|
|
223
226
|
return generator.generate()
|
|
224
227
|
except Exception as e:
|
|
225
|
-
raise ConfigurationError(f"Failed to generate
|
|
226
|
-
|
|
227
|
-
def _generate_django_q2_settings(self) -> Dict[str, Any]:
|
|
228
|
-
"""Generate Django-Q2 task scheduling settings."""
|
|
229
|
-
if not hasattr(self.config, "django_q2") or not self.config.django_q2:
|
|
230
|
-
return {}
|
|
228
|
+
raise ConfigurationError(f"Failed to generate Django-RQ settings: {e}") from e
|
|
231
229
|
|
|
230
|
+
def _generate_grpc_settings(self) -> Dict[str, Any]:
|
|
231
|
+
"""Generate gRPC framework settings."""
|
|
232
232
|
try:
|
|
233
|
-
from .integration_generators.
|
|
234
|
-
generator =
|
|
233
|
+
from .integration_generators.grpc_generator import GRPCSettingsGenerator
|
|
234
|
+
generator = GRPCSettingsGenerator(self.config)
|
|
235
235
|
return generator.generate()
|
|
236
236
|
except Exception as e:
|
|
237
|
-
raise ConfigurationError(f"Failed to generate
|
|
237
|
+
raise ConfigurationError(f"Failed to generate gRPC settings: {e}") from e
|
|
238
238
|
|
|
239
239
|
def _generate_tailwind_settings(self) -> Dict[str, Any]:
|
|
240
240
|
"""Generate Tailwind CSS settings."""
|
|
@@ -416,20 +416,20 @@ class StartupDisplayManager(BaseDisplayManager):
|
|
|
416
416
|
task_table.add_column("Value", style="white")
|
|
417
417
|
|
|
418
418
|
# Show real tasks status
|
|
419
|
-
tasks_enabled = self.config.
|
|
419
|
+
tasks_enabled = self.config.should_enable_rq()
|
|
420
420
|
if tasks_enabled:
|
|
421
421
|
task_table.add_row("Tasks Enabled", "[green]True[/green]")
|
|
422
422
|
else:
|
|
423
423
|
task_table.add_row("Tasks Enabled", "[yellow]False[/yellow]")
|
|
424
424
|
|
|
425
|
-
if hasattr(self.config, '
|
|
426
|
-
|
|
427
|
-
task_table.add_row("
|
|
425
|
+
if hasattr(self.config, 'django_rq') and self.config.django_rq:
|
|
426
|
+
queue_names = ', '.join(self.config.django_rq.get_queue_names())
|
|
427
|
+
task_table.add_row("Queues", f"[yellow]{queue_names}[/yellow]")
|
|
428
428
|
else:
|
|
429
|
-
task_table.add_row("
|
|
429
|
+
task_table.add_row("Queues", "[yellow]default[/yellow]")
|
|
430
430
|
|
|
431
431
|
# Add worker command
|
|
432
|
-
task_table.add_row("Start Workers", "[bright_blue]poetry run python manage.py
|
|
432
|
+
task_table.add_row("Start Workers", "[bright_blue]poetry run python manage.py rqworker default[/bright_blue]")
|
|
433
433
|
|
|
434
434
|
task_panel = self.create_full_width_panel(
|
|
435
435
|
task_table,
|
|
@@ -465,13 +465,6 @@ class StartupDisplayManager(BaseDisplayManager):
|
|
|
465
465
|
payments_count = 0
|
|
466
466
|
|
|
467
467
|
config = self.config
|
|
468
|
-
if config and config.should_enable_tasks():
|
|
469
|
-
try:
|
|
470
|
-
from django_cfg.modules.django_tasks import extend_constance_config_with_tasks
|
|
471
|
-
tasks_fields = extend_constance_config_with_tasks()
|
|
472
|
-
tasks_count = len(tasks_fields)
|
|
473
|
-
except:
|
|
474
|
-
pass
|
|
475
468
|
|
|
476
469
|
if config and config.enable_knowbase:
|
|
477
470
|
try:
|
|
@@ -623,13 +616,6 @@ class StartupDisplayManager(BaseDisplayManager):
|
|
|
623
616
|
|
|
624
617
|
# Try to get individual app field counts
|
|
625
618
|
config = self.config
|
|
626
|
-
if config and config.should_enable_tasks():
|
|
627
|
-
try:
|
|
628
|
-
from django_cfg.modules.django_tasks import extend_constance_config_with_tasks
|
|
629
|
-
tasks_fields = extend_constance_config_with_tasks()
|
|
630
|
-
tasks_count = len(tasks_fields)
|
|
631
|
-
except:
|
|
632
|
-
pass
|
|
633
619
|
|
|
634
620
|
if config and config.enable_knowbase:
|
|
635
621
|
try:
|
django_cfg/mixins/__init__.py
CHANGED
|
@@ -5,8 +5,10 @@ Shared mixins for DRF views and viewsets.
|
|
|
5
5
|
"""
|
|
6
6
|
from .admin_api import AdminAPIMixin
|
|
7
7
|
from .client_api import ClientAPIMixin
|
|
8
|
+
from .superadmin_api import SuperAdminAPIMixin
|
|
8
9
|
|
|
9
10
|
__all__ = [
|
|
10
11
|
"AdminAPIMixin",
|
|
11
12
|
"ClientAPIMixin",
|
|
13
|
+
"SuperAdminAPIMixin",
|
|
12
14
|
]
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SuperAdmin API Mixin.
|
|
3
|
+
|
|
4
|
+
Common configuration for superuser-only API endpoints.
|
|
5
|
+
More restrictive than AdminAPIMixin - requires is_superuser flag.
|
|
6
|
+
"""
|
|
7
|
+
from rest_framework.authentication import BasicAuthentication, SessionAuthentication
|
|
8
|
+
from rest_framework.permissions import BasePermission
|
|
9
|
+
from rest_framework_simplejwt.authentication import JWTAuthentication
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class IsSuperUser(BasePermission):
|
|
13
|
+
"""
|
|
14
|
+
Permission that allows access only to superusers.
|
|
15
|
+
|
|
16
|
+
More restrictive than IsAdminUser - requires is_superuser flag.
|
|
17
|
+
Use for sensitive operations like command execution.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def has_permission(self, request, view):
|
|
21
|
+
"""Check if user is authenticated and is a superuser."""
|
|
22
|
+
return bool(
|
|
23
|
+
request.user and
|
|
24
|
+
request.user.is_authenticated and
|
|
25
|
+
request.user.is_superuser
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SuperAdminAPIMixin:
|
|
30
|
+
"""
|
|
31
|
+
Mixin for superuser-only API endpoints.
|
|
32
|
+
|
|
33
|
+
Provides:
|
|
34
|
+
- JWT, Session, and Basic authentication
|
|
35
|
+
- IsSuperUser permission requirement (is_superuser=True)
|
|
36
|
+
|
|
37
|
+
Usage:
|
|
38
|
+
class MyViewSet(SuperAdminAPIMixin, viewsets.ModelViewSet):
|
|
39
|
+
queryset = MyModel.objects.all()
|
|
40
|
+
serializer_class = MySerializer
|
|
41
|
+
|
|
42
|
+
Authentication Methods:
|
|
43
|
+
1. JWT Token (Bearer): For frontend SPA authentication
|
|
44
|
+
2. Session: For Django admin integration
|
|
45
|
+
3. Basic Auth: For testing and scripts
|
|
46
|
+
|
|
47
|
+
All endpoints require superuser privileges.
|
|
48
|
+
Only use this for sensitive operations like:
|
|
49
|
+
- Command execution
|
|
50
|
+
- System configuration changes
|
|
51
|
+
- Direct database operations
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
authentication_classes = [
|
|
55
|
+
JWTAuthentication, # JWT tokens (Bearer)
|
|
56
|
+
SessionAuthentication, # Django session (for admin)
|
|
57
|
+
BasicAuthentication, # HTTP Basic (for testing)
|
|
58
|
+
]
|
|
59
|
+
permission_classes = [IsSuperUser]
|
django_cfg/models/__init__.py
CHANGED
|
@@ -35,7 +35,7 @@ from .base.module import BaseCfgAutoModule
|
|
|
35
35
|
from .django.axes import AxesConfig
|
|
36
36
|
from .django.constance import ConstanceConfig, ConstanceField
|
|
37
37
|
from .django.crypto_fields import CryptoFieldsConfig
|
|
38
|
-
from .django.
|
|
38
|
+
from .django.django_rq import DjangoRQConfig, RQQueueConfig
|
|
39
39
|
from .django.environment import EnvironmentConfig
|
|
40
40
|
from .django.openapi import OpenAPIClientConfig
|
|
41
41
|
from .infrastructure.cache import CacheConfig
|
|
@@ -80,8 +80,8 @@ __all__ = [
|
|
|
80
80
|
"EnvironmentConfig",
|
|
81
81
|
"ConstanceConfig",
|
|
82
82
|
"ConstanceField",
|
|
83
|
-
"
|
|
84
|
-
"
|
|
83
|
+
"DjangoRQConfig",
|
|
84
|
+
"RQQueueConfig",
|
|
85
85
|
"OpenAPIClientConfig",
|
|
86
86
|
"UnfoldConfig",
|
|
87
87
|
"AxesConfig",
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
gRPC configuration models.
|
|
3
|
+
|
|
4
|
+
Type-safe Pydantic v2 models for gRPC server, authentication, and proto generation.
|
|
5
|
+
|
|
6
|
+
Requires: pip install django-cfg[grpc]
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
>>> from django_cfg.models.api.grpc import GRPCConfig, GRPCServerConfig
|
|
10
|
+
>>> config = GRPCConfig(
|
|
11
|
+
... enabled=True,
|
|
12
|
+
... server=GRPCServerConfig(port=50051)
|
|
13
|
+
... )
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from .config import (
|
|
20
|
+
GRPCAuthConfig,
|
|
21
|
+
GRPCConfig,
|
|
22
|
+
GRPCProtoConfig,
|
|
23
|
+
GRPCServerConfig,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"GRPCConfig",
|
|
28
|
+
"GRPCServerConfig",
|
|
29
|
+
"GRPCAuthConfig",
|
|
30
|
+
"GRPCProtoConfig",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def __getattr__(name: str):
|
|
35
|
+
"""Lazy import with helpful error message."""
|
|
36
|
+
if name in __all__:
|
|
37
|
+
try:
|
|
38
|
+
from .config import (
|
|
39
|
+
GRPCAuthConfig,
|
|
40
|
+
GRPCConfig,
|
|
41
|
+
GRPCProtoConfig,
|
|
42
|
+
GRPCServerConfig,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
"GRPCConfig": GRPCConfig,
|
|
47
|
+
"GRPCServerConfig": GRPCServerConfig,
|
|
48
|
+
"GRPCAuthConfig": GRPCAuthConfig,
|
|
49
|
+
"GRPCProtoConfig": GRPCProtoConfig,
|
|
50
|
+
}[name]
|
|
51
|
+
|
|
52
|
+
except ImportError as e:
|
|
53
|
+
raise ImportError(
|
|
54
|
+
f"gRPC support requires additional dependencies. "
|
|
55
|
+
f"Install with: pip install django-cfg[grpc]\n"
|
|
56
|
+
f"Missing module: {e.name}"
|
|
57
|
+
) from e
|
|
58
|
+
|
|
59
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|