All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m4s
- Agregar botón "Crear Webhook de Debug" en WebhookReceiverSection - Detectar si ya existe un webhook apuntando al receptor de debug - Permitir eliminar el webhook de debug - Incluir todos los eventos disponibles al crear el webhook - También incluye mejoras previas de manejo de media y mensajes
274 lines
7.7 KiB
Vue
274 lines
7.7 KiB
Vue
<template>
|
|
<div class="space-y-6 p-4">
|
|
<!-- Setup Info -->
|
|
<div class="space-y-4">
|
|
<h3 class="text-lg font-medium text-[var(--wa-text)]">Receptor de Webhooks</h3>
|
|
<p class="text-sm text-[var(--wa-text-muted)]">
|
|
Configura un webhook con la siguiente URL para recibir eventos aqui:
|
|
</p>
|
|
<div class="flex items-center gap-2">
|
|
<code class="flex-1 p-3 rounded bg-gray-900 text-green-400 text-sm font-mono">
|
|
{{ receiverUrl }}
|
|
</code>
|
|
<button
|
|
@click="copyToClipboard(receiverUrl)"
|
|
class="px-3 py-3 rounded bg-gray-700 hover:bg-gray-600 text-gray-300"
|
|
title="Copiar URL"
|
|
>
|
|
<UIcon name="i-lucide-copy" class="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Auto-create webhook button -->
|
|
<div class="flex items-center gap-3">
|
|
<UButton
|
|
v-if="!debugWebhookExists"
|
|
:loading="creatingWebhook"
|
|
variant="soft"
|
|
color="primary"
|
|
@click="createDebugWebhook"
|
|
>
|
|
<UIcon name="i-lucide-zap" class="w-4 h-4 mr-2" />
|
|
Crear Webhook de Debug
|
|
</UButton>
|
|
<div v-else class="flex items-center gap-2 text-green-400">
|
|
<UIcon name="i-lucide-check-circle" class="w-4 h-4" />
|
|
<span class="text-sm">Webhook de debug activo</span>
|
|
<UButton
|
|
size="xs"
|
|
variant="ghost"
|
|
color="error"
|
|
@click="deleteDebugWebhook"
|
|
>
|
|
Eliminar
|
|
</UButton>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="border-[var(--wa-border)]" />
|
|
|
|
<!-- Controls -->
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center gap-2">
|
|
<UButton
|
|
:loading="loading"
|
|
variant="soft"
|
|
@click="fetchEvents"
|
|
>
|
|
<UIcon name="i-lucide-refresh-cw" class="w-4 h-4 mr-2" />
|
|
Actualizar
|
|
</UButton>
|
|
<UButton
|
|
variant="soft"
|
|
color="error"
|
|
:disabled="events.length === 0"
|
|
@click="clearEvents"
|
|
>
|
|
<UIcon name="i-lucide-trash-2" class="w-4 h-4 mr-2" />
|
|
Limpiar
|
|
</UButton>
|
|
</div>
|
|
<span class="text-sm text-[var(--wa-text-muted)]">
|
|
{{ events.length }} eventos
|
|
</span>
|
|
</div>
|
|
|
|
<!-- Events List -->
|
|
<div v-if="events.length === 0" class="text-center py-8">
|
|
<UIcon name="i-lucide-webhook" class="w-12 h-12 text-[var(--wa-text-muted)] mx-auto mb-3" />
|
|
<p class="text-[var(--wa-text-muted)]">No hay eventos recibidos</p>
|
|
<p class="text-sm text-[var(--wa-text-muted)] mt-1">
|
|
Configura un webhook apuntando a la URL de arriba
|
|
</p>
|
|
</div>
|
|
|
|
<div v-else class="space-y-3 max-h-[500px] overflow-y-auto">
|
|
<div
|
|
v-for="event in events"
|
|
:key="event.id"
|
|
class="p-3 rounded bg-gray-900 border border-gray-700"
|
|
>
|
|
<div class="flex items-center justify-between mb-2">
|
|
<div class="flex items-center gap-2">
|
|
<span class="px-2 py-1 rounded bg-blue-900/50 text-blue-400 text-xs font-medium">
|
|
{{ event.event }}
|
|
</span>
|
|
<span class="text-xs text-[var(--wa-text-muted)]">
|
|
{{ formatTime(event.receivedAt) }}
|
|
</span>
|
|
</div>
|
|
<button
|
|
@click="copyToClipboard(JSON.stringify(event, null, 2))"
|
|
class="px-2 py-1 rounded bg-gray-700 hover:bg-gray-600 text-gray-300"
|
|
title="Copiar evento"
|
|
>
|
|
<UIcon name="i-lucide-copy" class="w-3 h-3" />
|
|
</button>
|
|
</div>
|
|
<div class="relative">
|
|
<button
|
|
@click="toggleExpand(event.id)"
|
|
class="text-xs text-[var(--wa-text-muted)] hover:text-[var(--wa-text)] mb-1"
|
|
>
|
|
{{ expandedEvents.has(event.id) ? 'Colapsar' : 'Expandir' }} datos
|
|
</button>
|
|
<pre
|
|
v-if="expandedEvents.has(event.id)"
|
|
class="text-xs font-mono text-green-400 whitespace-pre-wrap overflow-x-auto max-h-60 mt-2"
|
|
>{{ JSON.stringify(event.data, null, 2) }}</pre>
|
|
<pre
|
|
v-else
|
|
class="text-xs font-mono text-green-400 truncate"
|
|
>{{ JSON.stringify(event.data) }}</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
interface WebhookEvent {
|
|
id: string
|
|
receivedAt: string
|
|
event: string
|
|
timestamp?: string
|
|
data: any
|
|
headers: Record<string, string | undefined>
|
|
}
|
|
|
|
interface Webhook {
|
|
id: string
|
|
name: string
|
|
url: string
|
|
events: string[]
|
|
}
|
|
|
|
const events = ref<WebhookEvent[]>([])
|
|
const loading = ref(false)
|
|
const expandedEvents = ref(new Set<string>())
|
|
const creatingWebhook = ref(false)
|
|
const debugWebhookId = ref<string | null>(null)
|
|
|
|
// All available event types
|
|
const allEventTypes = [
|
|
'message.received',
|
|
'message.sent',
|
|
'message.status',
|
|
'instance.connected',
|
|
'instance.disconnected',
|
|
'instance.status',
|
|
'instance.qr'
|
|
]
|
|
|
|
// Build receiver URL
|
|
const receiverUrl = computed(() => {
|
|
if (import.meta.client) {
|
|
return `${window.location.origin}/api/debug/webhook-receiver`
|
|
}
|
|
return '/api/debug/webhook-receiver'
|
|
})
|
|
|
|
const debugWebhookExists = computed(() => !!debugWebhookId.value)
|
|
|
|
// Check if debug webhook already exists
|
|
const checkDebugWebhook = async () => {
|
|
try {
|
|
const webhooks = await $fetch<Webhook[]>('/api/webhooks')
|
|
const debugWh = webhooks.find(w => w.url.includes('/api/debug/webhook-receiver'))
|
|
debugWebhookId.value = debugWh?.id || null
|
|
} catch (error) {
|
|
console.error('Error checking webhooks:', error)
|
|
}
|
|
}
|
|
|
|
// Create debug webhook
|
|
const createDebugWebhook = async () => {
|
|
creatingWebhook.value = true
|
|
try {
|
|
const result = await $fetch<{ id: string }>('/api/webhooks', {
|
|
method: 'POST',
|
|
body: {
|
|
name: 'Debug Receiver (Auto)',
|
|
url: receiverUrl.value,
|
|
events: allEventTypes,
|
|
instanceId: null // All instances
|
|
}
|
|
})
|
|
debugWebhookId.value = result.id
|
|
} catch (error) {
|
|
console.error('Error creating debug webhook:', error)
|
|
} finally {
|
|
creatingWebhook.value = false
|
|
}
|
|
}
|
|
|
|
// Delete debug webhook
|
|
const deleteDebugWebhook = async () => {
|
|
if (!debugWebhookId.value) return
|
|
try {
|
|
await $fetch(`/api/webhooks/${debugWebhookId.value}`, { method: 'DELETE' })
|
|
debugWebhookId.value = null
|
|
} catch (error) {
|
|
console.error('Error deleting webhook:', error)
|
|
}
|
|
}
|
|
|
|
const fetchEvents = async () => {
|
|
loading.value = true
|
|
try {
|
|
const result = await $fetch<{ events: WebhookEvent[] }>('/api/debug/webhook-events')
|
|
events.value = result.events
|
|
} catch (error) {
|
|
console.error('Error fetching events:', error)
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
const clearEvents = async () => {
|
|
try {
|
|
await $fetch('/api/debug/webhook-events', { method: 'DELETE' })
|
|
events.value = []
|
|
expandedEvents.value.clear()
|
|
} catch (error) {
|
|
console.error('Error clearing events:', error)
|
|
}
|
|
}
|
|
|
|
const toggleExpand = (id: string) => {
|
|
if (expandedEvents.value.has(id)) {
|
|
expandedEvents.value.delete(id)
|
|
} else {
|
|
expandedEvents.value.add(id)
|
|
}
|
|
}
|
|
|
|
const formatTime = (dateStr: string) => {
|
|
const date = new Date(dateStr)
|
|
return date.toLocaleTimeString('es-AR', {
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit'
|
|
})
|
|
}
|
|
|
|
const copyToClipboard = async (text: string) => {
|
|
try {
|
|
await navigator.clipboard.writeText(text)
|
|
} catch (err) {
|
|
console.error('Failed to copy:', err)
|
|
}
|
|
}
|
|
|
|
// Fetch events on mount and set up polling
|
|
onMounted(() => {
|
|
checkDebugWebhook()
|
|
fetchEvents()
|
|
|
|
// Poll every 5 seconds
|
|
const interval = setInterval(fetchEvents, 5000)
|
|
onUnmounted(() => clearInterval(interval))
|
|
})
|
|
</script>
|