appwrite/src/Appwrite/Platform/Workers/Webhooks.php

140 lines
4.7 KiB
PHP
Raw Normal View History

2023-05-29 13:58:45 +00:00
<?php
namespace Appwrite\Platform\Workers;
use Exception;
use Utopia\App;
use Utopia\Database\Document;
2023-11-14 08:50:26 +00:00
use Utopia\Database\Database;
2023-05-29 13:58:45 +00:00
use Utopia\Platform\Action;
use Utopia\Queue\Message;
class Webhooks extends Action
{
2023-10-01 17:39:26 +00:00
private array $errors = [];
2023-05-29 13:58:45 +00:00
public static function getName(): string
{
return 'webhooks';
}
2023-06-04 16:25:56 +00:00
/**
* @throws Exception
*/
2023-05-29 13:58:45 +00:00
public function __construct()
{
$this
->desc('Webhooks worker')
->inject('message')
2023-11-14 08:50:26 +00:00
->inject('dbForConsole')
->callback(fn($message, Database $dbForConsole) => $this->action($message, $dbForConsole));
2023-05-29 13:58:45 +00:00
}
2023-06-02 03:54:34 +00:00
/**
2023-10-01 17:39:26 +00:00
* @param Message $message
2023-11-14 08:50:26 +00:00
* @param Database $dbForConsole
2023-10-01 17:39:26 +00:00
* @return void
2023-06-02 03:54:34 +00:00
* @throws Exception
*/
2023-11-14 08:50:26 +00:00
public function action(Message $message, Database $dbForConsole): void
2023-05-29 13:58:45 +00:00
{
$payload = $message->getPayload() ?? [];
2023-06-04 08:19:49 +00:00
2023-05-29 13:58:45 +00:00
if (empty($payload)) {
throw new Exception('Missing payload');
2023-06-04 16:25:56 +00:00
}
2023-05-29 13:58:45 +00:00
2023-06-04 16:25:56 +00:00
$events = $payload['events'];
$webhookPayload = json_encode($payload['payload']);
$project = new Document($payload['project']);
$user = new Document($payload['user'] ?? []);
2023-05-29 13:58:45 +00:00
2023-06-04 16:25:56 +00:00
foreach ($project->getAttribute('webhooks', []) as $webhook) {
2023-11-14 08:50:26 +00:00
if ($webhook->getAttribute('status') === true && array_intersect($webhook->getAttribute('events', []), $events)) {
$this->execute($events, $webhookPayload, $webhook, $user, $project, $dbForConsole);
2023-05-29 13:58:45 +00:00
}
2023-06-04 16:25:56 +00:00
}
2023-05-29 13:58:45 +00:00
2023-10-01 17:39:26 +00:00
if (!empty($this->errors)) {
throw new Exception(\implode(" / \n\n", $this->errors));
2023-06-04 16:25:56 +00:00
}
2023-05-29 13:58:45 +00:00
}
2023-10-01 17:39:26 +00:00
/**
* @param array $events
* @param string $payload
* @param Document $webhook
* @param Document $user
* @param Document $project
2023-11-14 08:50:26 +00:00
* @param Database $dbForConsole
2023-10-01 17:39:26 +00:00
* @return void
*/
2023-11-14 08:50:26 +00:00
private function execute(array $events, string $payload, Document $webhook, Document $user, Document $project, Database $dbForConsole): void
2023-05-29 13:58:45 +00:00
{
2023-11-14 08:50:26 +00:00
if ($webhook->getAttribute('status') === false) {
return;
}
2023-06-11 14:08:48 +00:00
2023-05-29 13:58:45 +00:00
$url = \rawurldecode($webhook->getAttribute('url'));
$signatureKey = $webhook->getAttribute('signatureKey');
$signature = base64_encode(hash_hmac('sha1', $url . $payload, $signatureKey, true));
$httpUser = $webhook->getAttribute('httpUser');
$httpPass = $webhook->getAttribute('httpPass');
$ch = \curl_init($webhook->getAttribute('url'));
2023-10-01 17:39:26 +00:00
2023-05-29 13:58:45 +00:00
\curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
\curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
\curl_setopt($ch, CURLOPT_HEADER, 0);
2023-11-14 08:50:26 +00:00
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0);
\curl_setopt($ch, CURLOPT_TIMEOUT, 15);
\curl_setopt($ch, CURLOPT_MAXFILESIZE, 5242880);
2023-05-29 13:58:45 +00:00
\curl_setopt($ch, CURLOPT_USERAGENT, \sprintf(
APP_USERAGENT,
App::getEnv('_APP_VERSION', 'UNKNOWN'),
App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
));
\curl_setopt(
$ch,
CURLOPT_HTTPHEADER,
[
'Content-Type: application/json',
'Content-Length: ' . \strlen($payload),
'X-' . APP_NAME . '-Webhook-Id: ' . $webhook->getId(),
'X-' . APP_NAME . '-Webhook-Events: ' . implode(',', $events),
'X-' . APP_NAME . '-Webhook-Name: ' . $webhook->getAttribute('name', ''),
'X-' . APP_NAME . '-Webhook-User-Id: ' . $user->getId(),
'X-' . APP_NAME . '-Webhook-Project-Id: ' . $project->getId(),
'X-' . APP_NAME . '-Webhook-Signature: ' . $signature,
]
);
if (!$webhook->getAttribute('security', true)) {
\curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
\curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
}
if (!empty($httpUser) && !empty($httpPass)) {
\curl_setopt($ch, CURLOPT_USERPWD, "$httpUser:$httpPass");
\curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
}
if (false === \curl_exec($ch)) {
2023-11-14 08:50:26 +00:00
$errorCount = $webhook->getAttribute('errors', 0) + 1;
$lastErrorLogs = \curl_error($ch) . ' in events ' . implode(', ', $events) . ' for webhook ' . $webhook->getAttribute('name');
$webhook->setAttribute('errors', $errorCount);
$webhook->setAttribute('logs', $lastErrorLogs);
if ($errorCount > 9) {
$webhook->setAttribute('status', false);
}
$dbForConsole->updateDocument('webhooks', $webhook->getId(), $webhook);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$this->errors[] = $lastErrorLogs;
2023-05-29 13:58:45 +00:00
}
\curl_close($ch);
}
}