feat: initial commit — UniFi snap-in package
Full UniFi dashboard snap-in including: - WiFi/client/device stats with time-series snapshots - Client Dashboard with traffic, satisfaction, signal, download charts - Webhook alerting with debounced offline/online detection - AP snapshot collection, client snapshot collection - Device classification (type and OS) from OUI/hostname heuristics - Webhook cooldown, templates, and multi-platform delivery Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
89
src/Http/Controllers/WebhookController.php
Normal file
89
src/Http/Controllers/WebhookController.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace Dashboard\Unifi\Http\Controllers;
|
||||
|
||||
use Dashboard\Unifi\Models\WebhookConfig;
|
||||
use Dashboard\Unifi\Services\WebhookCheckService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class WebhookController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return Inertia::render('Unifi/Webhooks', [
|
||||
'webhooks' => WebhookConfig::latest()->get(),
|
||||
'recentLogs' => \Dashboard\Unifi\Models\WebhookLog::with('config:id,name')
|
||||
->latest('fired_at')->take(50)->get(),
|
||||
'eventTypes' => WebhookCheckService::EVENTS,
|
||||
'templateVars' => WebhookCheckService::TEMPLATE_VARS,
|
||||
'defaultTemplates' => WebhookCheckService::DEFAULT_TEMPLATES,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'url' => 'required|url|max:500',
|
||||
'secret' => 'nullable|string|max:255',
|
||||
'is_active' => 'boolean',
|
||||
'events' => 'required|array|min:1',
|
||||
'events.*' => 'string|in:' . implode(',', array_keys(WebhookCheckService::EVENTS)),
|
||||
'thresholds' => 'nullable|array',
|
||||
'device_filter' => 'nullable|array',
|
||||
'tracked_clients' => 'nullable|array',
|
||||
'templates' => 'nullable|array',
|
||||
'cooldown_minutes' => 'integer|min:1|max:1440',
|
||||
]);
|
||||
WebhookConfig::create($data);
|
||||
return back()->with('success', 'Webhook created.');
|
||||
}
|
||||
|
||||
public function update(Request $request, WebhookConfig $webhook)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'url' => 'required|url|max:500',
|
||||
'secret' => 'nullable|string|max:255',
|
||||
'is_active' => 'boolean',
|
||||
'events' => 'required|array|min:1',
|
||||
'events.*' => 'string|in:' . implode(',', array_keys(WebhookCheckService::EVENTS)),
|
||||
'thresholds' => 'nullable|array',
|
||||
'device_filter' => 'nullable|array',
|
||||
'tracked_clients' => 'nullable|array',
|
||||
'templates' => 'nullable|array',
|
||||
'cooldown_minutes' => 'integer|min:1|max:1440',
|
||||
]);
|
||||
$webhook->update($data);
|
||||
return back()->with('success', 'Webhook updated.');
|
||||
}
|
||||
|
||||
public function destroy(WebhookConfig $webhook)
|
||||
{
|
||||
$webhook->delete();
|
||||
return back()->with('success', 'Webhook deleted.');
|
||||
}
|
||||
|
||||
public function test(WebhookConfig $webhook)
|
||||
{
|
||||
$payload = [
|
||||
'event' => 'test',
|
||||
'timestamp' => now()->toIso8601String(),
|
||||
'data' => ['message' => 'This is a test webhook from ' . config('app.name')],
|
||||
];
|
||||
|
||||
$headers = ['Content-Type' => 'application/json'];
|
||||
if ($webhook->secret) {
|
||||
$headers['X-Webhook-Signature'] = hash_hmac('sha256', json_encode($payload), $webhook->secret);
|
||||
}
|
||||
|
||||
try {
|
||||
$response = \Illuminate\Support\Facades\Http::withHeaders($headers)->timeout(10)->post($webhook->url, $payload);
|
||||
return response()->json(['ok' => true, 'status' => $response->status()]);
|
||||
} catch (\Throwable $e) {
|
||||
return response()->json(['ok' => false, 'error' => $e->getMessage()], 422);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user