django-cfg 1.4.62__py3-none-any.whl → 1.4.64__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 +582 -0
- django_cfg/apps/centrifugo/services/client/config.py +236 -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 +380 -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.62.dist-info → django_cfg-1.4.64.dist-info}/METADATA +1 -1
- {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/RECORD +142 -70
- 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 -232
- django_cfg/apps/ipc/apps.py +0 -98
- django_cfg/apps/ipc/migrations/0001_initial.py +0 -137
- django_cfg/apps/ipc/migrations/0002_rpclog_is_event.py +0 -23
- django_cfg/apps/ipc/migrations/__init__.py +0 -0
- django_cfg/apps/ipc/models.py +0 -229
- 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/services/rpc_log_consumer.py +0 -330
- 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.62.dist-info → django_cfg-1.4.64.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RPC Overview Dashboard Module
|
|
3
|
-
* Handles overview, requests, notifications, and methods tabs
|
|
4
|
-
*/
|
|
5
|
-
export class OverviewModule {
|
|
6
|
-
constructor(api, dashboard) {
|
|
7
|
-
this.api = api;
|
|
8
|
-
this.dashboard = dashboard;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Load data based on current tab
|
|
13
|
-
*/
|
|
14
|
-
async loadData(tabName) {
|
|
15
|
-
switch (tabName) {
|
|
16
|
-
case 'overview':
|
|
17
|
-
await this.loadOverviewStats();
|
|
18
|
-
break;
|
|
19
|
-
case 'requests':
|
|
20
|
-
await this.loadRecentRequests();
|
|
21
|
-
break;
|
|
22
|
-
case 'notifications':
|
|
23
|
-
await this.loadNotificationStats();
|
|
24
|
-
break;
|
|
25
|
-
case 'methods':
|
|
26
|
-
await this.loadMethodStats();
|
|
27
|
-
break;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Load overview statistics
|
|
33
|
-
*/
|
|
34
|
-
async loadOverviewStats() {
|
|
35
|
-
try {
|
|
36
|
-
const stats = await this.api.ipcAdminApiMonitorOverviewRetrieve();
|
|
37
|
-
|
|
38
|
-
if (stats) {
|
|
39
|
-
// Update stats cards
|
|
40
|
-
this.updateElement('total-requests', stats.total_requests_today || 0);
|
|
41
|
-
this.updateElement('active-methods-count', stats.active_methods?.length || 0);
|
|
42
|
-
this.updateElement('avg-response-time', (stats.avg_response_time_ms || 0).toFixed(0));
|
|
43
|
-
this.updateElement('success-rate', (stats.success_rate || 0).toFixed(1));
|
|
44
|
-
|
|
45
|
-
// Update top method (XSS-safe)
|
|
46
|
-
if (stats.top_method) {
|
|
47
|
-
const topMethodElement = document.getElementById('top-method');
|
|
48
|
-
if (topMethodElement) {
|
|
49
|
-
const code = document.createElement('code');
|
|
50
|
-
code.className = 'bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded text-blue-600 dark:text-blue-300';
|
|
51
|
-
code.textContent = stats.top_method;
|
|
52
|
-
topMethodElement.innerHTML = '';
|
|
53
|
-
topMethodElement.appendChild(code);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
} catch (error) {
|
|
58
|
-
console.error('Error loading overview stats:', error);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Load recent RPC requests
|
|
64
|
-
*/
|
|
65
|
-
async loadRecentRequests() {
|
|
66
|
-
try {
|
|
67
|
-
const data = await this.api.ipcAdminApiMonitorRequestsRetrieve({ count: 50 });
|
|
68
|
-
|
|
69
|
-
if (data) {
|
|
70
|
-
const requests = data.requests || [];
|
|
71
|
-
this.renderRequestsTable(requests);
|
|
72
|
-
}
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.error('Error loading recent requests:', error);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Render requests table
|
|
80
|
-
*/
|
|
81
|
-
renderRequestsTable(requests) {
|
|
82
|
-
const tbody = document.getElementById('requests-table-body');
|
|
83
|
-
if (!tbody) return;
|
|
84
|
-
|
|
85
|
-
if (requests.length === 0) {
|
|
86
|
-
tbody.innerHTML = `
|
|
87
|
-
<tr>
|
|
88
|
-
<td colspan="4" class="px-4 py-8 text-center text-gray-500 dark:text-gray-400">
|
|
89
|
-
No recent requests found
|
|
90
|
-
</td>
|
|
91
|
-
</tr>
|
|
92
|
-
`;
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
tbody.innerHTML = requests.map(req => `
|
|
97
|
-
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
|
98
|
-
<td class="px-4 py-3 text-sm">
|
|
99
|
-
${this.formatTimestamp(req.timestamp)}
|
|
100
|
-
</td>
|
|
101
|
-
<td class="px-4 py-3">
|
|
102
|
-
<code class="text-sm bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">${this.escapeHtml(req.method || 'unknown')}</code>
|
|
103
|
-
</td>
|
|
104
|
-
<td class="px-4 py-3 text-sm font-mono">
|
|
105
|
-
${this.escapeHtml((req.correlation_id || '').substring(0, 8))}...
|
|
106
|
-
</td>
|
|
107
|
-
<td class="px-4 py-3 text-sm">
|
|
108
|
-
<details class="cursor-pointer">
|
|
109
|
-
<summary class="text-blue-600 dark:text-blue-400 hover:underline">View</summary>
|
|
110
|
-
<pre class="mt-2 text-xs bg-gray-100 dark:bg-gray-900 p-2 rounded overflow-auto max-h-40">${this.escapeHtml(JSON.stringify(req.params || {}, null, 2))}</pre>
|
|
111
|
-
</details>
|
|
112
|
-
</td>
|
|
113
|
-
</tr>
|
|
114
|
-
`).join('');
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Load notification statistics
|
|
119
|
-
*/
|
|
120
|
-
async loadNotificationStats() {
|
|
121
|
-
try {
|
|
122
|
-
const stats = await this.api.ipcAdminApiMonitorNotificationsRetrieve();
|
|
123
|
-
|
|
124
|
-
if (stats) {
|
|
125
|
-
this.renderNotificationStats(stats);
|
|
126
|
-
}
|
|
127
|
-
} catch (error) {
|
|
128
|
-
console.error('Error loading notification stats:', error);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Render notification statistics
|
|
134
|
-
*/
|
|
135
|
-
renderNotificationStats(stats) {
|
|
136
|
-
const container = document.getElementById('notification-stats-content');
|
|
137
|
-
if (!container) return;
|
|
138
|
-
|
|
139
|
-
const byType = stats.by_type || {};
|
|
140
|
-
const total = stats.total_sent || 0;
|
|
141
|
-
|
|
142
|
-
container.innerHTML = `
|
|
143
|
-
<div class="space-y-4">
|
|
144
|
-
<div class="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-700 rounded">
|
|
145
|
-
<span>Total Sent</span>
|
|
146
|
-
<span class="text-2xl font-bold">${total}</span>
|
|
147
|
-
</div>
|
|
148
|
-
<div class="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-700 rounded">
|
|
149
|
-
<span>Delivery Rate</span>
|
|
150
|
-
<span class="text-2xl font-bold text-green-600 dark:text-green-400">${stats.delivery_rate || 0}%</span>
|
|
151
|
-
</div>
|
|
152
|
-
<div>
|
|
153
|
-
<h4 class="text-md font-semibold mb-3">By Type</h4>
|
|
154
|
-
<div class="space-y-2">
|
|
155
|
-
${Object.entries(byType).map(([type, count]) => `
|
|
156
|
-
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-700 rounded">
|
|
157
|
-
<span>${this.escapeHtml(type)}</span>
|
|
158
|
-
<span class="font-medium">${count}</span>
|
|
159
|
-
</div>
|
|
160
|
-
`).join('') || '<p class="text-gray-500 dark:text-gray-400">No data available</p>'}
|
|
161
|
-
</div>
|
|
162
|
-
</div>
|
|
163
|
-
${stats.recent && stats.recent.length > 0 ? `
|
|
164
|
-
<div>
|
|
165
|
-
<h4 class="text-md font-semibold mb-3">Recent Notifications</h4>
|
|
166
|
-
<div class="space-y-2">
|
|
167
|
-
${stats.recent.slice(0, 5).map(notif => `
|
|
168
|
-
<div class="p-3 bg-gray-50 dark:bg-gray-700 rounded">
|
|
169
|
-
<div class="flex justify-between items-start">
|
|
170
|
-
<span class="text-sm">${this.escapeHtml(notif.type || 'unknown')}</span>
|
|
171
|
-
<span class="text-xs text-gray-500 dark:text-gray-400">${this.formatTimestamp(notif.timestamp)}</span>
|
|
172
|
-
</div>
|
|
173
|
-
${notif.message ? `<p class="text-xs text-gray-600 dark:text-gray-400 mt-1">${this.escapeHtml(notif.message)}</p>` : ''}
|
|
174
|
-
</div>
|
|
175
|
-
`).join('')}
|
|
176
|
-
</div>
|
|
177
|
-
</div>
|
|
178
|
-
` : ''}
|
|
179
|
-
</div>
|
|
180
|
-
`;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Load method statistics
|
|
185
|
-
*/
|
|
186
|
-
async loadMethodStats() {
|
|
187
|
-
try {
|
|
188
|
-
const data = await this.api.ipcAdminApiMonitorMethodsRetrieve();
|
|
189
|
-
|
|
190
|
-
if (data) {
|
|
191
|
-
const methods = data.methods || [];
|
|
192
|
-
this.renderMethodsTable(methods);
|
|
193
|
-
}
|
|
194
|
-
} catch (error) {
|
|
195
|
-
console.error('Error loading method stats:', error);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Render methods table
|
|
201
|
-
*/
|
|
202
|
-
renderMethodsTable(methods) {
|
|
203
|
-
const tbody = document.getElementById('methods-table-body');
|
|
204
|
-
if (!tbody) return;
|
|
205
|
-
|
|
206
|
-
if (methods.length === 0) {
|
|
207
|
-
tbody.innerHTML = `
|
|
208
|
-
<tr>
|
|
209
|
-
<td colspan="4" class="px-4 py-8 text-center text-gray-500 dark:text-gray-400">
|
|
210
|
-
No method data available
|
|
211
|
-
</td>
|
|
212
|
-
</tr>
|
|
213
|
-
`;
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
tbody.innerHTML = methods.map(method => `
|
|
218
|
-
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
|
219
|
-
<td class="px-4 py-3">
|
|
220
|
-
<code class="text-sm bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">${this.escapeHtml(method.method)}</code>
|
|
221
|
-
</td>
|
|
222
|
-
<td class="px-4 py-3 text-sm font-medium">${method.count}</td>
|
|
223
|
-
<td class="px-4 py-3 text-sm">${method.percentage}%</td>
|
|
224
|
-
<td class="px-4 py-3 text-sm">${method.avg_time_ms || method.avg_time || 0}ms</td>
|
|
225
|
-
</tr>
|
|
226
|
-
`).join('');
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Helper: Update element text content
|
|
231
|
-
*/
|
|
232
|
-
updateElement(id, value) {
|
|
233
|
-
const element = document.getElementById(id);
|
|
234
|
-
if (element) {
|
|
235
|
-
element.textContent = value;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Helper: Format ISO timestamp to local time
|
|
241
|
-
*/
|
|
242
|
-
formatTimestamp(isoString) {
|
|
243
|
-
try {
|
|
244
|
-
const date = new Date(isoString);
|
|
245
|
-
return date.toLocaleTimeString();
|
|
246
|
-
} catch {
|
|
247
|
-
return 'N/A';
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Helper: Escape HTML to prevent XSS
|
|
253
|
-
*/
|
|
254
|
-
escapeHtml(unsafe) {
|
|
255
|
-
const div = document.createElement('div');
|
|
256
|
-
div.textContent = unsafe;
|
|
257
|
-
return div.innerHTML;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RPC Testing Tools Module
|
|
3
|
-
* Handles test client and load testing functionality
|
|
4
|
-
*/
|
|
5
|
-
export class TestingModule {
|
|
6
|
-
constructor(api, dashboard) {
|
|
7
|
-
this.api = api;
|
|
8
|
-
this.dashboard = dashboard;
|
|
9
|
-
this.loadTestInterval = null;
|
|
10
|
-
this.isLoadTestRunning = false;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Initialize testing tools
|
|
15
|
-
*/
|
|
16
|
-
init() {
|
|
17
|
-
this.setupEventListeners();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Setup event listeners for testing tools
|
|
22
|
-
*/
|
|
23
|
-
setupEventListeners() {
|
|
24
|
-
// Test RPC Client
|
|
25
|
-
const sendTestBtn = document.getElementById('send-test-rpc-btn');
|
|
26
|
-
const clearTestBtn = document.getElementById('clear-test-rpc-btn');
|
|
27
|
-
|
|
28
|
-
if (sendTestBtn) {
|
|
29
|
-
sendTestBtn.addEventListener('click', () => this.sendTestRequest());
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (clearTestBtn) {
|
|
33
|
-
clearTestBtn.addEventListener('click', () => this.clearTestForm());
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Load Testing
|
|
37
|
-
const startLoadTestBtn = document.getElementById('start-load-test-btn');
|
|
38
|
-
const stopLoadTestBtn = document.getElementById('stop-load-test-btn');
|
|
39
|
-
|
|
40
|
-
if (startLoadTestBtn) {
|
|
41
|
-
startLoadTestBtn.addEventListener('click', () => this.startLoadTest());
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (stopLoadTestBtn) {
|
|
45
|
-
stopLoadTestBtn.addEventListener('click', () => this.stopLoadTest());
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Send a single test RPC request
|
|
51
|
-
*/
|
|
52
|
-
async sendTestRequest() {
|
|
53
|
-
try {
|
|
54
|
-
const method = document.getElementById('test-rpc-method')?.value;
|
|
55
|
-
const timeout = parseInt(document.getElementById('test-rpc-timeout')?.value || '10');
|
|
56
|
-
const paramsText = document.getElementById('test-rpc-params')?.value || '{}';
|
|
57
|
-
|
|
58
|
-
// Validate input
|
|
59
|
-
if (!method) {
|
|
60
|
-
this.showTestResponse('Please select a method', 'error');
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
let params = {};
|
|
65
|
-
try {
|
|
66
|
-
params = JSON.parse(paramsText);
|
|
67
|
-
} catch (e) {
|
|
68
|
-
this.showTestResponse('Invalid JSON in parameters: ' + e.message, 'error');
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Auto-add timestamp if not present (required by WebSocket server)
|
|
73
|
-
if (!params.timestamp) {
|
|
74
|
-
params.timestamp = new Date().toISOString();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Show loading state
|
|
78
|
-
this.showTestResponse('Sending request...', 'info');
|
|
79
|
-
|
|
80
|
-
// Send test request via API
|
|
81
|
-
const response = await this.api.ipcAdminApiTestSendCreate({
|
|
82
|
-
method,
|
|
83
|
-
params,
|
|
84
|
-
timeout
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
// Display response
|
|
88
|
-
this.displayTestResponse(response);
|
|
89
|
-
|
|
90
|
-
} catch (error) {
|
|
91
|
-
this.showTestResponse('Request failed: ' + error.message, 'error');
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Display test response
|
|
97
|
-
*/
|
|
98
|
-
displayTestResponse(response) {
|
|
99
|
-
const resultDiv = document.getElementById('test-rpc-result');
|
|
100
|
-
if (!resultDiv) return;
|
|
101
|
-
|
|
102
|
-
const isSuccess = response.success;
|
|
103
|
-
const statusClass = isSuccess ? 'text-green-600' : 'text-red-600';
|
|
104
|
-
const statusIcon = isSuccess ? 'check_circle' : 'error';
|
|
105
|
-
|
|
106
|
-
resultDiv.innerHTML = `
|
|
107
|
-
<div class="p-4 bg-gray-50 dark:bg-gray-700 rounded">
|
|
108
|
-
<!-- Status -->
|
|
109
|
-
<div class="flex items-center mb-3">
|
|
110
|
-
<span class="material-icons ${statusClass} mr-2">${statusIcon}</span>
|
|
111
|
-
<h4 class="font-semibold ${statusClass}">
|
|
112
|
-
${isSuccess ? 'Success' : 'Failed'}
|
|
113
|
-
</h4>
|
|
114
|
-
</div>
|
|
115
|
-
|
|
116
|
-
<!-- Timing -->
|
|
117
|
-
<div class="mb-3 text-sm">
|
|
118
|
-
<strong>Duration:</strong> ${response.duration_ms?.toFixed(2) || 'N/A'} ms
|
|
119
|
-
</div>
|
|
120
|
-
|
|
121
|
-
<!-- Correlation ID -->
|
|
122
|
-
<div class="mb-3 text-sm">
|
|
123
|
-
<strong>Correlation ID:</strong>
|
|
124
|
-
<code class="bg-gray-200 dark:bg-gray-600 px-2 py-1 rounded text-xs">
|
|
125
|
-
${this.escapeHtml(response.correlation_id || 'N/A')}
|
|
126
|
-
</code>
|
|
127
|
-
</div>
|
|
128
|
-
|
|
129
|
-
<!-- Response/Error -->
|
|
130
|
-
<div class="mt-3">
|
|
131
|
-
<strong class="text-sm">${isSuccess ? 'Response:' : 'Error:'}</strong>
|
|
132
|
-
<pre class="mt-2 text-xs bg-gray-100 dark:bg-gray-900 p-3 rounded overflow-auto max-h-60">${
|
|
133
|
-
this.escapeHtml(JSON.stringify(isSuccess ? response.response : response.error, null, 2))
|
|
134
|
-
}</pre>
|
|
135
|
-
</div>
|
|
136
|
-
</div>
|
|
137
|
-
`;
|
|
138
|
-
|
|
139
|
-
resultDiv.classList.remove('hidden');
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Show test response message
|
|
144
|
-
*/
|
|
145
|
-
showTestResponse(message, type = 'info') {
|
|
146
|
-
const resultDiv = document.getElementById('test-rpc-result');
|
|
147
|
-
if (!resultDiv) return;
|
|
148
|
-
|
|
149
|
-
const colors = {
|
|
150
|
-
info: 'text-blue-600 dark:text-blue-400',
|
|
151
|
-
error: 'text-red-600 dark:text-red-400',
|
|
152
|
-
success: 'text-green-600 dark:text-green-400'
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
const icons = {
|
|
156
|
-
info: 'info',
|
|
157
|
-
error: 'error',
|
|
158
|
-
success: 'check_circle'
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
resultDiv.innerHTML = `
|
|
162
|
-
<div class="p-4 bg-gray-50 dark:bg-gray-700 rounded">
|
|
163
|
-
<div class="flex items-center">
|
|
164
|
-
<span class="material-icons ${colors[type]} mr-2">${icons[type]}</span>
|
|
165
|
-
<span class="${colors[type]}">${this.escapeHtml(message)}</span>
|
|
166
|
-
</div>
|
|
167
|
-
</div>
|
|
168
|
-
`;
|
|
169
|
-
|
|
170
|
-
resultDiv.classList.remove('hidden');
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Clear test form
|
|
175
|
-
*/
|
|
176
|
-
clearTestForm() {
|
|
177
|
-
const methodSelect = document.getElementById('test-rpc-method');
|
|
178
|
-
const paramsTextarea = document.getElementById('test-rpc-params');
|
|
179
|
-
const timeoutInput = document.getElementById('test-rpc-timeout');
|
|
180
|
-
const resultDiv = document.getElementById('test-rpc-result');
|
|
181
|
-
|
|
182
|
-
if (methodSelect) methodSelect.value = '';
|
|
183
|
-
if (paramsTextarea) paramsTextarea.value = '{}';
|
|
184
|
-
if (timeoutInput) timeoutInput.value = '10';
|
|
185
|
-
if (resultDiv) resultDiv.classList.add('hidden');
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Start load test
|
|
190
|
-
*/
|
|
191
|
-
async startLoadTest() {
|
|
192
|
-
try {
|
|
193
|
-
const method = document.getElementById('load-test-method')?.value;
|
|
194
|
-
const totalRequests = parseInt(document.getElementById('load-test-total')?.value || '100');
|
|
195
|
-
const concurrency = parseInt(document.getElementById('load-test-concurrency')?.value || '10');
|
|
196
|
-
const paramsText = document.getElementById('load-test-params')?.value || '{}';
|
|
197
|
-
|
|
198
|
-
// Validate input
|
|
199
|
-
if (!method) {
|
|
200
|
-
window.showNotification?.('Please select a method', 'error');
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (totalRequests < 1 || totalRequests > 10000) {
|
|
205
|
-
window.showNotification?.('Total requests must be between 1 and 10,000', 'error');
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
if (concurrency < 1 || concurrency > 100) {
|
|
210
|
-
window.showNotification?.('Concurrency must be between 1 and 100', 'error');
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Parse params
|
|
215
|
-
let params = {};
|
|
216
|
-
try {
|
|
217
|
-
params = JSON.parse(paramsText);
|
|
218
|
-
} catch (e) {
|
|
219
|
-
window.showNotification?.('Invalid JSON in parameters: ' + e.message, 'error');
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Auto-add timestamp if not present
|
|
224
|
-
if (!params.timestamp) {
|
|
225
|
-
params.timestamp = new Date().toISOString();
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Start load test
|
|
229
|
-
const response = await this.api.ipcAdminApiTestLoadStartCreate({
|
|
230
|
-
method,
|
|
231
|
-
total_requests: totalRequests,
|
|
232
|
-
concurrency,
|
|
233
|
-
params: params
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
if (response && response.test_id) {
|
|
237
|
-
this.isLoadTestRunning = true;
|
|
238
|
-
this.toggleLoadTestButtons(true);
|
|
239
|
-
this.startLoadTestPolling();
|
|
240
|
-
window.showNotification?.('Load test started', 'success');
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
} catch (error) {
|
|
244
|
-
window.showNotification?.('Failed to start load test: ' + error.message, 'error');
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Stop load test
|
|
250
|
-
*/
|
|
251
|
-
async stopLoadTest() {
|
|
252
|
-
try {
|
|
253
|
-
await this.api.ipcAdminApiTestLoadStopCreate({
|
|
254
|
-
method: '',
|
|
255
|
-
params: {},
|
|
256
|
-
timeout: 10
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
this.isLoadTestRunning = false;
|
|
260
|
-
this.stopLoadTestPolling();
|
|
261
|
-
this.toggleLoadTestButtons(false);
|
|
262
|
-
window.showNotification?.('Load test stopped', 'info');
|
|
263
|
-
|
|
264
|
-
} catch (error) {
|
|
265
|
-
window.showNotification?.('Failed to stop load test: ' + error.message, 'error');
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Toggle load test buttons
|
|
271
|
-
*/
|
|
272
|
-
toggleLoadTestButtons(isRunning) {
|
|
273
|
-
const startBtn = document.getElementById('start-load-test-btn');
|
|
274
|
-
const stopBtn = document.getElementById('stop-load-test-btn');
|
|
275
|
-
|
|
276
|
-
if (startBtn) {
|
|
277
|
-
startBtn.disabled = isRunning;
|
|
278
|
-
startBtn.classList.toggle('opacity-50', isRunning);
|
|
279
|
-
startBtn.classList.toggle('cursor-not-allowed', isRunning);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
if (stopBtn) {
|
|
283
|
-
stopBtn.disabled = !isRunning;
|
|
284
|
-
stopBtn.classList.toggle('opacity-50', !isRunning);
|
|
285
|
-
stopBtn.classList.toggle('cursor-not-allowed', !isRunning);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Start polling for load test status
|
|
291
|
-
*/
|
|
292
|
-
startLoadTestPolling() {
|
|
293
|
-
// Initial update
|
|
294
|
-
this.updateLoadTestStatus();
|
|
295
|
-
|
|
296
|
-
// Poll every 500ms
|
|
297
|
-
if (this.loadTestInterval) {
|
|
298
|
-
clearInterval(this.loadTestInterval);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
this.loadTestInterval = setInterval(() => {
|
|
302
|
-
this.updateLoadTestStatus();
|
|
303
|
-
}, 500);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Stop polling for load test status
|
|
308
|
-
*/
|
|
309
|
-
stopLoadTestPolling() {
|
|
310
|
-
if (this.loadTestInterval) {
|
|
311
|
-
clearInterval(this.loadTestInterval);
|
|
312
|
-
this.loadTestInterval = null;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Update load test status
|
|
318
|
-
*/
|
|
319
|
-
async updateLoadTestStatus() {
|
|
320
|
-
try {
|
|
321
|
-
const status = await this.api.ipcAdminApiTestLoadStatusRetrieve();
|
|
322
|
-
|
|
323
|
-
if (status) {
|
|
324
|
-
// Update progress bar
|
|
325
|
-
const progressBar = document.getElementById('load-test-progress');
|
|
326
|
-
if (progressBar) {
|
|
327
|
-
const percentage = status.total > 0 ? (status.progress / status.total * 100) : 0;
|
|
328
|
-
progressBar.style.width = percentage + '%';
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Update progress text
|
|
332
|
-
const progressText = document.getElementById('load-test-progress-text');
|
|
333
|
-
if (progressText) {
|
|
334
|
-
progressText.textContent = `${status.progress} / ${status.total}`;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// Update stats
|
|
338
|
-
this.updateElement('load-test-success', status.success_count);
|
|
339
|
-
this.updateElement('load-test-failed', status.failed_count);
|
|
340
|
-
this.updateElement('load-test-avg-time', status.avg_duration_ms?.toFixed(2) || '0');
|
|
341
|
-
this.updateElement('load-test-rps', status.rps?.toFixed(2) || '0');
|
|
342
|
-
|
|
343
|
-
// Check if test is complete
|
|
344
|
-
if (!status.running && this.isLoadTestRunning) {
|
|
345
|
-
this.isLoadTestRunning = false;
|
|
346
|
-
this.stopLoadTestPolling();
|
|
347
|
-
this.toggleLoadTestButtons(false);
|
|
348
|
-
window.showNotification?.('Load test completed', 'success');
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
} catch (error) {
|
|
353
|
-
console.error('Error updating load test status:', error);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Helper: Update element text content
|
|
359
|
-
*/
|
|
360
|
-
updateElement(id, value) {
|
|
361
|
-
const element = document.getElementById(id);
|
|
362
|
-
if (element) {
|
|
363
|
-
element.textContent = value;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* Helper: Escape HTML to prevent XSS
|
|
369
|
-
*/
|
|
370
|
-
escapeHtml(unsafe) {
|
|
371
|
-
const div = document.createElement('div');
|
|
372
|
-
div.textContent = unsafe;
|
|
373
|
-
return div.innerHTML;
|
|
374
|
-
}
|
|
375
|
-
}
|