php-tenant/src/Controllers/Api/EntitlementWebhookController.php

256 lines
7.6 KiB
PHP
Raw Normal View History

2026-01-26 21:08:59 +00:00
<?php
declare(strict_types=1);
2026-01-27 00:31:43 +00:00
namespace Core\Core\Tenant\Controllers\Api;
2026-01-26 21:08:59 +00:00
2026-01-27 00:31:43 +00:00
use Core\Core\Tenant\Models\EntitlementWebhook;
use Core\Core\Tenant\Models\EntitlementWebhookDelivery;
use Core\Core\Tenant\Models\Workspace;
use Core\Core\Tenant\Services\EntitlementWebhookService;
2026-01-26 21:08:59 +00:00
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Validation\Rule;
/**
* API controller for entitlement webhook management.
*
* Provides CRUD operations for webhooks and delivery history.
*/
class EntitlementWebhookController extends Controller
{
public function __construct(
protected EntitlementWebhookService $webhookService
) {}
/**
* List webhooks for the current workspace.
*/
public function index(Request $request): JsonResponse
{
$workspace = $this->resolveWorkspace($request);
$webhooks = EntitlementWebhook::query()
->forWorkspace($workspace)
->withCount('deliveries')
->latest()
->paginate($request->integer('per_page', 25));
return response()->json($webhooks);
}
/**
* Create a new webhook.
*/
public function store(Request $request): JsonResponse
{
$workspace = $this->resolveWorkspace($request);
$validated = $request->validate([
'name' => ['required', 'string', 'max:255'],
'url' => ['required', 'url', 'max:2048'],
'events' => ['required', 'array', 'min:1'],
'events.*' => ['string', Rule::in(EntitlementWebhook::EVENTS)],
'secret' => ['nullable', 'string', 'min:32'],
'metadata' => ['nullable', 'array'],
]);
$webhook = $this->webhookService->register(
workspace: $workspace,
name: $validated['name'],
url: $validated['url'],
events: $validated['events'],
secret: $validated['secret'] ?? null,
metadata: $validated['metadata'] ?? []
);
return response()->json([
'message' => __('Webhook created successfully'),
'webhook' => $webhook,
'secret' => $webhook->secret, // Return secret on creation only
], 201);
}
/**
* Get a specific webhook.
*/
public function show(Request $request, EntitlementWebhook $webhook): JsonResponse
{
$this->authorizeWebhook($request, $webhook);
$webhook->loadCount('deliveries');
$webhook->load(['deliveries' => fn ($q) => $q->latest('created_at')->limit(10)]);
return response()->json([
'webhook' => $webhook,
'available_events' => $this->webhookService->getAvailableEvents(),
]);
}
/**
* Update a webhook.
*/
public function update(Request $request, EntitlementWebhook $webhook): JsonResponse
{
$this->authorizeWebhook($request, $webhook);
$validated = $request->validate([
'name' => ['sometimes', 'string', 'max:255'],
'url' => ['sometimes', 'url', 'max:2048'],
'events' => ['sometimes', 'array', 'min:1'],
'events.*' => ['string', Rule::in(EntitlementWebhook::EVENTS)],
'is_active' => ['sometimes', 'boolean'],
'max_attempts' => ['sometimes', 'integer', 'min:1', 'max:10'],
'metadata' => ['sometimes', 'array'],
]);
$webhook = $this->webhookService->update($webhook, $validated);
return response()->json([
'message' => __('Webhook updated successfully'),
'webhook' => $webhook,
]);
}
/**
* Delete a webhook.
*/
public function destroy(Request $request, EntitlementWebhook $webhook): JsonResponse
{
$this->authorizeWebhook($request, $webhook);
$this->webhookService->unregister($webhook);
return response()->json([
'message' => __('Webhook deleted successfully'),
]);
}
/**
* Regenerate webhook secret.
*/
public function regenerateSecret(Request $request, EntitlementWebhook $webhook): JsonResponse
{
$this->authorizeWebhook($request, $webhook);
$secret = $webhook->regenerateSecret();
return response()->json([
'message' => __('Secret regenerated successfully'),
'secret' => $secret,
]);
}
/**
* Send a test webhook.
*/
public function test(Request $request, EntitlementWebhook $webhook): JsonResponse
{
$this->authorizeWebhook($request, $webhook);
$delivery = $this->webhookService->testWebhook($webhook);
return response()->json([
'message' => $delivery->isSucceeded()
? __('Test webhook sent successfully')
: __('Test webhook failed'),
'delivery' => $delivery,
]);
}
/**
* Reset circuit breaker for a webhook.
*/
public function resetCircuitBreaker(Request $request, EntitlementWebhook $webhook): JsonResponse
{
$this->authorizeWebhook($request, $webhook);
$this->webhookService->resetCircuitBreaker($webhook);
return response()->json([
'message' => __('Webhook re-enabled successfully'),
'webhook' => $webhook->refresh(),
]);
}
/**
* Get delivery history for a webhook.
*/
public function deliveries(Request $request, EntitlementWebhook $webhook): JsonResponse
{
$this->authorizeWebhook($request, $webhook);
$deliveries = $webhook->deliveries()
->latest('created_at')
->paginate($request->integer('per_page', 50));
return response()->json($deliveries);
}
/**
* Retry a failed delivery.
*/
public function retryDelivery(Request $request, EntitlementWebhookDelivery $delivery): JsonResponse
{
$this->authorizeWebhook($request, $delivery->webhook);
if ($delivery->isSucceeded()) {
return response()->json([
'message' => __('Cannot retry a successful delivery'),
], 422);
}
$delivery = $this->webhookService->retryDelivery($delivery);
return response()->json([
'message' => $delivery->isSucceeded()
? __('Delivery retried successfully')
: __('Delivery retry failed'),
'delivery' => $delivery,
]);
}
/**
* Get available event types.
*/
public function events(): JsonResponse
{
return response()->json([
'events' => $this->webhookService->getAvailableEvents(),
]);
}
/**
* Resolve the workspace from the request.
*/
protected function resolveWorkspace(Request $request): Workspace
{
// First try explicit workspace_id parameter
if ($request->has('workspace_id')) {
$workspace = Workspace::findOrFail($request->integer('workspace_id'));
// Verify user has access
if (! $request->user()->workspaces->contains($workspace)) {
abort(403, 'You do not have access to this workspace');
}
return $workspace;
}
// Fall back to user's default workspace
return $request->user()->defaultHostWorkspace()
?? abort(400, 'No workspace specified and user has no default workspace');
}
/**
* Authorize that the user can access this webhook.
*/
protected function authorizeWebhook(Request $request, EntitlementWebhook $webhook): void
{
if (! $request->user()->workspaces->contains($webhook->workspace)) {
abort(403, 'You do not have access to this webhook');
}
}
}