Files
whatsappNucleo/app/components/debug/WebhookReceiverSection.vue
josedario87 80d0042c7e
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m4s
Feature: Agregar botón para crear webhook de debug automáticamente
- 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
2025-12-02 21:21:33 -06:00

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>