queen-mq 0.1.5 → 0.2.0
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.
- package/README.md +467 -1775
- package/package.json +12 -14
- package/src/client/client.js +664 -5
- package/src/managers/queueManagerOptimized.js +250 -47
- package/src/routes/ack.js +3 -3
- package/src/server.js +131 -3
- package/src/test/advanced-client-tests.js +761 -0
- package/src/test/advanced-pattern-tests.js +2 -2
- package/src/test/bus-mode-tests.js +6 -6
- package/src/test/core-tests.js +1 -1
- package/src/test/edge-case-tests.js +2 -2
- package/src/test/test-new.js +36 -1
- package/src/webapp-dist/assets/Analytics-DJrE4Rz2.css +1 -0
- package/src/webapp-dist/assets/Analytics-HEMXKmfJ.js +1 -0
- package/src/webapp-dist/assets/ConfirmDialog-YEoCdE7x.js +1 -0
- package/src/webapp-dist/assets/ConsumerGroups-CC5VJywp.css +1 -0
- package/src/webapp-dist/assets/ConsumerGroups-D8Z20HhZ.js +1 -0
- package/src/webapp-dist/assets/Dashboard-BRFOz6it.css +1 -0
- package/src/webapp-dist/assets/Dashboard-Bpx9I32h.js +1 -0
- package/src/webapp-dist/assets/LoadingSpinner-BPttXcbs.js +1 -0
- package/src/webapp-dist/assets/Messages-BOfVHKma.css +1 -0
- package/src/webapp-dist/assets/Messages-EKURCbLl.js +1 -0
- package/src/webapp-dist/assets/QueueDetail-BXXA7xoZ.css +1 -0
- package/src/webapp-dist/assets/QueueDetail-Bmxg0KiA.js +1 -0
- package/src/webapp-dist/assets/Queues-CvrJkrZT.css +1 -0
- package/src/webapp-dist/assets/Queues-yUx9_3-9.js +1 -0
- package/src/webapp-dist/assets/StatusBadge-C1bBtXHh.css +1 -0
- package/src/webapp-dist/assets/StatusBadge-C6N84XmO.js +1 -0
- package/src/webapp-dist/assets/analytics-BapTekLM.js +1 -0
- package/src/webapp-dist/assets/colors-5z6Q_kUr.js +18 -0
- package/src/webapp-dist/assets/index-B7w2bKT6.css +1 -0
- package/src/webapp-dist/assets/index-CUSnwYTH.js +31 -0
- package/src/webapp-dist/assets/messages-CRV6E-xU.js +1 -0
- package/src/webapp-dist/assets/queues-BdUbxfBC.js +1 -0
- package/src/webapp-dist/assets/resources-Bs9U-ceF.js +1 -0
- package/{webapp → src/webapp-dist}/index.html +2 -1
- package/API.md +0 -1226
- package/AUTH.md +0 -2044
- package/CHANGELOG_STATIC_SERVING.md +0 -213
- package/DEPLOYMENT.md +0 -425
- package/PROGRAMMATIC_SERVER.md +0 -239
- package/WEBAPP.md +0 -1889
- package/assets/dashboard-01.png +0 -0
- package/assets/dashboard.png +0 -0
- package/examples/batch-processing.js +0 -58
- package/examples/bus-mode.js +0 -239
- package/examples/continuous-consumer-optimized.js +0 -215
- package/examples/continuous-consumer.js +0 -159
- package/examples/continuous-producer.js +0 -343
- package/examples/mixed-mode.js +0 -277
- package/examples/multi-server-test.js +0 -305
- package/examples/programmatic-server.js +0 -58
- package/examples/server-with-frontend.js +0 -46
- package/examples/single.js +0 -64
- package/examples/smartchat-dealyed.js +0 -42
- package/examples/smartchat.js +0 -52
- package/examples/test-cache-invalidation.js +0 -119
- package/examples/test-cache-multi-server.js +0 -245
- package/examples/test-complete-client.js +0 -260
- package/examples/test-connection-recovery.js +0 -66
- package/examples/test-dashboard-api.js +0 -200
- package/examples/test-minimal-client.js +0 -112
- package/examples/test-queue-creation-policy.js +0 -137
- package/examples/test-traceid.js +0 -147
- package/server.log +0 -1
- package/test-keepalive-v2.sh +0 -22
- package/webapp/COLOR_GUIDE.md +0 -118
- package/webapp/README.md +0 -143
- package/webapp/package-lock.json +0 -3184
- package/webapp/package.json +0 -25
- package/webapp/postcss.config.js +0 -7
- package/webapp/public/assets/queen-logo-blue.svg +0 -210
- package/webapp/public/assets/queen-logo-cyan.svg +0 -210
- package/webapp/public/assets/queen-logo-indigo.svg +0 -210
- package/webapp/public/assets/queen-logo-orange.svg +0 -210
- package/webapp/public/assets/queen-logo-pink.svg +0 -210
- package/webapp/public/assets/queen-logo-purple.svg +0 -210
- package/webapp/public/assets/queen-logo-rose.svg +0 -239
- package/webapp/public/assets/queen-logo.svg +0 -263
- package/webapp/src/App.vue +0 -19
- package/webapp/src/api/analytics.js +0 -10
- package/webapp/src/api/client.js +0 -34
- package/webapp/src/api/consumers.js +0 -52
- package/webapp/src/api/health.js +0 -7
- package/webapp/src/api/messages.js +0 -26
- package/webapp/src/api/queues.js +0 -14
- package/webapp/src/api/resources.js +0 -8
- package/webapp/src/assets/styles/main.css +0 -357
- package/webapp/src/components/analytics/AnalyticsFilters.vue +0 -87
- package/webapp/src/components/analytics/AnalyticsMetrics.vue +0 -57
- package/webapp/src/components/analytics/MessageDistributionChart.vue +0 -111
- package/webapp/src/components/analytics/MessageFlowChart.vue +0 -173
- package/webapp/src/components/analytics/TimeRangeSelector.vue +0 -27
- package/webapp/src/components/analytics/TopQueuesChart.vue +0 -132
- package/webapp/src/components/common/ConfirmDialog.vue +0 -56
- package/webapp/src/components/common/LoadingSpinner.vue +0 -6
- package/webapp/src/components/common/MetricCard.vue +0 -43
- package/webapp/src/components/common/StatusBadge.vue +0 -45
- package/webapp/src/components/dashboard/MessageStatusCard.vue +0 -50
- package/webapp/src/components/dashboard/PerformanceCard.vue +0 -38
- package/webapp/src/components/dashboard/ThroughputChart.vue +0 -182
- package/webapp/src/components/dashboard/TopQueuesTable.vue +0 -53
- package/webapp/src/components/layout/AppLayout.vue +0 -110
- package/webapp/src/components/layout/AppSidebar.vue +0 -304
- package/webapp/src/components/messages/MessageDetailPanel.vue +0 -242
- package/webapp/src/components/messages/MessageFilters.vue +0 -101
- package/webapp/src/components/queue-detail/PartitionList.vue +0 -79
- package/webapp/src/components/queue-detail/PushMessageModal.vue +0 -175
- package/webapp/src/components/queue-detail/QueueConfig.vue +0 -63
- package/webapp/src/components/queue-detail/QueueDetailHeader.vue +0 -53
- package/webapp/src/components/queue-detail/RecentMessages.vue +0 -76
- package/webapp/src/components/queues/CreateQueueModal.vue +0 -193
- package/webapp/src/components/queues/QueueFilters.vue +0 -90
- package/webapp/src/composables/useApi.js +0 -34
- package/webapp/src/composables/useTheme.js +0 -36
- package/webapp/src/main.js +0 -11
- package/webapp/src/router/index.js +0 -42
- package/webapp/src/utils/colors.js +0 -96
- package/webapp/src/utils/formatters.js +0 -49
- package/webapp/src/views/Analytics.vue +0 -377
- package/webapp/src/views/ConsumerGroups.vue +0 -433
- package/webapp/src/views/Dashboard.vue +0 -418
- package/webapp/src/views/Messages.vue +0 -363
- package/webapp/src/views/QueueDetail.vue +0 -582
- package/webapp/src/views/Queues.vue +0 -496
- package/webapp/tailwind.config.js +0 -25
- package/webapp/vite.config.js +0 -10
- /package/{assets → src/webapp-dist/assets}/queen-logo-blue.svg +0 -0
- /package/{assets → src/webapp-dist/assets}/queen-logo-cyan.svg +0 -0
- /package/{assets → src/webapp-dist/assets}/queen-logo-indigo.svg +0 -0
- /package/{assets → src/webapp-dist/assets}/queen-logo-orange.svg +0 -0
- /package/{assets → src/webapp-dist/assets}/queen-logo-pink.svg +0 -0
- /package/{assets → src/webapp-dist/assets}/queen-logo-purple.svg +0 -0
- /package/{assets → src/webapp-dist/assets}/queen-logo-rose.svg +0 -0
- /package/{assets → src/webapp-dist/assets}/queen-logo.svg +0 -0
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
|
|
3
|
-
<div>
|
|
4
|
-
<div class="flex items-center gap-3 mb-2">
|
|
5
|
-
<button @click="goBack" class="p-1 hover:bg-gray-100 dark:hover:bg-slate-700 rounded transition-colors">
|
|
6
|
-
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
7
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
|
8
|
-
</svg>
|
|
9
|
-
</button>
|
|
10
|
-
<h2 class="text-xl font-bold">{{ queue?.name }}</h2>
|
|
11
|
-
</div>
|
|
12
|
-
<div class="flex items-center gap-3 text-sm text-gray-600 dark:text-gray-400 ml-9">
|
|
13
|
-
<span>Namespace: {{ queue?.namespace || '-' }}</span>
|
|
14
|
-
<span>Task: {{ queue?.task || '-' }}</span>
|
|
15
|
-
<span>Priority: {{ queue?.priority || 0 }}</span>
|
|
16
|
-
<span class="text-xs">Created: {{ formatDate(queue?.createdAt) }}</span>
|
|
17
|
-
</div>
|
|
18
|
-
</div>
|
|
19
|
-
|
|
20
|
-
<div class="flex gap-2 ml-9 sm:ml-0">
|
|
21
|
-
<button @click="$emit('push-message')" class="btn btn-primary">
|
|
22
|
-
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
23
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
|
24
|
-
</svg>
|
|
25
|
-
Push Message
|
|
26
|
-
</button>
|
|
27
|
-
<button @click="$emit('clear-queue')" class="btn btn-secondary">
|
|
28
|
-
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
29
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
|
30
|
-
</svg>
|
|
31
|
-
Clear Queue
|
|
32
|
-
</button>
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
</template>
|
|
36
|
-
|
|
37
|
-
<script setup>
|
|
38
|
-
import { useRouter } from 'vue-router';
|
|
39
|
-
import { formatDate } from '../../utils/formatters';
|
|
40
|
-
|
|
41
|
-
defineProps({
|
|
42
|
-
queue: Object,
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
defineEmits(['push-message', 'clear-queue']);
|
|
46
|
-
|
|
47
|
-
const router = useRouter();
|
|
48
|
-
|
|
49
|
-
function goBack() {
|
|
50
|
-
router.push('/queues');
|
|
51
|
-
}
|
|
52
|
-
</script>
|
|
53
|
-
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="messages-container">
|
|
3
|
-
<h3 class="text-base font-semibold mb-4">Recent Messages</h3>
|
|
4
|
-
|
|
5
|
-
<div class="table-container scrollbar-thin">
|
|
6
|
-
<table class="table">
|
|
7
|
-
<thead>
|
|
8
|
-
<tr>
|
|
9
|
-
<th>Transaction ID</th>
|
|
10
|
-
<th class="hidden sm:table-cell">Partition</th>
|
|
11
|
-
<th class="text-right">Created</th>
|
|
12
|
-
<th class="text-right hidden md:table-cell">Status</th>
|
|
13
|
-
</tr>
|
|
14
|
-
</thead>
|
|
15
|
-
<tbody>
|
|
16
|
-
<tr
|
|
17
|
-
v-for="message in messages"
|
|
18
|
-
:key="message.id"
|
|
19
|
-
class="cursor-pointer"
|
|
20
|
-
@click="$emit('message-click', message)"
|
|
21
|
-
>
|
|
22
|
-
<td>
|
|
23
|
-
<div class="font-mono text-xs">{{ message.transactionId?.substring(0, 16) }}...</div>
|
|
24
|
-
</td>
|
|
25
|
-
<td class="hidden sm:table-cell">
|
|
26
|
-
<span class="text-xs">{{ message.partition }}</span>
|
|
27
|
-
</td>
|
|
28
|
-
<td class="text-right text-xs">{{ formatTime(message.createdAt) }}</td>
|
|
29
|
-
<td class="text-right hidden md:table-cell">
|
|
30
|
-
<StatusBadge v-if="message.status" :status="message.status" />
|
|
31
|
-
</td>
|
|
32
|
-
</tr>
|
|
33
|
-
</tbody>
|
|
34
|
-
</table>
|
|
35
|
-
|
|
36
|
-
<div v-if="!messages?.length" class="text-center py-8 text-gray-500 text-sm">
|
|
37
|
-
No recent messages
|
|
38
|
-
</div>
|
|
39
|
-
</div>
|
|
40
|
-
</div>
|
|
41
|
-
</template>
|
|
42
|
-
|
|
43
|
-
<style scoped>
|
|
44
|
-
.messages-container {
|
|
45
|
-
background: #ffffff;
|
|
46
|
-
border: none;
|
|
47
|
-
box-shadow: none;
|
|
48
|
-
border-radius: 0.75rem;
|
|
49
|
-
padding: 1rem;
|
|
50
|
-
transition: all 0.3s ease;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
.dark .messages-container {
|
|
54
|
-
background: #0a0d14;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.messages-container:hover {
|
|
58
|
-
background: #fafafa;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
.dark .messages-container:hover {
|
|
62
|
-
background: #0d1117;
|
|
63
|
-
}
|
|
64
|
-
</style>
|
|
65
|
-
|
|
66
|
-
<script setup>
|
|
67
|
-
import { formatTime } from '../../utils/formatters';
|
|
68
|
-
import StatusBadge from '../common/StatusBadge.vue';
|
|
69
|
-
|
|
70
|
-
defineProps({
|
|
71
|
-
messages: Array,
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
defineEmits(['message-click']);
|
|
75
|
-
</script>
|
|
76
|
-
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div v-if="isOpen" class="fixed inset-0 z-50 overflow-y-auto">
|
|
3
|
-
<div class="flex items-center justify-center min-h-screen px-4">
|
|
4
|
-
<!-- Backdrop with blur -->
|
|
5
|
-
<div
|
|
6
|
-
class="fixed inset-0 bg-black bg-opacity-50 backdrop-blur-sm transition-opacity animate-fade-in"
|
|
7
|
-
@click="close"
|
|
8
|
-
></div>
|
|
9
|
-
|
|
10
|
-
<!-- Modal -->
|
|
11
|
-
<div class="relative bg-white dark:bg-slate-800 rounded-xl shadow-2xl max-w-md w-full p-6 animate-scale-in border border-gray-200 dark:border-gray-700">
|
|
12
|
-
<div class="flex items-center justify-between mb-4">
|
|
13
|
-
<h3 class="text-lg font-bold">Create New Queue</h3>
|
|
14
|
-
<button @click="close" class="text-gray-400 hover:text-gray-600">
|
|
15
|
-
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
16
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
17
|
-
</svg>
|
|
18
|
-
</button>
|
|
19
|
-
</div>
|
|
20
|
-
|
|
21
|
-
<form @submit.prevent="createQueue" class="space-y-4">
|
|
22
|
-
<div>
|
|
23
|
-
<label class="block text-sm font-medium mb-1">Queue Name *</label>
|
|
24
|
-
<input
|
|
25
|
-
v-model="form.queue"
|
|
26
|
-
type="text"
|
|
27
|
-
required
|
|
28
|
-
placeholder="my-queue"
|
|
29
|
-
class="input"
|
|
30
|
-
/>
|
|
31
|
-
</div>
|
|
32
|
-
|
|
33
|
-
<div>
|
|
34
|
-
<label class="block text-sm font-medium mb-1">Partition</label>
|
|
35
|
-
<input
|
|
36
|
-
v-model="form.partition"
|
|
37
|
-
type="text"
|
|
38
|
-
placeholder="Default"
|
|
39
|
-
class="input"
|
|
40
|
-
/>
|
|
41
|
-
</div>
|
|
42
|
-
|
|
43
|
-
<div class="grid grid-cols-2 gap-3">
|
|
44
|
-
<div>
|
|
45
|
-
<label class="block text-sm font-medium mb-1">TTL (seconds)</label>
|
|
46
|
-
<input
|
|
47
|
-
v-model.number="form.ttl"
|
|
48
|
-
type="number"
|
|
49
|
-
placeholder="3600"
|
|
50
|
-
class="input"
|
|
51
|
-
/>
|
|
52
|
-
</div>
|
|
53
|
-
|
|
54
|
-
<div>
|
|
55
|
-
<label class="block text-sm font-medium mb-1">Priority</label>
|
|
56
|
-
<input
|
|
57
|
-
v-model.number="form.priority"
|
|
58
|
-
type="number"
|
|
59
|
-
min="0"
|
|
60
|
-
max="100"
|
|
61
|
-
placeholder="0"
|
|
62
|
-
class="input"
|
|
63
|
-
/>
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
66
|
-
|
|
67
|
-
<div class="grid grid-cols-2 gap-3">
|
|
68
|
-
<div>
|
|
69
|
-
<label class="block text-sm font-medium mb-1">Namespace</label>
|
|
70
|
-
<input
|
|
71
|
-
v-model="form.namespace"
|
|
72
|
-
type="text"
|
|
73
|
-
placeholder="optional"
|
|
74
|
-
class="input"
|
|
75
|
-
/>
|
|
76
|
-
</div>
|
|
77
|
-
|
|
78
|
-
<div>
|
|
79
|
-
<label class="block text-sm font-medium mb-1">Task</label>
|
|
80
|
-
<input
|
|
81
|
-
v-model="form.task"
|
|
82
|
-
type="text"
|
|
83
|
-
placeholder="optional"
|
|
84
|
-
class="input"
|
|
85
|
-
/>
|
|
86
|
-
</div>
|
|
87
|
-
</div>
|
|
88
|
-
|
|
89
|
-
<div>
|
|
90
|
-
<label class="block text-sm font-medium mb-1">Max Queue Size</label>
|
|
91
|
-
<input
|
|
92
|
-
v-model.number="form.maxQueueSize"
|
|
93
|
-
type="number"
|
|
94
|
-
placeholder="10000"
|
|
95
|
-
class="input"
|
|
96
|
-
/>
|
|
97
|
-
</div>
|
|
98
|
-
|
|
99
|
-
<div v-if="error" class="text-sm text-red-600 dark:text-red-400">
|
|
100
|
-
{{ error }}
|
|
101
|
-
</div>
|
|
102
|
-
|
|
103
|
-
<div class="flex gap-3 pt-2">
|
|
104
|
-
<button type="submit" :disabled="loading" class="btn btn-primary flex-1">
|
|
105
|
-
<span v-if="loading">Creating...</span>
|
|
106
|
-
<span v-else>Create Queue</span>
|
|
107
|
-
</button>
|
|
108
|
-
<button type="button" @click="close" class="btn btn-secondary">
|
|
109
|
-
Cancel
|
|
110
|
-
</button>
|
|
111
|
-
</div>
|
|
112
|
-
</form>
|
|
113
|
-
</div>
|
|
114
|
-
</div>
|
|
115
|
-
</div>
|
|
116
|
-
</template>
|
|
117
|
-
|
|
118
|
-
<script setup>
|
|
119
|
-
import { ref, watch } from 'vue';
|
|
120
|
-
import { queuesApi } from '../../api/queues';
|
|
121
|
-
|
|
122
|
-
const props = defineProps({
|
|
123
|
-
isOpen: Boolean,
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
const emit = defineEmits(['close', 'created']);
|
|
127
|
-
|
|
128
|
-
const loading = ref(false);
|
|
129
|
-
const error = ref(null);
|
|
130
|
-
const form = ref({
|
|
131
|
-
queue: '',
|
|
132
|
-
partition: 'Default',
|
|
133
|
-
ttl: 3600,
|
|
134
|
-
priority: 0,
|
|
135
|
-
namespace: '',
|
|
136
|
-
task: '',
|
|
137
|
-
maxQueueSize: 10000,
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
watch(() => props.isOpen, (newVal) => {
|
|
141
|
-
if (newVal) {
|
|
142
|
-
resetForm();
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
function resetForm() {
|
|
147
|
-
form.value = {
|
|
148
|
-
queue: '',
|
|
149
|
-
partition: 'Default',
|
|
150
|
-
ttl: 3600,
|
|
151
|
-
priority: 0,
|
|
152
|
-
namespace: '',
|
|
153
|
-
task: '',
|
|
154
|
-
maxQueueSize: 10000,
|
|
155
|
-
};
|
|
156
|
-
error.value = null;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async function createQueue() {
|
|
160
|
-
loading.value = true;
|
|
161
|
-
error.value = null;
|
|
162
|
-
|
|
163
|
-
try {
|
|
164
|
-
const payload = {
|
|
165
|
-
queue: form.value.queue,
|
|
166
|
-
partition: form.value.partition || 'Default',
|
|
167
|
-
ttl: form.value.ttl,
|
|
168
|
-
priority: form.value.priority,
|
|
169
|
-
maxQueueSize: form.value.maxQueueSize,
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
if (form.value.namespace) {
|
|
173
|
-
payload.namespace = form.value.namespace;
|
|
174
|
-
}
|
|
175
|
-
if (form.value.task) {
|
|
176
|
-
payload.task = form.value.task;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
await queuesApi.configureQueue(payload);
|
|
180
|
-
emit('created');
|
|
181
|
-
close();
|
|
182
|
-
} catch (err) {
|
|
183
|
-
error.value = err.response?.data?.error || err.message;
|
|
184
|
-
} finally {
|
|
185
|
-
loading.value = false;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function close() {
|
|
190
|
-
emit('close');
|
|
191
|
-
}
|
|
192
|
-
</script>
|
|
193
|
-
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="filter-container">
|
|
3
|
-
<div class="flex flex-col sm:flex-row gap-3">
|
|
4
|
-
<!-- Search -->
|
|
5
|
-
<div class="flex-1">
|
|
6
|
-
<input
|
|
7
|
-
v-model="localSearch"
|
|
8
|
-
@input="onSearchChange"
|
|
9
|
-
type="text"
|
|
10
|
-
placeholder="Search queues..."
|
|
11
|
-
class="input"
|
|
12
|
-
/>
|
|
13
|
-
</div>
|
|
14
|
-
|
|
15
|
-
<!-- Namespace Filter -->
|
|
16
|
-
<select
|
|
17
|
-
v-model="localNamespace"
|
|
18
|
-
@change="onNamespaceChange"
|
|
19
|
-
class="input sm:w-48"
|
|
20
|
-
>
|
|
21
|
-
<option value="">All Namespaces</option>
|
|
22
|
-
<option v-for="ns in namespaces" :key="ns.namespace" :value="ns.namespace">
|
|
23
|
-
{{ ns.namespace }} ({{ ns.queues }})
|
|
24
|
-
</option>
|
|
25
|
-
</select>
|
|
26
|
-
|
|
27
|
-
<!-- Clear Filters -->
|
|
28
|
-
<button
|
|
29
|
-
v-if="localSearch || localNamespace"
|
|
30
|
-
@click="clearFilters"
|
|
31
|
-
class="btn btn-secondary whitespace-nowrap"
|
|
32
|
-
>
|
|
33
|
-
Clear Filters
|
|
34
|
-
</button>
|
|
35
|
-
</div>
|
|
36
|
-
</div>
|
|
37
|
-
</template>
|
|
38
|
-
|
|
39
|
-
<style scoped>
|
|
40
|
-
.filter-container {
|
|
41
|
-
background: #ffffff;
|
|
42
|
-
border: none;
|
|
43
|
-
box-shadow: none;
|
|
44
|
-
border-radius: 0.75rem;
|
|
45
|
-
padding: 1rem;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.dark .filter-container {
|
|
49
|
-
background: #0a0d14;
|
|
50
|
-
}
|
|
51
|
-
</style>
|
|
52
|
-
|
|
53
|
-
<script setup>
|
|
54
|
-
import { ref, watch } from 'vue';
|
|
55
|
-
|
|
56
|
-
const props = defineProps({
|
|
57
|
-
search: String,
|
|
58
|
-
namespace: String,
|
|
59
|
-
namespaces: Array,
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
const emit = defineEmits(['update:search', 'update:namespace']);
|
|
63
|
-
|
|
64
|
-
const localSearch = ref(props.search || '');
|
|
65
|
-
const localNamespace = ref(props.namespace || '');
|
|
66
|
-
|
|
67
|
-
watch(() => props.search, (newVal) => {
|
|
68
|
-
localSearch.value = newVal || '';
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
watch(() => props.namespace, (newVal) => {
|
|
72
|
-
localNamespace.value = newVal || '';
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
function onSearchChange() {
|
|
76
|
-
emit('update:search', localSearch.value);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function onNamespaceChange() {
|
|
80
|
-
emit('update:namespace', localNamespace.value);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function clearFilters() {
|
|
84
|
-
localSearch.value = '';
|
|
85
|
-
localNamespace.value = '';
|
|
86
|
-
emit('update:search', '');
|
|
87
|
-
emit('update:namespace', '');
|
|
88
|
-
}
|
|
89
|
-
</script>
|
|
90
|
-
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { ref } from 'vue';
|
|
2
|
-
|
|
3
|
-
export function useApi(apiCall) {
|
|
4
|
-
const data = ref(null);
|
|
5
|
-
const error = ref(null);
|
|
6
|
-
const loading = ref(false);
|
|
7
|
-
|
|
8
|
-
const execute = async (...params) => {
|
|
9
|
-
loading.value = true;
|
|
10
|
-
error.value = null;
|
|
11
|
-
|
|
12
|
-
try {
|
|
13
|
-
const response = await apiCall(...params);
|
|
14
|
-
data.value = response.data;
|
|
15
|
-
return response.data;
|
|
16
|
-
} catch (err) {
|
|
17
|
-
error.value = err.response?.data?.error || err.message || 'An error occurred';
|
|
18
|
-
throw err;
|
|
19
|
-
} finally {
|
|
20
|
-
loading.value = false;
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const refresh = () => execute();
|
|
25
|
-
|
|
26
|
-
return {
|
|
27
|
-
data,
|
|
28
|
-
error,
|
|
29
|
-
loading,
|
|
30
|
-
execute,
|
|
31
|
-
refresh,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { ref, watch, onMounted } from 'vue';
|
|
2
|
-
|
|
3
|
-
export function useTheme() {
|
|
4
|
-
const isDark = ref(false);
|
|
5
|
-
|
|
6
|
-
const toggleTheme = () => {
|
|
7
|
-
isDark.value = !isDark.value;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
// Watch for changes and update localStorage and document class
|
|
11
|
-
watch(isDark, (newValue) => {
|
|
12
|
-
if (newValue) {
|
|
13
|
-
document.documentElement.classList.add('dark');
|
|
14
|
-
localStorage.setItem('theme', 'dark');
|
|
15
|
-
} else {
|
|
16
|
-
document.documentElement.classList.remove('dark');
|
|
17
|
-
localStorage.setItem('theme', 'light');
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
// Initialize theme from localStorage or system preference
|
|
22
|
-
onMounted(() => {
|
|
23
|
-
const savedTheme = localStorage.getItem('theme');
|
|
24
|
-
if (savedTheme) {
|
|
25
|
-
isDark.value = savedTheme === 'dark';
|
|
26
|
-
} else {
|
|
27
|
-
isDark.value = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
isDark,
|
|
33
|
-
toggleTheme,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
package/webapp/src/main.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { createRouter, createWebHistory } from 'vue-router';
|
|
2
|
-
|
|
3
|
-
const routes = [
|
|
4
|
-
{
|
|
5
|
-
path: '/',
|
|
6
|
-
name: 'Dashboard',
|
|
7
|
-
component: () => import('../views/Dashboard.vue'),
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
path: '/queues',
|
|
11
|
-
name: 'Queues',
|
|
12
|
-
component: () => import('../views/Queues.vue'),
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
path: '/queues/:queueName',
|
|
16
|
-
name: 'QueueDetail',
|
|
17
|
-
component: () => import('../views/QueueDetail.vue'),
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
path: '/consumer-groups',
|
|
21
|
-
name: 'ConsumerGroups',
|
|
22
|
-
component: () => import('../views/ConsumerGroups.vue'),
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
path: '/messages',
|
|
26
|
-
name: 'Messages',
|
|
27
|
-
component: () => import('../views/Messages.vue'),
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
path: '/analytics',
|
|
31
|
-
name: 'Analytics',
|
|
32
|
-
component: () => import('../views/Analytics.vue'),
|
|
33
|
-
},
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
const router = createRouter({
|
|
37
|
-
history: createWebHistory(),
|
|
38
|
-
routes,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
export default router;
|
|
42
|
-
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
// Centralized color configuration for the entire app
|
|
2
|
-
// Change these values to update the color scheme everywhere
|
|
3
|
-
|
|
4
|
-
export const colors = {
|
|
5
|
-
// Primary brand colors (used for charts, accents, etc.)
|
|
6
|
-
primary: {
|
|
7
|
-
name: 'Rose',
|
|
8
|
-
hex: '#f43f5e', // rose-500
|
|
9
|
-
rgb: 'rgb(244, 63, 94)',
|
|
10
|
-
rgba: (opacity) => `rgba(244, 63, 94, ${opacity})`,
|
|
11
|
-
},
|
|
12
|
-
secondary: {
|
|
13
|
-
name: 'Purple',
|
|
14
|
-
hex: '#a855f7', // purple-500
|
|
15
|
-
rgb: 'rgb(168, 85, 247)',
|
|
16
|
-
rgba: (opacity) => `rgba(168, 85, 247, ${opacity})`,
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
// Status colors
|
|
20
|
-
success: {
|
|
21
|
-
hex: '#10b981', // green-500
|
|
22
|
-
rgb: 'rgb(16, 185, 129)',
|
|
23
|
-
rgba: (opacity) => `rgba(16, 185, 129, ${opacity})`,
|
|
24
|
-
},
|
|
25
|
-
warning: {
|
|
26
|
-
hex: '#f59e0b', // yellow-500
|
|
27
|
-
rgb: 'rgb(245, 158, 11)',
|
|
28
|
-
rgba: (opacity) => `rgba(245, 158, 11, ${opacity})`,
|
|
29
|
-
},
|
|
30
|
-
danger: {
|
|
31
|
-
hex: '#ef4444', // red-500
|
|
32
|
-
rgb: 'rgb(239, 68, 68)',
|
|
33
|
-
rgba: (opacity) => `rgba(239, 68, 68, ${opacity})`,
|
|
34
|
-
},
|
|
35
|
-
info: {
|
|
36
|
-
hex: '#3b82f6', // blue-500
|
|
37
|
-
rgb: 'rgb(59, 130, 246)',
|
|
38
|
-
rgba: (opacity) => `rgba(59, 130, 246, ${opacity})`,
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
// Chart colors (for consistency)
|
|
42
|
-
charts: {
|
|
43
|
-
// Main data series
|
|
44
|
-
ingested: {
|
|
45
|
-
border: 'rgb(244, 63, 94)', // Rose
|
|
46
|
-
background: 'rgba(244, 63, 94, 0.1)',
|
|
47
|
-
},
|
|
48
|
-
processed: {
|
|
49
|
-
border: 'rgb(168, 85, 247)', // Purple
|
|
50
|
-
background: 'rgba(168, 85, 247, 0.1)',
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
// Status distribution
|
|
54
|
-
pending: {
|
|
55
|
-
bg: 'rgba(245, 158, 11, 0.8)', // Yellow
|
|
56
|
-
border: 'rgb(245, 158, 11)',
|
|
57
|
-
},
|
|
58
|
-
processing: {
|
|
59
|
-
bg: 'rgba(59, 130, 246, 0.8)', // Blue
|
|
60
|
-
border: 'rgb(59, 130, 246)',
|
|
61
|
-
},
|
|
62
|
-
completed: {
|
|
63
|
-
bg: 'rgba(16, 185, 129, 0.8)', // Green
|
|
64
|
-
border: 'rgb(16, 185, 129)',
|
|
65
|
-
},
|
|
66
|
-
failed: {
|
|
67
|
-
bg: 'rgba(239, 68, 68, 0.8)', // Red
|
|
68
|
-
border: 'rgb(239, 68, 68)',
|
|
69
|
-
},
|
|
70
|
-
deadLetter: {
|
|
71
|
-
bg: 'rgba(156, 163, 175, 0.8)', // Gray
|
|
72
|
-
border: 'rgb(156, 163, 175)',
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// Helper to get all chart colors for status distribution
|
|
78
|
-
export function getStatusColors() {
|
|
79
|
-
return {
|
|
80
|
-
backgrounds: [
|
|
81
|
-
colors.charts.pending.bg,
|
|
82
|
-
colors.charts.processing.bg,
|
|
83
|
-
colors.charts.completed.bg,
|
|
84
|
-
colors.charts.failed.bg,
|
|
85
|
-
colors.charts.deadLetter.bg,
|
|
86
|
-
],
|
|
87
|
-
borders: [
|
|
88
|
-
colors.charts.pending.border,
|
|
89
|
-
colors.charts.processing.border,
|
|
90
|
-
colors.charts.completed.border,
|
|
91
|
-
colors.charts.failed.border,
|
|
92
|
-
colors.charts.deadLetter.border,
|
|
93
|
-
],
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
export function formatNumber(num) {
|
|
2
|
-
if (num >= 1000000) {
|
|
3
|
-
return (num / 1000000).toFixed(1) + 'M';
|
|
4
|
-
}
|
|
5
|
-
if (num >= 1000) {
|
|
6
|
-
return (num / 1000).toFixed(1) + 'K';
|
|
7
|
-
}
|
|
8
|
-
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function formatBytes(bytes) {
|
|
12
|
-
if (bytes === 0) return '0 B';
|
|
13
|
-
const k = 1024;
|
|
14
|
-
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
15
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
16
|
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function formatDuration(ms) {
|
|
20
|
-
if (ms < 1000) return `${ms}ms`;
|
|
21
|
-
|
|
22
|
-
const seconds = Math.floor(ms / 1000);
|
|
23
|
-
const minutes = Math.floor(seconds / 60);
|
|
24
|
-
const hours = Math.floor(minutes / 60);
|
|
25
|
-
const days = Math.floor(hours / 24);
|
|
26
|
-
|
|
27
|
-
if (days > 0) return `${days}d ${hours % 24}h`;
|
|
28
|
-
if (hours > 0) return `${hours}h ${minutes % 60}m`;
|
|
29
|
-
if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
|
|
30
|
-
return `${seconds}s`;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function formatDate(date) {
|
|
34
|
-
return new Date(date).toLocaleString('en-US', {
|
|
35
|
-
month: 'short',
|
|
36
|
-
day: 'numeric',
|
|
37
|
-
hour: '2-digit',
|
|
38
|
-
minute: '2-digit',
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function formatTime(date) {
|
|
43
|
-
return new Date(date).toLocaleTimeString('en-US', {
|
|
44
|
-
hour: '2-digit',
|
|
45
|
-
minute: '2-digit',
|
|
46
|
-
second: '2-digit',
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|