django-cfg 1.4.120__py3-none-any.whl → 1.5.1__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.

Files changed (80) hide show
  1. django_cfg/__init__.py +8 -4
  2. django_cfg/apps/centrifugo/admin/centrifugo_log.py +33 -71
  3. django_cfg/apps/grpc/__init__.py +9 -0
  4. django_cfg/apps/grpc/admin/__init__.py +11 -0
  5. django_cfg/apps/grpc/admin/config.py +89 -0
  6. django_cfg/apps/grpc/admin/grpc_request_log.py +252 -0
  7. django_cfg/apps/grpc/apps.py +28 -0
  8. django_cfg/apps/grpc/auth/__init__.py +9 -0
  9. django_cfg/apps/grpc/auth/jwt_auth.py +295 -0
  10. django_cfg/apps/grpc/interceptors/__init__.py +19 -0
  11. django_cfg/apps/grpc/interceptors/errors.py +241 -0
  12. django_cfg/apps/grpc/interceptors/logging.py +270 -0
  13. django_cfg/apps/grpc/interceptors/metrics.py +306 -0
  14. django_cfg/apps/grpc/interceptors/request_logger.py +515 -0
  15. django_cfg/apps/grpc/management/__init__.py +1 -0
  16. django_cfg/apps/grpc/management/commands/__init__.py +0 -0
  17. django_cfg/apps/grpc/management/commands/rungrpc.py +302 -0
  18. django_cfg/apps/grpc/managers/__init__.py +10 -0
  19. django_cfg/apps/grpc/managers/grpc_request_log.py +310 -0
  20. django_cfg/apps/grpc/migrations/0001_initial.py +69 -0
  21. django_cfg/apps/grpc/migrations/0002_rename_django_cfg__service_4c4a8e_idx_django_cfg__service_584308_idx_and_more.py +38 -0
  22. django_cfg/apps/grpc/migrations/__init__.py +0 -0
  23. django_cfg/apps/grpc/models/__init__.py +9 -0
  24. django_cfg/apps/grpc/models/grpc_request_log.py +219 -0
  25. django_cfg/apps/grpc/serializers/__init__.py +23 -0
  26. django_cfg/apps/grpc/serializers/health.py +18 -0
  27. django_cfg/apps/grpc/serializers/requests.py +18 -0
  28. django_cfg/apps/grpc/serializers/services.py +50 -0
  29. django_cfg/apps/grpc/serializers/stats.py +22 -0
  30. django_cfg/apps/grpc/services/__init__.py +16 -0
  31. django_cfg/apps/grpc/services/base.py +375 -0
  32. django_cfg/apps/grpc/services/discovery.py +415 -0
  33. django_cfg/apps/grpc/urls.py +23 -0
  34. django_cfg/apps/grpc/utils/__init__.py +13 -0
  35. django_cfg/apps/grpc/utils/proto_gen.py +423 -0
  36. django_cfg/apps/grpc/views/__init__.py +9 -0
  37. django_cfg/apps/grpc/views/monitoring.py +497 -0
  38. django_cfg/apps/maintenance/admin/api_key_admin.py +7 -8
  39. django_cfg/apps/maintenance/admin/site_admin.py +5 -4
  40. django_cfg/apps/payments/admin/balance_admin.py +26 -36
  41. django_cfg/apps/payments/admin/payment_admin.py +65 -85
  42. django_cfg/apps/payments/admin/withdrawal_admin.py +65 -100
  43. django_cfg/apps/tasks/admin/task_log.py +20 -47
  44. django_cfg/apps/urls.py +7 -1
  45. django_cfg/config.py +106 -0
  46. django_cfg/core/base/config_model.py +6 -0
  47. django_cfg/core/builders/apps_builder.py +3 -0
  48. django_cfg/core/generation/integration_generators/grpc_generator.py +318 -0
  49. django_cfg/core/generation/orchestrator.py +10 -0
  50. django_cfg/models/api/grpc/__init__.py +59 -0
  51. django_cfg/models/api/grpc/config.py +364 -0
  52. django_cfg/modules/base.py +15 -0
  53. django_cfg/modules/django_admin/base/pydantic_admin.py +2 -2
  54. django_cfg/modules/django_admin/utils/__init__.py +41 -3
  55. django_cfg/modules/django_admin/utils/badges/__init__.py +13 -0
  56. django_cfg/modules/django_admin/utils/{badges.py → badges/status_badges.py} +3 -3
  57. django_cfg/modules/django_admin/utils/displays/__init__.py +13 -0
  58. django_cfg/modules/django_admin/utils/{displays.py → displays/data_displays.py} +2 -2
  59. django_cfg/modules/django_admin/utils/html/__init__.py +26 -0
  60. django_cfg/modules/django_admin/utils/html/badges.py +47 -0
  61. django_cfg/modules/django_admin/utils/html/base.py +167 -0
  62. django_cfg/modules/django_admin/utils/html/code.py +87 -0
  63. django_cfg/modules/django_admin/utils/html/composition.py +198 -0
  64. django_cfg/modules/django_admin/utils/html/formatting.py +231 -0
  65. django_cfg/modules/django_admin/utils/html/keyvalue.py +219 -0
  66. django_cfg/modules/django_admin/utils/html/markdown_integration.py +108 -0
  67. django_cfg/modules/django_admin/utils/html/progress.py +127 -0
  68. django_cfg/modules/django_admin/utils/html_builder.py +55 -408
  69. django_cfg/modules/django_admin/utils/markdown/__init__.py +21 -0
  70. django_cfg/modules/django_unfold/navigation.py +28 -0
  71. django_cfg/pyproject.toml +3 -5
  72. django_cfg/registry/modules.py +6 -0
  73. {django_cfg-1.4.120.dist-info → django_cfg-1.5.1.dist-info}/METADATA +10 -1
  74. {django_cfg-1.4.120.dist-info → django_cfg-1.5.1.dist-info}/RECORD +79 -30
  75. django_cfg/modules/django_admin/utils/CODE_BLOCK_DOCS.md +0 -396
  76. /django_cfg/modules/django_admin/utils/{mermaid_plugin.py → markdown/mermaid_plugin.py} +0 -0
  77. /django_cfg/modules/django_admin/utils/{markdown_renderer.py → markdown/renderer.py} +0 -0
  78. {django_cfg-1.4.120.dist-info → django_cfg-1.5.1.dist-info}/WHEEL +0 -0
  79. {django_cfg-1.4.120.dist-info → django_cfg-1.5.1.dist-info}/entry_points.txt +0 -0
  80. {django_cfg-1.4.120.dist-info → django_cfg-1.5.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,419 +1,66 @@
1
1
  """
2
- Universal HTML builder for Django Admin display methods.
3
- """
2
+ HtmlBuilder - Facade class for backward compatibility.
4
3
 
5
- from pathlib import Path
6
- from typing import Any, List, Optional, Union
7
- import re
4
+ This class provides the old `self.html.*` API by delegating to new modular components.
5
+ All existing admin code using `self.html.badge()`, `self.html.inline()`, etc. will continue to work.
8
6
 
9
- from django.utils.html import escape, format_html
10
- from django.utils.safestring import SafeString
7
+ New code should import and use the specific modules directly:
8
+ from django_cfg.modules.django_admin.utils import BaseElements, FormattingElements, etc.
9
+ """
11
10
 
12
- from ..icons import Icons
13
- from .markdown_renderer import MarkdownRenderer
11
+ from .html import (
12
+ BadgeElements,
13
+ BaseElements,
14
+ CodeElements,
15
+ CompositionElements,
16
+ FormattingElements,
17
+ KeyValueElements,
18
+ MarkdownIntegration,
19
+ ProgressElements,
20
+ )
14
21
 
15
22
 
16
23
  class HtmlBuilder:
17
24
  """
18
- Universal HTML builder with Material Icons support and Markdown rendering.
19
-
20
- Usage in admin methods:
21
- def stats(self, obj):
22
- return self.html.inline([
23
- self.html.icon_text(Icons.EDIT, obj.posts_count),
24
- self.html.icon_text(Icons.CHAT, obj.comments_count),
25
- ])
25
+ Facade class that delegates to modular HTML utilities.
26
26
 
27
- def documentation(self, obj):
28
- return self.html.markdown_docs(obj.docs_path)
27
+ Provides backward compatibility for `self.html.*` API in admin classes.
29
28
  """
30
29
 
31
- @staticmethod
32
- def icon(icon_name: str, size: str = "xs", css_class: str = "") -> SafeString:
33
- """
34
- Render Material Icon.
35
-
36
- Args:
37
- icon_name: Icon name from Icons class
38
- size: xs, sm, base, lg, xl
39
- css_class: Additional CSS classes
40
- """
41
- size_classes = {
42
- 'xs': 'text-xs',
43
- 'sm': 'text-sm',
44
- 'base': 'text-base',
45
- 'lg': 'text-lg',
46
- 'xl': 'text-xl'
47
- }
48
- size_class = size_classes.get(size, 'text-xs')
49
- classes = f"material-symbols-outlined {size_class}"
50
- if css_class:
51
- classes += f" {css_class}"
52
-
53
- return format_html('<span class="{}">{}</span>', classes, icon_name)
54
-
55
- @staticmethod
56
- def icon_text(icon_or_text: Union[str, Any], text: Any = None,
57
- icon_size: str = "xs", separator: str = " ") -> SafeString:
58
- """
59
- Render icon with text or emoji with text.
60
-
61
- Args:
62
- icon_or_text: Icon from Icons class, emoji, or text if text param is None
63
- text: Optional text to display after icon
64
- icon_size: Icon size (xs, sm, base, lg, xl)
65
- separator: Separator between icon and text
66
-
67
- Usage:
68
- html.icon_text(Icons.EDIT, 5) # Icon with number
69
- html.icon_text("📝", 5) # Emoji with number
70
- html.icon_text("Active") # Just text
71
- """
72
- if text is None:
73
- # Just text
74
- return format_html('<span>{}</span>', escape(str(icon_or_text)))
75
-
76
- # Check if it's a Material Icon (from Icons class) or emoji
77
- icon_str = str(icon_or_text)
78
-
79
- # Detect if it's emoji by checking for non-ASCII characters
80
- is_emoji = any(ord(c) > 127 for c in icon_str)
81
-
82
- if is_emoji or icon_str in ['📝', '💬', '🛒', '👤', '📧', '🔔', '⚙️', '🔧', '📊', '🎯']:
83
- # Emoji
84
- icon_html = escape(icon_str)
85
- else:
86
- # Material Icon
87
- icon_html = HtmlBuilder.icon(icon_str, size=icon_size)
88
-
89
- return format_html('{}{}<span>{}</span>', icon_html, separator, escape(str(text)))
90
-
91
- @staticmethod
92
- def inline(items: List[Any], separator: str = " | ",
93
- size: str = "small", css_class: str = "") -> SafeString:
94
- """
95
- Render items inline with separator.
96
-
97
- Args:
98
- items: List of SafeString/str items to join
99
- separator: Separator between items
100
- size: small, medium, large
101
- css_class: Additional CSS classes
102
-
103
- Usage:
104
- html.inline([
105
- html.icon_text(Icons.EDIT, 5),
106
- html.icon_text(Icons.CHAT, 10),
107
- ])
108
- """
109
- if not items:
110
- return format_html('<span class="text-font-subtle-light dark:text-font-subtle-dark">—</span>')
111
-
112
- size_classes = {
113
- 'small': 'text-xs',
114
- 'medium': 'text-sm',
115
- 'large': 'text-base'
116
- }
117
- size_class = size_classes.get(size, 'text-xs')
118
-
119
- classes = size_class
120
- if css_class:
121
- classes += f" {css_class}"
122
-
123
- # Convert items to strings, keeping SafeString as-is
124
- from django.utils.safestring import SafeString, mark_safe
125
- processed_items = []
126
- for item in items:
127
- if isinstance(item, (SafeString, str)):
128
- processed_items.append(item)
129
- else:
130
- processed_items.append(escape(str(item)))
131
-
132
- # Join with separator
133
- joined = mark_safe(separator.join(str(item) for item in processed_items))
134
-
135
- return format_html('<span class="{}">{}</span>', classes, joined)
136
-
137
- @staticmethod
138
- def span(text: Any, css_class: str = "") -> SafeString:
139
- """
140
- Render text in span with optional CSS class.
141
-
142
- Args:
143
- text: Text to display
144
- css_class: CSS classes
145
- """
146
- if css_class:
147
- return format_html('<span class="{}">{}</span>', css_class, escape(str(text)))
148
- return format_html('<span>{}</span>', escape(str(text)))
149
-
150
- @staticmethod
151
- def div(content: Any, css_class: str = "") -> SafeString:
152
- """
153
- Render content in div with optional CSS class.
154
-
155
- Args:
156
- content: Content to display (can be SafeString)
157
- css_class: CSS classes
158
- """
159
- if css_class:
160
- return format_html('<div class="{}">{}</div>', css_class, content)
161
- return format_html('<div>{}</div>', content)
162
-
163
- @staticmethod
164
- def link(url: str, text: str, css_class: str = "", target: str = "") -> SafeString:
165
- """
166
- Render link.
167
-
168
- Args:
169
- url: URL
170
- text: Link text
171
- css_class: CSS classes
172
- target: Target attribute (_blank, _self, etc)
173
- """
174
- if target:
175
- return format_html(
176
- '<a href="{}" class="{}" target="{}">{}</a>',
177
- url, css_class, target, escape(text)
178
- )
179
- return format_html('<a href="{}" class="{}">{}</a>', url, css_class, escape(text))
180
-
181
- @staticmethod
182
- def badge(text: Any, variant: str = "primary", icon: Optional[str] = None) -> SafeString:
183
- """
184
- Render badge with optional icon.
185
-
186
- Args:
187
- text: Badge text
188
- variant: primary, success, warning, danger, info, secondary
189
- icon: Optional Material Icon
190
-
191
- Usage:
192
- html.badge("Active", variant="success", icon=Icons.CHECK_CIRCLE)
193
- """
194
- variant_classes = {
195
- 'success': 'bg-success-100 text-success-800 dark:bg-success-900 dark:text-success-200',
196
- 'warning': 'bg-warning-100 text-warning-800 dark:bg-warning-900 dark:text-warning-200',
197
- 'danger': 'bg-danger-100 text-danger-800 dark:bg-danger-900 dark:text-danger-200',
198
- 'info': 'bg-info-100 text-info-800 dark:bg-info-900 dark:text-info-200',
199
- 'primary': 'bg-primary-100 text-primary-800 dark:bg-primary-900 dark:text-primary-200',
200
- 'secondary': 'bg-base-100 text-font-default-light dark:bg-base-800 dark:text-font-default-dark',
201
- }
202
-
203
- css_classes = variant_classes.get(variant, variant_classes['primary'])
204
-
205
- icon_html = ""
206
- if icon:
207
- icon_html = format_html('<span class="material-symbols-outlined text-xs mr-1">{}</span>', icon)
208
-
209
- return format_html(
210
- '<span class="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium {}">{}{}</span>',
211
- css_classes, icon_html, escape(str(text))
212
- )
213
-
214
- @staticmethod
215
- def empty(text: str = "—") -> SafeString:
216
- """Render empty/placeholder value."""
217
- return format_html(
218
- '<span class="text-font-subtle-light dark:text-font-subtle-dark">{}</span>',
219
- escape(text)
220
- )
221
-
222
- @staticmethod
223
- def code(text: Any, css_class: str = "") -> SafeString:
224
- """
225
- Render inline code.
226
-
227
- Args:
228
- text: Code text
229
- css_class: Additional CSS classes
230
-
231
- Usage:
232
- html.code("/path/to/file")
233
- html.code("command --arg value")
234
- """
235
- base_classes = "font-mono text-xs bg-base-100 dark:bg-base-800 px-1.5 py-0.5 rounded"
236
- classes = f"{base_classes} {css_class}".strip()
237
-
238
- return format_html(
239
- '<code class="{}">{}</code>',
240
- classes,
241
- escape(str(text))
242
- )
243
-
244
- @staticmethod
245
- def code_block(
246
- text: Any,
247
- language: Optional[str] = None,
248
- max_height: Optional[str] = None,
249
- variant: str = "default"
250
- ) -> SafeString:
251
- """
252
- Render code block with optional syntax highlighting and scrolling.
253
-
254
- Args:
255
- text: Code content
256
- language: Programming language (json, python, bash, etc.) - for future syntax highlighting
257
- max_height: Max height with scrolling (e.g., "400px", "20rem")
258
- variant: Color variant - default, warning, danger, success, info
259
-
260
- Usage:
261
- html.code_block(json.dumps(data, indent=2), language="json")
262
- html.code_block(stdout, max_height="400px")
263
- html.code_block(stderr, max_height="400px", variant="warning")
264
- """
265
- # Variant-specific styles
266
- variant_classes = {
267
- 'default': 'bg-base-50 dark:bg-base-900 border-base-200 dark:border-base-700',
268
- 'warning': 'bg-warning-50 dark:bg-warning-900/20 border-warning-200 dark:border-warning-700',
269
- 'danger': 'bg-danger-50 dark:bg-danger-900/20 border-danger-200 dark:border-danger-700',
270
- 'success': 'bg-success-50 dark:bg-success-900/20 border-success-200 dark:border-success-700',
271
- 'info': 'bg-info-50 dark:bg-info-900/20 border-info-200 dark:border-info-700',
272
- }
273
-
274
- variant_class = variant_classes.get(variant, variant_classes['default'])
275
-
276
- # Base styles
277
- base_classes = f"font-mono text-xs whitespace-pre-wrap break-words border rounded-md p-3 {variant_class}"
278
-
279
- # Add max-height and overflow if specified
280
- style = ""
281
- if max_height:
282
- style = f'style="max-height: {max_height}; overflow-y: auto;"'
283
-
284
- # Add language class for potential syntax highlighting
285
- lang_class = f"language-{language}" if language else ""
286
-
287
- return format_html(
288
- '<pre class="{} {}" {}><code>{}</code></pre>',
289
- base_classes,
290
- lang_class,
291
- style,
292
- escape(str(text))
293
- )
294
-
295
- @staticmethod
296
- def markdown(
297
- text: str,
298
- css_class: str = "",
299
- max_height: Optional[str] = None,
300
- enable_plugins: bool = True
301
- ) -> SafeString:
302
- """
303
- Render markdown text to beautifully styled HTML.
304
-
305
- Delegates to MarkdownRenderer.render_markdown() for actual rendering.
306
-
307
- Args:
308
- text: Markdown content
309
- css_class: Additional CSS classes
310
- max_height: Max height with scrolling (e.g., "400px", "20rem")
311
- enable_plugins: Enable mistune plugins (tables, strikethrough, etc.)
312
-
313
- Usage:
314
- # Simple markdown rendering
315
- html.markdown("# Hello\\n\\nThis is **bold** text")
316
-
317
- # With custom styling
318
- html.markdown(obj.description, css_class="my-custom-class")
319
-
320
- # With max height for long content
321
- html.markdown(obj.documentation, max_height="500px")
322
-
323
- Returns:
324
- SafeString with rendered HTML
325
- """
326
- return MarkdownRenderer.render_markdown(
327
- text=text,
328
- css_class=css_class,
329
- max_height=max_height,
330
- enable_plugins=enable_plugins
331
- )
332
-
333
- @staticmethod
334
- def uuid_short(uuid_value: Any, length: int = 6, show_tooltip: bool = True) -> SafeString:
335
- """
336
- Shorten UUID to first N characters with optional tooltip.
337
-
338
- Args:
339
- uuid_value: UUID string or UUID object
340
- length: Number of characters to show (default: 6)
341
- show_tooltip: Show full UUID on hover (default: True)
342
-
343
- Usage:
344
- html.uuid_short(obj.id) # "a1b2c3..."
345
- html.uuid_short(obj.id, length=8) # "a1b2c3d4..."
346
- html.uuid_short(obj.id, show_tooltip=False) # Just short version
347
-
348
- Returns:
349
- SafeString with shortened UUID
350
- """
351
- uuid_str = str(uuid_value)
352
-
353
- # Remove dashes for cleaner display
354
- uuid_clean = uuid_str.replace('-', '')
355
-
356
- # Take first N characters
357
- short_uuid = uuid_clean[:length]
358
-
359
- if show_tooltip:
360
- return format_html(
361
- '<code class="font-mono text-xs bg-base-100 dark:bg-base-800 px-1.5 py-0.5 rounded cursor-help" title="{}">{}</code>',
362
- uuid_str,
363
- short_uuid
364
- )
365
-
366
- return format_html(
367
- '<code class="font-mono text-xs bg-base-100 dark:bg-base-800 px-1.5 py-0.5 rounded">{}</code>',
368
- short_uuid
369
- )
370
-
371
- @staticmethod
372
- def markdown_docs(
373
- content: Union[str, Path],
374
- collapsible: bool = True,
375
- title: str = "Documentation",
376
- icon: str = "description",
377
- max_height: Optional[str] = "500px",
378
- enable_plugins: bool = True,
379
- default_open: bool = False
380
- ) -> SafeString:
381
- """
382
- Render markdown documentation from string or file with collapsible UI.
383
-
384
- Auto-detects whether content is a file path or markdown string.
385
-
386
- Args:
387
- content: Markdown string or path to .md file
388
- collapsible: Wrap in collapsible details/summary
389
- title: Title for collapsible section
390
- icon: Material icon name for title
391
- max_height: Max height for scrolling
392
- enable_plugins: Enable markdown plugins
393
- default_open: Open by default if collapsible
394
-
395
- Usage:
396
- # From string with collapse
397
- html.markdown_docs(obj.description, title="Description")
398
-
399
- # From file
400
- html.markdown_docs("docs/api.md", title="API Documentation")
401
-
402
- # Simple, no collapse
403
- html.markdown_docs(obj.notes, collapsible=False)
404
-
405
- # Open by default
406
- html.markdown_docs(obj.readme, default_open=True)
407
-
408
- Returns:
409
- Rendered markdown with beautiful Tailwind styling
410
- """
411
- return MarkdownRenderer.render(
412
- content=content,
413
- collapsible=collapsible,
414
- title=title,
415
- icon=icon,
416
- max_height=max_height,
417
- enable_plugins=enable_plugins,
418
- default_open=default_open
419
- )
30
+ # === BaseElements ===
31
+ icon = staticmethod(BaseElements.icon)
32
+ span = staticmethod(BaseElements.span)
33
+ text = staticmethod(BaseElements.text)
34
+ div = staticmethod(BaseElements.div)
35
+ link = staticmethod(BaseElements.link)
36
+ empty = staticmethod(BaseElements.empty)
37
+
38
+ # === CodeElements ===
39
+ code = staticmethod(CodeElements.code)
40
+ code_block = staticmethod(CodeElements.code_block)
41
+
42
+ # === BadgeElements ===
43
+ badge = staticmethod(BadgeElements.badge)
44
+
45
+ # === CompositionElements ===
46
+ inline = staticmethod(CompositionElements.inline)
47
+ icon_text = staticmethod(CompositionElements.icon_text)
48
+ header = staticmethod(CompositionElements.header)
49
+
50
+ # === FormattingElements ===
51
+ number = staticmethod(FormattingElements.number)
52
+ uuid_short = staticmethod(FormattingElements.uuid_short)
53
+
54
+ # === KeyValueElements ===
55
+ key_value = staticmethod(KeyValueElements.key_value)
56
+ breakdown = staticmethod(KeyValueElements.breakdown)
57
+ divider = staticmethod(KeyValueElements.divider)
58
+ key_value_list = staticmethod(KeyValueElements.key_value_list)
59
+
60
+ # === ProgressElements ===
61
+ segment = staticmethod(ProgressElements.segment)
62
+ progress_bar = staticmethod(ProgressElements.progress_bar)
63
+
64
+ # === MarkdownIntegration ===
65
+ markdown = staticmethod(MarkdownIntegration.markdown)
66
+ markdown_docs = staticmethod(MarkdownIntegration.markdown_docs)
@@ -0,0 +1,21 @@
1
+ """
2
+ Markdown rendering utilities for Django Admin.
3
+
4
+ Provides MarkdownRenderer and mermaid plugin support.
5
+ """
6
+
7
+ from .mermaid_plugin import (
8
+ get_mermaid_resources,
9
+ get_mermaid_script,
10
+ get_mermaid_styles,
11
+ mermaid_plugin,
12
+ )
13
+ from .renderer import MarkdownRenderer
14
+
15
+ __all__ = [
16
+ "MarkdownRenderer",
17
+ "mermaid_plugin",
18
+ "get_mermaid_styles",
19
+ "get_mermaid_script",
20
+ "get_mermaid_resources",
21
+ ]
@@ -146,6 +146,34 @@ class NavigationManager(BaseCfgModule):
146
146
  )
147
147
  )
148
148
 
149
+ # gRPC Dashboard (if enabled)
150
+ if self.is_grpc_enabled():
151
+ grpc_items = []
152
+
153
+ # Monitoring API endpoint
154
+ grpc_items.append(
155
+ NavigationItem(title="Monitor", icon=Icons.MONITOR_HEART, link="/cfg/grpc/monitor/overview/")
156
+ )
157
+
158
+ # Request Logs with safe URL resolution
159
+ logs_item = self._create_nav_item(
160
+ title="Request Logs",
161
+ icon=Icons.LIST_ALT,
162
+ url_name="admin:grpc_grpcrequestlog_changelist",
163
+ fallback_link="/admin/grpc/grpcrequestlog/"
164
+ )
165
+ if logs_item:
166
+ grpc_items.append(logs_item)
167
+
168
+ navigation_sections.append(
169
+ NavigationSection(
170
+ title="gRPC",
171
+ separator=True,
172
+ collapsible=True,
173
+ items=grpc_items
174
+ )
175
+ )
176
+
149
177
  # Background Tasks section (if enabled)
150
178
  if self.should_enable_tasks():
151
179
  tasks_items = []
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.120"
7
+ version = "1.5.1"
8
8
  description = "Modern Django framework with type-safe Pydantic v2 configuration, Next.js admin integration, real-time WebSockets, and 8 enterprise apps. Replace settings.py with validated models, 90% less code. Production-ready with AI agents, auto-generated TypeScript clients, and zero-config features."
9
9
  readme = "README.md"
10
10
  keywords = [ "django", "configuration", "pydantic", "settings", "type-safety", "pydantic-settings", "django-environ", "startup-validation", "ide-autocomplete", "nextjs-admin", "react-admin", "websocket", "centrifugo", "real-time", "typescript-generation", "ai-agents", "enterprise-django", "django-settings", "type-safe-config", "modern-django",]
@@ -26,11 +26,12 @@ text = "MIT"
26
26
  local = []
27
27
  django52 = [ "django>=5.2,<6.0",]
28
28
  ai = [ "pydantic-ai>=1.0.10,<2.0",]
29
+ grpc = [ "grpcio>=1.50.0,<2.0", "grpcio-tools>=1.50.0,<2.0", "protobuf>=4.21.0,<5.0", "djangogrpcframework>=0.2.1",]
29
30
  dev = [ "django>=5.2,<6.0", "pytest>=8.4,<9.0", "pytest-django>=4.11,<5.0", "pytest-cov>=7.0,<8.0", "pytest-mock>=3.15,<4.0", "factory-boy>=3.3,<4.0", "fakeredis>=2.28,<3.0", "black>=25.9,<26.0", "isort>=6.0,<7.0", "flake8>=6.0.0,<8.0", "mypy>=1.18,<2.0", "pre-commit>=4.3,<5.0", "build>=1.3,<2.0", "twine>=6.2,<7.0", "tomlkit>=0.13.3,<1.0", "questionary>=2.1.0,<3.0", "rich>=13.0.0,<15.0", "mkdocs>=1.6,<2.0", "mkdocs-material>=9.6,<10.0", "mkdocstrings[python]>=0.30,<1.0", "redis>=6.4.0,<7.0",]
30
31
  test = [ "django>=5.2,<6.0", "pytest>=8.4,<9.0", "pytest-django>=4.11,<5.0", "pytest-cov>=7.0,<8.0", "pytest-mock>=3.15,<4.0", "pytest-xdist>=3.8,<4.0", "factory-boy>=3.3,<4.0", "fakeredis>=2.28,<3.0",]
31
32
  docs = [ "mkdocs>=1.6,<2.0", "mkdocs-material>=9.6,<10.0", "mkdocstrings[python]>=0.30,<1.0", "pymdown-extensions>=10.16,<11.0",]
32
33
  tasks = [ "redis>=6.4.0,<7.0",]
33
- full = [ "django>=5.2,<6.0", "pytest>=8.4,<9.0", "pytest-django>=4.11,<5.0", "pytest-cov>=7.0,<8.0", "pytest-mock>=3.15,<4.0", "pytest-xdist>=3.8,<4.0", "factory-boy>=3.3,<4.0", "black>=25.9,<26.0", "isort>=6.0,<7.0", "flake8>=6.0.0,<8.0", "mypy>=1.18,<2.0", "pre-commit>=4.3,<5.0", "build>=1.3,<2.0", "twine>=6.2,<7.0", "tomlkit>=0.13.3,<1.0", "questionary>=2.1.0,<3.0", "rich>=13.0.0,<15.0", "mkdocs>=1.6,<2.0", "mkdocs-material>=9.6,<10.0", "mkdocstrings[python]>=0.30,<1.0", "pymdown-extensions>=10.16,<11.0", "redis>=6.4.0,<7.0",]
34
+ full = [ "django>=5.2,<6.0", "pytest>=8.4,<9.0", "pytest-django>=4.11,<5.0", "pytest-cov>=7.0,<8.0", "pytest-mock>=3.15,<4.0", "pytest-xdist>=3.8,<4.0", "factory-boy>=3.3,<4.0", "black>=25.9,<26.0", "isort>=6.0,<7.0", "flake8>=6.0.0,<8.0", "mypy>=1.18,<2.0", "pre-commit>=4.3,<5.0", "build>=1.3,<2.0", "twine>=6.2,<7.0", "tomlkit>=0.13.3,<1.0", "questionary>=2.1.0,<3.0", "rich>=13.0.0,<15.0", "mkdocs>=1.6,<2.0", "mkdocs-material>=9.6,<10.0", "mkdocstrings[python]>=0.30,<1.0", "pymdown-extensions>=10.16,<11.0", "redis>=6.4.0,<7.0", "grpcio>=1.50.0,<2.0", "grpcio-tools>=1.50.0,<2.0", "protobuf>=4.21.0,<5.0", "djangogrpcframework>=0.2.1",]
34
35
 
35
36
  [project.urls]
36
37
  Homepage = "https://djangocfg.com"
@@ -114,9 +115,6 @@ exclude_lines = [ "pragma: no cover", "def __repr__", "if self.debug:", "if sett
114
115
  [tool.coverage.html]
115
116
  directory = "htmlcov"
116
117
 
117
- [tool.poetry.group.local]
118
- optional = true
119
-
120
118
  [tool.poetry.group.dev.dependencies]
121
119
  tomlkit = "^0.13.3"
122
120
  build = "^1.3.0"
@@ -13,6 +13,12 @@ MODULES_REGISTRY = {
13
13
  # Centrifugo module
14
14
  "DjangoCfgCentrifugoConfig": ("django_cfg.apps.centrifugo.services.client.config", "DjangoCfgCentrifugoConfig"),
15
15
 
16
+ # gRPC module
17
+ "GRPCConfig": ("django_cfg.models.api.grpc", "GRPCConfig"),
18
+ "GRPCServerConfig": ("django_cfg.models.api.grpc", "GRPCServerConfig"),
19
+ "GRPCAuthConfig": ("django_cfg.models.api.grpc", "GRPCAuthConfig"),
20
+ "GRPCProtoConfig": ("django_cfg.models.api.grpc", "GRPCProtoConfig"),
21
+
16
22
  # Next.js Admin Integration
17
23
  "NextJsAdminConfig": ("django_cfg.modules.nextjs_admin", "NextJsAdminConfig"),
18
24
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-cfg
3
- Version: 1.4.120
3
+ Version: 1.5.1
4
4
  Summary: Modern Django framework with type-safe Pydantic v2 configuration, Next.js admin integration, real-time WebSockets, and 8 enterprise apps. Replace settings.py with validated models, 90% less code. Production-ready with AI agents, auto-generated TypeScript clients, and zero-config features.
5
5
  Project-URL: Homepage, https://djangocfg.com
6
6
  Project-URL: Documentation, https://djangocfg.com
@@ -119,14 +119,18 @@ Provides-Extra: full
119
119
  Requires-Dist: black<26.0,>=25.9; extra == 'full'
120
120
  Requires-Dist: build<2.0,>=1.3; extra == 'full'
121
121
  Requires-Dist: django<6.0,>=5.2; extra == 'full'
122
+ Requires-Dist: djangogrpcframework>=0.2.1; extra == 'full'
122
123
  Requires-Dist: factory-boy<4.0,>=3.3; extra == 'full'
123
124
  Requires-Dist: flake8<8.0,>=6.0.0; extra == 'full'
125
+ Requires-Dist: grpcio-tools<2.0,>=1.50.0; extra == 'full'
126
+ Requires-Dist: grpcio<2.0,>=1.50.0; extra == 'full'
124
127
  Requires-Dist: isort<7.0,>=6.0; extra == 'full'
125
128
  Requires-Dist: mkdocs-material<10.0,>=9.6; extra == 'full'
126
129
  Requires-Dist: mkdocs<2.0,>=1.6; extra == 'full'
127
130
  Requires-Dist: mkdocstrings[python]<1.0,>=0.30; extra == 'full'
128
131
  Requires-Dist: mypy<2.0,>=1.18; extra == 'full'
129
132
  Requires-Dist: pre-commit<5.0,>=4.3; extra == 'full'
133
+ Requires-Dist: protobuf<5.0,>=4.21.0; extra == 'full'
130
134
  Requires-Dist: pymdown-extensions<11.0,>=10.16; extra == 'full'
131
135
  Requires-Dist: pytest-cov<8.0,>=7.0; extra == 'full'
132
136
  Requires-Dist: pytest-django<5.0,>=4.11; extra == 'full'
@@ -138,6 +142,11 @@ Requires-Dist: redis<7.0,>=6.4.0; extra == 'full'
138
142
  Requires-Dist: rich<15.0,>=13.0.0; extra == 'full'
139
143
  Requires-Dist: tomlkit<1.0,>=0.13.3; extra == 'full'
140
144
  Requires-Dist: twine<7.0,>=6.2; extra == 'full'
145
+ Provides-Extra: grpc
146
+ Requires-Dist: djangogrpcframework>=0.2.1; extra == 'grpc'
147
+ Requires-Dist: grpcio-tools<2.0,>=1.50.0; extra == 'grpc'
148
+ Requires-Dist: grpcio<2.0,>=1.50.0; extra == 'grpc'
149
+ Requires-Dist: protobuf<5.0,>=4.21.0; extra == 'grpc'
141
150
  Provides-Extra: local
142
151
  Provides-Extra: tasks
143
152
  Requires-Dist: redis<7.0,>=6.4.0; extra == 'tasks'