django-cfg 1.4.91__py3-none-any.whl → 1.4.92__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 CHANGED
@@ -32,7 +32,7 @@ Example:
32
32
  default_app_config = "django_cfg.apps.DjangoCfgConfig"
33
33
 
34
34
  # Version information
35
- __version__ = "1.4.91"
35
+ __version__ = "1.4.92"
36
36
  __license__ = "MIT"
37
37
 
38
38
  # Import registry for organized lazy loading
@@ -571,13 +571,19 @@ class Command(BaseCommand):
571
571
 
572
572
  self.stdout.write(f"\n📦 Copying TypeScript clients to Next.js admin...")
573
573
 
574
- # Copy each group
574
+ # Copy each group (exclude 'cfg' for Next.js admin)
575
575
  copied_count = 0
576
576
  for group_dir in ts_source.iterdir():
577
577
  if not group_dir.is_dir():
578
578
  continue
579
579
 
580
580
  group_name = group_dir.name
581
+
582
+ # Skip 'cfg' group for Next.js admin
583
+ if group_name == 'cfg':
584
+ self.stdout.write(f" ⏭️ Skipping 'cfg' group (excluded from Next.js admin)")
585
+ continue
586
+
581
587
  target_dir = api_output_path / group_name
582
588
 
583
589
  # Remove old
@@ -48,7 +48,6 @@ class NavigationManager(BaseCfgModule):
48
48
  collapsible=True,
