@innertia-solutions/innertia-nuxt 0.1.58 → 0.1.59
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.
|
@@ -10,6 +10,7 @@ const TABS = [
|
|
|
10
10
|
{ key: 'overview', label: 'Resumen' },
|
|
11
11
|
{ key: 'database', label: 'Base de datos' },
|
|
12
12
|
{ key: 'errors', label: 'Errores' },
|
|
13
|
+
{ key: 'queues', label: 'Colas' },
|
|
13
14
|
{ key: 'logs', label: 'Logs' },
|
|
14
15
|
{ key: 'files', label: 'Archivos' },
|
|
15
16
|
]
|
|
@@ -34,6 +35,7 @@ onBeforeUnmount(() => rt.unsubscribe('observability'))
|
|
|
34
35
|
<div v-show="active === 'overview'"><Observability.Overview :touched-at="touchedAt" /></div>
|
|
35
36
|
<div v-show="active === 'database'"><Observability.Dashboard /></div>
|
|
36
37
|
<div v-show="active === 'errors'"><Observability.Errors :touched-at="touchedAt" /></div>
|
|
38
|
+
<div v-show="active === 'queues'"><Observability.Queues /></div>
|
|
37
39
|
<div v-show="active === 'logs'"><Observability.Logs channel="operational" /></div>
|
|
38
40
|
<div v-show="active === 'files'">
|
|
39
41
|
<p class="rounded-card border border-dashed border-card-line p-8 text-center text-sm text-muted-foreground">
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
// Colas & workers (v1): profundidad de cola + gestión de failed_jobs
|
|
3
|
+
// (reintentar/borrar por job + masivas). Refresca cada ~15s.
|
|
4
|
+
const api = useApi()
|
|
5
|
+
const toast = useToast()
|
|
6
|
+
const { confirm } = useModal()
|
|
7
|
+
const qc = useQueryClient()
|
|
8
|
+
const REFETCH = 15_000
|
|
9
|
+
|
|
10
|
+
const overview = useQuery({ queryKey: ['obs-queues', 'overview'], queryFn: () => api.get('platform/observability/queues/overview'), refetchInterval: REFETCH })
|
|
11
|
+
const failed = useQuery({ queryKey: ['obs-queues', 'failed'], queryFn: () => api.get('platform/observability/queues/failed'), refetchInterval: REFETCH })
|
|
12
|
+
|
|
13
|
+
function reload() { qc.invalidateQueries({ queryKey: ['obs-queues'] }) }
|
|
14
|
+
|
|
15
|
+
async function retry(id) {
|
|
16
|
+
try { await api.post(`platform/observability/queues/failed/${id}/retry`); toast.success('Job reintentado.'); reload() }
|
|
17
|
+
catch { toast.error('No se pudo reintentar.') }
|
|
18
|
+
}
|
|
19
|
+
async function remove(id) {
|
|
20
|
+
if (! await confirm({ severity: 'danger', title: 'Borrar job fallido', message: '¿Eliminar este job fallido?', confirmText: 'Borrar' })) return
|
|
21
|
+
try { await api.delete(`platform/observability/queues/failed/${id}`); toast.success('Borrado.'); reload() }
|
|
22
|
+
catch { toast.error('No se pudo borrar.') }
|
|
23
|
+
}
|
|
24
|
+
async function retryAll() {
|
|
25
|
+
if (! await confirm({ title: 'Reintentar todos', message: '¿Reintentar todos los jobs fallidos?', confirmText: 'Reintentar' })) return
|
|
26
|
+
try { await api.post('platform/observability/queues/failed/retry-all'); toast.success('Reintentados.'); reload() }
|
|
27
|
+
catch { toast.error('Error al reintentar.') }
|
|
28
|
+
}
|
|
29
|
+
async function flush() {
|
|
30
|
+
if (! await confirm({ severity: 'danger', title: 'Vaciar fallidos', message: '¿Eliminar TODOS los jobs fallidos? No se puede deshacer.', confirmText: 'Vaciar' })) return
|
|
31
|
+
try { await api.post('platform/observability/queues/failed/flush'); toast.success('Vaciado.'); reload() }
|
|
32
|
+
catch { toast.error('Error al vaciar.') }
|
|
33
|
+
}
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<template>
|
|
37
|
+
<div class="space-y-6">
|
|
38
|
+
<div class="grid grid-cols-2 gap-4 lg:grid-cols-3">
|
|
39
|
+
<Observability.MetricCard label="Pendientes" :value="overview.data.value?.depth?.total ?? '—'"
|
|
40
|
+
:sublabel="`${overview.data.value?.depth?.queues?.length ?? 0} colas`" />
|
|
41
|
+
<Observability.MetricCard label="Fallidos" :value="overview.data.value?.failed ?? 0"
|
|
42
|
+
:tone="(overview.data.value?.failed ?? 0) > 0 ? 'bad' : 'good'" />
|
|
43
|
+
<Observability.MetricCard label="Driver de cola" :value="overview.data.value?.depth?.driver ?? '—'" />
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<section>
|
|
47
|
+
<h3 class="mb-2 text-sm font-semibold text-foreground">Profundidad por cola</h3>
|
|
48
|
+
<div v-if="overview.data.value?.depth && !overview.data.value.depth.available"
|
|
49
|
+
class="rounded-card border border-amber-200 bg-amber-50 p-3 text-xs text-amber-700 dark:border-amber-800/50 dark:bg-amber-900/20 dark:text-amber-400">
|
|
50
|
+
{{ overview.data.value.depth.reason }}
|
|
51
|
+
</div>
|
|
52
|
+
<table v-else class="w-full text-sm">
|
|
53
|
+
<thead><tr class="text-left text-muted-foreground"><th class="py-1">Cola</th><th class="py-1">Pendientes</th></tr></thead>
|
|
54
|
+
<tbody>
|
|
55
|
+
<tr v-for="q in overview.data.value?.depth?.queues ?? []" :key="q.queue" class="border-t border-card-line">
|
|
56
|
+
<td class="py-1 font-mono text-xs">{{ q.queue }}</td><td class="py-1">{{ q.count }}</td>
|
|
57
|
+
</tr>
|
|
58
|
+
<tr v-if="!(overview.data.value?.depth?.queues?.length)"><td colspan="2" class="py-2 text-xs text-muted-foreground">Sin pendientes 🎉</td></tr>
|
|
59
|
+
</tbody>
|
|
60
|
+
</table>
|
|
61
|
+
</section>
|
|
62
|
+
|
|
63
|
+
<section>
|
|
64
|
+
<div class="mb-2 flex items-center justify-between gap-2">
|
|
65
|
+
<h3 class="text-sm font-semibold text-foreground">Jobs fallidos</h3>
|
|
66
|
+
<div v-if="failed.data.value?.jobs?.length" class="flex gap-2">
|
|
67
|
+
<button class="text-xs text-primary hover:underline" @click="retryAll">Reintentar todos</button>
|
|
68
|
+
<button class="text-xs text-red-500 hover:underline" @click="flush">Vaciar</button>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
<table class="w-full text-sm">
|
|
72
|
+
<thead><tr class="text-left text-muted-foreground">
|
|
73
|
+
<th class="py-1 pr-4">Job</th><th class="py-1 pr-4">Cola</th><th class="py-1 pr-4">Excepción</th><th class="py-1 pr-4">Fecha</th><th class="py-1"></th>
|
|
74
|
+
</tr></thead>
|
|
75
|
+
<tbody>
|
|
76
|
+
<tr v-for="j in failed.data.value?.jobs ?? []" :key="j.id" class="border-t border-card-line align-top">
|
|
77
|
+
<td class="py-1 pr-4 font-mono text-xs">{{ j.name }}</td>
|
|
78
|
+
<td class="py-1 pr-4 text-xs">{{ j.queue }}</td>
|
|
79
|
+
<td class="py-1 pr-4 text-xs text-muted-foreground truncate max-w-sm">{{ j.exception }}</td>
|
|
80
|
+
<td class="py-1 pr-4 text-xs whitespace-nowrap">{{ j.failed_at }}</td>
|
|
81
|
+
<td class="py-1 whitespace-nowrap">
|
|
82
|
+
<button class="text-xs text-primary hover:underline mr-2" @click="retry(j.id)">Reintentar</button>
|
|
83
|
+
<button class="text-xs text-red-500 hover:underline" @click="remove(j.id)">Borrar</button>
|
|
84
|
+
</td>
|
|
85
|
+
</tr>
|
|
86
|
+
<tr v-if="!(failed.data.value?.jobs?.length)"><td colspan="5" class="py-2 text-xs text-muted-foreground">Sin jobs fallidos 🎉</td></tr>
|
|
87
|
+
</tbody>
|
|
88
|
+
</table>
|
|
89
|
+
</section>
|
|
90
|
+
</div>
|
|
91
|
+
</template>
|