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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/centrifugo/views/__init__.py +0 -2
- django_cfg/apps/dashboard/permissions.py +48 -0
- django_cfg/apps/dashboard/serializers/__init__.py +8 -1
- django_cfg/apps/dashboard/serializers/commands.py +29 -0
- django_cfg/apps/dashboard/services/__init__.py +2 -0
- django_cfg/{modules/django_unfold/callbacks/base.py → apps/dashboard/services/commands_security.py} +28 -90
- django_cfg/apps/dashboard/services/commands_service.py +208 -9
- django_cfg/apps/dashboard/services/overview_service.py +205 -0
- django_cfg/apps/dashboard/views/commands_views.py +92 -4
- django_cfg/apps/frontend/test_routing.py +134 -0
- django_cfg/apps/frontend/views.py +73 -28
- django_cfg/apps/urls.py +0 -1
- django_cfg/core/builders/apps_builder.py +0 -58
- django_cfg/modules/django_unfold/__init__.py +5 -24
- django_cfg/modules/django_unfold/models/__init__.py +0 -23
- django_cfg/modules/django_unfold/models/config.py +11 -65
- django_cfg/modules/django_unfold/{dashboard.py → navigation.py} +21 -152
- django_cfg/modules/django_unfold/tailwind.py +2 -4
- django_cfg/pyproject.toml +1 -1
- django_cfg/registry/third_party.py +0 -9
- django_cfg/routing/callbacks.py +1 -43
- django_cfg/static/frontend/admin/404/index.html +1 -1
- django_cfg/static/frontend/admin/404.html +1 -1
- django_cfg/static/frontend/admin/500/index.html +1 -1
- django_cfg/static/frontend/admin/_next/static/{ZJZBgOL9mO1koHrgaaLEV → 0sN9ktsgXH48ygtGSrhfu}/_buildManifest.js +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/{19430.fe7bff7372f8a256.js → 19430.c4c95603c23c17fe.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/50314-9443faa6df24aebf.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/94141-bc6d47f419b26b21.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/pages/{_app-c336f254967dd101.js → _app-c7dcd3aa616fab68.js} +6 -6
- django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{cookies-b39c7f22c066e2c6.js → cookies-97d279800f12aab4.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{privacy-5aedad0cf3a4f80f.js → privacy-1d5e6cd94689247e.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{security-dbd854d0d5d483e2.js → security-55e49700e7a01f5a.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/legal/{terms-f3e1d2b9e5edf12f.js → terms-14c02bb2d3198352.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private/{centrifugo-22532c65971225eb.js → centrifugo-f9ecbc3ae0052a03.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private-d4ccbe1265cbd853.js +1 -0
- django_cfg/static/frontend/admin/_next/static/chunks/{webpack-da114020a6b940f5.js → webpack-5a92f81363b62aa7.js} +1 -1
- django_cfg/static/frontend/admin/_next/static/css/3063068f0d5a8a00.css +3 -0
- django_cfg/static/frontend/admin/_next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/21350d82a1f187e9-s.p.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
- django_cfg/static/frontend/admin/auth/index.html +1 -1
- django_cfg/static/frontend/admin/index.html +1 -1
- django_cfg/static/frontend/admin/legal/cookies/index.html +1 -1
- django_cfg/static/frontend/admin/legal/privacy/index.html +1 -1
- django_cfg/static/frontend/admin/legal/security/index.html +1 -1
- django_cfg/static/frontend/admin/legal/terms/index.html +1 -1
- django_cfg/static/frontend/admin/private/centrifugo/index.html +1 -1
- django_cfg/static/frontend/admin/private/index.html +1 -1
- django_cfg/static/frontend/admin/private/profile/index.html +1 -1
- django_cfg/static/frontend/admin/private/ui/index.html +2 -2
- django_cfg/templates/admin/index.html +1 -1
- {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/METADATA +1 -1
- {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/RECORD +62 -163
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/css/dashboard.css +0 -260
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_channels.mjs +0 -313
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_testing.mjs +0 -803
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/main.mjs +0 -341
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/overview.mjs +0 -432
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/testing.mjs +0 -33
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/websocket.mjs +0 -210
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/channels_content.html +0 -46
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/live_channels_content.html +0 -123
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/overview_content.html +0 -45
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/publishes_content.html +0 -84
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/stat_cards.html +0 -53
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/system_status.html +0 -91
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/tab_navigation.html +0 -29
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/testing_tools.html +0 -415
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/layout/base.html +0 -61
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/pages/dashboard.html +0 -58
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/tags/connection_script.html +0 -48
- django_cfg/apps/centrifugo/templatetags/__init__.py +0 -1
- django_cfg/apps/centrifugo/templatetags/centrifugo_tags.py +0 -81
- django_cfg/apps/centrifugo/urls_admin.py +0 -20
- django_cfg/apps/centrifugo/views/dashboard.py +0 -28
- django_cfg/modules/django_dashboard/__init__.py +0 -23
- django_cfg/modules/django_dashboard/components.py +0 -312
- django_cfg/modules/django_dashboard/debug.py +0 -174
- django_cfg/modules/django_dashboard/management/__init__.py +0 -0
- django_cfg/modules/django_dashboard/management/commands/__init__.py +0 -0
- django_cfg/modules/django_dashboard/management/commands/debug_dashboard.py +0 -109
- django_cfg/modules/django_dashboard/sections/__init__.py +0 -1
- django_cfg/modules/django_dashboard/sections/base.py +0 -129
- django_cfg/modules/django_dashboard/sections/commands.py +0 -33
- django_cfg/modules/django_dashboard/sections/documentation.py +0 -393
- django_cfg/modules/django_dashboard/sections/overview.py +0 -398
- django_cfg/modules/django_dashboard/sections/stats.py +0 -48
- django_cfg/modules/django_dashboard/sections/system.py +0 -74
- django_cfg/modules/django_dashboard/sections/widgets.py +0 -222
- django_cfg/modules/django_unfold/callbacks/__init__.py +0 -9
- django_cfg/modules/django_unfold/callbacks/actions.py +0 -51
- django_cfg/modules/django_unfold/callbacks/apizones.py +0 -122
- django_cfg/modules/django_unfold/callbacks/charts.py +0 -223
- django_cfg/modules/django_unfold/callbacks/commands.py +0 -40
- django_cfg/modules/django_unfold/callbacks/main.py +0 -322
- django_cfg/modules/django_unfold/callbacks/statistics.py +0 -240
- django_cfg/modules/django_unfold/callbacks/system.py +0 -180
- django_cfg/modules/django_unfold/callbacks/users.py +0 -65
- django_cfg/modules/django_unfold/models/dashboard.py +0 -207
- django_cfg/modules/django_unfold/models/tabs.py +0 -26
- django_cfg/modules/django_unfold/models.py +0 -98
- django_cfg/modules/django_unfold/templates/unfold/helpers/app_list.html +0 -102
- django_cfg/static/frontend/admin/_next/static/chunks/23004-faae121bbfecc163.js +0 -1
- django_cfg/static/frontend/admin/_next/static/chunks/50314-48bd5701f62faf27.js +0 -1
- django_cfg/static/frontend/admin/_next/static/chunks/pages/private-fe9faa86ecdb0ce6.js +0 -1
- django_cfg/static/frontend/admin/_next/static/css/5f9a37b6e6a72303.css +0 -3
- django_cfg/static/frontend/admin/_next/static/media/438aa629764e75f3-s.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/4c9affa5bc8f420e-s.p.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/51251f8b9793cdb3-s.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/875ae681bfde4580-s.p.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/cc978ac5ee68c2b6-s.woff2 +0 -0
- django_cfg/static/frontend/admin/_next/static/media/e857b654a2caa584-s.woff2 +0 -0
- django_cfg/templates/admin/sections/commands_section.html +0 -5
- django_cfg/templates/admin/sections/documentation_section.html +0 -5
- django_cfg/templates/admin/sections/overview_section.html +0 -5
- django_cfg/templates/admin/sections/stats_section.html +0 -5
- django_cfg/templates/admin/sections/system_section.html +0 -5
- django_cfg/templates/admin/sections/widgets_section.html +0 -11
- django_cfg/templates/admin_old/components/action_grid.html +0 -49
- django_cfg/templates/admin_old/components/card.html +0 -50
- django_cfg/templates/admin_old/components/data_table.html +0 -67
- django_cfg/templates/admin_old/components/metric_card.html +0 -39
- django_cfg/templates/admin_old/components/modal.html +0 -58
- django_cfg/templates/admin_old/components/progress_bar.html +0 -20
- django_cfg/templates/admin_old/components/section_header.html +0 -26
- django_cfg/templates/admin_old/components/stat_item.html +0 -32
- django_cfg/templates/admin_old/components/stats_grid.html +0 -72
- django_cfg/templates/admin_old/components/status_badge.html +0 -28
- django_cfg/templates/admin_old/components/user_avatar.html +0 -27
- django_cfg/templates/admin_old/constance/change_list.html +0 -74
- django_cfg/templates/admin_old/constance/includes/default_value.html +0 -24
- django_cfg/templates/admin_old/constance/includes/fieldset_header.html +0 -15
- django_cfg/templates/admin_old/constance/includes/results_list.html +0 -16
- django_cfg/templates/admin_old/constance/includes/setting_row.html +0 -50
- django_cfg/templates/admin_old/constance/includes/table_headers.html +0 -10
- django_cfg/templates/admin_old/examples/component_class_example.html +0 -156
- django_cfg/templates/admin_old/import_export/change_list_export.html +0 -24
- django_cfg/templates/admin_old/import_export/change_list_import.html +0 -24
- django_cfg/templates/admin_old/import_export/change_list_import_export.html +0 -34
- django_cfg/templates/admin_old/index.html +0 -80
- django_cfg/templates/admin_old/index_new.html +0 -119
- django_cfg/templates/admin_old/layouts/base_dashboard.html +0 -62
- django_cfg/templates/admin_old/layouts/dashboard_with_tabs.html +0 -176
- django_cfg/templates/admin_old/sections/commands_section.html +0 -549
- django_cfg/templates/admin_old/sections/documentation_section.html +0 -152
- django_cfg/templates/admin_old/sections/overview_section.html +0 -112
- django_cfg/templates/admin_old/sections/stats_section.html +0 -35
- django_cfg/templates/admin_old/sections/system_section.html +0 -99
- django_cfg/templates/admin_old/sections/widgets_section.html +0 -129
- django_cfg/templates/admin_old/snippets/components/activity_tracker.html +0 -70
- django_cfg/templates/admin_old/snippets/components/charts_section.html +0 -113
- django_cfg/templates/admin_old/snippets/components/django_commands.html +0 -270
- django_cfg/templates/admin_old/snippets/components/quick_actions.html +0 -66
- django_cfg/templates/admin_old/snippets/components/recent_activity_improved.html +0 -25
- django_cfg/templates/admin_old/snippets/components/recent_users_table.html +0 -102
- django_cfg/templates/admin_old/snippets/components/stats_cards.html +0 -4
- django_cfg/templates/admin_old/snippets/components/stats_tiles.html +0 -92
- django_cfg/templates/admin_old/snippets/components/system_health.html +0 -22
- django_cfg/templates/admin_old/snippets/components/system_metrics.html +0 -199
- django_cfg/templates/admin_old/snippets/components/user_permissions.html +0 -57
- django_cfg/templates/admin_old/snippets/tabs/app_stats_tab.html +0 -201
- django_cfg/templates/admin_old/snippets/tabs/commands_tab.html +0 -114
- django_cfg/templates/admin_old/snippets/tabs/documentation_tab.html +0 -42
- django_cfg/templates/admin_old/snippets/tabs/overview_tab.html +0 -116
- django_cfg/templates/admin_old/snippets/tabs/stats_tab.html +0 -89
- django_cfg/templates/admin_old/snippets/tabs/users_tab.html +0 -51
- django_cfg/templates/admin_old/snippets/tabs/widgets_tab.html +0 -38
- django_cfg/templates/admin_old/snippets/zones/zones_table.html +0 -176
- /django_cfg/static/frontend/admin/_next/static/{ZJZBgOL9mO1koHrgaaLEV → 0sN9ktsgXH48ygtGSrhfu}/_ssgManifest.js +0 -0
- {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.87.dist-info → django_cfg-1.4.89.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,803 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Live Testing Module
|
|
3
|
-
* Interactive WebSocket testing client for Centrifugo integration
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export class LiveTestingModule {
|
|
7
|
-
constructor(api, dashboard) {
|
|
8
|
-
this.api = api;
|
|
9
|
-
this.dashboard = dashboard;
|
|
10
|
-
this.centrifuge = null;
|
|
11
|
-
this.subscriptions = new Map(); // channel -> subscription object
|
|
12
|
-
this.receivedMessages = [];
|
|
13
|
-
this.sentAcks = [];
|
|
14
|
-
this.eventsLog = []; // WebSocket events log
|
|
15
|
-
this.autoAck = true;
|
|
16
|
-
this.clientId = this.generateClientId();
|
|
17
|
-
this.connectionToken = null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Generate unique client ID for this session
|
|
22
|
-
*/
|
|
23
|
-
generateClientId() {
|
|
24
|
-
const stored = localStorage.getItem('centrifugo_client_id');
|
|
25
|
-
if (stored) return stored;
|
|
26
|
-
|
|
27
|
-
const id = 'client_' + Math.random().toString(36).substr(2, 9);
|
|
28
|
-
localStorage.setItem('centrifugo_client_id', id);
|
|
29
|
-
return id;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Log WebSocket event
|
|
34
|
-
* @param {string} type - Event type: connection, subscription, publication, error, ack
|
|
35
|
-
* @param {string} message - Human-readable message
|
|
36
|
-
* @param {object} data - Additional event data
|
|
37
|
-
*/
|
|
38
|
-
logEvent(type, message, data = null) {
|
|
39
|
-
const event = {
|
|
40
|
-
type,
|
|
41
|
-
message,
|
|
42
|
-
data,
|
|
43
|
-
timestamp: new Date().toISOString()
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
this.eventsLog.unshift(event);
|
|
47
|
-
|
|
48
|
-
// Limit to 100 events
|
|
49
|
-
if (this.eventsLog.length > 100) {
|
|
50
|
-
this.eventsLog = this.eventsLog.slice(0, 100);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
console.log(`[${type.toUpperCase()}]`, message, data || '');
|
|
54
|
-
this.renderEventsLog();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Connect to Centrifugo WebSocket
|
|
59
|
-
*/
|
|
60
|
-
async connectToCentrifugo(userId) {
|
|
61
|
-
try {
|
|
62
|
-
console.log('Requesting connection token...');
|
|
63
|
-
this.logEvent('connection', 'Requesting connection token...', { user_id: userId });
|
|
64
|
-
|
|
65
|
-
// Get JWT token from backend
|
|
66
|
-
const tokenResponse = await this.api.centrifugoAdminApiTestingConnectionTokenCreate({
|
|
67
|
-
user_id: userId || 'test-user',
|
|
68
|
-
channels: []
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
if (!tokenResponse || !tokenResponse.token) {
|
|
72
|
-
throw new Error('Failed to get connection token');
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
this.connectionToken = tokenResponse.token;
|
|
76
|
-
const wsUrl = tokenResponse.centrifugo_url;
|
|
77
|
-
|
|
78
|
-
console.log('Connecting to Centrifugo:', wsUrl);
|
|
79
|
-
this.logEvent('connection', 'Connecting to Centrifugo...', { url: wsUrl });
|
|
80
|
-
|
|
81
|
-
// Initialize Centrifuge client
|
|
82
|
-
this.centrifuge = new Centrifuge(wsUrl, {
|
|
83
|
-
token: this.connectionToken,
|
|
84
|
-
debug: true
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
// Setup event handlers
|
|
88
|
-
this.centrifuge.on('connected', (ctx) => {
|
|
89
|
-
console.log('Connected to Centrifugo!', ctx);
|
|
90
|
-
this.onConnected(ctx);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
this.centrifuge.on('disconnected', (ctx) => {
|
|
94
|
-
console.log('Disconnected from Centrifugo', ctx);
|
|
95
|
-
this.onDisconnected(ctx);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
this.centrifuge.on('error', (ctx) => {
|
|
99
|
-
console.error('Centrifugo error:', ctx);
|
|
100
|
-
this.onError(ctx);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// Connect
|
|
104
|
-
this.centrifuge.connect();
|
|
105
|
-
|
|
106
|
-
return { success: true };
|
|
107
|
-
|
|
108
|
-
} catch (error) {
|
|
109
|
-
console.error('Failed to connect to Centrifugo:', error);
|
|
110
|
-
this.logEvent('error', 'Connection failed: ' + error.message, { error: error.message });
|
|
111
|
-
return { success: false, error: error.message };
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Disconnect from Centrifugo
|
|
117
|
-
*/
|
|
118
|
-
disconnect() {
|
|
119
|
-
if (this.centrifuge) {
|
|
120
|
-
this.centrifuge.disconnect();
|
|
121
|
-
this.centrifuge = null;
|
|
122
|
-
}
|
|
123
|
-
this.subscriptions.clear();
|
|
124
|
-
this.updateConnectionStatus(false);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Subscribe to channel
|
|
129
|
-
*/
|
|
130
|
-
subscribeToChannel(channel) {
|
|
131
|
-
if (!this.centrifuge) {
|
|
132
|
-
alert('Connect to Centrifugo first!');
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (this.subscriptions.has(channel)) {
|
|
137
|
-
alert(`Already subscribed to ${channel}`);
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
console.log('Subscribing to channel:', channel);
|
|
142
|
-
|
|
143
|
-
const subscription = this.centrifuge.newSubscription(channel);
|
|
144
|
-
|
|
145
|
-
// Handle publications
|
|
146
|
-
subscription.on('publication', async (ctx) => {
|
|
147
|
-
console.log('Received publication:', ctx.data);
|
|
148
|
-
await this.onPublication(channel, ctx.data);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
subscription.on('subscribed', (ctx) => {
|
|
152
|
-
console.log(`Subscribed to ${channel}`, ctx);
|
|
153
|
-
this.onSubscribed(channel);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
subscription.on('unsubscribed', (ctx) => {
|
|
157
|
-
console.log(`Unsubscribed from ${channel}`, ctx);
|
|
158
|
-
this.onUnsubscribed(channel);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
subscription.subscribe();
|
|
162
|
-
this.subscriptions.set(channel, subscription);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Unsubscribe from channel
|
|
167
|
-
*/
|
|
168
|
-
unsubscribeFromChannel(channel) {
|
|
169
|
-
const subscription = this.subscriptions.get(channel);
|
|
170
|
-
if (subscription) {
|
|
171
|
-
subscription.unsubscribe();
|
|
172
|
-
this.subscriptions.delete(channel);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Publish test message via wrapper
|
|
178
|
-
*/
|
|
179
|
-
async publishTestMessage(channel, data, waitForAck = false, ackTimeout = 10) {
|
|
180
|
-
try {
|
|
181
|
-
console.log('Publishing test message...', { channel, data, waitForAck });
|
|
182
|
-
|
|
183
|
-
const response = await this.api.centrifugoAdminApiTestingPublishTestCreate({
|
|
184
|
-
channel,
|
|
185
|
-
data,
|
|
186
|
-
wait_for_ack: waitForAck,
|
|
187
|
-
ack_timeout: ackTimeout
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
console.log('Publish response:', response);
|
|
191
|
-
|
|
192
|
-
return response;
|
|
193
|
-
|
|
194
|
-
} catch (error) {
|
|
195
|
-
console.error('Failed to publish test message:', error);
|
|
196
|
-
return {
|
|
197
|
-
success: false,
|
|
198
|
-
error: error.message
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Send manual ACK for message
|
|
205
|
-
*/
|
|
206
|
-
async sendManualAck(messageId) {
|
|
207
|
-
try {
|
|
208
|
-
console.log('Sending manual ACK for message:', messageId);
|
|
209
|
-
this.logEvent('ack', `Sending ACK for message ${messageId}`, { message_id: messageId });
|
|
210
|
-
|
|
211
|
-
const response = await this.api.centrifugoAdminApiTestingSendAckCreate({
|
|
212
|
-
message_id: messageId,
|
|
213
|
-
client_id: this.clientId
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
console.log('ACK response:', response);
|
|
217
|
-
|
|
218
|
-
if (response.success) {
|
|
219
|
-
this.logEvent('ack', `ACK sent successfully for ${messageId}`, { message_id: messageId });
|
|
220
|
-
} else {
|
|
221
|
-
this.logEvent('error', `ACK failed for ${messageId}`, { message_id: messageId, error: response.error });
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Track sent ACK
|
|
225
|
-
this.sentAcks.push({
|
|
226
|
-
message_id: messageId,
|
|
227
|
-
timestamp: new Date().toISOString(),
|
|
228
|
-
success: response.success
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
this.renderAckHistory();
|
|
232
|
-
|
|
233
|
-
return response;
|
|
234
|
-
|
|
235
|
-
} catch (error) {
|
|
236
|
-
console.error('Failed to send ACK:', error);
|
|
237
|
-
this.logEvent('error', `ACK request failed: ${error.message}`, { message_id: messageId, error: error.message });
|
|
238
|
-
return {
|
|
239
|
-
success: false,
|
|
240
|
-
error: error.message
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Handle connection established
|
|
247
|
-
*/
|
|
248
|
-
onConnected(ctx) {
|
|
249
|
-
this.logEvent('connection', 'Connected to Centrifugo', { client_id: ctx.client });
|
|
250
|
-
this.updateConnectionStatus(true);
|
|
251
|
-
this.updateElement('ws-client-id', ctx.client);
|
|
252
|
-
|
|
253
|
-
// Show success notification
|
|
254
|
-
if (window.showNotification) {
|
|
255
|
-
window.showNotification('Connected to Centrifugo!', 'success');
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Handle disconnection
|
|
261
|
-
*/
|
|
262
|
-
onDisconnected(ctx) {
|
|
263
|
-
this.logEvent('connection', 'Disconnected from Centrifugo', { reason: ctx.reason, code: ctx.code });
|
|
264
|
-
this.updateConnectionStatus(false);
|
|
265
|
-
|
|
266
|
-
if (window.showNotification) {
|
|
267
|
-
window.showNotification('Disconnected from Centrifugo', 'warning');
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Handle connection error
|
|
273
|
-
*/
|
|
274
|
-
onError(ctx) {
|
|
275
|
-
console.error('Connection error:', ctx);
|
|
276
|
-
this.logEvent('error', 'WebSocket error: ' + ctx.message, { error: ctx });
|
|
277
|
-
|
|
278
|
-
if (window.showNotification) {
|
|
279
|
-
window.showNotification('Centrifugo connection error: ' + ctx.message, 'error');
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Handle incoming publication
|
|
285
|
-
*/
|
|
286
|
-
async onPublication(channel, data) {
|
|
287
|
-
this.logEvent('publication', `Message received on ${channel}`, {
|
|
288
|
-
channel,
|
|
289
|
-
message_id: data._message_id,
|
|
290
|
-
ack_required: data._ack_required
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
// Add to received messages
|
|
294
|
-
this.receivedMessages.unshift({
|
|
295
|
-
channel,
|
|
296
|
-
data,
|
|
297
|
-
timestamp: new Date().toISOString(),
|
|
298
|
-
ack_required: data._ack_required || false,
|
|
299
|
-
message_id: data._message_id || null,
|
|
300
|
-
ack_sent: false
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
// Limit history to 50 messages
|
|
304
|
-
if (this.receivedMessages.length > 50) {
|
|
305
|
-
this.receivedMessages = this.receivedMessages.slice(0, 50);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// Render message in UI
|
|
309
|
-
this.renderReceivedMessages();
|
|
310
|
-
|
|
311
|
-
// Auto-send ACK if required and enabled
|
|
312
|
-
if (this.autoAck && data._ack_required && data._message_id) {
|
|
313
|
-
console.log('Auto-sending ACK for message:', data._message_id);
|
|
314
|
-
await this.sendManualAck(data._message_id);
|
|
315
|
-
|
|
316
|
-
// Mark message as ACK sent
|
|
317
|
-
const msg = this.receivedMessages.find(m => m.message_id === data._message_id);
|
|
318
|
-
if (msg) {
|
|
319
|
-
msg.ack_sent = true;
|
|
320
|
-
this.renderReceivedMessages();
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Handle channel subscription success
|
|
327
|
-
*/
|
|
328
|
-
onSubscribed(channel) {
|
|
329
|
-
this.logEvent('subscription', `Subscribed to ${channel}`, { channel });
|
|
330
|
-
this.renderSubscriptionsList();
|
|
331
|
-
|
|
332
|
-
if (window.showNotification) {
|
|
333
|
-
window.showNotification(`Subscribed to ${channel}`, 'success');
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Handle channel unsubscription
|
|
339
|
-
*/
|
|
340
|
-
onUnsubscribed(channel) {
|
|
341
|
-
this.logEvent('subscription', `Unsubscribed from ${channel}`, { channel });
|
|
342
|
-
this.renderSubscriptionsList();
|
|
343
|
-
|
|
344
|
-
if (window.showNotification) {
|
|
345
|
-
window.showNotification(`Unsubscribed from ${channel}`, 'info');
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Update connection status indicator
|
|
351
|
-
*/
|
|
352
|
-
updateConnectionStatus(connected) {
|
|
353
|
-
const statusEl = document.getElementById('ws-connection-status');
|
|
354
|
-
const connectBtn = document.getElementById('ws-connect-btn');
|
|
355
|
-
const disconnectBtn = document.getElementById('ws-disconnect-btn');
|
|
356
|
-
const subscribeBtn = document.getElementById('ws-subscribe-btn');
|
|
357
|
-
|
|
358
|
-
if (statusEl) {
|
|
359
|
-
if (connected) {
|
|
360
|
-
statusEl.textContent = 'Connected';
|
|
361
|
-
statusEl.className = 'px-3 py-1 bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 rounded-full text-sm font-medium';
|
|
362
|
-
} else {
|
|
363
|
-
statusEl.textContent = 'Disconnected';
|
|
364
|
-
statusEl.className = 'px-3 py-1 bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200 rounded-full text-sm font-medium';
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
if (connectBtn) connectBtn.disabled = connected;
|
|
369
|
-
if (disconnectBtn) disconnectBtn.disabled = !connected;
|
|
370
|
-
if (subscribeBtn) subscribeBtn.disabled = !connected;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* Render subscriptions list
|
|
375
|
-
*/
|
|
376
|
-
renderSubscriptionsList() {
|
|
377
|
-
const container = document.getElementById('ws-subscriptions-list');
|
|
378
|
-
if (!container) return;
|
|
379
|
-
|
|
380
|
-
if (this.subscriptions.size === 0) {
|
|
381
|
-
container.innerHTML = `
|
|
382
|
-
<div class="text-center py-4 text-gray-500 dark:text-gray-400 text-sm">
|
|
383
|
-
No active subscriptions
|
|
384
|
-
</div>
|
|
385
|
-
`;
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
let html = '<div class="space-y-2">';
|
|
390
|
-
this.subscriptions.forEach((sub, channel) => {
|
|
391
|
-
html += `
|
|
392
|
-
<div class="flex items-center justify-between bg-gray-50 dark:bg-gray-700 rounded-lg p-3">
|
|
393
|
-
<div class="flex items-center gap-2">
|
|
394
|
-
<span class="material-icons text-purple-500 text-sm">radio_button_checked</span>
|
|
395
|
-
<code class="text-sm font-mono text-gray-900 dark:text-white">${this.escapeHtml(channel)}</code>
|
|
396
|
-
</div>
|
|
397
|
-
<button onclick="window.centrifugoDashboard.liveTestingModule.unsubscribeFromChannel('${this.escapeHtml(channel)}')"
|
|
398
|
-
class="px-3 py-1 bg-red-500 hover:bg-red-600 text-white rounded text-xs font-medium">
|
|
399
|
-
Unsubscribe
|
|
400
|
-
</button>
|
|
401
|
-
</div>
|
|
402
|
-
`;
|
|
403
|
-
});
|
|
404
|
-
html += '</div>';
|
|
405
|
-
|
|
406
|
-
container.innerHTML = html;
|
|
407
|
-
|
|
408
|
-
// Update count badge
|
|
409
|
-
const badge = document.getElementById('ws-subscriptions-count');
|
|
410
|
-
if (badge) {
|
|
411
|
-
badge.textContent = this.subscriptions.size;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Render received messages
|
|
417
|
-
*/
|
|
418
|
-
renderReceivedMessages() {
|
|
419
|
-
const container = document.getElementById('ws-received-messages');
|
|
420
|
-
if (!container) return;
|
|
421
|
-
|
|
422
|
-
if (this.receivedMessages.length === 0) {
|
|
423
|
-
container.innerHTML = `
|
|
424
|
-
<div class="text-center py-8 text-gray-500 dark:text-gray-400">
|
|
425
|
-
No messages received yet...
|
|
426
|
-
</div>
|
|
427
|
-
`;
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
let html = '<div class="space-y-3">';
|
|
432
|
-
this.receivedMessages.forEach(msg => {
|
|
433
|
-
const timeStr = new Date(msg.timestamp).toLocaleTimeString();
|
|
434
|
-
const ackBadge = msg.ack_required ?
|
|
435
|
-
(msg.ack_sent ?
|
|
436
|
-
'<span class="px-2 py-1 bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 rounded text-xs">ACK Sent</span>' :
|
|
437
|
-
'<span class="px-2 py-1 bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200 rounded text-xs">ACK Required</span>'
|
|
438
|
-
) : '';
|
|
439
|
-
|
|
440
|
-
html += `
|
|
441
|
-
<div class="bg-white dark:bg-gray-700 rounded-lg p-4 border border-gray-200 dark:border-gray-600">
|
|
442
|
-
<div class="flex items-start justify-between mb-2">
|
|
443
|
-
<div class="flex-1">
|
|
444
|
-
<div class="flex items-center gap-2 mb-1">
|
|
445
|
-
<span class="text-xs font-medium text-gray-600 dark:text-gray-400">${timeStr}</span>
|
|
446
|
-
<code class="text-xs bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 px-2 py-0.5 rounded">${this.escapeHtml(msg.channel)}</code>
|
|
447
|
-
${ackBadge}
|
|
448
|
-
</div>
|
|
449
|
-
</div>
|
|
450
|
-
${msg.ack_required && !msg.ack_sent && msg.message_id ? `
|
|
451
|
-
<button onclick="window.centrifugoDashboard.liveTestingModule.sendManualAck('${msg.message_id}')"
|
|
452
|
-
class="px-3 py-1 bg-blue-500 hover:bg-blue-600 text-white rounded text-xs font-medium flex items-center gap-1">
|
|
453
|
-
<span class="material-icons text-sm">check</span>
|
|
454
|
-
Send ACK
|
|
455
|
-
</button>
|
|
456
|
-
` : ''}
|
|
457
|
-
</div>
|
|
458
|
-
<pre class="text-xs bg-gray-50 dark:bg-gray-800 p-3 rounded overflow-x-auto"><code>${this.escapeHtml(JSON.stringify(msg.data, null, 2))}</code></pre>
|
|
459
|
-
</div>
|
|
460
|
-
`;
|
|
461
|
-
});
|
|
462
|
-
html += '</div>';
|
|
463
|
-
|
|
464
|
-
container.innerHTML = html;
|
|
465
|
-
|
|
466
|
-
// Update count
|
|
467
|
-
const countEl = document.getElementById('ws-messages-count');
|
|
468
|
-
if (countEl) {
|
|
469
|
-
countEl.textContent = this.receivedMessages.length;
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
/**
|
|
474
|
-
* Render ACK history
|
|
475
|
-
*/
|
|
476
|
-
renderAckHistory() {
|
|
477
|
-
const container = document.getElementById('ws-ack-history');
|
|
478
|
-
if (!container) return;
|
|
479
|
-
|
|
480
|
-
if (this.sentAcks.length === 0) {
|
|
481
|
-
container.innerHTML = `
|
|
482
|
-
<div class="text-center py-4 text-gray-500 dark:text-gray-400 text-sm">
|
|
483
|
-
No ACKs sent yet
|
|
484
|
-
</div>
|
|
485
|
-
`;
|
|
486
|
-
return;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
let html = '<div class="space-y-2">';
|
|
490
|
-
this.sentAcks.slice().reverse().slice(0, 10).forEach(ack => {
|
|
491
|
-
const timeStr = new Date(ack.timestamp).toLocaleTimeString();
|
|
492
|
-
const statusIcon = ack.success ?
|
|
493
|
-
'<span class="material-icons text-green-500 text-sm">check_circle</span>' :
|
|
494
|
-
'<span class="material-icons text-red-500 text-sm">error</span>';
|
|
495
|
-
|
|
496
|
-
html += `
|
|
497
|
-
<div class="flex items-center justify-between bg-gray-50 dark:bg-gray-700 rounded p-2">
|
|
498
|
-
<div class="flex items-center gap-2">
|
|
499
|
-
${statusIcon}
|
|
500
|
-
<code class="text-xs font-mono text-gray-900 dark:text-white">${this.escapeHtml(ack.message_id.substring(0, 12))}...</code>
|
|
501
|
-
</div>
|
|
502
|
-
<span class="text-xs text-gray-500 dark:text-gray-400">${timeStr}</span>
|
|
503
|
-
</div>
|
|
504
|
-
`;
|
|
505
|
-
});
|
|
506
|
-
html += '</div>';
|
|
507
|
-
|
|
508
|
-
container.innerHTML = html;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
/**
|
|
512
|
-
* Render events log
|
|
513
|
-
*/
|
|
514
|
-
renderEventsLog() {
|
|
515
|
-
const container = document.getElementById('ws-events-log');
|
|
516
|
-
if (!container) return;
|
|
517
|
-
|
|
518
|
-
if (this.eventsLog.length === 0) {
|
|
519
|
-
container.innerHTML = `
|
|
520
|
-
<div class="text-center py-4 text-gray-500 dark:text-gray-400 text-sm">
|
|
521
|
-
No events logged yet
|
|
522
|
-
</div>
|
|
523
|
-
`;
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
let html = '<div class="space-y-1 max-h-[400px] overflow-y-auto">';
|
|
528
|
-
this.eventsLog.forEach(event => {
|
|
529
|
-
const timeStr = new Date(event.timestamp).toLocaleTimeString();
|
|
530
|
-
|
|
531
|
-
// Color coding by event type
|
|
532
|
-
let colorClass = 'text-gray-600 dark:text-gray-400';
|
|
533
|
-
let iconName = 'info';
|
|
534
|
-
let bgClass = 'bg-gray-50 dark:bg-gray-700';
|
|
535
|
-
|
|
536
|
-
switch (event.type) {
|
|
537
|
-
case 'connection':
|
|
538
|
-
colorClass = 'text-green-600 dark:text-green-400';
|
|
539
|
-
iconName = 'link';
|
|
540
|
-
bgClass = 'bg-green-50 dark:bg-green-900/20';
|
|
541
|
-
break;
|
|
542
|
-
case 'subscription':
|
|
543
|
-
colorClass = 'text-blue-600 dark:text-blue-400';
|
|
544
|
-
iconName = 'radio_button_checked';
|
|
545
|
-
bgClass = 'bg-blue-50 dark:bg-blue-900/20';
|
|
546
|
-
break;
|
|
547
|
-
case 'publication':
|
|
548
|
-
colorClass = 'text-purple-600 dark:text-purple-400';
|
|
549
|
-
iconName = 'message';
|
|
550
|
-
bgClass = 'bg-purple-50 dark:bg-purple-900/20';
|
|
551
|
-
break;
|
|
552
|
-
case 'ack':
|
|
553
|
-
colorClass = 'text-indigo-600 dark:text-indigo-400';
|
|
554
|
-
iconName = 'check_circle';
|
|
555
|
-
bgClass = 'bg-indigo-50 dark:bg-indigo-900/20';
|
|
556
|
-
break;
|
|
557
|
-
case 'error':
|
|
558
|
-
colorClass = 'text-red-600 dark:text-red-400';
|
|
559
|
-
iconName = 'error';
|
|
560
|
-
bgClass = 'bg-red-50 dark:bg-red-900/20';
|
|
561
|
-
break;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
html += `
|
|
565
|
-
<div class="${bgClass} rounded p-2 hover:shadow-sm transition-shadow">
|
|
566
|
-
<div class="flex items-start gap-2">
|
|
567
|
-
<span class="material-icons text-sm ${colorClass} flex-shrink-0 mt-0.5">${iconName}</span>
|
|
568
|
-
<div class="flex-1 min-w-0">
|
|
569
|
-
<div class="flex items-center gap-2 mb-0.5">
|
|
570
|
-
<span class="text-xs font-medium ${colorClass} uppercase">${event.type}</span>
|
|
571
|
-
<span class="text-xs text-gray-500 dark:text-gray-400">${timeStr}</span>
|
|
572
|
-
</div>
|
|
573
|
-
<p class="text-xs text-gray-700 dark:text-gray-300">${this.escapeHtml(event.message)}</p>
|
|
574
|
-
${event.data ? `
|
|
575
|
-
<details class="mt-1">
|
|
576
|
-
<summary class="text-xs text-gray-500 dark:text-gray-400 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200">
|
|
577
|
-
Details
|
|
578
|
-
</summary>
|
|
579
|
-
<pre class="text-xs bg-white dark:bg-gray-800 p-2 rounded mt-1 overflow-x-auto"><code>${this.escapeHtml(JSON.stringify(event.data, null, 2))}</code></pre>
|
|
580
|
-
</details>
|
|
581
|
-
` : ''}
|
|
582
|
-
</div>
|
|
583
|
-
</div>
|
|
584
|
-
</div>
|
|
585
|
-
`;
|
|
586
|
-
});
|
|
587
|
-
html += '</div>';
|
|
588
|
-
|
|
589
|
-
container.innerHTML = html;
|
|
590
|
-
|
|
591
|
-
// Update count badge
|
|
592
|
-
const badge = document.getElementById('ws-events-count');
|
|
593
|
-
if (badge) {
|
|
594
|
-
badge.textContent = this.eventsLog.length;
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
/**
|
|
599
|
-
* Clear events log
|
|
600
|
-
*/
|
|
601
|
-
clearEventsLog() {
|
|
602
|
-
this.eventsLog = [];
|
|
603
|
-
this.renderEventsLog();
|
|
604
|
-
this.logEvent('connection', 'Events log cleared');
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
/**
|
|
608
|
-
* Export events log to JSON
|
|
609
|
-
*/
|
|
610
|
-
exportEventsLog() {
|
|
611
|
-
const dataStr = JSON.stringify(this.eventsLog, null, 2);
|
|
612
|
-
const dataBlob = new Blob([dataStr], { type: 'application/json' });
|
|
613
|
-
const url = URL.createObjectURL(dataBlob);
|
|
614
|
-
const link = document.createElement('a');
|
|
615
|
-
link.href = url;
|
|
616
|
-
link.download = `centrifugo-events-${new Date().toISOString()}.json`;
|
|
617
|
-
link.click();
|
|
618
|
-
URL.revokeObjectURL(url);
|
|
619
|
-
|
|
620
|
-
this.logEvent('connection', 'Events log exported to JSON');
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
/**
|
|
624
|
-
* Clear received messages
|
|
625
|
-
*/
|
|
626
|
-
clearReceivedMessages() {
|
|
627
|
-
this.receivedMessages = [];
|
|
628
|
-
this.renderReceivedMessages();
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
/**
|
|
632
|
-
* Clear ACK history
|
|
633
|
-
*/
|
|
634
|
-
clearAckHistory() {
|
|
635
|
-
this.sentAcks = [];
|
|
636
|
-
this.renderAckHistory();
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
/**
|
|
640
|
-
* Toggle auto ACK
|
|
641
|
-
*/
|
|
642
|
-
toggleAutoAck(enabled) {
|
|
643
|
-
this.autoAck = enabled;
|
|
644
|
-
console.log('Auto ACK:', this.autoAck ? 'enabled' : 'disabled');
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
/**
|
|
648
|
-
* Run quick test scenario
|
|
649
|
-
*/
|
|
650
|
-
async runQuickScenario(scenarioName) {
|
|
651
|
-
console.log('Running quick scenario:', scenarioName);
|
|
652
|
-
|
|
653
|
-
const scenarios = {
|
|
654
|
-
'simple-notification': {
|
|
655
|
-
name: 'Simple Notification',
|
|
656
|
-
channel: 'user#test-123',
|
|
657
|
-
data: {
|
|
658
|
-
type: 'notification',
|
|
659
|
-
title: 'Test Notification',
|
|
660
|
-
message: 'This is a simple test notification',
|
|
661
|
-
timestamp: new Date().toISOString()
|
|
662
|
-
},
|
|
663
|
-
waitForAck: false,
|
|
664
|
-
autoConnect: true,
|
|
665
|
-
autoSubscribe: true
|
|
666
|
-
},
|
|
667
|
-
'ack-notification': {
|
|
668
|
-
name: 'ACK Required Notification',
|
|
669
|
-
channel: 'user#test-123',
|
|
670
|
-
data: {
|
|
671
|
-
type: 'important',
|
|
672
|
-
title: 'Important Message',
|
|
673
|
-
message: 'Please acknowledge this message',
|
|
674
|
-
priority: 'high',
|
|
675
|
-
timestamp: new Date().toISOString()
|
|
676
|
-
},
|
|
677
|
-
waitForAck: true,
|
|
678
|
-
ackTimeout: 10,
|
|
679
|
-
autoConnect: true,
|
|
680
|
-
autoSubscribe: true
|
|
681
|
-
},
|
|
682
|
-
'broadcast': {
|
|
683
|
-
name: 'Team Broadcast',
|
|
684
|
-
channel: 'team#developers',
|
|
685
|
-
data: {
|
|
686
|
-
type: 'broadcast',
|
|
687
|
-
title: 'Team Announcement',
|
|
688
|
-
message: 'Testing broadcast to all team members',
|
|
689
|
-
from: 'admin',
|
|
690
|
-
timestamp: new Date().toISOString()
|
|
691
|
-
},
|
|
692
|
-
waitForAck: false,
|
|
693
|
-
autoConnect: true,
|
|
694
|
-
autoSubscribe: true
|
|
695
|
-
},
|
|
696
|
-
'ack-flow': {
|
|
697
|
-
name: 'Complete ACK Flow Test',
|
|
698
|
-
channel: 'test#ack-flow',
|
|
699
|
-
data: {
|
|
700
|
-
type: 'test',
|
|
701
|
-
title: 'ACK Flow Test',
|
|
702
|
-
message: 'Testing complete publish → receive → ACK flow',
|
|
703
|
-
test_id: Math.random().toString(36).substr(2, 9),
|
|
704
|
-
timestamp: new Date().toISOString()
|
|
705
|
-
},
|
|
706
|
-
waitForAck: true,
|
|
707
|
-
ackTimeout: 15,
|
|
708
|
-
autoConnect: true,
|
|
709
|
-
autoSubscribe: true
|
|
710
|
-
}
|
|
711
|
-
};
|
|
712
|
-
|
|
713
|
-
const scenario = scenarios[scenarioName];
|
|
714
|
-
if (!scenario) {
|
|
715
|
-
alert('Unknown scenario: ' + scenarioName);
|
|
716
|
-
return;
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
try {
|
|
720
|
-
// Step 1: Connect if needed
|
|
721
|
-
if (scenario.autoConnect && !this.centrifuge) {
|
|
722
|
-
if (window.showNotification) {
|
|
723
|
-
window.showNotification('Connecting to Centrifugo...', 'info');
|
|
724
|
-
}
|
|
725
|
-
const connectResult = await this.connectToCentrifugo('test-user-123');
|
|
726
|
-
if (!connectResult.success) {
|
|
727
|
-
throw new Error('Failed to connect: ' + connectResult.error);
|
|
728
|
-
}
|
|
729
|
-
// Wait for connection
|
|
730
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
// Step 2: Subscribe if needed
|
|
734
|
-
if (scenario.autoSubscribe && !this.subscriptions.has(scenario.channel)) {
|
|
735
|
-
if (window.showNotification) {
|
|
736
|
-
window.showNotification('Subscribing to ' + scenario.channel + '...', 'info');
|
|
737
|
-
}
|
|
738
|
-
this.subscribeToChannel(scenario.channel);
|
|
739
|
-
// Wait for subscription
|
|
740
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
// Step 3: Publish message
|
|
744
|
-
if (window.showNotification) {
|
|
745
|
-
window.showNotification('Publishing test message...', 'info');
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
const result = await this.publishTestMessage(
|
|
749
|
-
scenario.channel,
|
|
750
|
-
scenario.data,
|
|
751
|
-
scenario.waitForAck || false,
|
|
752
|
-
scenario.ackTimeout || 10
|
|
753
|
-
);
|
|
754
|
-
|
|
755
|
-
// Step 4: Show result
|
|
756
|
-
if (result.success) {
|
|
757
|
-
let message = `✅ Scenario "${scenario.name}" completed!\n\n`;
|
|
758
|
-
message += `Message ID: ${result.message_id}\n`;
|
|
759
|
-
message += `Channel: ${result.channel}\n`;
|
|
760
|
-
if (scenario.waitForAck) {
|
|
761
|
-
message += `ACKs Received: ${result.acks_received}\n`;
|
|
762
|
-
message += `Delivered: ${result.delivered ? 'Yes' : 'No'}`;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
alert(message);
|
|
766
|
-
|
|
767
|
-
if (window.showNotification) {
|
|
768
|
-
window.showNotification('Scenario completed successfully!', 'success');
|
|
769
|
-
}
|
|
770
|
-
} else {
|
|
771
|
-
throw new Error(result.error || 'Unknown error');
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
} catch (error) {
|
|
775
|
-
console.error('Scenario failed:', error);
|
|
776
|
-
alert('❌ Scenario failed: ' + error.message);
|
|
777
|
-
|
|
778
|
-
if (window.showNotification) {
|
|
779
|
-
window.showNotification('Scenario failed: ' + error.message, 'error');
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
/**
|
|
785
|
-
* Update element text content
|
|
786
|
-
*/
|
|
787
|
-
updateElement(id, value) {
|
|
788
|
-
const element = document.getElementById(id);
|
|
789
|
-
if (element) {
|
|
790
|
-
element.textContent = value;
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
/**
|
|
795
|
-
* Escape HTML for safe rendering
|
|
796
|
-
*/
|
|
797
|
-
escapeHtml(unsafe) {
|
|
798
|
-
if (typeof unsafe !== 'string') return unsafe;
|
|
799
|
-
const div = document.createElement('div');
|
|
800
|
-
div.textContent = unsafe;
|
|
801
|
-
return div.innerHTML;
|
|
802
|
-
}
|
|
803
|
-
}
|