django-cfg 1.4.87__py3-none-any.whl → 1.4.89__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 (177) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/centrifugo/views/__init__.py +0 -2
  3. django_cfg/apps/dashboard/permissions.py +48 -0
  4. django_cfg/apps/dashboard/serializers/__init__.py +8 -1
  5. django_cfg/apps/dashboard/serializers/commands.py +29 -0
  6. django_cfg/apps/dashboard/services/__init__.py +2 -0
  7. django_cfg/{modules/django_unfold/callbacks/base.py → apps/dashboard/services/commands_security.py} +28 -90
  8. django_cfg/apps/dashboard/services/commands_service.py +208 -9
  9. django_cfg/apps/dashboard/services/overview_service.py +205 -0
  10. django_cfg/apps/dashboard/views/commands_views.py +92 -4
  11. django_cfg/apps/frontend/test_routing.py +134 -0
  12. django_cfg/apps/frontend/views.py +73 -28
  13. django_cfg/apps/urls.py +0 -1
  14. django_cfg/core/builders/apps_builder.py +0 -58
  15. django_cfg/modules/django_unfold/__init__.py +5 -24
  16. django_cfg/modules/django_unfold/models/__init__.py +0 -23
  17. django_cfg/modules/django_unfold/models/config.py +11 -65
  18. django_cfg/modules/django_unfold/{dashboard.py → navigation.py} +21 -152
  19. django_cfg/modules/django_unfold/tailwind.py +2 -4
  20. django_cfg/pyproject.toml +1 -1
  21. django_cfg/registry/third_party.py +0 -9
  22. django_cfg/routing/callbacks.py +1 -43
  23. django_cfg/static/frontend/admin/404/index.html +1 -1
  24. django_cfg/static/frontend/admin/404.html +1 -1
  25. django_cfg/static/frontend/admin/500/index.html +1 -1
  26. django_cfg/static/frontend/admin/_next/static/{ZJZBgOL9mO1koHrgaaLEV → 0sN9ktsgXH48ygtGSrhfu}/_buildManifest.js +1 -1
  27. django_cfg/static/frontend/admin/_next/static/chunks/{19430.fe7bff7372f8a256.js → 19430.c4c95603c23c17fe.js} +1 -1
  28. django_cfg/static/frontend/admin/_next/static/chunks/50314-9443faa6df24aebf.js +1 -0
  29. django_cfg/static/frontend/admin/_next/static/chunks/94141-bc6d47f419b26b21.js +1 -0
  30. django_cfg/static/frontend/admin/_next/static/chunks/pages/{_app-c336f254967dd101.js → _app-c7dcd3aa616fab68.js} +6 -6
  31. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{cookies-b39c7f22c066e2c6.js → cookies-97d279800f12aab4.js} +1 -1
  32. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{privacy-5aedad0cf3a4f80f.js → privacy-1d5e6cd94689247e.js} +1 -1
  33. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{security-dbd854d0d5d483e2.js → security-55e49700e7a01f5a.js} +1 -1
  34. django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{terms-f3e1d2b9e5edf12f.js → terms-14c02bb2d3198352.js} +1 -1
  35. django_cfg/static/frontend/admin/_next/static/chunks/pages/private/{centrifugo-22532c65971225eb.js → centrifugo-f9ecbc3ae0052a03.js} +1 -1
  36. django_cfg/static/frontend/admin/_next/static/chunks/pages/private-d4ccbe1265cbd853.js +1 -0
  37. django_cfg/static/frontend/admin/_next/static/chunks/{webpack-da114020a6b940f5.js → webpack-5a92f81363b62aa7.js} +1 -1
  38. django_cfg/static/frontend/admin/_next/static/css/3063068f0d5a8a00.css +3 -0
  39. django_cfg/static/frontend/admin/_next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
  40. django_cfg/static/frontend/admin/_next/static/media/21350d82a1f187e9-s.p.woff2 +0 -0
  41. django_cfg/static/frontend/admin/_next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
  42. django_cfg/static/frontend/admin/_next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
  43. django_cfg/static/frontend/admin/_next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
  44. django_cfg/static/frontend/admin/_next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
  45. django_cfg/static/frontend/admin/_next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
  46. django_cfg/static/frontend/admin/auth/index.html +1 -1
  47. django_cfg/static/frontend/admin/index.html +1 -1
  48. django_cfg/static/frontend/admin/legal/cookies/index.html +1 -1
  49. django_cfg/static/frontend/admin/legal/privacy/index.html +1 -1
  50. django_cfg/static/frontend/admin/legal/security/index.html +1 -1
  51. django_cfg/static/frontend/admin/legal/terms/index.html +1 -1
  52. django_cfg/static/frontend/admin/private/centrifugo/index.html +1 -1
  53. django_cfg/static/frontend/admin/private/index.html +1 -1
  54. django_cfg/static/frontend/admin/private/profile/index.html +1 -1
  55. django_cfg/static/frontend/admin/private/ui/index.html +2 -2
  56. django_cfg/templates/admin/index.html +1 -1
  57. {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/METADATA +1 -1
  58. {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/RECORD +62 -163
  59. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/css/dashboard.css +0 -260
  60. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_channels.mjs +0 -313
  61. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_testing.mjs +0 -803
  62. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/main.mjs +0 -341
  63. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/overview.mjs +0 -432
  64. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/testing.mjs +0 -33
  65. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/websocket.mjs +0 -210
  66. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/channels_content.html +0 -46
  67. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/live_channels_content.html +0 -123
  68. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/overview_content.html +0 -45
  69. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/publishes_content.html +0 -84
  70. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/stat_cards.html +0 -53
  71. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/system_status.html +0 -91
  72. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/tab_navigation.html +0 -29
  73. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/testing_tools.html +0 -415
  74. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/layout/base.html +0 -61
  75. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/pages/dashboard.html +0 -58
  76. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/tags/connection_script.html +0 -48
  77. django_cfg/apps/centrifugo/templatetags/__init__.py +0 -1
  78. django_cfg/apps/centrifugo/templatetags/centrifugo_tags.py +0 -81
  79. django_cfg/apps/centrifugo/urls_admin.py +0 -20
  80. django_cfg/apps/centrifugo/views/dashboard.py +0 -28
  81. django_cfg/modules/django_dashboard/__init__.py +0 -23
  82. django_cfg/modules/django_dashboard/components.py +0 -312
  83. django_cfg/modules/django_dashboard/debug.py +0 -174
  84. django_cfg/modules/django_dashboard/management/__init__.py +0 -0
  85. django_cfg/modules/django_dashboard/management/commands/__init__.py +0 -0
  86. django_cfg/modules/django_dashboard/management/commands/debug_dashboard.py +0 -109
  87. django_cfg/modules/django_dashboard/sections/__init__.py +0 -1
  88. django_cfg/modules/django_dashboard/sections/base.py +0 -129
  89. django_cfg/modules/django_dashboard/sections/commands.py +0 -33
  90. django_cfg/modules/django_dashboard/sections/documentation.py +0 -393
  91. django_cfg/modules/django_dashboard/sections/overview.py +0 -398
  92. django_cfg/modules/django_dashboard/sections/stats.py +0 -48
  93. django_cfg/modules/django_dashboard/sections/system.py +0 -74
  94. django_cfg/modules/django_dashboard/sections/widgets.py +0 -222
  95. django_cfg/modules/django_unfold/callbacks/__init__.py +0 -9
  96. django_cfg/modules/django_unfold/callbacks/actions.py +0 -51
  97. django_cfg/modules/django_unfold/callbacks/apizones.py +0 -122
  98. django_cfg/modules/django_unfold/callbacks/charts.py +0 -223
  99. django_cfg/modules/django_unfold/callbacks/commands.py +0 -40
  100. django_cfg/modules/django_unfold/callbacks/main.py +0 -322
  101. django_cfg/modules/django_unfold/callbacks/statistics.py +0 -240
  102. django_cfg/modules/django_unfold/callbacks/system.py +0 -180
  103. django_cfg/modules/django_unfold/callbacks/users.py +0 -65
  104. django_cfg/modules/django_unfold/models/dashboard.py +0 -207
  105. django_cfg/modules/django_unfold/models/tabs.py +0 -26
  106. django_cfg/modules/django_unfold/models.py +0 -98
  107. django_cfg/modules/django_unfold/templates/unfold/helpers/app_list.html +0 -102
  108. django_cfg/static/frontend/admin/_next/static/chunks/23004-faae121bbfecc163.js +0 -1
  109. django_cfg/static/frontend/admin/_next/static/chunks/50314-48bd5701f62faf27.js +0 -1
  110. django_cfg/static/frontend/admin/_next/static/chunks/pages/private-fe9faa86ecdb0ce6.js +0 -1
  111. django_cfg/static/frontend/admin/_next/static/css/5f9a37b6e6a72303.css +0 -3
  112. django_cfg/static/frontend/admin/_next/static/media/438aa629764e75f3-s.woff2 +0 -0
  113. django_cfg/static/frontend/admin/_next/static/media/4c9affa5bc8f420e-s.p.woff2 +0 -0
  114. django_cfg/static/frontend/admin/_next/static/media/51251f8b9793cdb3-s.woff2 +0 -0
  115. django_cfg/static/frontend/admin/_next/static/media/875ae681bfde4580-s.p.woff2 +0 -0
  116. django_cfg/static/frontend/admin/_next/static/media/cc978ac5ee68c2b6-s.woff2 +0 -0
  117. django_cfg/static/frontend/admin/_next/static/media/e857b654a2caa584-s.woff2 +0 -0
  118. django_cfg/templates/admin/sections/commands_section.html +0 -5
  119. django_cfg/templates/admin/sections/documentation_section.html +0 -5
  120. django_cfg/templates/admin/sections/overview_section.html +0 -5
  121. django_cfg/templates/admin/sections/stats_section.html +0 -5
  122. django_cfg/templates/admin/sections/system_section.html +0 -5
  123. django_cfg/templates/admin/sections/widgets_section.html +0 -11
  124. django_cfg/templates/admin_old/components/action_grid.html +0 -49
  125. django_cfg/templates/admin_old/components/card.html +0 -50
  126. django_cfg/templates/admin_old/components/data_table.html +0 -67
  127. django_cfg/templates/admin_old/components/metric_card.html +0 -39
  128. django_cfg/templates/admin_old/components/modal.html +0 -58
  129. django_cfg/templates/admin_old/components/progress_bar.html +0 -20
  130. django_cfg/templates/admin_old/components/section_header.html +0 -26
  131. django_cfg/templates/admin_old/components/stat_item.html +0 -32
  132. django_cfg/templates/admin_old/components/stats_grid.html +0 -72
  133. django_cfg/templates/admin_old/components/status_badge.html +0 -28
  134. django_cfg/templates/admin_old/components/user_avatar.html +0 -27
  135. django_cfg/templates/admin_old/constance/change_list.html +0 -74
  136. django_cfg/templates/admin_old/constance/includes/default_value.html +0 -24
  137. django_cfg/templates/admin_old/constance/includes/fieldset_header.html +0 -15
  138. django_cfg/templates/admin_old/constance/includes/results_list.html +0 -16
  139. django_cfg/templates/admin_old/constance/includes/setting_row.html +0 -50
  140. django_cfg/templates/admin_old/constance/includes/table_headers.html +0 -10
  141. django_cfg/templates/admin_old/examples/component_class_example.html +0 -156
  142. django_cfg/templates/admin_old/import_export/change_list_export.html +0 -24
  143. django_cfg/templates/admin_old/import_export/change_list_import.html +0 -24
  144. django_cfg/templates/admin_old/import_export/change_list_import_export.html +0 -34
  145. django_cfg/templates/admin_old/index.html +0 -80
  146. django_cfg/templates/admin_old/index_new.html +0 -119
  147. django_cfg/templates/admin_old/layouts/base_dashboard.html +0 -62
  148. django_cfg/templates/admin_old/layouts/dashboard_with_tabs.html +0 -176
  149. django_cfg/templates/admin_old/sections/commands_section.html +0 -549
  150. django_cfg/templates/admin_old/sections/documentation_section.html +0 -152
  151. django_cfg/templates/admin_old/sections/overview_section.html +0 -112
  152. django_cfg/templates/admin_old/sections/stats_section.html +0 -35
  153. django_cfg/templates/admin_old/sections/system_section.html +0 -99
  154. django_cfg/templates/admin_old/sections/widgets_section.html +0 -129
  155. django_cfg/templates/admin_old/snippets/components/activity_tracker.html +0 -70
  156. django_cfg/templates/admin_old/snippets/components/charts_section.html +0 -113
  157. django_cfg/templates/admin_old/snippets/components/django_commands.html +0 -270
  158. django_cfg/templates/admin_old/snippets/components/quick_actions.html +0 -66
  159. django_cfg/templates/admin_old/snippets/components/recent_activity_improved.html +0 -25
  160. django_cfg/templates/admin_old/snippets/components/recent_users_table.html +0 -102
  161. django_cfg/templates/admin_old/snippets/components/stats_cards.html +0 -4
  162. django_cfg/templates/admin_old/snippets/components/stats_tiles.html +0 -92
  163. django_cfg/templates/admin_old/snippets/components/system_health.html +0 -22
  164. django_cfg/templates/admin_old/snippets/components/system_metrics.html +0 -199
  165. django_cfg/templates/admin_old/snippets/components/user_permissions.html +0 -57
  166. django_cfg/templates/admin_old/snippets/tabs/app_stats_tab.html +0 -201
  167. django_cfg/templates/admin_old/snippets/tabs/commands_tab.html +0 -114
  168. django_cfg/templates/admin_old/snippets/tabs/documentation_tab.html +0 -42
  169. django_cfg/templates/admin_old/snippets/tabs/overview_tab.html +0 -116
  170. django_cfg/templates/admin_old/snippets/tabs/stats_tab.html +0 -89
  171. django_cfg/templates/admin_old/snippets/tabs/users_tab.html +0 -51
  172. django_cfg/templates/admin_old/snippets/tabs/widgets_tab.html +0 -38
  173. django_cfg/templates/admin_old/snippets/zones/zones_table.html +0 -176
  174. /django_cfg/static/frontend/admin/_next/static/{ZJZBgOL9mO1koHrgaaLEV → 0sN9ktsgXH48ygtGSrhfu}/_ssgManifest.js +0 -0
  175. {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/WHEEL +0 -0
  176. {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/entry_points.txt +0 -0
  177. {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/licenses/LICENSE +0 -0
@@ -1,260 +0,0 @@
1
- /**
2
- * Centrifugo Dashboard Styles
3
- * Purple/Magenta theme for Centrifugo monitoring
4
- */
5
-
6
- /* Stat Cards */
7
- .stat-card {
8
- transition: transform 0.2s, box-shadow 0.2s;
9
- }
10
-
11
- .stat-card:hover {
12
- transform: translateY(-2px);
13
- box-shadow: 0 10px 25px rgba(168, 85, 247, 0.15);
14
- }
15
-
16
- /* Pulse Animation */
17
- .pulse-dot {
18
- animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
19
- }
20
-
21
- @keyframes pulse {
22
- 0%, 100% {
23
- opacity: 1;
24
- }
25
- 50% {
26
- opacity: 0.5;
27
- }
28
- }
29
-
30
- /* Tab Navigation */
31
- .tab-button {
32
- transition: all 0.2s ease;
33
- }
34
-
35
- .tab-button:hover {
36
- border-bottom-color: rgb(168 85 247 / 0.5);
37
- }
38
-
39
- .tab-button.active {
40
- color: rgb(168 85 247);
41
- border-bottom-color: rgb(168 85 247);
42
- }
43
-
44
- /* Dark mode adjustments */
45
- @media (prefers-color-scheme: dark) {
46
- .stat-card:hover {
47
- box-shadow: 0 10px 25px rgba(168, 85, 247, 0.25);
48
- }
49
-
50
- .tab-button.active {
51
- color: rgb(192 132 252);
52
- border-bottom-color: rgb(192 132 252);
53
- }
54
- }
55
-
56
- /* Tab Panels */
57
- .tab-panel {
58
- animation: fadeIn 0.3s ease-in;
59
- }
60
-
61
- @keyframes fadeIn {
62
- from {
63
- opacity: 0;
64
- transform: translateY(10px);
65
- }
66
- to {
67
- opacity: 1;
68
- transform: translateY(0);
69
- }
70
- }
71
-
72
- /* Charts */
73
- canvas {
74
- max-height: 300px;
75
- }
76
-
77
- /* Status Indicators */
78
- .status-badge {
79
- display: inline-flex;
80
- align-items: center;
81
- padding: 0.25rem 0.75rem;
82
- border-radius: 9999px;
83
- font-size: 0.75rem;
84
- font-weight: 500;
85
- }
86
-
87
- .status-badge.success {
88
- background-color: rgb(220 252 231);
89
- color: rgb(22 101 52);
90
- }
91
-
92
- .status-badge.failed {
93
- background-color: rgb(254 226 226);
94
- color: rgb(153 27 27);
95
- }
96
-
97
- .status-badge.timeout {
98
- background-color: rgb(254 243 199);
99
- color: rgb(146 64 14);
100
- }
101
-
102
- /* Dark mode status badges */
103
- @media (prefers-color-scheme: dark) {
104
- .status-badge.success {
105
- background-color: rgb(22 101 52 / 0.2);
106
- color: rgb(134 239 172);
107
- }
108
-
109
- .status-badge.failed {
110
- background-color: rgb(153 27 27 / 0.2);
111
- color: rgb(252 165 165);
112
- }
113
-
114
- .status-badge.timeout {
115
- background-color: rgb(146 64 14 / 0.2);
116
- color: rgb(253 224 71);
117
- }
118
- }
119
-
120
- /* Table Styles */
121
- table tbody tr {
122
- transition: background-color 0.15s ease;
123
- }
124
-
125
- table tbody tr:hover {
126
- background-color: rgb(250 245 255);
127
- }
128
-
129
- @media (prefers-color-scheme: dark) {
130
- table tbody tr:hover {
131
- background-color: rgb(88 28 135 / 0.1);
132
- }
133
- }
134
-
135
- /* Quick Test Buttons */
136
- .quick-test-btn {
137
- transition: all 0.2s ease;
138
- cursor: pointer;
139
- }
140
-
141
- .quick-test-btn:active {
142
- transform: scale(0.98);
143
- }
144
-
145
- /* Loading Spinner */
146
- .spinner {
147
- border: 2px solid rgba(168, 85, 247, 0.2);
148
- border-top-color: rgb(168, 85, 247);
149
- border-radius: 50%;
150
- width: 20px;
151
- height: 20px;
152
- animation: spin 0.6s linear infinite;
153
- }
154
-
155
- @keyframes spin {
156
- to {
157
- transform: rotate(360deg);
158
- }
159
- }
160
-
161
- /* Progress Bar */
162
- .progress-bar {
163
- transition: width 0.3s ease;
164
- }
165
-
166
- /* Notification Styles */
167
- .notification {
168
- animation: slideIn 0.3s ease-out;
169
- }
170
-
171
- @keyframes slideIn {
172
- from {
173
- transform: translateX(100%);
174
- opacity: 0;
175
- }
176
- to {
177
- transform: translateX(0);
178
- opacity: 1;
179
- }
180
- }
181
-
182
- /* Success Rate Color Coding */
183
- .success-rate-high {
184
- color: rgb(22 163 74);
185
- }
186
-
187
- .success-rate-medium {
188
- color: rgb(234 179 8);
189
- }
190
-
191
- .success-rate-low {
192
- color: rgb(220 38 38);
193
- }
194
-
195
- @media (prefers-color-scheme: dark) {
196
- .success-rate-high {
197
- color: rgb(134 239 172);
198
- }
199
-
200
- .success-rate-medium {
201
- color: rgb(253 224 71);
202
- }
203
-
204
- .success-rate-low {
205
- color: rgb(252 165 165);
206
- }
207
- }
208
-
209
- /* ACK Badge */
210
- .ack-badge {
211
- display: inline-flex;
212
- align-items: center;
213
- gap: 0.25rem;
214
- padding: 0.125rem 0.5rem;
215
- border-radius: 0.375rem;
216
- font-size: 0.75rem;
217
- font-weight: 500;
218
- background-color: rgb(243 232 255);
219
- color: rgb(107 33 168);
220
- }
221
-
222
- @media (prefers-color-scheme: dark) {
223
- .ack-badge {
224
- background-color: rgb(107 33 168 / 0.2);
225
- color: rgb(216 180 254);
226
- }
227
- }
228
-
229
- /* Message ID Badge */
230
- .message-id-badge {
231
- font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace;
232
- font-size: 0.75rem;
233
- color: rgb(88 28 135);
234
- background-color: rgb(250 245 255);
235
- padding: 0.125rem 0.5rem;
236
- border-radius: 0.25rem;
237
- }
238
-
239
- @media (prefers-color-scheme: dark) {
240
- .message-id-badge {
241
- color: rgb(216 180 254);
242
- background-color: rgb(88 28 135 / 0.2);
243
- }
244
- }
245
-
246
- /* Responsive adjustments */
247
- @media (max-width: 768px) {
248
- .stat-card {
249
- padding: 1rem;
250
- }
251
-
252
- .tab-button {
253
- font-size: 0.875rem;
254
- padding: 0.5rem 0.75rem;
255
- }
256
-
257
- .tab-button .material-icons {
258
- display: none;
259
- }
260
- }
@@ -1,313 +0,0 @@
1
- /**
2
- * Live Channels Module
3
- * Handles real-time channel monitoring from Centrifugo server
4
- */
5
- export class LiveChannelsModule {
6
- constructor(api, dashboard) {
7
- this.api = api;
8
- this.dashboard = dashboard;
9
- this.currentChannel = null;
10
- }
11
-
12
- async loadLiveChannels() {
13
- try {
14
- console.log('Loading live channels from Centrifugo server...');
15
-
16
- const patternInput = document.getElementById('live-channel-pattern');
17
- const pattern = patternInput ? patternInput.value.trim() : '';
18
-
19
- // Call Centrifugo channels API
20
- const data = { pattern: pattern || '' };
21
- const response = await this.api.centrifugoAdminApiServerChannelsCreate(data);
22
-
23
- if (response && response.result) {
24
- const channels = response.result.channels || {};
25
- this.renderChannelsGrid(channels);
26
- this.updateLiveChannelsBadge(Object.keys(channels).length);
27
- } else if (response && response.error) {
28
- console.error('Centrifugo API error:', response.error);
29
- this.showError('Failed to load channels: ' + response.error.message);
30
- }
31
- } catch (error) {
32
- console.error('Error loading live channels:', error);
33
- this.showError('Failed to connect to Centrifugo server');
34
- }
35
- }
36
-
37
- renderChannelsGrid(channels) {
38
- const grid = document.getElementById('live-channels-grid');
39
- const emptyState = document.getElementById('live-channels-empty');
40
-
41
- if (!grid) return;
42
-
43
- const channelEntries = Object.entries(channels);
44
-
45
- if (channelEntries.length === 0) {
46
- grid.innerHTML = '';
47
- if (emptyState) emptyState.classList.remove('hidden');
48
- return;
49
- }
50
-
51
- if (emptyState) emptyState.classList.add('hidden');
52
-
53
- let html = '';
54
- channelEntries.forEach(([channelName, info]) => {
55
- const numClients = info.num_clients || 0;
56
- const statusColor = numClients > 0 ? 'text-green-500' : 'text-gray-400';
57
- const statusIcon = numClients > 0 ? 'radio_button_checked' : 'radio_button_unchecked';
58
-
59
- html += `
60
- <div class="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-4 hover:border-purple-500 dark:hover:border-purple-400 transition-colors">
61
- <!-- Channel Header -->
62
- <div class="flex items-start justify-between mb-3">
63
- <div class="flex items-center gap-2 min-w-0 flex-1">
64
- <span class="material-icons ${statusColor}">${statusIcon}</span>
65
- <code class="text-sm font-mono text-gray-900 dark:text-white truncate">${this.escapeHtml(channelName)}</code>
66
- </div>
67
- </div>
68
-
69
- <!-- Stats -->
70
- <div class="grid grid-cols-2 gap-3 mb-3">
71
- <div>
72
- <p class="text-xs text-gray-600 dark:text-gray-400">Clients</p>
73
- <p class="text-xl font-bold text-purple-600 dark:text-purple-400">${numClients}</p>
74
- </div>
75
- <div>
76
- <p class="text-xs text-gray-600 dark:text-gray-400">Status</p>
77
- <p class="text-sm font-medium ${numClients > 0 ? 'text-green-600 dark:text-green-400' : 'text-gray-500 dark:text-gray-400'}">
78
- ${numClients > 0 ? 'Active' : 'Idle'}
79
- </p>
80
- </div>
81
- </div>
82
-
83
- <!-- Actions -->
84
- <div class="flex gap-2">
85
- <button onclick="window.centrifugoDashboard.liveChannelsModule.viewChannelDetails('${this.escapeHtml(channelName)}')"
86
- class="flex-1 px-3 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded text-sm font-medium flex items-center justify-center gap-1">
87
- <span class="material-icons text-base">visibility</span>
88
- View Details
89
- </button>
90
- </div>
91
- </div>
92
- `;
93
- });
94
-
95
- grid.innerHTML = html;
96
- }
97
-
98
- async viewChannelDetails(channel) {
99
- this.currentChannel = channel;
100
- const modal = document.getElementById('channel-details-modal');
101
- if (!modal) return;
102
-
103
- // Show modal
104
- modal.classList.remove('hidden');
105
-
106
- // Set channel name
107
- const modalTitle = document.getElementById('modal-channel-name');
108
- if (modalTitle) {
109
- modalTitle.textContent = channel;
110
- }
111
-
112
- // Load presence and history
113
- await this.loadChannelPresence(channel);
114
- await this.loadChannelHistory(channel);
115
- await this.loadPresenceStats(channel);
116
- }
117
-
118
- async loadPresenceStats(channel) {
119
- try {
120
- const data = { channel };
121
- const response = await this.api.centrifugoAdminApiServerPresenceStatsCreate(data);
122
-
123
- if (response && response.error) {
124
- // Feature not available (e.g., code 108)
125
- console.warn('Presence stats not available:', response.error.message);
126
- this.updateElement('modal-clients-count', 'N/A');
127
- this.updateElement('modal-users-count', 'N/A');
128
- } else if (response && response.result) {
129
- const stats = response.result;
130
- this.updateElement('modal-clients-count', stats.num_clients || 0);
131
- this.updateElement('modal-users-count', stats.num_users || 0);
132
- }
133
- } catch (error) {
134
- console.error('Error loading presence stats:', error);
135
- this.updateElement('modal-clients-count', 'Error');
136
- this.updateElement('modal-users-count', 'Error');
137
- }
138
- }
139
-
140
- async loadChannelPresence(channel) {
141
- try {
142
- const data = { channel };
143
- const response = await this.api.centrifugoAdminApiServerPresenceCreate(data);
144
-
145
- if (response && response.error) {
146
- // Feature not available (e.g., code 108)
147
- console.warn('Presence not available:', response.error.message);
148
- this.renderPresenceList({}, 'Presence tracking not enabled for this channel');
149
- } else if (response && response.result) {
150
- const presence = response.result.presence || {};
151
- this.renderPresenceList(presence);
152
- }
153
- } catch (error) {
154
- console.error('Error loading channel presence:', error);
155
- this.renderPresenceList({}, 'Error loading presence data');
156
- }
157
- }
158
-
159
- renderPresenceList(presence, errorMessage = null) {
160
- const tbody = document.getElementById('modal-presence-list');
161
- if (!tbody) return;
162
-
163
- if (errorMessage) {
164
- tbody.innerHTML = `
165
- <tr>
166
- <td colspan="3" class="px-4 py-8 text-center text-yellow-600 dark:text-yellow-400">
167
- <span class="material-icons text-2xl mb-2">info</span>
168
- <p>${errorMessage}</p>
169
- </td>
170
- </tr>
171
- `;
172
- return;
173
- }
174
-
175
- const clients = Object.values(presence);
176
-
177
- if (clients.length === 0) {
178
- tbody.innerHTML = `
179
- <tr>
180
- <td colspan="3" class="px-4 py-8 text-center text-gray-500 dark:text-gray-400">
181
- No clients connected to this channel
182
- </td>
183
- </tr>
184
- `;
185
- return;
186
- }
187
-
188
- let html = '';
189
- clients.forEach(client => {
190
- const userId = client.user || 'anonymous';
191
- const clientId = (client.client || '').substring(0, 12) + '...';
192
- const connInfo = client.conn_info ? JSON.stringify(client.conn_info) : '-';
193
-
194
- html += `
195
- <tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
196
- <td class="px-4 py-3 text-sm font-medium text-gray-900 dark:text-white">${this.escapeHtml(userId)}</td>
197
- <td class="px-4 py-3 text-xs font-mono text-gray-600 dark:text-gray-400">${this.escapeHtml(clientId)}</td>
198
- <td class="px-4 py-3 text-xs text-gray-600 dark:text-gray-400">${this.escapeHtml(connInfo)}</td>
199
- </tr>
200
- `;
201
- });
202
-
203
- tbody.innerHTML = html;
204
- }
205
-
206
- async loadChannelHistory(channel) {
207
- try {
208
- const data = { channel, limit: 10 };
209
- const response = await this.api.centrifugoAdminApiServerHistoryCreate(data);
210
-
211
- if (response && response.error) {
212
- // Feature not available (e.g., code 108)
213
- console.warn('History not available:', response.error.message);
214
- this.renderHistoryList([], 'History not enabled for this channel');
215
- this.updateElement('modal-history-count', 'N/A');
216
- } else if (response && response.result) {
217
- const publications = response.result.publications || [];
218
- this.renderHistoryList(publications);
219
- this.updateElement('modal-history-count', publications.length);
220
- }
221
- } catch (error) {
222
- console.error('Error loading channel history:', error);
223
- this.renderHistoryList([], 'Error loading history');
224
- this.updateElement('modal-history-count', 'Error');
225
- }
226
- }
227
-
228
- renderHistoryList(publications, errorMessage = null) {
229
- const container = document.getElementById('modal-history-list');
230
- if (!container) return;
231
-
232
- if (errorMessage) {
233
- container.innerHTML = `
234
- <p class="text-sm text-yellow-600 dark:text-yellow-400 text-center py-4">
235
- <span class="material-icons text-xl">info</span><br>
236
- ${errorMessage}
237
- </p>
238
- `;
239
- return;
240
- }
241
-
242
- if (publications.length === 0) {
243
- container.innerHTML = `
244
- <p class="text-sm text-gray-500 dark:text-gray-400 text-center py-4">
245
- No recent messages in this channel
246
- </p>
247
- `;
248
- return;
249
- }
250
-
251
- let html = '';
252
- publications.forEach((pub, index) => {
253
- const data = pub.data || {};
254
- const offset = pub.offset || '-';
255
- const dataStr = JSON.stringify(data, null, 2);
256
-
257
- html += `
258
- <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4">
259
- <div class="flex items-center justify-between mb-2">
260
- <span class="text-xs font-medium text-gray-600 dark:text-gray-400">Message #${index + 1}</span>
261
- <span class="text-xs text-gray-500 dark:text-gray-400">Offset: ${offset}</span>
262
- </div>
263
- <pre class="text-xs text-gray-900 dark:text-white overflow-x-auto"><code>${this.escapeHtml(dataStr)}</code></pre>
264
- </div>
265
- `;
266
- });
267
-
268
- container.innerHTML = html;
269
- }
270
-
271
- closeModal() {
272
- const modal = document.getElementById('channel-details-modal');
273
- if (modal) {
274
- modal.classList.add('hidden');
275
- }
276
- this.currentChannel = null;
277
- }
278
-
279
- updateLiveChannelsBadge(count) {
280
- const badge = document.getElementById('live-channels-count-badge');
281
- if (badge) {
282
- badge.textContent = count;
283
- }
284
- }
285
-
286
- showError(message) {
287
- const grid = document.getElementById('live-channels-grid');
288
- if (grid) {
289
- grid.innerHTML = `
290
- <div class="col-span-full bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4">
291
- <div class="flex items-center gap-3">
292
- <span class="material-icons text-red-500">error</span>
293
- <p class="text-sm text-red-700 dark:text-red-400">${this.escapeHtml(message)}</p>
294
- </div>
295
- </div>
296
- `;
297
- }
298
- }
299
-
300
- updateElement(id, value) {
301
- const element = document.getElementById(id);
302
- if (element) {
303
- element.textContent = value;
304
- }
305
- }
306
-
307
- escapeHtml(unsafe) {
308
- if (typeof unsafe !== 'string') return unsafe;
309
- const div = document.createElement('div');
310
- div.textContent = unsafe;
311
- return div.innerHTML;
312
- }
313
- }