queen-mq 0.1.0 → 0.1.2

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.
Files changed (160) hide show
  1. package/API.md +862 -752
  2. package/AUTH.md +2044 -0
  3. package/LICENSE.md +202 -0
  4. package/README.md +1705 -1051
  5. package/WEBAPP.md +1889 -0
  6. package/assets/dashboard-01.png +0 -0
  7. package/assets/queen-logo-blue.svg +210 -0
  8. package/assets/queen-logo-cyan.svg +210 -0
  9. package/assets/queen-logo-indigo.svg +210 -0
  10. package/assets/queen-logo-orange.svg +210 -0
  11. package/assets/queen-logo-pink.svg +210 -0
  12. package/assets/queen-logo-purple.svg +210 -0
  13. package/assets/queen-logo-rose.svg +239 -0
  14. package/assets/queen-logo.svg +263 -0
  15. package/examples/batch-processing.js +58 -0
  16. package/examples/test-complete-client.js +260 -0
  17. package/examples/test-dashboard-api.js +200 -0
  18. package/examples/test-traceid.js +147 -0
  19. package/package.json +17 -4
  20. package/server.log +1 -0
  21. package/src/benchmark/consumer.js +207 -0
  22. package/src/benchmark/consumer_multi.js +216 -0
  23. package/src/benchmark/producer.js +75 -0
  24. package/src/benchmark/producer_multi.js +115 -0
  25. package/src/client/client.js +300 -31
  26. package/src/client/queenClient.js +5 -0
  27. package/src/cluster-server.js +242 -0
  28. package/src/config.js +19 -5
  29. package/src/database/connection.js +42 -16
  30. package/src/database/poolManager.js +7 -0
  31. package/src/database/schema-v2.sql +194 -130
  32. package/src/managers/queueManagerOptimized.js +823 -933
  33. package/src/managers/systemEventManager.js +8 -3
  34. package/src/routes/messages.js +127 -57
  35. package/src/routes/pop.js +27 -43
  36. package/src/routes/resources.js +61 -27
  37. package/src/routes/status.js +1037 -0
  38. package/src/server.js +308 -272
  39. package/src/services/evictionService.js +57 -28
  40. package/src/services/retentionService.js +44 -11
  41. package/src/test/MIGRATION_ISSUES.md +174 -0
  42. package/src/test/README.md +203 -0
  43. package/src/test/advanced-pattern-tests.js +1137 -0
  44. package/src/test/bus-mode-tests.js +361 -0
  45. package/src/test/core-tests.js +342 -0
  46. package/src/test/edge-case-tests.js +561 -0
  47. package/src/test/enterprise-tests.js +637 -0
  48. package/src/test/partition-locking-tests.js +545 -0
  49. package/src/test/test-new.js +278 -0
  50. package/src/test/test.js +6 -3
  51. package/src/test/utils.js +169 -0
  52. package/src/utils/streaming.js +231 -0
  53. package/src/utils/uuid.js +2 -2
  54. package/src/websocket/wsServer.js +10 -3
  55. package/test-keepalive-v2.sh +22 -0
  56. package/webapp/COLOR_GUIDE.md +118 -0
  57. package/webapp/README.md +143 -0
  58. package/webapp/index.html +14 -0
  59. package/webapp/package-lock.json +3184 -0
  60. package/webapp/package.json +25 -0
  61. package/webapp/postcss.config.js +7 -0
  62. package/webapp/public/assets/queen-logo-blue.svg +210 -0
  63. package/webapp/public/assets/queen-logo-cyan.svg +210 -0
  64. package/webapp/public/assets/queen-logo-indigo.svg +210 -0
  65. package/webapp/public/assets/queen-logo-orange.svg +210 -0
  66. package/webapp/public/assets/queen-logo-pink.svg +210 -0
  67. package/webapp/public/assets/queen-logo-purple.svg +210 -0
  68. package/webapp/public/assets/queen-logo-rose.svg +239 -0
  69. package/webapp/public/assets/queen-logo.svg +263 -0
  70. package/webapp/src/App.vue +19 -0
  71. package/webapp/src/api/analytics.js +10 -0
  72. package/webapp/src/api/client.js +29 -0
  73. package/webapp/src/api/consumers.js +52 -0
  74. package/webapp/src/api/health.js +7 -0
  75. package/webapp/src/api/messages.js +26 -0
  76. package/webapp/src/api/queues.js +14 -0
  77. package/webapp/src/api/resources.js +8 -0
  78. package/webapp/src/assets/styles/main.css +357 -0
  79. package/webapp/src/components/analytics/AnalyticsFilters.vue +87 -0
  80. package/webapp/src/components/analytics/AnalyticsMetrics.vue +57 -0
  81. package/webapp/src/components/analytics/MessageDistributionChart.vue +111 -0
  82. package/webapp/src/components/analytics/MessageFlowChart.vue +173 -0
  83. package/webapp/src/components/analytics/TimeRangeSelector.vue +27 -0
  84. package/webapp/src/components/analytics/TopQueuesChart.vue +132 -0
  85. package/webapp/src/components/common/ConfirmDialog.vue +56 -0
  86. package/webapp/src/components/common/LoadingSpinner.vue +6 -0
  87. package/webapp/src/components/common/MetricCard.vue +43 -0
  88. package/webapp/src/components/common/StatusBadge.vue +45 -0
  89. package/webapp/src/components/dashboard/MessageStatusCard.vue +50 -0
  90. package/webapp/src/components/dashboard/PerformanceCard.vue +38 -0
  91. package/webapp/src/components/dashboard/ThroughputChart.vue +182 -0
  92. package/webapp/src/components/dashboard/TopQueuesTable.vue +53 -0
  93. package/webapp/src/components/layout/AppLayout.vue +110 -0
  94. package/webapp/src/components/layout/AppSidebar.vue +304 -0
  95. package/webapp/src/components/messages/MessageDetailPanel.vue +242 -0
  96. package/webapp/src/components/messages/MessageFilters.vue +114 -0
  97. package/webapp/src/components/queue-detail/PartitionList.vue +79 -0
  98. package/webapp/src/components/queue-detail/PushMessageModal.vue +175 -0
  99. package/webapp/src/components/queue-detail/QueueConfig.vue +63 -0
  100. package/webapp/src/components/queue-detail/QueueDetailHeader.vue +53 -0
  101. package/webapp/src/components/queue-detail/RecentMessages.vue +76 -0
  102. package/webapp/src/components/queues/CreateQueueModal.vue +193 -0
  103. package/webapp/src/components/queues/QueueFilters.vue +90 -0
  104. package/webapp/src/composables/useApi.js +34 -0
  105. package/webapp/src/composables/useTheme.js +36 -0
  106. package/webapp/src/main.js +11 -0
  107. package/webapp/src/router/index.js +42 -0
  108. package/webapp/src/utils/colors.js +96 -0
  109. package/webapp/src/utils/formatters.js +49 -0
  110. package/webapp/src/views/Analytics.vue +377 -0
  111. package/webapp/src/views/ConsumerGroups.vue +433 -0
  112. package/webapp/src/views/Dashboard.vue +418 -0
  113. package/webapp/src/views/Messages.vue +361 -0
  114. package/webapp/src/views/QueueDetail.vue +582 -0
  115. package/webapp/src/views/Queues.vue +496 -0
  116. package/webapp/tailwind.config.js +25 -0
  117. package/webapp/vite.config.js +10 -0
  118. package/CACHE.md +0 -519
  119. package/DASHBOARD-V3.md +0 -478
  120. package/DASHBOARD.md +0 -382
  121. package/MOD_QUEUE.md +0 -453
  122. package/PARTITION_LOCKING_DESIGN.md +0 -989
  123. package/PLAN.md +0 -707
  124. package/QUERY_ANALSYS.md +0 -72
  125. package/QUEUE_BUS.md +0 -334
  126. package/V2-PLAN.md +0 -236
  127. package/dashboard/.vscode/extensions.json +0 -3
  128. package/dashboard/README.md +0 -5
  129. package/dashboard/index.html +0 -14
  130. package/dashboard/package-lock.json +0 -1458
  131. package/dashboard/package.json +0 -25
  132. package/dashboard/public/vite.svg +0 -1
  133. package/dashboard/src/App.vue +0 -29
  134. package/dashboard/src/assets/styles/main.css +0 -908
  135. package/dashboard/src/assets/vue.svg +0 -1
  136. package/dashboard/src/components/cards/MetricCard.vue +0 -298
  137. package/dashboard/src/components/charts/QueueDepthChart.vue +0 -276
  138. package/dashboard/src/components/charts/QueueLagChart.vue +0 -436
  139. package/dashboard/src/components/charts/ThroughputChart.vue +0 -302
  140. package/dashboard/src/components/common/ActivityFeed.vue +0 -251
  141. package/dashboard/src/components/layout/AppHeader.vue +0 -208
  142. package/dashboard/src/components/layout/AppLayout.vue +0 -88
  143. package/dashboard/src/components/layout/AppSidebar.vue +0 -261
  144. package/dashboard/src/main.js +0 -44
  145. package/dashboard/src/router.js +0 -54
  146. package/dashboard/src/services/api.js +0 -187
  147. package/dashboard/src/services/websocket.js +0 -167
  148. package/dashboard/src/utils/constants.js +0 -56
  149. package/dashboard/src/utils/helpers.js +0 -118
  150. package/dashboard/src/views/Analytics.vue +0 -912
  151. package/dashboard/src/views/Dashboard.vue +0 -906
  152. package/dashboard/src/views/Messages.vue +0 -437
  153. package/dashboard/src/views/QueueDetail.vue +0 -501
  154. package/dashboard/src/views/Queues.vue +0 -333
  155. package/dashboard/vite.config.js +0 -30
  156. package/debug-namespace.js +0 -110
  157. package/docs/long-polling.md +0 -159
  158. package/docs/multi-server-cache-solutions.md +0 -185
  159. package/docs/performance-tuning.md +0 -222
  160. package/src/routes/analytics.js +0 -812
