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,364 @@
|
|
|
1
|
+
"""
|
|
2
|
+
gRPC Configuration Models
|
|
3
|
+
|
|
4
|
+
Type-safe Pydantic v2 models for gRPC server, authentication, and proto generation.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> from django_cfg.models.api.grpc import GRPCConfig
|
|
8
|
+
>>> config = GRPCConfig(
|
|
9
|
+
... enabled=True,
|
|
10
|
+
... server=GRPCServerConfig(port=50051),
|
|
11
|
+
... auth=GRPCAuthConfig(require_auth=True)
|
|
12
|
+
... )
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import warnings
|
|
16
|
+
from typing import Dict, List, Optional
|
|
17
|
+
|
|
18
|
+
from pydantic import Field, field_validator, model_validator
|
|
19
|
+
|
|
20
|
+
from django_cfg.models.base import BaseConfig
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class GRPCServerConfig(BaseConfig):
|
|
24
|
+
"""
|
|
25
|
+
gRPC server configuration.
|
|
26
|
+
|
|
27
|
+
Configures the gRPC server including host, port, workers, compression,
|
|
28
|
+
message limits, and keepalive settings.
|
|
29
|
+
|
|
30
|
+
Example:
|
|
31
|
+
>>> config = GRPCServerConfig(
|
|
32
|
+
... host="0.0.0.0",
|
|
33
|
+
... port=50051,
|
|
34
|
+
... max_workers=10,
|
|
35
|
+
... compression="gzip"
|
|
36
|
+
... )
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
enabled: bool = Field(
|
|
40
|
+
default=True,
|
|
41
|
+
description="Enable gRPC server",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
host: str = Field(
|
|
45
|
+
default="[::]",
|
|
46
|
+
description="Server bind address (IPv6 by default, use 0.0.0.0 for IPv4)",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
port: int = Field(
|
|
50
|
+
default=50051,
|
|
51
|
+
description="Server port",
|
|
52
|
+
ge=1024,
|
|
53
|
+
le=65535,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
max_workers: int = Field(
|
|
57
|
+
default=10,
|
|
58
|
+
description="ThreadPoolExecutor max workers",
|
|
59
|
+
ge=1,
|
|
60
|
+
le=1000,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
enable_reflection: bool = Field(
|
|
64
|
+
default=False,
|
|
65
|
+
description="Enable server reflection for dynamic clients (grpcurl, etc.)",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
enable_health_check: bool = Field(
|
|
69
|
+
default=True,
|
|
70
|
+
description="Enable gRPC health check service",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
compression: Optional[str] = Field(
|
|
74
|
+
default=None,
|
|
75
|
+
description="Compression algorithm: 'gzip', 'deflate', or None",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
max_send_message_length: int = Field(
|
|
79
|
+
default=4 * 1024 * 1024, # 4 MB
|
|
80
|
+
description="Maximum outbound message size in bytes",
|
|
81
|
+
ge=1024, # Min 1KB
|
|
82
|
+
le=100 * 1024 * 1024, # Max 100MB
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
max_receive_message_length: int = Field(
|
|
86
|
+
default=4 * 1024 * 1024, # 4 MB
|
|
87
|
+
description="Maximum inbound message size in bytes",
|
|
88
|
+
ge=1024,
|
|
89
|
+
le=100 * 1024 * 1024,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
keepalive_time_ms: int = Field(
|
|
93
|
+
default=7200000, # 2 hours
|
|
94
|
+
description="Keepalive ping interval in milliseconds",
|
|
95
|
+
ge=1000, # Min 1 second
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
keepalive_timeout_ms: int = Field(
|
|
99
|
+
default=20000, # 20 seconds
|
|
100
|
+
description="Keepalive ping timeout in milliseconds",
|
|
101
|
+
ge=1000,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
interceptors: List[str] = Field(
|
|
105
|
+
default_factory=list,
|
|
106
|
+
description="Server interceptor import paths (e.g., 'myapp.interceptors.AuthInterceptor')",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
@field_validator("compression")
|
|
110
|
+
@classmethod
|
|
111
|
+
def validate_compression(cls, v: Optional[str]) -> Optional[str]:
|
|
112
|
+
"""Validate compression algorithm."""
|
|
113
|
+
if v and v not in ("gzip", "deflate"):
|
|
114
|
+
raise ValueError(
|
|
115
|
+
f"Invalid compression: {v}. Must be 'gzip', 'deflate', or None"
|
|
116
|
+
)
|
|
117
|
+
return v
|
|
118
|
+
|
|
119
|
+
@field_validator("host")
|
|
120
|
+
@classmethod
|
|
121
|
+
def validate_host(cls, v: str) -> str:
|
|
122
|
+
"""Validate host format."""
|
|
123
|
+
if not v or not v.strip():
|
|
124
|
+
raise ValueError("Host cannot be empty")
|
|
125
|
+
return v.strip()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class GRPCAuthConfig(BaseConfig):
|
|
129
|
+
"""
|
|
130
|
+
gRPC authentication configuration.
|
|
131
|
+
|
|
132
|
+
Supports JWT authentication with configurable token handling.
|
|
133
|
+
|
|
134
|
+
Example:
|
|
135
|
+
>>> config = GRPCAuthConfig(
|
|
136
|
+
... enabled=True,
|
|
137
|
+
... require_auth=True,
|
|
138
|
+
... jwt_algorithm="HS256"
|
|
139
|
+
... )
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
enabled: bool = Field(
|
|
143
|
+
default=True,
|
|
144
|
+
description="Enable authentication",
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
require_auth: bool = Field(
|
|
148
|
+
default=True,
|
|
149
|
+
description="Require authentication for all services (except public_methods)",
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
token_header: str = Field(
|
|
153
|
+
default="authorization",
|
|
154
|
+
description="Metadata key for auth token",
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
token_prefix: str = Field(
|
|
158
|
+
default="Bearer",
|
|
159
|
+
description="Token prefix (e.g., 'Bearer' for JWT)",
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
jwt_secret_key: Optional[str] = Field(
|
|
163
|
+
default=None,
|
|
164
|
+
description="JWT secret key (defaults to Django SECRET_KEY if None)",
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
jwt_algorithm: str = Field(
|
|
168
|
+
default="HS256",
|
|
169
|
+
description="JWT signing algorithm",
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
jwt_verify_exp: bool = Field(
|
|
173
|
+
default=True,
|
|
174
|
+
description="Verify JWT expiration",
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
jwt_leeway: int = Field(
|
|
178
|
+
default=0,
|
|
179
|
+
description="JWT expiration leeway in seconds",
|
|
180
|
+
ge=0,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
public_methods: List[str] = Field(
|
|
184
|
+
default_factory=lambda: [
|
|
185
|
+
"/grpc.health.v1.Health/Check",
|
|
186
|
+
"/grpc.health.v1.Health/Watch",
|
|
187
|
+
],
|
|
188
|
+
description="RPC methods that don't require authentication",
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
@field_validator("jwt_algorithm")
|
|
192
|
+
@classmethod
|
|
193
|
+
def validate_jwt_algorithm(cls, v: str) -> str:
|
|
194
|
+
"""Validate JWT algorithm."""
|
|
195
|
+
valid_algorithms = {
|
|
196
|
+
"HS256",
|
|
197
|
+
"HS384",
|
|
198
|
+
"HS512",
|
|
199
|
+
"RS256",
|
|
200
|
+
"RS384",
|
|
201
|
+
"RS512",
|
|
202
|
+
"ES256",
|
|
203
|
+
"ES384",
|
|
204
|
+
"ES512",
|
|
205
|
+
}
|
|
206
|
+
if v not in valid_algorithms:
|
|
207
|
+
raise ValueError(
|
|
208
|
+
f"Invalid JWT algorithm: {v}. "
|
|
209
|
+
f"Must be one of: {', '.join(sorted(valid_algorithms))}"
|
|
210
|
+
)
|
|
211
|
+
return v
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class GRPCProtoConfig(BaseConfig):
|
|
215
|
+
"""
|
|
216
|
+
Proto file generation configuration.
|
|
217
|
+
|
|
218
|
+
Controls automatic proto file generation from Django models.
|
|
219
|
+
|
|
220
|
+
Example:
|
|
221
|
+
>>> config = GRPCProtoConfig(
|
|
222
|
+
... auto_generate=True,
|
|
223
|
+
... output_dir="protos",
|
|
224
|
+
... package_prefix="mycompany"
|
|
225
|
+
... )
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
auto_generate: bool = Field(
|
|
229
|
+
default=True,
|
|
230
|
+
description="Auto-generate proto files from Django models",
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
output_dir: str = Field(
|
|
234
|
+
default="protos",
|
|
235
|
+
description="Proto files output directory (relative to BASE_DIR)",
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
package_prefix: str = Field(
|
|
239
|
+
default="",
|
|
240
|
+
description="Package prefix for all generated protos (e.g., 'mycompany')",
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
include_services: bool = Field(
|
|
244
|
+
default=True,
|
|
245
|
+
description="Include service definitions in generated protos",
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
field_naming: str = Field(
|
|
249
|
+
default="snake_case",
|
|
250
|
+
description="Proto field naming convention",
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
@field_validator("field_naming")
|
|
254
|
+
@classmethod
|
|
255
|
+
def validate_field_naming(cls, v: str) -> str:
|
|
256
|
+
"""Validate field naming convention."""
|
|
257
|
+
if v not in ("snake_case", "camelCase"):
|
|
258
|
+
raise ValueError(
|
|
259
|
+
f"Invalid field_naming: {v}. Must be 'snake_case' or 'camelCase'"
|
|
260
|
+
)
|
|
261
|
+
return v
|
|
262
|
+
|
|
263
|
+
@field_validator("output_dir")
|
|
264
|
+
@classmethod
|
|
265
|
+
def validate_output_dir(cls, v: str) -> str:
|
|
266
|
+
"""Validate output directory."""
|
|
267
|
+
if not v or not v.strip():
|
|
268
|
+
raise ValueError("output_dir cannot be empty")
|
|
269
|
+
# Remove leading/trailing slashes
|
|
270
|
+
return v.strip().strip("/")
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class GRPCConfig(BaseConfig):
|
|
274
|
+
"""
|
|
275
|
+
Main gRPC configuration.
|
|
276
|
+
|
|
277
|
+
Combines server, authentication, and proto generation settings.
|
|
278
|
+
|
|
279
|
+
Example:
|
|
280
|
+
Basic setup:
|
|
281
|
+
>>> config = GRPCConfig(enabled=True)
|
|
282
|
+
|
|
283
|
+
Advanced setup:
|
|
284
|
+
>>> config = GRPCConfig(
|
|
285
|
+
... enabled=True,
|
|
286
|
+
... server=GRPCServerConfig(port=8080, max_workers=50),
|
|
287
|
+
... auth=GRPCAuthConfig(require_auth=True),
|
|
288
|
+
... auto_register_apps=["accounts", "support"]
|
|
289
|
+
... )
|
|
290
|
+
"""
|
|
291
|
+
|
|
292
|
+
enabled: bool = Field(
|
|
293
|
+
default=False,
|
|
294
|
+
description="Enable gRPC integration",
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
server: GRPCServerConfig = Field(
|
|
298
|
+
default_factory=GRPCServerConfig,
|
|
299
|
+
description="Server configuration",
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
auth: GRPCAuthConfig = Field(
|
|
303
|
+
default_factory=GRPCAuthConfig,
|
|
304
|
+
description="Authentication configuration",
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
proto: GRPCProtoConfig = Field(
|
|
308
|
+
default_factory=GRPCProtoConfig,
|
|
309
|
+
description="Proto generation configuration",
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
handlers_hook: str = Field(
|
|
313
|
+
default="{ROOT_URLCONF}.grpc_handlers",
|
|
314
|
+
description="Import path to grpc_handlers function",
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
auto_register_apps: bool = Field(
|
|
318
|
+
default=True,
|
|
319
|
+
description="Auto-register gRPC services for Django-CFG apps",
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
enabled_apps: List[str] = Field(
|
|
323
|
+
default_factory=lambda: [
|
|
324
|
+
"accounts",
|
|
325
|
+
"support",
|
|
326
|
+
"knowbase",
|
|
327
|
+
"agents",
|
|
328
|
+
"payments",
|
|
329
|
+
"leads",
|
|
330
|
+
],
|
|
331
|
+
description="Django-CFG apps to expose via gRPC (if auto_register_apps=True)",
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
custom_services: Dict[str, str] = Field(
|
|
335
|
+
default_factory=dict,
|
|
336
|
+
description="Custom service import paths: {service_name: 'path.to.Service'}",
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
@model_validator(mode="after")
|
|
340
|
+
def validate_grpc_config(self) -> "GRPCConfig":
|
|
341
|
+
"""Cross-field validation."""
|
|
342
|
+
# Check dependencies if enabled
|
|
343
|
+
if self.enabled:
|
|
344
|
+
from django_cfg.config import require_feature
|
|
345
|
+
|
|
346
|
+
require_feature("grpc")
|
|
347
|
+
|
|
348
|
+
# Validate server enabled
|
|
349
|
+
if not self.server.enabled:
|
|
350
|
+
raise ValueError(
|
|
351
|
+
"Cannot enable gRPC with server disabled. "
|
|
352
|
+
"Set server.enabled=True or grpc.enabled=False"
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
# Warn if auto_register but no apps
|
|
356
|
+
if self.auto_register_apps and not self.enabled_apps:
|
|
357
|
+
warnings.warn(
|
|
358
|
+
"auto_register_apps is True but enabled_apps is empty. "
|
|
359
|
+
"No services will be auto-registered.",
|
|
360
|
+
UserWarning,
|
|
361
|
+
stacklevel=2,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
return self
|
|
@@ -6,7 +6,7 @@ Django integrations and extensions.
|
|
|
6
6
|
|
|
7
7
|
from .axes import AxesConfig
|
|
8
8
|
from .constance import ConstanceConfig, ConstanceField
|
|
9
|
-
from .
|
|
9
|
+
from .django_rq import DjangoRQConfig, RQQueueConfig
|
|
10
10
|
from .environment import EnvironmentConfig
|
|
11
11
|
from .openapi import OpenAPIClientConfig
|
|
12
12
|
|
|
@@ -14,8 +14,8 @@ __all__ = [
|
|
|
14
14
|
"EnvironmentConfig",
|
|
15
15
|
"ConstanceConfig",
|
|
16
16
|
"ConstanceField",
|
|
17
|
-
"
|
|
18
|
-
"
|
|
17
|
+
"DjangoRQConfig",
|
|
18
|
+
"RQQueueConfig",
|
|
19
19
|
"OpenAPIClientConfig",
|
|
20
20
|
"AxesConfig",
|
|
21
21
|
]
|