49
49
  items=[
50
50
  NavigationItem(title="Overview", icon=Icons.DASHBOARD, link=str(reverse_lazy("admin:index"))),
51
- NavigationItem(title="Frontend Admin", icon=Icons.WEB_ASSET, link="/cfg/admin/"),
52
51
  NavigationItem(title="Settings", icon=Icons.SETTINGS, link=str(reverse_lazy("admin:constance_config_changelist"))),
53
52
  NavigationItem(title="Health Check", icon=Icons.HEALTH_AND_SAFETY, link=str(reverse_lazy("django_cfg_drf_health"))),
54
53
  NavigationItem(title="Endpoints Status", icon=Icons.API, link=str(reverse_lazy("endpoints_status_drf"))),
@@ -118,27 +118,23 @@ class NextJsAdminView(LoginRequiredMixin, View):
118
118
  Resolve SPA path with Next.js routing conventions.
119
119
 
120
120
  Resolution order:
121
- 1. Default to iframe_route for empty path
121
+ 1. Default to /admin for empty path or /admin path
122
122
  2. Exact file match (static assets)
123
123
  3. path/index.html (SPA routes)
124
124
  4. path.html (single page)
125
125
  5. Fallback to index.html
126
126
 
127
127
  Examples:
128
- '' → 'private.html' (from iframe_route)
129
- 'private/centrifugo' → 'private/centrifugo/index.html'
128
+ '' → 'admin/index.html'
129
+ 'admin' → 'admin/index.html'
130
+ 'admin/centrifugo' → 'admin/centrifugo/index.html'
130
131
  '_next/static/...' → '_next/static/...' (exact)
131
132
  """
132
- # Empty path - use iframe_route
133
- if not path or path == '/':
134
- iframe_route = nextjs_config.get_iframe_route().strip('/')
135
- # Try iframe_route.html or iframe_route/index.html
136
- html_file = base_dir / f"{iframe_route}.html"
137
- if html_file.exists():
138
- return f"{iframe_route}.html"
139
- index_file = base_dir / iframe_route / 'index.html'
140
- if index_file.exists():
141
- return f"{iframe_route}/index.html"
133
+ # Empty path or 'admin' - serve /admin route
134
+ if not path or path == '/' or path == 'admin' or path == 'admin/':
135
+ admin_index = base_dir / 'admin' / 'index.html'
136
+ if admin_index.exists():
137
+ return 'admin/index.html'
142
138
  # Fallback to root index.html
143
139
  return 'index.html'
144
140
 
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.91"
7
+ version = "1.4.92"
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",]
@@ -5,13 +5,40 @@ Provides template tags for accessing django-cfg configuration constants.
5
5
  """
6
6
 
7
7
  import os
8
+ import socket
8
9
  from django import template
10
+ from django.conf import settings
9
11
  from django.utils.safestring import mark_safe
10
12
  from rest_framework_simplejwt.tokens import RefreshToken
11
13
 
12
14
  register = template.Library()
13
15
 
14
16
 
17
+ def _is_port_available(host: str, port: int, timeout: float = 0.1) -> bool:
18
+ """
19
+ Check if a port is available (listening) on the specified host.
20
+
21
+ Uses a quick socket connection test with minimal timeout.
22
+ Returns True if the port is open and accepting connections.
23
+
24
+ Args:
25
+ host: Host to check (e.g., 'localhost', '127.0.0.1')
26
+ port: Port number to check
27
+ timeout: Connection timeout in seconds (default: 0.1s)
28
+
29
+ Returns:
30
+ bool: True if port is available, False otherwise
31
+ """
32
+ try:
33
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
34
+ sock.settimeout(timeout)
35
+ result = sock.connect_ex((host, port))
36
+ sock.close()
37
+ return result == 0
38
+ except Exception:
39
+ return False
40
+
41
+
15
42
  @register.simple_tag
16
43
  def lib_name():
17
44
  """Get the library name."""
@@ -140,37 +167,42 @@ def inject_jwt_tokens_script(context):
140
167
  @register.simple_tag
141
168
  def nextjs_admin_url(path=''):
142
169
  """
143
- Get the URL for Next.js Admin Panel.
170
+ Get the URL for Next.js Admin Panel (Built-in Dashboard - Tab 1).
144
171
 
145
- In development mode (when DJANGO_CFG_FRONTEND_DEV_MODE=true):
146
- Returns http://localhost:3000/{path}
172
+ Auto-detects development mode with priority:
173
+ 1. If port 3000 is available → use static files (Tab 2 gets dev server)
174
+ 2. If only port 3001 is available → http://localhost:3001/admin/{path}
175
+ 3. Otherwise → /cfg/admin/admin/{path} (static files)
147
176
 
148
- In production mode:
149
- Returns /cfg/admin/{path}
177
+ This ensures only ONE tab shows dev server at a time.
150
178
 
151
179
  Usage in template:
152
180
  {% load django_cfg %}
153
- <iframe src="{% nextjs_admin_url 'private' %}"></iframe>
154
-
155
- Environment variable:
156
- DJANGO_CFG_FRONTEND_DEV_MODE=true # Enable dev mode
157
- DJANGO_CFG_FRONTEND_DEV_PORT=3000 # Custom dev port (default: 3000)
181
+ <iframe src="{% nextjs_admin_url %}"></iframe>
182
+ <iframe src="{% nextjs_admin_url 'centrifugo' %}"></iframe>
158
183
  """
159
- # Check if frontend dev mode is enabled
160
- is_dev_mode = os.environ.get('DJANGO_CFG_FRONTEND_DEV_MODE', '').lower() in ('true', '1', 'yes')
161
-
162
- # Normalize path - ensure trailing slash for Django static view
163
- if path and not path.endswith('/'):
164
- path = f'{path}/'
165
-
166
- if is_dev_mode:
167
- # Development mode: use Next.js dev server
168
- dev_port = os.environ.get('DJANGO_CFG_FRONTEND_DEV_PORT', '3000')
169
- base_url = f'http://localhost:{dev_port}'
184
+ # Normalize path - remove leading/trailing slashes
185
+ path = path.strip('/')
186
+
187
+ if not settings.DEBUG:
188
+ # Production mode: always use static files
189
+ return f'/cfg/admin/admin/{path}' if path else '/cfg/admin/admin/'
190
+
191
+ # Check which ports are available
192
+ port_3000_available = _is_port_available('localhost', 3000)
193
+ port_3001_available = _is_port_available('localhost', 3001)
194
+
195
+ # Priority: if port 3000 is running, Tab 2 gets it, Tab 1 uses static
196
+ if port_3000_available:
197
+ # Solution project is running - use static files for Tab 1
198
+ return f'/cfg/admin/admin/{path}' if path else '/cfg/admin/admin/'
199
+ elif port_3001_available:
200
+ # Dev project is running - use dev server for Tab 1
201
+ base_url = 'http://localhost:3001/admin'
170
202
  return f'{base_url}/{path}' if path else base_url
171
203
  else:
172
- # Production mode: use Django static files
173
- return f'/cfg/admin/{path}' if path else '/cfg/admin/'
204
+ # No dev server - use static files
205
+ return f'/cfg/admin/admin/{path}' if path else '/cfg/admin/admin/'
174
206
 
175
207
 
176
208
  @register.simple_tag
@@ -178,7 +210,11 @@ def is_frontend_dev_mode():
178
210
  """
179
211
  Check if frontend is in development mode.
180
212
 
181
- Returns True if DJANGO_CFG_FRONTEND_DEV_MODE environment variable is set to true.
213
+ Auto-detects by checking:
214
+ - DEBUG=True
215
+ - AND (port 3000 OR port 3001 is available)
216
+
217
+ Returns True if any Next.js dev server is detected.
182
218
 
183
219
  Usage in template:
184
220
  {% load django_cfg %}
@@ -186,7 +222,12 @@ def is_frontend_dev_mode():
186
222
  <div class="dev-badge">Dev Mode</div>
187
223
  {% endif %}
188
224
  """
189
- return os.environ.get('DJANGO_CFG_FRONTEND_DEV_MODE', '').lower() in ('true', '1', 'yes')
225
+ if not settings.DEBUG:
226
+ return False
227
+
228
+ # Check if either dev server is running
229
+ return (_is_port_available('localhost', 3000) or
230
+ _is_port_available('localhost', 3001))
190
231
 
191
232
 
192
233
  @register.simple_tag
@@ -214,10 +255,14 @@ def has_nextjs_external_admin():
214
255
  @register.simple_tag
215
256
  def nextjs_external_admin_url(route=''):
216
257
  """
217
- Get URL for external Next.js admin (Tab 2).
258
+ Get URL for external Next.js admin (Tab 2 - solution project).
259
+
260
+ Auto-detects development mode:
261
+ - If DEBUG=True AND localhost:3000 is available → http://localhost:3000/admin/{route}
262
+ - Otherwise → /cfg/nextjs-admin/admin/{route} (static files)
218
263
 
219
- Always returns relative URL (Django serves from static/nextjs_admin.zip):
220
- Returns /cfg/nextjs-admin/{route}
264
+ This is for the external admin panel (solution project).
265
+ No env variable needed - automatically detects running dev server!
221
266
 
222
267
  Usage in template:
223
268
  {% load django_cfg %}
@@ -231,10 +276,16 @@ def nextjs_external_admin_url(route=''):
231
276
  if not config or not config.nextjs_admin:
232
277
  return ''
233
278
 
234
- route = route.strip().lstrip('/')
279
+ route = route.strip('/')
235
280
 
236
- # Always use relative URL - Django serves from extracted ZIP
237
- return f"/cfg/nextjs-admin/{route}" if route else "/cfg/nextjs-admin/"
281
+ # Auto-detect development mode: DEBUG=True + port 3000 available
282
+ if settings.DEBUG and _is_port_available('localhost', 3000):
283
+ # Development mode: solution project on port 3000
284
+ base_url = 'http://localhost:3000/admin'
285
+ return f'{base_url}/{route}' if route else base_url
286
+ else:
287
+ # Production mode: use relative URL - Django serves from extracted ZIP with /admin prefix
288
+ return f"/cfg/nextjs-admin/admin/{route}" if route else "/cfg/nextjs-admin/admin/"
238
289
  except Exception:
239
290
  return ''
240
291
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-cfg
3
- Version: 1.4.91
3
+ Version: 1.4.92
4
4
  Summary: 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.
5
5
  Project-URL: Homepage, https://djangocfg.com
6
6
  Project-URL: Documentation, https://djangocfg.com
@@ -1,5 +1,5 @@
1
1
  django_cfg/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- django_cfg/__init__.py,sha256=kj3zXzYr4WyjjFFMMWhygk3WwbqcB2fu0kwdpUhl6ok,1620
2
+ django_cfg/__init__.py,sha256=pz7YbfETPVh0YKhFH8_iJUk4fnnsYc3GSgN4nzqt-cE,1620
3
3
  django_cfg/apps.py,sha256=72m3uuvyqGiLx6gOfE-BD3P61jddCCERuBOYpxTX518,1605
4
4
  django_cfg/config.py,sha256=y4Z3rnYsHBE0TehpwAIPaxr---mkvyKrZGGsNwYso74,1398
5
5
  django_cfg/apps/__init__.py,sha256=JtDmEYt1OcleWM2ZaeX0LKDnRQzPOavfaXBWG4ECB5Q,26
@@ -859,7 +859,7 @@ django_cfg/modules/django_client/core/validation/rules/base.py,sha256=xVJli0eSEz
859
859
  django_cfg/modules/django_client/core/validation/rules/type_hints.py,sha256=hwjTMADillsTPruDvXZQeZMj4LVV443zxY9o0Gqgg6k,10200
860
860
  django_cfg/modules/django_client/management/__init__.py,sha256=mCTPP_bIOmqNnn0WAG2n4BuF6zwc9PTgdZr_dORfNDk,54
861
861
  django_cfg/modules/django_client/management/commands/__init__.py,sha256=CJ55pHUNYQ5h-QHUe3axeTtxzlUJv7wbEuZmGN21iCM,36
862
- django_cfg/modules/django_client/management/commands/generate_client.py,sha256=524NucpzT93V0ODuQ_L5od7UEudYy2v4mumZ4wA9y94,30099
862
+ django_cfg/modules/django_client/management/commands/generate_client.py,sha256=Mzc0QyxCtL814MevY7oM9wg4DtqK0rTu2MKmBpBBj08,30360
863
863
  django_cfg/modules/django_client/management/commands/validate_openapi.py,sha256=IBKk7oRP3tMQzXjvZNIgQtMPk3k_mB2diNS7bkaSLz4,11011
864
864
  django_cfg/modules/django_client/spectacular/__init__.py,sha256=M8fG-odu2ltkG36aMMr0KDkCKGX676TwdrJO8vky2cI,345
865
865
  django_cfg/modules/django_client/spectacular/async_detection.py,sha256=S_pwGR7_2SIWHjZJyiu7SCfySF3Nr3P8eqjDyBSkkLs,5731
@@ -1010,7 +1010,7 @@ django_cfg/modules/django_twilio/templates/guide.md,sha256=nZfwx-sgWyK5NApm93zOe
1010
1010
  django_cfg/modules/django_twilio/templates/sendgrid_otp_email.html,sha256=sXR6_D9hmOFfk9CrfPizpLddVhkRirBWpZd_ioEsxVk,6671
1011
1011
  django_cfg/modules/django_twilio/templates/sendgrid_test_data.json,sha256=fh1VyuSiDELHsS_CIz9gp7tlsMAEjaDOoqbAPSZ3yyo,339
1012
1012
  django_cfg/modules/django_unfold/__init__.py,sha256=Uquez6xgPUIc8FBMP7qif-adRYQSjQ2dBHxnJPom3eQ,1337
1013
- django_cfg/modules/django_unfold/navigation.py,sha256=_trIm8f-K-8Nc5Gp9iEE5rKX7Bjo-Sn03GM3_oK92vI,11623
1013
+ django_cfg/modules/django_unfold/navigation.py,sha256=BSJk4NxTpkTh2Pf_NTkLf77EJSm54ta3l1-SMzYKz2U,11521
1014
1014
  django_cfg/modules/django_unfold/system_monitor.py,sha256=KcrTa5irstdB1pDKe3sC0zl4tz9LVjhp7xvbqUM4YVk,6781
1015
1015
  django_cfg/modules/django_unfold/tailwind.py,sha256=NY8nWcUdQw61oNmjPoPl7agcTb-r5IqcpFYy35MNsTM,9107
1016
1016
  django_cfg/modules/django_unfold/utils.py,sha256=5aFaceRc9B3eXfpOVyKRD2wWqFt8KcHxjQg54oD7Oyg,4482
@@ -1021,7 +1021,7 @@ django_cfg/modules/django_unfold/models/navigation.py,sha256=PPEeqA2HBaA1-VjADiX
1021
1021
  django_cfg/modules/nextjs_admin/__init__.py,sha256=lfrZYyNRExH3Z5De8G4hQBIZoFlW5Ejze3couNrztbY,312
1022
1022
  django_cfg/modules/nextjs_admin/apps.py,sha256=HxVUMmWTKdYpwJ00iIfWVFsBzsawsOVhEPZqjk_izjI,347
1023
1023
  django_cfg/modules/nextjs_admin/urls.py,sha256=7n0yStm0WNchw14Rtu_mgsIA3WKQsYP9WZt3-YOUWjU,603
1024
- django_cfg/modules/nextjs_admin/views.py,sha256=TFnf21-e0klcjTbcmfvq-YT1fD6y_G8kKXImYuEVNSs,9857
1024
+ django_cfg/modules/nextjs_admin/views.py,sha256=SxpFQeRas0FsolZ4QNCqDEAVhncGxnDP4HtamuUsTu0,9655
1025
1025
  django_cfg/modules/nextjs_admin/models/__init__.py,sha256=WGw9KXcYd1O9AoA_bpMoz2gLZUlRzjGmUBjjbObcUi0,100
1026
1026
  django_cfg/modules/nextjs_admin/models/config.py,sha256=0ADqLuiywSCQfx_z9dkwjFCca3lr3F2uQffIjTr_QXw,5864
1027
1027
  django_cfg/modules/nextjs_admin/templatetags/__init__.py,sha256=ChVBnJggCIY8rMhfyJFoA8k0qKo-8FtJknrk54Vx4wM,51
@@ -1051,6 +1051,7 @@ django_cfg/static/admin/js/alpine/dashboard-tabs.js,sha256=ob8Q_I9lFLDv_hFERXgTy
1051
1051
  django_cfg/static/admin/js/alpine/system-metrics.js,sha256=m-Fg55K_vpHXToD46PXL9twl4OBF_V9MONvbSWbQqDw,440
1052
1052
  django_cfg/static/admin/js/alpine/toggle-section.js,sha256=T141NFmy0fRJyGGuuaCJRjJXwPam-xxtQNW1hi8BJbc,672
1053
1053
  django_cfg/static/frontend/admin.zip,sha256=n2G8ajruLqojcjqvw2pMEPjgpSTTwcmJxkeIQxJVw9U,7626768
1054
+ django_cfg/static/frontend/nextjs_admin.zip,sha256=KZMpJgmsL-Gx-la-zLCpV0W2pZ0X94IrPqNNK8fmGB0,7509389
1054
1055
  django_cfg/static/js/api-loader.mjs,sha256=boGqqRGnFR-Mzo_RQOjhAzNvsb7QxZddSwMKROzkk9Q,5163
1055
1056
  django_cfg/static/js/api/base.mjs,sha256=KUxZHHdELAV8mNnACpwJRvaQhdJxp-n5LFEQ4oUZxBo,4707
1056
1057
  django_cfg/static/js/api/index.mjs,sha256=_-Q04jjHcgwi4CGfiaLyiOR6NW7Yu1HBhJWp2J1cjpc,2538
@@ -1076,7 +1077,7 @@ django_cfg/templates/admin/index.html,sha256=6-f3b2IfMkK_Qog5jHzeotqn3GAeLE7JpJI
1076
1077
  django_cfg/templates/emails/base_email.html,sha256=TWcvYa2IHShlF_E8jf1bWZStRO0v8G4L_GexPxvz6XQ,8836
1077
1078
  django_cfg/templates/unfold/layouts/skeleton.html,sha256=2ArkcNZ34mFs30cOAsTQ1EZiDXcB0aVxkO71lJq9SLE,718
1078
1079
  django_cfg/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1079
- django_cfg/templatetags/django_cfg.py,sha256=spM6-33g-dHeOcKdeK8ZJMeNM98-HGg9Ta326f7ITbI,7664
1080
+ django_cfg/templatetags/django_cfg.py,sha256=7lGod6_K8AonRAUIZksluN6SWoxqQjke67D-lllMpY8,9670
1080
1081
  django_cfg/utils/__init__.py,sha256=64wwXJuXytvwt8Ze_erSR2HmV07nGWJ6DV5wloRBvYE,435
1081
1082
  django_cfg/utils/path_resolution.py,sha256=2n0I04lQkSssFaELu3A93YyMAl1K10KPdpxMt5k4Iy0,13341
1082
1083
  django_cfg/utils/smart_defaults.py,sha256=ZUj6K_Deq-fp5O0Dy_Emt257UWFn0f9bkgFv9YCR58U,9239
@@ -1084,9 +1085,9 @@ django_cfg/utils/version_check.py,sha256=WO51J2m2e-wVqWCRwbultEwu3q1lQasV67Mw2aa
1084
1085
  django_cfg/CHANGELOG.md,sha256=jtT3EprqEJkqSUh7IraP73vQ8PmKUMdRtznQsEnqDZk,2052
1085
1086
  django_cfg/CONTRIBUTING.md,sha256=DU2kyQ6PU0Z24ob7O_OqKWEYHcZmJDgzw-lQCmu6uBg,3041
1086
1087
  django_cfg/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1087
- django_cfg/pyproject.toml,sha256=b-09CpZ_pSkvI6H-nr_QDNbM16IrS26fwbkpTUOo2Xs,8370
1088
- django_cfg-1.4.91.dist-info/METADATA,sha256=dZ2bEbDGD0OVuLl7I0DsX_Snuznxhht1l4NcnfW_gUA,22624
1089
- django_cfg-1.4.91.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1090
- django_cfg-1.4.91.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
1091
- django_cfg-1.4.91.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1092
- django_cfg-1.4.91.dist-info/RECORD,,
1088
+ django_cfg/pyproject.toml,sha256=HlcU42lQgCt8Qn4XJAjcIuIaU2X1wsjmzqMV-9G0fKI,8370
1089
+ django_cfg-1.4.92.dist-info/METADATA,sha256=CiXTzDGbsjBbZ7aDnUUNERfR1FR8Kz_VlV50BAOjUNM,22624
1090
+ django_cfg-1.4.92.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1091
+ django_cfg-1.4.92.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
1092
+ django_cfg-1.4.92.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1093
+ django_cfg-1.4.92.dist-info/RECORD,,