fix(webhooks): add missing columns; add pre-save URL test endpoint
* The model+validation referenced tracked_clients and templates columns but they were never in the unifi_webhook_configs migration. Any save attempt that included those keys 500'd with "Unknown column". Added an additive migration (idempotent) that adds both as nullable json columns. * New POST /settings/webhooks/test-url endpoint takes a url+secret in the body and fires the standard test payload. Lets operators validate their endpoint before saving the row — useful when first wiring up Google Chat, Slack, etc. v1.5.4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -67,6 +67,25 @@ class WebhookController extends Controller
|
||||
}
|
||||
|
||||
public function test(WebhookConfig $webhook)
|
||||
{
|
||||
return $this->fireTest($webhook->url, $webhook->secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test an arbitrary URL+secret before the webhook is saved. Lets the
|
||||
* operator validate their endpoint from the form without first
|
||||
* committing a row.
|
||||
*/
|
||||
public function testUrl(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'url' => 'required|url|max:500',
|
||||
'secret' => 'nullable|string|max:255',
|
||||
]);
|
||||
return $this->fireTest($data['url'], $data['secret'] ?? null);
|
||||
}
|
||||
|
||||
private function fireTest(string $url, ?string $secret)
|
||||
{
|
||||
$payload = [
|
||||
'event' => 'test',
|
||||
@@ -75,13 +94,17 @@ class WebhookController extends Controller
|
||||
];
|
||||
|
||||
$headers = ['Content-Type' => 'application/json'];
|
||||
if ($webhook->secret) {
|
||||
$headers['X-Webhook-Signature'] = hash_hmac('sha256', json_encode($payload), $webhook->secret);
|
||||
if ($secret) {
|
||||
$headers['X-Webhook-Signature'] = hash_hmac('sha256', json_encode($payload), $secret);
|
||||
}
|
||||
|
||||
try {
|
||||
$response = \Illuminate\Support\Facades\Http::withHeaders($headers)->timeout(10)->post($webhook->url, $payload);
|
||||
return response()->json(['ok' => true, 'status' => $response->status()]);
|
||||
$response = \Illuminate\Support\Facades\Http::withHeaders($headers)->timeout(10)->post($url, $payload);
|
||||
return response()->json([
|
||||
'ok' => $response->successful(),
|
||||
'status' => $response->status(),
|
||||
'body' => mb_substr((string) $response->body(), 0, 500),
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
return response()->json(['ok' => false, 'error' => $e->getMessage()], 422);
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ Route::middleware(['web', 'auth', 'app.access:unifi'])
|
||||
Route::put('/settings/webhooks/{webhook}', [WebhookController::class, 'update']) ->name('webhooks.update');
|
||||
Route::delete('/settings/webhooks/{webhook}', [WebhookController::class, 'destroy'])->name('webhooks.destroy');
|
||||
Route::post('/settings/webhooks/{webhook}/test', [WebhookController::class, 'test']) ->name('webhooks.test');
|
||||
Route::post('/settings/webhooks/test-url', [WebhookController::class, 'testUrl'])->name('webhooks.test-url');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user