agent/php/Mod/Api/Services/WebhookService.php
Snider 5385385314 feat(agent/api): RFC foundation — API keys, webhooks, rate limiting, docs split
Foundation slice for Mantis #844 php/Mod/Api RFC implementation:

* New php/Mod/Api/ package: Boot, Controllers, Documentation, Jobs,
  Middleware, Models, RateLimit, Routes, Services
* Models: ApiKey, WebhookEndpoint, WebhookDelivery
* WebhookService::dispatch() with DB::transaction + afterCommit
* DeliverWebhookJob with retry/backoff
* WebhookSignature with timing-safe verification + 5-minute tolerance +
  dual-secret rotation support
* Sliding-window rate limiter in RateLimit/RateLimitService.php
* AuthenticateApiKey middleware: hk_ prefix + Sanctum fallback
* DocsController / DocumentationController split
* 3 root migrations: api_keys, webhook_endpoints, webhook_deliveries
* Foundation tests under php/tests/Feature/Mod/Api/
* FOLLOWUP.md tracks remaining RFC scope

php -l clean across 21 PHP files. Pest unrunnable in sandbox (no vendor/).

Co-authored-by: Codex <noreply@openai.com>
Closes tasks.lthn.sh/view.php?id=844
2026-04-25 21:01:54 +01:00

49 lines
1.3 KiB
PHP

<?php
// SPDX-License-Identifier: EUPL-1.2
declare(strict_types=1);
namespace Core\Mod\Agentic\Mod\Api\Services;
use Core\Mod\Agentic\Mod\Api\Jobs\DeliverWebhookJob;
use Core\Mod\Agentic\Mod\Api\Models\WebhookDelivery;
use Core\Mod\Agentic\Mod\Api\Models\WebhookEndpoint;
use Illuminate\Support\Facades\DB;
class WebhookService
{
/**
* Dispatch an event to every active endpoint subscribed to the event type.
*
* Example:
* `dispatch(12, 'mcp.tool.executed', ['tool' => 'brain_recall'])`
*
* @return array<int, WebhookDelivery>
*/
public function dispatch(int $workspaceId, string $eventType, array $data): array
{
$endpoints = WebhookEndpoint::query()
->forWorkspace($workspaceId)
->active()
->forEvent($eventType)
->get();
if ($endpoints->isEmpty()) {
return [];
}
$deliveries = [];
DB::transaction(function () use ($data, $endpoints, $eventType, $workspaceId, &$deliveries): void {
foreach ($endpoints as $endpoint) {
$delivery = WebhookDelivery::createForEvent($endpoint, $eventType, $data, $workspaceId);
$deliveries[] = $delivery;
DeliverWebhookJob::dispatch($delivery)->afterCommit();
}
});
return $deliveries;
}
}