@@ -0,0 +1,361 @@
1
+ <template>
2
+ <div class="page-flat">
3
+ <div class="py-4 px-3">
4
+ <div class="space-y-2.5">
5
+ <!-- Info Banner (shown when API needs restart) -->
6
+ <div v-if="apiNeedsRestart" class="warning-banner">
7
+ <div class="flex gap-3">
8
+ <svg class="w-5 h-5 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
9
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
10
+ </svg>
11
+ <div class="text-sm">
12
+ <p class="font-semibold mb-1">API Update Required</p>
13
+ <p class="text-yellow-800 dark:text-yellow-200">
14
+ The messages API has been fixed. Please restart the Queen server for changes to take effect.
15
+ </p>
16
+ </div>
17
+ </div>
18
+ </div>
19
+
20
+ <!-- Filters -->
21
+ <MessageFilters
22
+ v-model:search="searchQuery"
23
+ v-model:queue="queueFilter"
24
+ v-model:status="statusFilter"
25
+ :queues="queues"
26
+ />
27
+
28
+ <LoadingSpinner v-if="loading && !messages.length" />
29
+
30
+ <div v-else-if="error" class="error-flat">
31
+ <p><strong>Error loading messages:</strong> {{ error }}</p>
32
+ </div>
33
+
34
+ <!-- Messages Table -->
35
+ <div v-else class="table-flat">
36
+ <div class="table-container scrollbar-thin">
37
+ <table class="table">
38
+ <thead>
39
+ <tr>
40
+ <th>Transaction ID</th>
41
+ <th class="hidden md:table-cell">Queue</th>
42
+ <th class="hidden lg:table-cell">Partition</th>
43
+ <th class="text-right">Created</th>
44
+ <th class="text-right">Status</th>
45
+ </tr>
46
+ </thead>
47
+ <tbody>
48
+ <tr
49
+ v-for="message in messages"
50
+ :key="message.id"
51
+ class="cursor-pointer"
52
+ @click="selectMessage(message)"
53
+ >
54
+ <td>
55
+ <div class="font-mono text-xs">{{ message.transactionId.substring(0, 20) }}...</div>
56
+ <div class="text-xs text-gray-500 dark:text-gray-400 md:hidden">
57
+ {{ message.queue }}
58
+ </div>
59
+ </td>
60
+ <td class="hidden md:table-cell">
61
+ <div class="text-sm">{{ message.queue }}</div>
62
+ </td>
63
+ <td class="hidden lg:table-cell">
64
+ <span class="text-xs text-gray-600 dark:text-gray-400">{{ message.partition }}</span>
65
+ </td>
66
+ <td class="text-right text-xs">{{ formatTime(message.createdAt) }}</td>
67
+ <td class="text-right">
68
+ <StatusBadge :status="message.status" :show-dot="false" />
69
+ </td>
70
+ </tr>
71
+ </tbody>
72
+ </table>
73
+
74
+ <div v-if="!messages.length && !loading" class="text-center py-12 text-gray-500 text-sm">
75
+ <svg class="w-12 h-12 mx-auto mb-3 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
76
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4" />
77
+ </svg>
78
+ <p>No messages found</p>
79
+ </div>
80
+ </div>
81
+
82
+ <!-- Pagination -->
83
+ <div v-if="totalPages > 1" class="flex items-center justify-between mt-4 pt-4 border-t border-gray-200/30 dark:border-gray-700/30">
84
+ <div class="text-sm text-gray-600 dark:text-gray-400">
85
+ Page {{ currentPage }} of {{ totalPages }}
86
+ </div>
87
+ <div class="flex gap-2">
88
+ <button
89
+ @click="currentPage--"
90
+ :disabled="currentPage === 1"
91
+ class="btn btn-secondary px-3 py-1.5"
92
+ >
93
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
94
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
95
+ </svg>
96
+ </button>
97
+ <button
98
+ @click="currentPage++"
99
+ :disabled="currentPage === totalPages"
100
+ class="btn btn-secondary px-3 py-1.5"
101
+ >
102
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
103
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
104
+ </svg>
105
+ </button>
106
+ </div>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ </div>
111
+
112
+ <!-- Message Detail Panel -->
113
+ <MessageDetailPanel
114
+ :is-open="!!selectedMessage"
115
+ :message="selectedMessage"
116
+ @close="selectedMessage = null"
117
+ @action-complete="onActionComplete"
118
+ />
119
+ </div>
120
+ </template>
121
+
122
+ <script setup>
123
+ import { ref, watch, onMounted, onUnmounted } from 'vue';
124
+ import { useRoute } from 'vue-router';
125
+ import { messagesApi } from '../api/messages';
126
+ import { queuesApi } from '../api/queues';
127
+ import { formatTime } from '../utils/formatters';
128
+
129
+ import MessageFilters from '../components/messages/MessageFilters.vue';
130
+ import MessageDetailPanel from '../components/messages/MessageDetailPanel.vue';
131
+ import StatusBadge from '../components/common/StatusBadge.vue';
132
+ import LoadingSpinner from '../components/common/LoadingSpinner.vue';
133
+
134
+ const route = useRoute();
135
+
136
+ const loading = ref(false);
137
+ const error = ref(null);
138
+ const messages = ref([]);
139
+ const queues = ref([]);
140
+ const searchQuery = ref('');
141
+ const queueFilter = ref('');
142
+ const statusFilter = ref('');
143
+ const currentPage = ref(1);
144
+ const itemsPerPage = 50;
145
+ const totalPages = ref(1);
146
+ const selectedMessage = ref(null);
147
+ const apiNeedsRestart = ref(false);
148
+
149
+ watch(() => route.query.transactionId, (transactionId) => {
150
+ if (transactionId && messages.value.length) {
151
+ const message = messages.value.find(m => m.transactionId === transactionId);
152
+ if (message) {
153
+ selectedMessage.value = message;
154
+ }
155
+ }
156
+ }, { immediate: true });
157
+
158
+ async function loadData() {
159
+ loading.value = true;
160
+ error.value = null;
161
+ apiNeedsRestart.value = false;
162
+
163
+ try {
164
+ const queuesRes = await queuesApi.getQueues();
165
+ queues.value = queuesRes.data.queues;
166
+
167
+ const params = {
168
+ limit: itemsPerPage,
169
+ offset: (currentPage.value - 1) * itemsPerPage,
170
+ };
171
+
172
+ if (searchQuery.value) {
173
+ try {
174
+ const msgRes = await messagesApi.getMessage(searchQuery.value);
175
+ messages.value = [msgRes.data];
176
+ totalPages.value = 1;
177
+ return;
178
+ } catch {
179
+ // Fall through to regular list
180
+ }
181
+ }
182
+
183
+ if (queueFilter.value) params.queue = queueFilter.value;
184
+ if (statusFilter.value) params.status = statusFilter.value;
185
+
186
+ const messagesRes = await messagesApi.getMessages(params);
187
+ messages.value = messagesRes.data.messages || [];
188
+
189
+ totalPages.value = messages.value.length === itemsPerPage ? currentPage.value + 1 : currentPage.value;
190
+ } catch (err) {
191
+ error.value = err.response?.data?.error || err.message;
192
+
193
+ if (error.value.includes('column m.status does not exist')) {
194
+ apiNeedsRestart.value = true;
195
+ }
196
+ } finally {
197
+ loading.value = false;
198
+ }
199
+ }
200
+
201
+ function selectMessage(message) {
202
+ selectedMessage.value = message;
203
+ }
204
+
205
+ async function onActionComplete() {
206
+ selectedMessage.value = null;
207
+ await loadData();
208
+ }
209
+
210
+ watch([searchQuery, queueFilter, statusFilter], () => {
211
+ currentPage.value = 1;
212
+ loadData();
213
+ });
214
+
215
+ watch(currentPage, () => {
216
+ loadData();
217
+ });
218
+
219
+ onMounted(() => {
220
+ loadData();
221
+
222
+ if (window.registerRefreshCallback) {
223
+ window.registerRefreshCallback('/messages', loadData);
224
+ }
225
+ });
226
+
227
+ onUnmounted(() => {
228
+ if (window.registerRefreshCallback) {
229
+ window.registerRefreshCallback('/messages', null);
230
+ }
231
+ });
232
+ </script>
233
+
234
+ <style scoped>
235
+ .page-flat {
236
+ min-height: 100%;
237
+ }
238
+
239
+ .warning-banner {
240
+ background: #ffffff;
241
+ border: none;
242
+ box-shadow: none;
243
+ border-radius: 0.75rem;
244
+ padding: 1rem;
245
+ border-left: 3px solid rgba(245, 158, 11, 0.5);
246
+ }
247
+
248
+ .dark .warning-banner {
249
+ background: #0a0d14;
250
+ border-left-color: rgba(245, 158, 11, 0.7);
251
+ }
252
+
253
+ .filter-flat {
254
+ background: #ffffff;
255
+ border: none;
256
+ box-shadow: none;
257
+ border-radius: 0.75rem;
258
+ padding: 1rem;
259
+ }
260
+
261
+ .dark .filter-flat {
262
+ background: #0a0d14;
263
+ }
264
+
265
+ .filter-flat :deep(.input) {
266
+ background: transparent;
267
+ border: 1px solid rgba(156, 163, 175, 0.15);
268
+ transition: all 0.2s ease;
269
+ }
270
+
271
+ .filter-flat :deep(.input:hover) {
272
+ border-color: rgba(156, 163, 175, 0.25);
273
+ }
274
+
275
+ .filter-flat :deep(.input:focus) {
276
+ background: rgba(244, 63, 94, 0.02);
277
+ border-color: rgba(244, 63, 94, 0.4);
278
+ box-shadow: 0 0 0 3px rgba(244, 63, 94, 0.05);
279
+ }
280
+
281
+ .dark .filter-flat :deep(.input) {
282
+ border-color: rgba(156, 163, 175, 0.1);
283
+ }
284
+
285
+ .dark .filter-flat :deep(.input:focus) {
286
+ background: rgba(244, 63, 94, 0.03);
287
+ border-color: rgba(244, 63, 94, 0.5);
288
+ box-shadow: 0 0 0 3px rgba(244, 63, 94, 0.08);
289
+ }
290
+
291
+ .table-flat {
292
+ background: #ffffff;
293
+ border: none;
294
+ box-shadow: none;
295
+ border-radius: 0.75rem;
296
+ padding: 1rem 0;
297
+ }
298
+
299
+ .dark .table-flat {
300
+ background: #0a0d14;
301
+ }
302
+
303
+ .table-flat :deep(.table) {
304
+ border-collapse: separate;
305
+ border-spacing: 0;
306
+ }
307
+
308
+ .table-flat :deep(.table thead) {
309
+ background: transparent;
310
+ border-bottom: 1px solid rgba(156, 163, 175, 0.08);
311
+ }
312
+
313
+ .dark .table-flat :deep(.table thead) {
314
+ border-bottom-color: rgba(156, 163, 175, 0.1);
315
+ }
316
+
317
+ .table-flat :deep(.table th) {
318
+ padding: 1rem 1rem;
319
+ font-weight: 600;
320
+ letter-spacing: 0.05em;
321
+ }
322
+
323
+ .table-flat :deep(.table tbody tr) {
324
+ border: none;
325
+ transition: all 0.15s ease;
326
+ }
327
+
328
+ .table-flat :deep(.table tbody tr:nth-child(even)) {
329
+ background: rgba(0, 0, 0, 0.015);
330
+ }
331
+
332
+ .dark .table-flat :deep(.table tbody tr:nth-child(even)) {
333
+ background: rgba(255, 255, 255, 0.02);
334
+ }
335
+
336
+ .table-flat :deep(.table tbody tr:hover) {
337
+ background: rgba(244, 63, 94, 0.03);
338
+ box-shadow: inset 3px 0 0 0 rgba(244, 63, 94, 0.6);
339
+ }
340
+
341
+ .dark .table-flat :deep(.table tbody tr:hover) {
342
+ background: rgba(244, 63, 94, 0.05);
343
+ box-shadow: inset 3px 0 0 0 rgba(244, 63, 94, 0.8);
344
+ }
345
+
346
+ .table-flat :deep(.table td) {
347
+ padding: 0.875rem 1rem;
348
+ border: none;
349
+ }
350
+
351
+ .error-flat {
352
+ background: transparent;
353
+ color: #dc2626;
354
+ font-size: 0.875rem;
355
+ padding: 1rem;
356
+ }
357
+
358
+ .dark .error-flat {
359
+ color: #fca5a5;
360
+ }
361
+ </style>