diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php index a7f4595966..c5abf4f755 100644 --- a/src/Appwrite/Platform/Workers/Webhooks.php +++ b/src/Appwrite/Platform/Workers/Webhooks.php @@ -2,8 +2,8 @@ namespace Appwrite\Platform\Workers; -use Appwrite\Event\Mail; use Appwrite\Event\Message\Usage as UsageMessage; +use Appwrite\Event\Notification; use Appwrite\Event\Publisher\Usage as UsagePublisher; use Appwrite\Template\Template; use Appwrite\Usage\Context as UsageContext; @@ -36,7 +36,7 @@ class Webhooks extends Action ->inject('message') ->inject('project') ->inject('dbForPlatform') - ->inject('queueForMails') + ->inject('queueForNotifications') ->inject('publisherForUsage') ->inject('log') ->inject('plan') @@ -47,14 +47,14 @@ class Webhooks extends Action * @param Message $message * @param Document $project * @param Database $dbForPlatform - * @param Mail $queueForMails + * @param Notification $queueForNotifications * @param UsagePublisher $publisherForUsage * @param Log $log * @param array $plan * @return void * @throws Exception */ - public function action(Message $message, Document $project, Database $dbForPlatform, Mail $queueForMails, UsagePublisher $publisherForUsage, Log $log, array $plan): void + public function action(Message $message, Document $project, Database $dbForPlatform, Notification $queueForNotifications, UsagePublisher $publisherForUsage, Log $log, array $plan): void { $this->errors = []; $payload = $message->getPayload(); @@ -73,7 +73,7 @@ class Webhooks extends Action foreach ($project->getAttribute('webhooks', []) as $webhook) { if (array_intersect($webhook->getAttribute('events', []), $events)) { - $this->execute($events, $webhookPayload, $webhook, $user, $project, $dbForPlatform, $queueForMails, $publisherForUsage, $plan); + $this->execute($events, $webhookPayload, $webhook, $user, $project, $dbForPlatform, $queueForNotifications, $publisherForUsage, $plan); } } @@ -89,11 +89,11 @@ class Webhooks extends Action * @param Document $user * @param Document $project * @param Database $dbForPlatform - * @param Mail $queueForMails + * @param Notification $queueForNotifications * @param array $plan * @return void */ - private function execute(array $events, string $payload, Document $webhook, Document $user, Document $project, Database $dbForPlatform, Mail $queueForMails, UsagePublisher $publisherForUsage, array $plan): void + private function execute(array $events, string $payload, Document $webhook, Document $user, Document $project, Database $dbForPlatform, Notification $queueForNotifications, UsagePublisher $publisherForUsage, array $plan): void { if ($webhook->getAttribute('enabled') !== true) { return; @@ -171,7 +171,7 @@ class Webhooks extends Action if ($attempts >= \intval(System::getEnv('_APP_WEBHOOK_MAX_FAILED_ATTEMPTS', '10'))) { $webhook->setAttribute('enabled', false); $updatePayload['enabled'] = false; - $this->sendEmailAlert($attempts, $statusCode, $webhook, $project, $dbForPlatform, $queueForMails, $plan); + $this->sendAlert($attempts, $statusCode, $webhook, $project, $dbForPlatform, $queueForNotifications, $plan); } $dbForPlatform->updateDocument('webhooks', $webhook->getId(), new Document($updatePayload)); @@ -203,26 +203,49 @@ class Webhooks extends Action * @param Document $webhook * @param Document $project * @param Database $dbForPlatform - * @param Mail $queueForMails + * @param Notification $queueForNotifications * @param array $plan * @return void */ - public function sendEmailAlert(int $attempts, mixed $statusCode, Document $webhook, Document $project, Database $dbForPlatform, Mail $queueForMails, array $plan): void + public function sendAlert(int $attempts, mixed $statusCode, Document $webhook, Document $project, Database $dbForPlatform, Notification $queueForNotifications, array $plan): void { $memberships = $dbForPlatform->find('memberships', [ Query::equal('teamInternalId', [$project->getAttribute('teamInternalId')]), Query::limit(APP_LIMIT_SUBQUERY) ]); - $userIds = array_column(\array_map(fn ($membership) => $membership->getArrayCopy(), $memberships), 'userId'); + // Webhook-paused alerts go only to project owners — non-owner team members do not receive them. + $ownerMemberships = \array_filter( + $memberships, + fn (Document $membership) => \in_array('owner', $membership->getAttribute('roles', []), true) + ); + + if (empty($ownerMemberships)) { + return; + } + + $userIds = \array_values(\array_unique(\array_filter(\array_map( + fn (Document $membership) => $membership->getAttribute('userId'), + $ownerMemberships + )))); + + if (empty($userIds)) { + return; + } $users = $dbForPlatform->find('users', [ Query::equal('$id', $userIds), + Query::limit(APP_LIMIT_SUBQUERY), ]); + if (empty($users)) { + return; + } + $projectId = $project->getId(); $region = $project->getAttribute('region', 'default'); $webhookId = $webhook->getId(); + $teamId = $project->getAttribute('teamId'); $template = Template::fromFile(__DIR__ . '/../../../../app/config/locale/templates/email-webhook-failed.tpl'); @@ -241,7 +264,6 @@ class Webhooks extends Action $template->setParam('{{termsUrl}}', $plan['termsUrl'] ?? APP_EMAIL_TERMS_URL); $template->setParam('{{privacyUrl}}', $plan['privacyUrl'] ?? APP_EMAIL_PRIVACY_URL); - // TODO: Use setbodyTemplate once #7307 is merged $subject = 'Webhook deliveries have been paused'; $preview = 'Webhook deliveries to your endpoint have been paused.'; $body = Template::fromFile(__DIR__ . '/../../../../app/config/locale/templates/email-base-styled.tpl'); @@ -249,20 +271,38 @@ class Webhooks extends Action $body ->setParam('{{subject}}', $subject) ->setParam('{{message}}', $template->render()) - ->setParam('{{year}}', date("Y")); + ->setParam('{{year}}', date('Y')); - $queueForMails + $queueForNotifications ->setProject($project) ->setSubject($subject) ->setPreview($preview) - ->setBody($body->render()); + ->setBody($body->render()) + ->setDeduplicationKey('webhook:' . $webhook->getId() . ':paused:' . $attempts); foreach ($users as $user) { - $queueForMails - ->setVariables(['user' => $user->getAttribute('name', '')]) - ->setName($user->getAttribute('name', '')) - ->setRecipient($user->getAttribute('email')) - ->trigger(); + $email = $user->getAttribute('email'); + $userId = $user->getId(); + + if (!empty($email)) { + $queueForNotifications->addRecipient( + $email, + NOTIFICATION_TYPE_EMAIL, + null, + $userId, + $teamId, + ); + } + + $queueForNotifications->addRecipient( + $userId, + NOTIFICATION_TYPE_CONSOLE, + null, + $userId, + $teamId, + ); } + + $queueForNotifications->trigger(); } }