django-cfg 1.4.61__py3-none-any.whl → 1.4.63__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 +1 -1
- django_cfg/apps/accounts/services/otp_service.py +3 -14
- django_cfg/apps/centrifugo/__init__.py +57 -0
- django_cfg/apps/centrifugo/admin/__init__.py +13 -0
- django_cfg/apps/centrifugo/admin/centrifugo_log.py +249 -0
- django_cfg/apps/centrifugo/admin/config.py +82 -0
- django_cfg/apps/centrifugo/apps.py +31 -0
- django_cfg/apps/centrifugo/codegen/IMPLEMENTATION_SUMMARY.md +475 -0
- django_cfg/apps/centrifugo/codegen/README.md +242 -0
- django_cfg/apps/centrifugo/codegen/USAGE.md +616 -0
- django_cfg/apps/centrifugo/codegen/__init__.py +19 -0
- django_cfg/apps/centrifugo/codegen/discovery.py +246 -0
- django_cfg/apps/centrifugo/codegen/generators/go_thin/__init__.py +5 -0
- django_cfg/apps/centrifugo/codegen/generators/go_thin/generator.py +174 -0
- django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/README.md.j2 +182 -0
- django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/client.go.j2 +64 -0
- django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/go.mod.j2 +10 -0
- django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/rpc_client.go.j2 +300 -0
- django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/rpc_client.go.j2.old +267 -0
- django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/types.go.j2 +16 -0
- django_cfg/apps/centrifugo/codegen/generators/python_thin/__init__.py +7 -0
- django_cfg/apps/centrifugo/codegen/generators/python_thin/generator.py +241 -0
- django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/README.md.j2 +128 -0
- django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/__init__.py.j2 +22 -0
- django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/client.py.j2 +73 -0
- django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/models.py.j2 +19 -0
- django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/requirements.txt.j2 +8 -0
- django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/rpc_client.py.j2 +193 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/__init__.py +5 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/generator.py +124 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/README.md.j2 +38 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/client.ts.j2 +25 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/index.ts.j2 +12 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/package.json.j2 +13 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/rpc-client.ts.j2 +137 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/tsconfig.json.j2 +14 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/types.ts.j2 +9 -0
- django_cfg/apps/centrifugo/codegen/utils/__init__.py +37 -0
- django_cfg/apps/centrifugo/codegen/utils/naming.py +155 -0
- django_cfg/apps/centrifugo/codegen/utils/type_converter.py +349 -0
- django_cfg/apps/centrifugo/decorators.py +137 -0
- django_cfg/apps/centrifugo/management/__init__.py +1 -0
- django_cfg/apps/centrifugo/management/commands/__init__.py +1 -0
- django_cfg/apps/centrifugo/management/commands/generate_centrifugo_clients.py +254 -0
- django_cfg/apps/centrifugo/managers/__init__.py +12 -0
- django_cfg/apps/centrifugo/managers/centrifugo_log.py +264 -0
- django_cfg/apps/centrifugo/migrations/0001_initial.py +164 -0
- django_cfg/apps/centrifugo/migrations/__init__.py +3 -0
- django_cfg/apps/centrifugo/models/__init__.py +11 -0
- django_cfg/apps/centrifugo/models/centrifugo_log.py +210 -0
- django_cfg/apps/centrifugo/registry.py +106 -0
- django_cfg/apps/centrifugo/router.py +125 -0
- django_cfg/apps/centrifugo/serializers/__init__.py +40 -0
- django_cfg/apps/centrifugo/serializers/admin_api.py +264 -0
- django_cfg/apps/centrifugo/serializers/channels.py +26 -0
- django_cfg/apps/centrifugo/serializers/health.py +17 -0
- django_cfg/apps/centrifugo/serializers/publishes.py +16 -0
- django_cfg/apps/centrifugo/serializers/stats.py +21 -0
- django_cfg/apps/centrifugo/services/__init__.py +12 -0
- django_cfg/apps/centrifugo/services/client/__init__.py +29 -0
- django_cfg/apps/centrifugo/services/client/client.py +577 -0
- django_cfg/apps/centrifugo/services/client/config.py +228 -0
- django_cfg/apps/centrifugo/services/client/exceptions.py +212 -0
- django_cfg/apps/centrifugo/services/config_helper.py +63 -0
- django_cfg/apps/centrifugo/services/dashboard_notifier.py +157 -0
- django_cfg/apps/centrifugo/services/logging.py +677 -0
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/css/dashboard.css +260 -0
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_channels.mjs +313 -0
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_testing.mjs +803 -0
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/main.mjs +333 -0
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/overview.mjs +432 -0
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/testing.mjs +33 -0
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/websocket.mjs +210 -0
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/channels_content.html +46 -0
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/live_channels_content.html +123 -0
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/overview_content.html +45 -0
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/publishes_content.html +84 -0
- django_cfg/apps/{ipc/templates/django_cfg_ipc → centrifugo/templates/django_cfg_centrifugo}/components/stat_cards.html +23 -20
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/system_status.html +91 -0
- django_cfg/apps/{ipc/templates/django_cfg_ipc → centrifugo/templates/django_cfg_centrifugo}/components/tab_navigation.html +15 -15
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/testing_tools.html +415 -0
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/layout/base.html +61 -0
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/pages/dashboard.html +58 -0
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/tags/connection_script.html +48 -0
- django_cfg/apps/centrifugo/templatetags/__init__.py +1 -0
- django_cfg/apps/centrifugo/templatetags/centrifugo_tags.py +81 -0
- django_cfg/apps/centrifugo/urls.py +31 -0
- django_cfg/apps/{ipc → centrifugo}/urls_admin.py +4 -4
- django_cfg/apps/centrifugo/views/__init__.py +15 -0
- django_cfg/apps/centrifugo/views/admin_api.py +374 -0
- django_cfg/apps/centrifugo/views/dashboard.py +15 -0
- django_cfg/apps/centrifugo/views/monitoring.py +286 -0
- django_cfg/apps/centrifugo/views/testing_api.py +422 -0
- django_cfg/apps/support/utils/support_email_service.py +5 -18
- django_cfg/apps/tasks/templates/tasks/layout/base.html +0 -2
- django_cfg/apps/urls.py +5 -5
- django_cfg/core/base/config_model.py +4 -44
- django_cfg/core/builders/apps_builder.py +2 -2
- django_cfg/core/generation/integration_generators/third_party.py +8 -8
- django_cfg/core/utils/__init__.py +5 -0
- django_cfg/core/utils/url_helpers.py +73 -0
- django_cfg/modules/base.py +7 -7
- django_cfg/modules/django_client/core/__init__.py +2 -1
- django_cfg/modules/django_client/core/config/config.py +8 -0
- django_cfg/modules/django_client/core/generator/__init__.py +42 -2
- django_cfg/modules/django_client/core/generator/go/__init__.py +14 -0
- django_cfg/modules/django_client/core/generator/go/client_generator.py +124 -0
- django_cfg/modules/django_client/core/generator/go/files_generator.py +133 -0
- django_cfg/modules/django_client/core/generator/go/generator.py +203 -0
- django_cfg/modules/django_client/core/generator/go/models_generator.py +304 -0
- django_cfg/modules/django_client/core/generator/go/naming.py +193 -0
- django_cfg/modules/django_client/core/generator/go/operations_generator.py +134 -0
- django_cfg/modules/django_client/core/generator/go/templates/Makefile.j2 +38 -0
- django_cfg/modules/django_client/core/generator/go/templates/README.md.j2 +55 -0
- django_cfg/modules/django_client/core/generator/go/templates/client.go.j2 +122 -0
- django_cfg/modules/django_client/core/generator/go/templates/enums.go.j2 +49 -0
- django_cfg/modules/django_client/core/generator/go/templates/errors.go.j2 +182 -0
- django_cfg/modules/django_client/core/generator/go/templates/go.mod.j2 +6 -0
- django_cfg/modules/django_client/core/generator/go/templates/main_client.go.j2 +60 -0
- django_cfg/modules/django_client/core/generator/go/templates/middleware.go.j2 +388 -0
- django_cfg/modules/django_client/core/generator/go/templates/models.go.j2 +28 -0
- django_cfg/modules/django_client/core/generator/go/templates/operations_client.go.j2 +142 -0
- django_cfg/modules/django_client/core/generator/go/templates/validation.go.j2 +217 -0
- django_cfg/modules/django_client/core/generator/go/type_mapper.py +380 -0
- django_cfg/modules/django_client/management/commands/generate_client.py +53 -3
- django_cfg/modules/django_client/system/generate_mjs_clients.py +3 -1
- django_cfg/modules/django_client/system/schema_parser.py +5 -1
- django_cfg/modules/django_tailwind/templates/django_tailwind/base.html +1 -0
- django_cfg/modules/django_twilio/sendgrid_service.py +7 -4
- django_cfg/modules/django_unfold/dashboard.py +25 -19
- django_cfg/pyproject.toml +1 -1
- django_cfg/registry/core.py +2 -0
- django_cfg/registry/modules.py +2 -2
- django_cfg/static/js/api/centrifugo/client.mjs +164 -0
- django_cfg/static/js/api/centrifugo/index.mjs +13 -0
- django_cfg/static/js/api/index.mjs +5 -5
- django_cfg/static/js/api/types.mjs +89 -26
- {django_cfg-1.4.61.dist-info → django_cfg-1.4.63.dist-info}/METADATA +1 -1
- {django_cfg-1.4.61.dist-info → django_cfg-1.4.63.dist-info}/RECORD +142 -68
- django_cfg/apps/ipc/README.md +0 -346
- django_cfg/apps/ipc/RPC_LOGGING.md +0 -321
- django_cfg/apps/ipc/TESTING.md +0 -539
- django_cfg/apps/ipc/__init__.py +0 -60
- django_cfg/apps/ipc/admin.py +0 -212
- django_cfg/apps/ipc/apps.py +0 -28
- django_cfg/apps/ipc/migrations/0001_initial.py +0 -137
- django_cfg/apps/ipc/migrations/__init__.py +0 -0
- django_cfg/apps/ipc/models.py +0 -221
- django_cfg/apps/ipc/serializers/__init__.py +0 -29
- django_cfg/apps/ipc/serializers/serializers.py +0 -343
- django_cfg/apps/ipc/services/__init__.py +0 -7
- django_cfg/apps/ipc/services/client/__init__.py +0 -23
- django_cfg/apps/ipc/services/client/client.py +0 -621
- django_cfg/apps/ipc/services/client/config.py +0 -214
- django_cfg/apps/ipc/services/client/exceptions.py +0 -201
- django_cfg/apps/ipc/services/logging.py +0 -239
- django_cfg/apps/ipc/services/monitor.py +0 -466
- django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/main.mjs +0 -269
- django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/overview.mjs +0 -259
- django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/testing.mjs +0 -375
- django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard.mjs.old +0 -441
- django_cfg/apps/ipc/templates/django_cfg_ipc/components/methods_content.html +0 -22
- django_cfg/apps/ipc/templates/django_cfg_ipc/components/notifications_content.html +0 -9
- django_cfg/apps/ipc/templates/django_cfg_ipc/components/overview_content.html +0 -9
- django_cfg/apps/ipc/templates/django_cfg_ipc/components/requests_content.html +0 -23
- django_cfg/apps/ipc/templates/django_cfg_ipc/components/system_status.html +0 -47
- django_cfg/apps/ipc/templates/django_cfg_ipc/components/testing_tools.html +0 -184
- django_cfg/apps/ipc/templates/django_cfg_ipc/layout/base.html +0 -71
- django_cfg/apps/ipc/templates/django_cfg_ipc/pages/dashboard.html +0 -56
- django_cfg/apps/ipc/urls.py +0 -23
- django_cfg/apps/ipc/views/__init__.py +0 -13
- django_cfg/apps/ipc/views/dashboard.py +0 -15
- django_cfg/apps/ipc/views/monitoring.py +0 -251
- django_cfg/apps/ipc/views/testing.py +0 -285
- django_cfg/static/js/api/ipc/client.mjs +0 -114
- django_cfg/static/js/api/ipc/index.mjs +0 -13
- {django_cfg-1.4.61.dist-info → django_cfg-1.4.63.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.61.dist-info → django_cfg-1.4.63.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.61.dist-info → django_cfg-1.4.63.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Type mapping from OpenAPI/IR to Go types.
|
|
3
|
+
|
|
4
|
+
Handles conversion of IR schemas to Go types with proper handling of:
|
|
5
|
+
- Primitive types (string, integer, number, boolean)
|
|
6
|
+
- Optional fields (pointers)
|
|
7
|
+
- Arrays and slices
|
|
8
|
+
- Nested objects (struct references)
|
|
9
|
+
- Enums (custom types)
|
|
10
|
+
- Format-specific types (time.Time, uuid, etc.)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import TYPE_CHECKING
|
|
16
|
+
|
|
17
|
+
from .naming import sanitize_go_identifier, to_pascal_case
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from ...ir import IRSchemaObject
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class GoTypeMapper:
|
|
24
|
+
"""
|
|
25
|
+
Maps OpenAPI/IR types to Go types.
|
|
26
|
+
|
|
27
|
+
Handles:
|
|
28
|
+
- Primitive types (string → string, integer → int64, etc.)
|
|
29
|
+
- Optional fields (User.email? → *string)
|
|
30
|
+
- Arrays ([]string, []int64)
|
|
31
|
+
- Nested objects (references to other structs)
|
|
32
|
+
- Enums (custom types with constants)
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
# Primitive type mapping
|
|
36
|
+
PRIMITIVE_TYPES = {
|
|
37
|
+
"string": "string",
|
|
38
|
+
"integer": "int64",
|
|
39
|
+
"number": "float64",
|
|
40
|
+
"boolean": "bool",
|
|
41
|
+
"object": "map[string]interface{}",
|
|
42
|
+
"null": "interface{}",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Format-specific type overrides
|
|
46
|
+
FORMAT_OVERRIDES = {
|
|
47
|
+
"int32": "int32",
|
|
48
|
+
"int64": "int64",
|
|
49
|
+
"float": "float32",
|
|
50
|
+
"double": "float64",
|
|
51
|
+
"date-time": "time.Time",
|
|
52
|
+
"date": "string", # YYYY-MM-DD format
|
|
53
|
+
"uuid": "string", # Or use google/uuid package
|
|
54
|
+
"binary": "[]byte",
|
|
55
|
+
"byte": "[]byte",
|
|
56
|
+
"email": "string",
|
|
57
|
+
"uri": "string",
|
|
58
|
+
"url": "string",
|
|
59
|
+
"hostname": "string",
|
|
60
|
+
"ipv4": "string",
|
|
61
|
+
"ipv6": "string",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
def __init__(self, use_types_package: bool = False):
|
|
65
|
+
"""Initialize type mapper.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
use_types_package: If True, prefix enum types with "types."
|
|
69
|
+
"""
|
|
70
|
+
self._imports_needed = set()
|
|
71
|
+
self.use_types_package = use_types_package
|
|
72
|
+
|
|
73
|
+
def get_imports(self) -> set[str]:
|
|
74
|
+
"""
|
|
75
|
+
Get required imports for generated code.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Set of import paths (e.g., {"time", "encoding/json"})
|
|
79
|
+
"""
|
|
80
|
+
return self._imports_needed
|
|
81
|
+
|
|
82
|
+
def ir_schema_to_go_type(
|
|
83
|
+
self,
|
|
84
|
+
schema: IRSchemaObject,
|
|
85
|
+
required: bool = True,
|
|
86
|
+
parent_schema: IRSchemaObject | None = None,
|
|
87
|
+
) -> str:
|
|
88
|
+
"""
|
|
89
|
+
Convert IR schema to Go type.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
schema: IRSchemaObject to convert
|
|
93
|
+
required: Whether field is required (affects pointer usage)
|
|
94
|
+
parent_schema: Parent schema (for context)
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Go type string
|
|
98
|
+
|
|
99
|
+
Examples:
|
|
100
|
+
>>> mapper.ir_schema_to_go_type(
|
|
101
|
+
... IRSchemaObject(name="email", type="string"),
|
|
102
|
+
... required=False
|
|
103
|
+
... )
|
|
104
|
+
'*string'
|
|
105
|
+
|
|
106
|
+
>>> mapper.ir_schema_to_go_type(
|
|
107
|
+
... IRSchemaObject(name="items", type="array", items=IRSchemaObject(type="string"))
|
|
108
|
+
... )
|
|
109
|
+
'[]string'
|
|
110
|
+
|
|
111
|
+
>>> mapper.ir_schema_to_go_type(
|
|
112
|
+
... IRSchemaObject(name="created_at", type="string", format="date-time")
|
|
113
|
+
... )
|
|
114
|
+
'time.Time'
|
|
115
|
+
"""
|
|
116
|
+
# Handle $ref
|
|
117
|
+
if schema.ref:
|
|
118
|
+
type_name = schema.ref.split('/')[-1]
|
|
119
|
+
# Struct references are already pointers in Go when optional
|
|
120
|
+
if not required:
|
|
121
|
+
return f"*{type_name}"
|
|
122
|
+
return type_name
|
|
123
|
+
|
|
124
|
+
# Handle enums
|
|
125
|
+
if schema.enum:
|
|
126
|
+
enum_type = self._get_enum_type_name(schema)
|
|
127
|
+
if self.use_types_package:
|
|
128
|
+
enum_type = f"types.{enum_type}"
|
|
129
|
+
return f"*{enum_type}" if not required else enum_type
|
|
130
|
+
|
|
131
|
+
# Handle arrays
|
|
132
|
+
if schema.type == "array" and schema.items:
|
|
133
|
+
item_type = self.ir_schema_to_go_type(schema.items, required=True)
|
|
134
|
+
# Arrays are always non-nil in Go, but can be empty
|
|
135
|
+
# omitempty in JSON tag handles the serialization
|
|
136
|
+
return f"[]{item_type}"
|
|
137
|
+
|
|
138
|
+
# Handle format overrides
|
|
139
|
+
if schema.format and schema.format in self.FORMAT_OVERRIDES:
|
|
140
|
+
go_type = self.FORMAT_OVERRIDES[schema.format]
|
|
141
|
+
|
|
142
|
+
# Track imports
|
|
143
|
+
if go_type == "time.Time":
|
|
144
|
+
self._imports_needed.add("time")
|
|
145
|
+
|
|
146
|
+
# time.Time is a struct, so we use pointer for optionals
|
|
147
|
+
if not required:
|
|
148
|
+
return f"*{go_type}"
|
|
149
|
+
return go_type
|
|
150
|
+
|
|
151
|
+
# Handle primitive types
|
|
152
|
+
if schema.type in self.PRIMITIVE_TYPES:
|
|
153
|
+
go_type = self.PRIMITIVE_TYPES[schema.type]
|
|
154
|
+
|
|
155
|
+
# Optionals become pointers (except for maps and interfaces)
|
|
156
|
+
if not required and go_type not in ["interface{}", "map[string]interface{}"]:
|
|
157
|
+
return f"*{go_type}"
|
|
158
|
+
|
|
159
|
+
return go_type
|
|
160
|
+
|
|
161
|
+
# Fallback to interface{} for unknown types
|
|
162
|
+
return "interface{}"
|
|
163
|
+
|
|
164
|
+
def ir_schema_to_struct(
|
|
165
|
+
self,
|
|
166
|
+
schema: IRSchemaObject,
|
|
167
|
+
) -> dict:
|
|
168
|
+
"""
|
|
169
|
+
Convert IR schema to Go struct definition.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
schema: IRSchemaObject to convert
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Dictionary with struct definition:
|
|
176
|
+
{
|
|
177
|
+
"name": "User",
|
|
178
|
+
"fields": [
|
|
179
|
+
{
|
|
180
|
+
"name": "ID",
|
|
181
|
+
"type": "int64",
|
|
182
|
+
"json_tag": '`json:"id"`',
|
|
183
|
+
"description": "User ID",
|
|
184
|
+
"required": True
|
|
185
|
+
},
|
|
186
|
+
...
|
|
187
|
+
],
|
|
188
|
+
"doc": "User represents a registered user.",
|
|
189
|
+
"needs_time_import": False,
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
Examples:
|
|
193
|
+
>>> schema = IRSchemaObject(
|
|
194
|
+
... name="User",
|
|
195
|
+
... type="object",
|
|
196
|
+
... properties={
|
|
197
|
+
... "id": IRSchemaObject(name="id", type="integer"),
|
|
198
|
+
... "username": IRSchemaObject(name="username", type="string"),
|
|
199
|
+
... "email": IRSchemaObject(name="email", type="string"),
|
|
200
|
+
... },
|
|
201
|
+
... required=["id", "username"],
|
|
202
|
+
... )
|
|
203
|
+
>>> struct = mapper.ir_schema_to_struct(schema)
|
|
204
|
+
>>> struct["name"]
|
|
205
|
+
'User'
|
|
206
|
+
>>> len(struct["fields"])
|
|
207
|
+
3
|
|
208
|
+
"""
|
|
209
|
+
fields = []
|
|
210
|
+
|
|
211
|
+
# Reset imports for this struct
|
|
212
|
+
self._imports_needed = set()
|
|
213
|
+
|
|
214
|
+
# Process properties
|
|
215
|
+
for prop_name, prop_schema in (schema.properties or {}).items():
|
|
216
|
+
is_required = prop_name in (schema.required or [])
|
|
217
|
+
|
|
218
|
+
# Get Go type
|
|
219
|
+
go_type = self.ir_schema_to_go_type(prop_schema, is_required, parent_schema=schema)
|
|
220
|
+
|
|
221
|
+
# Build JSON tag
|
|
222
|
+
if not is_required:
|
|
223
|
+
json_tag = f'`json:"{prop_name},omitempty"`'
|
|
224
|
+
else:
|
|
225
|
+
json_tag = f'`json:"{prop_name}"`'
|
|
226
|
+
|
|
227
|
+
# Create field definition
|
|
228
|
+
field = {
|
|
229
|
+
"name": to_pascal_case(prop_name),
|
|
230
|
+
"type": go_type,
|
|
231
|
+
"json_tag": json_tag,
|
|
232
|
+
"description": prop_schema.description or "",
|
|
233
|
+
"required": is_required,
|
|
234
|
+
"deprecated": prop_schema.deprecated,
|
|
235
|
+
}
|
|
236
|
+
fields.append(field)
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
"name": schema.name,
|
|
240
|
+
"fields": fields,
|
|
241
|
+
"doc": schema.description or f"{schema.name} model.",
|
|
242
|
+
"needs_time_import": "time" in self._imports_needed,
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
def _get_enum_type_name(self, schema: IRSchemaObject) -> str:
|
|
246
|
+
"""
|
|
247
|
+
Get enum type name from schema.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
schema: IRSchemaObject with enum
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
Enum type name (PascalCase)
|
|
254
|
+
|
|
255
|
+
Examples:
|
|
256
|
+
>>> schema = IRSchemaObject(name="status", type="integer", enum=[1, 2, 3])
|
|
257
|
+
>>> mapper._get_enum_type_name(schema)
|
|
258
|
+
'Status'
|
|
259
|
+
|
|
260
|
+
>>> schema = IRSchemaObject(name="User.role", type="string", enum=["admin", "user"])
|
|
261
|
+
>>> mapper._get_enum_type_name(schema)
|
|
262
|
+
'UserRole'
|
|
263
|
+
"""
|
|
264
|
+
if not schema.name:
|
|
265
|
+
return "Enum"
|
|
266
|
+
|
|
267
|
+
# Handle nested enum names (e.g., "User.role" → "UserRole")
|
|
268
|
+
if '.' in schema.name:
|
|
269
|
+
parts = schema.name.split('.')
|
|
270
|
+
return ''.join(to_pascal_case(p) for p in parts)
|
|
271
|
+
|
|
272
|
+
return to_pascal_case(schema.name)
|
|
273
|
+
|
|
274
|
+
def get_enum_base_type(self, schema: IRSchemaObject) -> str:
|
|
275
|
+
"""
|
|
276
|
+
Get Go base type for enum.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
schema: IRSchemaObject with enum
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
Go base type ("int64", "string", etc.)
|
|
283
|
+
|
|
284
|
+
Examples:
|
|
285
|
+
>>> schema = IRSchemaObject(name="status", type="integer", enum=[1, 2, 3])
|
|
286
|
+
>>> mapper.get_enum_base_type(schema)
|
|
287
|
+
'int64'
|
|
288
|
+
|
|
289
|
+
>>> schema = IRSchemaObject(name="role", type="string", enum=["admin", "user"])
|
|
290
|
+
>>> mapper.get_enum_base_type(schema)
|
|
291
|
+
'string'
|
|
292
|
+
"""
|
|
293
|
+
if schema.type == "integer":
|
|
294
|
+
# Check format for int32 vs int64
|
|
295
|
+
if schema.format == "int32":
|
|
296
|
+
return "int32"
|
|
297
|
+
return "int64"
|
|
298
|
+
elif schema.type == "number":
|
|
299
|
+
if schema.format == "float":
|
|
300
|
+
return "float32"
|
|
301
|
+
return "float64"
|
|
302
|
+
elif schema.type == "string":
|
|
303
|
+
return "string"
|
|
304
|
+
|
|
305
|
+
# Default to int64
|
|
306
|
+
return "int64"
|
|
307
|
+
|
|
308
|
+
def generate_enum_definition(self, schema: IRSchemaObject) -> dict:
|
|
309
|
+
"""
|
|
310
|
+
Generate enum type definition for Go.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
schema: IRSchemaObject with enum + enum_var_names
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
Dictionary with enum definition:
|
|
317
|
+
{
|
|
318
|
+
"name": "StatusEnum",
|
|
319
|
+
"base_type": "int64",
|
|
320
|
+
"values": [
|
|
321
|
+
{"name": "StatusNew", "value": 1, "description": "New user"},
|
|
322
|
+
{"name": "StatusActive", "value": 2, "description": "Active user"},
|
|
323
|
+
],
|
|
324
|
+
"doc": "StatusEnum represents user status.",
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
Examples:
|
|
328
|
+
>>> schema = IRSchemaObject(
|
|
329
|
+
... name="status",
|
|
330
|
+
... type="integer",
|
|
331
|
+
... enum=[1, 2, 3],
|
|
332
|
+
... enum_var_names=["STATUS_NEW", "STATUS_ACTIVE", "STATUS_COMPLETE"],
|
|
333
|
+
... )
|
|
334
|
+
>>> enum_def = mapper.generate_enum_definition(schema)
|
|
335
|
+
>>> enum_def["name"]
|
|
336
|
+
'Status'
|
|
337
|
+
>>> len(enum_def["values"])
|
|
338
|
+
3
|
|
339
|
+
"""
|
|
340
|
+
enum_type_name = self._get_enum_type_name(schema)
|
|
341
|
+
base_type = self.get_enum_base_type(schema)
|
|
342
|
+
|
|
343
|
+
# Build enum values
|
|
344
|
+
values = []
|
|
345
|
+
enum_values = schema.enum or []
|
|
346
|
+
enum_var_names = schema.enum_var_names or []
|
|
347
|
+
|
|
348
|
+
for i, enum_value in enumerate(enum_values):
|
|
349
|
+
# Get variable name
|
|
350
|
+
if i < len(enum_var_names):
|
|
351
|
+
# Even if var name is provided, sanitize it for Go
|
|
352
|
+
var_name = sanitize_go_identifier(enum_var_names[i])
|
|
353
|
+
else:
|
|
354
|
+
# Auto-generate from value
|
|
355
|
+
if isinstance(enum_value, str):
|
|
356
|
+
# Use sanitize_go_identifier to handle dots, hyphens, spaces, etc.
|
|
357
|
+
var_name = sanitize_go_identifier(enum_value)
|
|
358
|
+
else:
|
|
359
|
+
var_name = f"VALUE_{enum_value}"
|
|
360
|
+
|
|
361
|
+
# var_name is now always PascalCase from sanitize_go_identifier
|
|
362
|
+
go_var_name = var_name
|
|
363
|
+
|
|
364
|
+
# Add enum type prefix if not present
|
|
365
|
+
if not go_var_name.startswith(enum_type_name):
|
|
366
|
+
go_var_name = enum_type_name + go_var_name
|
|
367
|
+
|
|
368
|
+
values.append({
|
|
369
|
+
"name": go_var_name,
|
|
370
|
+
"value": enum_value,
|
|
371
|
+
"description": "", # Could extract from x-choices if available
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
return {
|
|
375
|
+
"name": enum_type_name,
|
|
376
|
+
"base_type": base_type,
|
|
377
|
+
"values": values,
|
|
378
|
+
"doc": schema.description or f"{enum_type_name} enum.",
|
|
379
|
+
"is_string_enum": schema.type == "string",
|
|
380
|
+
}
|
|
@@ -14,7 +14,7 @@ from django.core.management.base import BaseCommand, CommandError
|
|
|
14
14
|
class Command(BaseCommand):
|
|
15
15
|
"""Generate OpenAPI clients for configured application groups."""
|
|
16
16
|
|
|
17
|
-
help = "Generate Python and
|
|
17
|
+
help = "Generate Python, TypeScript, and Go API clients from OpenAPI schemas"
|
|
18
18
|
|
|
19
19
|
def add_arguments(self, parser):
|
|
20
20
|
"""Add command arguments."""
|
|
@@ -37,6 +37,12 @@ class Command(BaseCommand):
|
|
|
37
37
|
help="Generate TypeScript client only",
|
|
38
38
|
)
|
|
39
39
|
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"--go",
|
|
42
|
+
action="store_true",
|
|
43
|
+
help="Generate Go client only",
|
|
44
|
+
)
|
|
45
|
+
|
|
40
46
|
parser.add_argument(
|
|
41
47
|
"--no-python",
|
|
42
48
|
action="store_true",
|
|
@@ -49,6 +55,12 @@ class Command(BaseCommand):
|
|
|
49
55
|
help="Skip TypeScript client generation",
|
|
50
56
|
)
|
|
51
57
|
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
"--no-go",
|
|
60
|
+
action="store_true",
|
|
61
|
+
help="Skip Go client generation",
|
|
62
|
+
)
|
|
63
|
+
|
|
52
64
|
# Utility options
|
|
53
65
|
parser.add_argument(
|
|
54
66
|
"--dry-run",
|
|
@@ -193,15 +205,22 @@ class Command(BaseCommand):
|
|
|
193
205
|
def _generate_clients(self, service, options):
|
|
194
206
|
"""Generate clients."""
|
|
195
207
|
# Determine languages
|
|
196
|
-
if options["python"] and not options["typescript"]:
|
|
208
|
+
if options["python"] and not options["typescript"] and not options["go"]:
|
|
197
209
|
python = True
|
|
198
210
|
typescript = False
|
|
199
|
-
|
|
211
|
+
go = False
|
|
212
|
+
elif options["typescript"] and not options["python"] and not options["go"]:
|
|
200
213
|
python = False
|
|
201
214
|
typescript = True
|
|
215
|
+
go = False
|
|
216
|
+
elif options["go"] and not options["python"] and not options["typescript"]:
|
|
217
|
+
python = False
|
|
218
|
+
typescript = False
|
|
219
|
+
go = True
|
|
202
220
|
else:
|
|
203
221
|
python = not options["no_python"]
|
|
204
222
|
typescript = not options["no_typescript"]
|
|
223
|
+
go = not options["no_go"]
|
|
205
224
|
|
|
206
225
|
# Get groups
|
|
207
226
|
groups = options.get("groups")
|
|
@@ -233,6 +252,8 @@ class Command(BaseCommand):
|
|
|
233
252
|
self.stdout.write(" → Python")
|
|
234
253
|
if typescript:
|
|
235
254
|
self.stdout.write(" → TypeScript")
|
|
255
|
+
if go:
|
|
256
|
+
self.stdout.write(" → Go")
|
|
236
257
|
|
|
237
258
|
if dry_run:
|
|
238
259
|
self.stdout.write(self.style.WARNING("\n✅ Dry run completed - no files generated"))
|
|
@@ -248,6 +269,7 @@ class Command(BaseCommand):
|
|
|
248
269
|
|
|
249
270
|
from django_cfg.modules.django_client.core import (
|
|
250
271
|
ArchiveManager,
|
|
272
|
+
GoGenerator,
|
|
251
273
|
GroupManager,
|
|
252
274
|
PythonGenerator,
|
|
253
275
|
TypeScriptGenerator,
|
|
@@ -395,6 +417,32 @@ class Command(BaseCommand):
|
|
|
395
417
|
|
|
396
418
|
self.stdout.write(f" ✅ TypeScript client: {ts_dir} ({len(ts_files)} files)")
|
|
397
419
|
|
|
420
|
+
# Generate Go client
|
|
421
|
+
if go:
|
|
422
|
+
self.stdout.write(" → Generating Go client...")
|
|
423
|
+
go_dir = service.config.get_group_go_dir(group_name)
|
|
424
|
+
go_dir.mkdir(parents=True, exist_ok=True)
|
|
425
|
+
|
|
426
|
+
go_generator = GoGenerator(
|
|
427
|
+
ir_context,
|
|
428
|
+
client_structure=service.config.client_structure,
|
|
429
|
+
openapi_schema=schema_dict,
|
|
430
|
+
tag_prefix=f"{group_name}_",
|
|
431
|
+
generate_package_files=service.config.generate_package_files,
|
|
432
|
+
package_config={
|
|
433
|
+
"module_name": f"djangocfg__{group_name}",
|
|
434
|
+
"version": "v1.0.0",
|
|
435
|
+
},
|
|
436
|
+
)
|
|
437
|
+
go_files = go_generator.generate()
|
|
438
|
+
|
|
439
|
+
for generated_file in go_files:
|
|
440
|
+
full_path = go_dir / generated_file.path
|
|
441
|
+
full_path.parent.mkdir(parents=True, exist_ok=True)
|
|
442
|
+
full_path.write_text(generated_file.content)
|
|
443
|
+
|
|
444
|
+
self.stdout.write(f" ✅ Go client: {go_dir} ({len(go_files)} files)")
|
|
445
|
+
|
|
398
446
|
# Archive if enabled
|
|
399
447
|
if service.config.enable_archive:
|
|
400
448
|
self.stdout.write(" → Archiving...")
|
|
@@ -428,3 +476,5 @@ class Command(BaseCommand):
|
|
|
428
476
|
self.stdout.write(f" Python: {service.config.get_python_clients_dir()}")
|
|
429
477
|
if typescript:
|
|
430
478
|
self.stdout.write(f" TypeScript: {service.config.get_typescript_clients_dir()}")
|
|
479
|
+
if go:
|
|
480
|
+
self.stdout.write(f" Go: {service.config.get_go_clients_dir()}")
|
|
@@ -54,7 +54,9 @@ def main():
|
|
|
54
54
|
django_cfg_dir = modules_dir.parent # django_cfg
|
|
55
55
|
src_dir = django_cfg_dir.parent # src
|
|
56
56
|
dev_dir = src_dir.parent # django-cfg-dev
|
|
57
|
-
|
|
57
|
+
projects_dir = dev_dir.parent # projects
|
|
58
|
+
root_dir = projects_dir.parent # root (django-cfg)
|
|
59
|
+
example_django_dir = root_dir / "solution" / "projects" / "django"
|
|
58
60
|
|
|
59
61
|
# Output directory for MJS clients
|
|
60
62
|
if args.output:
|
|
@@ -120,7 +120,11 @@ class SchemaParser:
|
|
|
120
120
|
return 'Object'
|
|
121
121
|
# Check for additionalProperties (dictionary-like)
|
|
122
122
|
if 'additionalProperties' in schema:
|
|
123
|
-
|
|
123
|
+
additional = schema['additionalProperties']
|
|
124
|
+
# additionalProperties can be bool or schema object
|
|
125
|
+
if isinstance(additional, bool):
|
|
126
|
+
return 'Record<string, any>' if additional else 'Object'
|
|
127
|
+
value_type = self.get_js_type(additional)
|
|
124
128
|
return f'Record<string, {value_type}>'
|
|
125
129
|
return 'Object'
|
|
126
130
|
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
<!-- Alpine.js for interactivity -->
|
|
15
15
|
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
|
16
|
+
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
|
16
17
|
|
|
17
18
|
<style>
|
|
18
19
|
/* Prevent flash of unstyled content */
|
|
@@ -17,6 +17,7 @@ from django.template.loader import render_to_string
|
|
|
17
17
|
from sendgrid import SendGridAPIClient
|
|
18
18
|
from sendgrid.helpers.mail import Mail
|
|
19
19
|
|
|
20
|
+
from django_cfg.core.utils import get_otp_url
|
|
20
21
|
from django_cfg.modules.base import BaseCfgModule
|
|
21
22
|
from django_cfg.modules.django_twilio.exceptions import (
|
|
22
23
|
TwilioConfigurationError,
|
|
@@ -305,6 +306,9 @@ class SendGridService(BaseCfgModule):
|
|
|
305
306
|
# Prepare subject
|
|
306
307
|
subject = f"Your {cfg.project_name if cfg else 'Django CFG'} verification code"
|
|
307
308
|
|
|
309
|
+
# Generate OTP link using utility function
|
|
310
|
+
otp_link = get_otp_url(otp_code)
|
|
311
|
+
|
|
308
312
|
# Prepare template data matching sendgrid_otp_email.html template
|
|
309
313
|
dynamic_data = {
|
|
310
314
|
'site_name': cfg.project_name if cfg else 'Django CFG',
|
|
@@ -317,7 +321,7 @@ class SendGridService(BaseCfgModule):
|
|
|
317
321
|
'email': email
|
|
318
322
|
},
|
|
319
323
|
'otp_code': otp_code, # This is what the template expects!
|
|
320
|
-
'otp_link':
|
|
324
|
+
'otp_link': otp_link, # Add OTP verification link
|
|
321
325
|
'expires_minutes': 10,
|
|
322
326
|
'subject': subject, # Add subject to template data
|
|
323
327
|
**config.custom_template_data,
|
|
@@ -370,9 +374,8 @@ class SendGridService(BaseCfgModule):
|
|
|
370
374
|
"user": {"username": email.split("@")[0]}, # Fallback user object
|
|
371
375
|
}
|
|
372
376
|
|
|
373
|
-
# Add OTP link
|
|
374
|
-
|
|
375
|
-
context["otp_link"] = current_config.get_otp_url(otp_code)
|
|
377
|
+
# Add OTP link using utility function
|
|
378
|
+
context["otp_link"] = get_otp_url(otp_code)
|
|
376
379
|
|
|
377
380
|
# Render Django templates
|
|
378
381
|
try:
|
|
@@ -68,16 +68,22 @@ class DashboardManager(BaseCfgModule):
|
|
|
68
68
|
),
|
|
69
69
|
]
|
|
70
70
|
|
|
71
|
+
# Centrifugo Dashboard (if enabled)
|
|
72
|
+
if self.is_centrifugo_enabled():
|
|
73
|
+
navigation_sections.append(
|
|
74
|
+
NavigationSection(
|
|
75
|
+
title="Centrifugo",
|
|
76
|
+
separator=True,
|
|
77
|
+
collapsible=True,
|
|
78
|
+
items=[NavigationItem(title="Dashboard", icon=Icons.MONITOR_HEART, link="/cfg/centrifugo/admin/"),
|
|
79
|
+
NavigationItem(title="Logs", icon=Icons.LIST_ALT, link=str(reverse_lazy("admin:django_cfg_centrifugo_centrifugolog_changelist"))),
|
|
80
|
+
]
|
|
81
|
+
)
|
|
82
|
+
)
|
|
83
|
+
|
|
71
84
|
# Add Operations section (System & Monitoring tools)
|
|
72
85
|
operations_items = []
|
|
73
86
|
|
|
74
|
-
# RPC Dashboard (if enabled)
|
|
75
|
-
if self.is_rpc_enabled():
|
|
76
|
-
operations_items.extend([
|
|
77
|
-
NavigationItem(title="IPC/RPC Dashboard", icon=Icons.MONITOR_HEART, link="/cfg/ipc/admin/"),
|
|
78
|
-
NavigationItem(title="RPC Logs", icon=Icons.LIST_ALT, link=str(reverse_lazy("admin:django_cfg_ipc_rpclog_changelist"))),
|
|
79
|
-
])
|
|
80
|
-
|
|
81
87
|
# Background Tasks (if enabled)
|
|
82
88
|
if self.should_enable_tasks():
|
|
83
89
|
operations_items.extend([
|
|
@@ -327,38 +333,38 @@ class DashboardManager(BaseCfgModule):
|
|
|
327
333
|
)
|
|
328
334
|
widgets.append(system_overview_widget.to_dict())
|
|
329
335
|
|
|
330
|
-
# Add
|
|
331
|
-
if self.
|
|
332
|
-
|
|
333
|
-
title="
|
|
336
|
+
# Add Centrifugo monitoring widget if enabled
|
|
337
|
+
if self.is_centrifugo_enabled():
|
|
338
|
+
centrifugo_monitoring_widget = StatsCardsWidget(
|
|
339
|
+
title="Centrifugo Monitoring",
|
|
334
340
|
cards=[
|
|
335
341
|
StatCard(
|
|
336
|
-
title="Total
|
|
337
|
-
value="{{
|
|
342
|
+
title="Total Publishes",
|
|
343
|
+
value="{{ centrifugo_total_publishes }}",
|
|
338
344
|
icon=Icons.API,
|
|
339
345
|
color="blue",
|
|
340
346
|
),
|
|
341
347
|
StatCard(
|
|
342
348
|
title="Success Rate",
|
|
343
|
-
value="{{
|
|
349
|
+
value="{{ centrifugo_success_rate }}%",
|
|
344
350
|
icon=Icons.CHECK_CIRCLE,
|
|
345
351
|
color="green",
|
|
346
352
|
),
|
|
347
353
|
StatCard(
|
|
348
|
-
title="Avg
|
|
349
|
-
value="{{
|
|
354
|
+
title="Avg Duration",
|
|
355
|
+
value="{{ centrifugo_avg_duration }}ms",
|
|
350
356
|
icon=Icons.SPEED,
|
|
351
357
|
color="purple",
|
|
352
358
|
),
|
|
353
359
|
StatCard(
|
|
354
|
-
title="Failed
|
|
355
|
-
value="{{
|
|
360
|
+
title="Failed Publishes",
|
|
361
|
+
value="{{ centrifugo_failed_publishes }}",
|
|
356
362
|
icon=Icons.ERROR,
|
|
357
363
|
color="red",
|
|
358
364
|
),
|
|
359
365
|
]
|
|
360
366
|
)
|
|
361
|
-
widgets.append(
|
|
367
|
+
widgets.append(centrifugo_monitoring_widget.to_dict())
|
|
362
368
|
|
|
363
369
|
# Convert to dictionaries for Unfold
|
|
364
370
|
return widgets
|
django_cfg/pyproject.toml
CHANGED
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "django-cfg"
|
|
7
|
-
version = "1.4.
|
|
7
|
+
version = "1.4.63"
|
|
8
8
|
description = "Django AI framework with built-in agents, type-safe Pydantic v2 configuration, and 8 enterprise apps. Replace settings.py, validate at startup, 90% less code. Production-ready AI workflows for Django."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
keywords = [ "django", "configuration", "pydantic", "settings", "type-safety", "pydantic-settings", "django-environ", "startup-validation", "ide-autocomplete", "ai-agents", "enterprise-django", "django-settings", "type-safe-config",]
|
django_cfg/registry/core.py
CHANGED
|
@@ -61,6 +61,8 @@ CORE_REGISTRY = {
|
|
|
61
61
|
|
|
62
62
|
# Utils
|
|
63
63
|
"version_check": ("django_cfg.utils.version_check", "version_check"),
|
|
64
|
+
"get_ticket_url": ("django_cfg.core.utils.url_helpers", "get_ticket_url"),
|
|
65
|
+
"get_otp_url": ("django_cfg.core.utils.url_helpers", "get_otp_url"),
|
|
64
66
|
|
|
65
67
|
# Routing
|
|
66
68
|
"DynamicRouter": ("django_cfg.routing.routers", "DynamicRouter"),
|
django_cfg/registry/modules.py
CHANGED
|
@@ -10,8 +10,8 @@ MODULES_REGISTRY = {
|
|
|
10
10
|
# Configuration utilities
|
|
11
11
|
"set_current_config": ("django_cfg.core.config", "set_current_config"),
|
|
12
12
|
|
|
13
|
-
#
|
|
14
|
-
"
|
|
13
|
+
# Centrifugo module
|
|
14
|
+
"DjangoCfgCentrifugoConfig": ("django_cfg.apps.centrifugo.services.client.config", "DjangoCfgCentrifugoConfig"),
|
|
15
15
|
|
|
16
16
|
# Import/Export integration (simple re-exports)
|
|
17
17
|
"ImportForm": ("django_cfg.modules.django_import_export", "ImportForm"),
|