From 2a7c77d7be7e529e34e0982feb0236f349441a75 Mon Sep 17 00:00:00 2001 From: idevakk <219866223+idevakk@users.noreply.github.com> Date: Thu, 5 Mar 2026 14:03:07 +0530 Subject: [PATCH] Step 3: Webhook endpoint (middleware, form request, controller, route) --- .../Controllers/EmailWebhookController.php | 23 ++++++++ app/Http/Middleware/VerifyWebhookSecret.php | 30 ++++++++++ app/Http/Requests/IncomingEmailRequest.php | 58 +++++++++++++++++++ routes/api.php | 6 +- 4 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 app/Http/Controllers/EmailWebhookController.php create mode 100644 app/Http/Middleware/VerifyWebhookSecret.php create mode 100644 app/Http/Requests/IncomingEmailRequest.php diff --git a/app/Http/Controllers/EmailWebhookController.php b/app/Http/Controllers/EmailWebhookController.php new file mode 100644 index 0000000..8f43f87 --- /dev/null +++ b/app/Http/Controllers/EmailWebhookController.php @@ -0,0 +1,23 @@ +validated()); + + return response()->json(['status' => 'queued'], 200); + } +} diff --git a/app/Http/Middleware/VerifyWebhookSecret.php b/app/Http/Middleware/VerifyWebhookSecret.php new file mode 100644 index 0000000..6495677 --- /dev/null +++ b/app/Http/Middleware/VerifyWebhookSecret.php @@ -0,0 +1,30 @@ +json(['error' => 'Webhook secret not configured'], 500); + } + + $token = $request->bearerToken(); + + if (! $token || ! hash_equals($secret, $token)) { + return response()->json(['error' => 'Unauthorized'], 401); + } + + return $next($request); + } +} diff --git a/app/Http/Requests/IncomingEmailRequest.php b/app/Http/Requests/IncomingEmailRequest.php new file mode 100644 index 0000000..6ecb575 --- /dev/null +++ b/app/Http/Requests/IncomingEmailRequest.php @@ -0,0 +1,58 @@ +|string> + */ + public function rules(): array + { + return [ + 'hash' => ['required', 'string', 'max:64'], + 'metadata.recipientEmail' => ['required', 'email', 'max:255'], + 'metadata.recipientName' => ['nullable', 'string', 'max:255'], + 'metadata.senderEmail' => ['required', 'email', 'max:255'], + 'metadata.senderName' => ['nullable', 'string', 'max:255'], + 'metadata.domain' => ['required', 'string', 'max:255'], + 'metadata.subject' => ['nullable', 'string', 'max:500'], + 'metadata.received_at' => ['required', 'date'], + 'metadata.attachments' => ['nullable', 'array'], + 'metadata.attachments.*.filename' => ['required_with:metadata.attachments', 'string'], + 'metadata.attachments.*.mimeType' => ['required_with:metadata.attachments', 'string'], + 'metadata.attachments.*.size' => ['required_with:metadata.attachments', 'integer'], + 'metadata.attachmentSize' => ['nullable', 'integer', 'min:0'], + 'bodyText' => ['nullable', 'string'], + 'bodyHtml' => ['nullable', 'string'], + ]; + } + + /** + * Custom error messages. + * + * @return array + */ + public function messages(): array + { + return [ + 'hash.required' => 'The email hash identifier is required.', + 'metadata.recipientEmail.required' => 'A recipient email address is required.', + 'metadata.senderEmail.required' => 'A sender email address is required.', + 'metadata.domain.required' => 'The recipient domain is required.', + 'metadata.received_at.required' => 'The email received timestamp is required.', + ]; + } +} diff --git a/routes/api.php b/routes/api.php index 38075ee..00e806c 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,3 +1,7 @@ middleware('verify.webhook.secret');