fixed n8n webhooks
This commit is contained in:
@@ -118,6 +118,10 @@ try {
|
||||
handleWebhookQueueStatus($db);
|
||||
break;
|
||||
|
||||
case 'webhook_queue_clear':
|
||||
handleWebhookQueueClear($db);
|
||||
break;
|
||||
|
||||
case 'update_sort_order':
|
||||
handleUpdateSortOrder($db);
|
||||
break;
|
||||
@@ -1173,7 +1177,7 @@ function handleWebhookSettingsGet($db) {
|
||||
$settings = [
|
||||
'webhook_url' => getSetting($db, 'n8n_webhook_url', ''),
|
||||
'webhook_enabled' => getSetting($db, 'n8n_webhook_enabled', '0') === '1',
|
||||
'webhook_delay_minutes' => intval(getSetting($db, 'n8n_webhook_delay_minutes', '3'))
|
||||
'webhook_delay_minutes' => intval(getSetting($db, 'n8n_webhook_delay_minutes', '0'))
|
||||
];
|
||||
|
||||
jsonResponse(['success' => true, 'data' => $settings]);
|
||||
@@ -1196,7 +1200,7 @@ function handleWebhookSettingsSave($db) {
|
||||
|
||||
$webhookUrl = trim($input['webhook_url'] ?? '');
|
||||
$webhookEnabled = !empty($input['webhook_enabled']) ? '1' : '0';
|
||||
$delayMinutes = max(1, min(60, intval($input['webhook_delay_minutes'] ?? 3)));
|
||||
$delayMinutes = max(0, min(60, intval($input['webhook_delay_minutes'] ?? 0)));
|
||||
|
||||
// Validate URL if provided
|
||||
if (!empty($webhookUrl) && !filter_var($webhookUrl, FILTER_VALIDATE_URL)) {
|
||||
@@ -1362,6 +1366,55 @@ function handleWebhookQueueStatus($db) {
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear webhook queue
|
||||
*/
|
||||
function handleWebhookQueueClear($db) {
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
jsonResponse(['error' => 'Method not allowed'], 405);
|
||||
}
|
||||
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
// Validate CSRF
|
||||
if (!validateCSRFToken($input['csrf_token'] ?? '')) {
|
||||
jsonResponse(['error' => 'Invalid CSRF token'], 403);
|
||||
}
|
||||
|
||||
$clearType = $input['clear_type'] ?? 'pending';
|
||||
|
||||
try {
|
||||
switch ($clearType) {
|
||||
case 'pending':
|
||||
$stmt = $db->prepare("DELETE FROM webhook_queue WHERE status IN ('pending', 'processing')");
|
||||
$stmt->execute();
|
||||
$message = 'Pending webhooks cleared';
|
||||
break;
|
||||
case 'failed':
|
||||
$stmt = $db->prepare("DELETE FROM webhook_queue WHERE status = 'failed'");
|
||||
$stmt->execute();
|
||||
$message = 'Failed webhooks cleared';
|
||||
break;
|
||||
case 'all':
|
||||
$stmt = $db->prepare("DELETE FROM webhook_queue");
|
||||
$stmt->execute();
|
||||
$message = 'All webhook history cleared';
|
||||
break;
|
||||
default:
|
||||
jsonResponse(['error' => 'Invalid clear type'], 400);
|
||||
}
|
||||
|
||||
$deletedCount = $stmt->rowCount();
|
||||
jsonResponse([
|
||||
'success' => true,
|
||||
'message' => $message,
|
||||
'deleted' => $deletedCount
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
jsonResponse(['error' => 'Failed to clear queue: ' . $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update sort order for entries
|
||||
*/
|
||||
|
||||
@@ -171,7 +171,13 @@ function queueWebhookNotification($db, $reason = 'manual', $entriesAffected = 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
$delayMinutes = intval(getSetting($db, 'n8n_webhook_delay_minutes', '3'));
|
||||
$delayMinutes = intval(getSetting($db, 'n8n_webhook_delay_minutes', '0'));
|
||||
|
||||
// If delay is 0, send immediately without queuing
|
||||
if ($delayMinutes <= 0) {
|
||||
return sendWebhookImmediately($db, $webhookUrl, $reason, $entriesAffected);
|
||||
}
|
||||
|
||||
$scheduledFor = date('Y-m-d H:i:s', strtotime("+{$delayMinutes} minutes"));
|
||||
|
||||
// Check if there's already a pending webhook scheduled
|
||||
@@ -198,7 +204,7 @@ function queueWebhookNotification($db, $reason = 'manual', $entriesAffected = 1)
|
||||
':reason' => $reason,
|
||||
':id' => $existing['id']
|
||||
]);
|
||||
return $existing['id'];
|
||||
$queueId = $existing['id'];
|
||||
} else {
|
||||
// Create new webhook queue entry
|
||||
$stmt = $db->prepare("
|
||||
@@ -210,8 +216,56 @@ function queueWebhookNotification($db, $reason = 'manual', $entriesAffected = 1)
|
||||
':entries' => $entriesAffected,
|
||||
':scheduled_for' => $scheduledFor
|
||||
]);
|
||||
return $db->lastInsertId();
|
||||
$queueId = $db->lastInsertId();
|
||||
}
|
||||
|
||||
// Also process any due webhooks opportunistically
|
||||
processWebhookQueue($db);
|
||||
|
||||
return $queueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send webhook immediately without queuing (for zero-delay mode)
|
||||
*/
|
||||
function sendWebhookImmediately($db, $webhookUrl, $reason, $entriesAffected) {
|
||||
// Log to queue for history purposes
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO webhook_queue (webhook_type, trigger_reason, entries_affected, scheduled_for, status)
|
||||
VALUES ('geofeed_update', :reason, :entries, NOW(), 'processing')
|
||||
");
|
||||
$stmt->execute([
|
||||
':reason' => $reason,
|
||||
':entries' => $entriesAffected
|
||||
]);
|
||||
$queueId = $db->lastInsertId();
|
||||
|
||||
// Send the webhook immediately
|
||||
$payload = [
|
||||
'event' => 'geofeed_update',
|
||||
'queue_id' => $queueId,
|
||||
'trigger_reason' => $reason,
|
||||
'entries_affected' => $entriesAffected,
|
||||
'timestamp' => date('c')
|
||||
];
|
||||
|
||||
$result = sendWebhook($webhookUrl, $payload);
|
||||
|
||||
// Update status
|
||||
$finalStatus = $result['success'] ? 'completed' : 'failed';
|
||||
$updateStmt = $db->prepare("
|
||||
UPDATE webhook_queue
|
||||
SET status = :status, processed_at = NOW(), response_code = :code, response_body = :body
|
||||
WHERE id = :id
|
||||
");
|
||||
$updateStmt->execute([
|
||||
':status' => $finalStatus,
|
||||
':code' => $result['http_code'],
|
||||
':body' => substr($result['response'] ?? '', 0, 1000),
|
||||
':id' => $queueId
|
||||
]);
|
||||
|
||||
return $queueId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -784,6 +784,36 @@ function renderWebhookQueueStatus(data) {
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
// Clear webhook queue
|
||||
async function clearWebhookQueue() {
|
||||
const clearType = document.getElementById('clearQueueType')?.value || 'pending';
|
||||
const typeLabels = {
|
||||
'pending': 'pending webhooks',
|
||||
'failed': 'failed webhooks',
|
||||
'all': 'all webhook history'
|
||||
};
|
||||
|
||||
if (!confirm(`Are you sure you want to clear ${typeLabels[clearType]}?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await api('webhook_queue_clear', {
|
||||
csrf_token: CSRF_TOKEN,
|
||||
clear_type: clearType
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
showToast(`${result.message} (${result.deleted} removed)`, 'success');
|
||||
loadWebhookQueueStatus();
|
||||
} else {
|
||||
showToast(result.error || 'Failed to clear queue', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showToast('Network error', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Get time until a future date
|
||||
function getTimeUntil(date) {
|
||||
const seconds = Math.floor((date - new Date()) / 1000);
|
||||
|
||||
@@ -279,13 +279,27 @@ require_once __DIR__ . '/includes/header.php';
|
||||
<div class="table-container" style="margin-top: 16px;">
|
||||
<div class="table-header">
|
||||
<h3 class="table-title">Webhook Queue</h3>
|
||||
<button class="btn btn-ghost btn-sm" onclick="loadWebhookQueueStatus()">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="23 4 23 10 17 10"/>
|
||||
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
|
||||
</svg>
|
||||
Refresh
|
||||
</button>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<button class="btn btn-ghost btn-sm" onclick="loadWebhookQueueStatus()">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="23 4 23 10 17 10"/>
|
||||
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
|
||||
</svg>
|
||||
Refresh
|
||||
</button>
|
||||
<select id="clearQueueType" class="form-select" style="width: auto; padding: 6px 8px; font-size: 12px;">
|
||||
<option value="pending">Pending</option>
|
||||
<option value="failed">Failed</option>
|
||||
<option value="all">All History</option>
|
||||
</select>
|
||||
<button class="btn btn-danger btn-sm" onclick="clearWebhookQueue()">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="3 6 5 6 21 6"/>
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||||
</svg>
|
||||
Clear
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="webhookQueueContainer" style="padding: 20px;">
|
||||
<div class="loading"><div class="spinner"></div></div>
|
||||
|
||||
Reference in New Issue
Block a user