mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-12 15:35:15 +00:00
Make sure webhook messages can be versionised later.
This commit is contained in:
57
app/Generator/Webhook/MessageGeneratorInterface.php
Normal file
57
app/Generator/Webhook/MessageGeneratorInterface.php
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* MessageGeneratorInterface.php
|
||||||
|
* Copyright (c) 2020 james@firefly-iii.org
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FireflyIII\Generator\Webhook;
|
||||||
|
|
||||||
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface MessageGeneratorInterface
|
||||||
|
*/
|
||||||
|
interface MessageGeneratorInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getVersion(): int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function generateMessages(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
*/
|
||||||
|
public function setUser(User $user): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $transactionGroups
|
||||||
|
*/
|
||||||
|
public function setTransactionGroups(Collection $transactionGroups): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $trigger
|
||||||
|
*/
|
||||||
|
public function setTrigger(int $trigger): void;
|
||||||
|
}
|
@@ -22,7 +22,6 @@
|
|||||||
namespace FireflyIII\Generator\Webhook;
|
namespace FireflyIII\Generator\Webhook;
|
||||||
|
|
||||||
use FireflyIII\Events\RequestedSendWebhookMessages;
|
use FireflyIII\Events\RequestedSendWebhookMessages;
|
||||||
use FireflyIII\Events\StoredWebhookMessage;
|
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionGroup;
|
use FireflyIII\Models\TransactionGroup;
|
||||||
@@ -38,10 +37,11 @@ use Ramsey\Uuid\Uuid;
|
|||||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class WebhookMessageGenerator
|
* Class StandardMessageGenerator
|
||||||
*/
|
*/
|
||||||
class WebhookMessageGenerator
|
class StandardMessageGenerator implements MessageGeneratorInterface
|
||||||
{
|
{
|
||||||
|
private int $version = 1;
|
||||||
private User $user;
|
private User $user;
|
||||||
private Collection $transactionGroups;
|
private Collection $transactionGroups;
|
||||||
private int $trigger;
|
private int $trigger;
|
||||||
@@ -89,6 +89,9 @@ class WebhookMessageGenerator
|
|||||||
return $this->user->webhooks()->where('active', 1)->where('trigger', $this->trigger)->get(['webhooks.*']);
|
return $this->user->webhooks()->where('active', 1)->where('trigger', $this->trigger)->get(['webhooks.*']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will also trigger a send.
|
||||||
|
*/
|
||||||
private function run(): void
|
private function run(): void
|
||||||
{
|
{
|
||||||
/** @var Webhook $webhook */
|
/** @var Webhook $webhook */
|
||||||
@@ -100,8 +103,6 @@ class WebhookMessageGenerator
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Webhook $webhook
|
* @param Webhook $webhook
|
||||||
*
|
|
||||||
* @throws FireflyException
|
|
||||||
*/
|
*/
|
||||||
private function runWebhook(Webhook $webhook): void
|
private function runWebhook(Webhook $webhook): void
|
||||||
{
|
{
|
||||||
@@ -126,19 +127,28 @@ class WebhookMessageGenerator
|
|||||||
'trigger' => config('firefly.webhooks.triggers')[$webhook->trigger],
|
'trigger' => config('firefly.webhooks.triggers')[$webhook->trigger],
|
||||||
'url' => $webhook->url,
|
'url' => $webhook->url,
|
||||||
'uuid' => $uuid->toString(),
|
'uuid' => $uuid->toString(),
|
||||||
'version' => 0,
|
'version' => sprintf('v%d',$this->getVersion()),
|
||||||
'response' => config('firefly.webhooks.responses')[$webhook->response],
|
'response' => config('firefly.webhooks.responses')[$webhook->response],
|
||||||
'content' => [],
|
'content' => [],
|
||||||
];
|
];
|
||||||
|
|
||||||
switch ($webhook->response) {
|
switch ($webhook->response) {
|
||||||
default:
|
default:
|
||||||
throw new FireflyException(sprintf('Cannot handle this webhook response (%d)', $webhook->response));
|
Log::error(
|
||||||
|
sprintf('The response code for webhook #%d is "%d" and the message generator cant handle it. Soft fail.', $webhook->id, $webhook->response)
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
case Webhook::RESPONSE_NONE:
|
case Webhook::RESPONSE_NONE:
|
||||||
$message['content'] = [];
|
$message['content'] = [];
|
||||||
|
break;
|
||||||
case Webhook::RESPONSE_TRANSACTIONS:
|
case Webhook::RESPONSE_TRANSACTIONS:
|
||||||
$transformer = new TransactionGroupTransformer;
|
$transformer = new TransactionGroupTransformer;
|
||||||
|
try {
|
||||||
$message['content'] = $transformer->transformObject($transactionGroup);
|
$message['content'] = $transformer->transformObject($transactionGroup);
|
||||||
|
} catch (FireflyException $e) {
|
||||||
|
$message['content'] = ['error' => 'Internal error prevented Firefly III from including data', 'message' => $e->getMessage()];
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Webhook::RESPONSE_ACCOUNTS:
|
case Webhook::RESPONSE_ACCOUNTS:
|
||||||
$accounts = $this->collectAccounts($transactionGroup);
|
$accounts = $this->collectAccounts($transactionGroup);
|
||||||
@@ -173,6 +183,8 @@ class WebhookMessageGenerator
|
|||||||
/**
|
/**
|
||||||
* @param Webhook $webhook
|
* @param Webhook $webhook
|
||||||
* @param array $message
|
* @param array $message
|
||||||
|
*
|
||||||
|
* @return WebhookMessage
|
||||||
*/
|
*/
|
||||||
private function storeMessage(Webhook $webhook, array $message): WebhookMessage
|
private function storeMessage(Webhook $webhook, array $message): WebhookMessage
|
||||||
{
|
{
|
||||||
@@ -188,4 +200,11 @@ class WebhookMessageGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getVersion(): int
|
||||||
|
{
|
||||||
|
return $this->version;
|
||||||
|
}
|
||||||
}
|
}
|
@@ -23,7 +23,7 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Handlers\Events;
|
namespace FireflyIII\Handlers\Events;
|
||||||
|
|
||||||
use FireflyIII\Events\StoredTransactionGroup;
|
use FireflyIII\Events\StoredTransactionGroup;
|
||||||
use FireflyIII\Generator\Webhook\WebhookMessageGenerator;
|
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Models\Webhook;
|
use FireflyIII\Models\Webhook;
|
||||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||||
@@ -82,7 +82,8 @@ class StoredGroupEventHandler
|
|||||||
Log::debug('StoredTransactionGroup:triggerWebhooks');
|
Log::debug('StoredTransactionGroup:triggerWebhooks');
|
||||||
$group = $storedGroupEvent->transactionGroup;
|
$group = $storedGroupEvent->transactionGroup;
|
||||||
$user = $group->user;
|
$user = $group->user;
|
||||||
$engine = new WebhookMessageGenerator;
|
/** @var MessageGeneratorInterface $engine */
|
||||||
|
$engine = app(MessageGeneratorInterface::class);
|
||||||
$engine->setUser($user);
|
$engine->setUser($user);
|
||||||
$engine->setTransactionGroups(new Collection([$group]));
|
$engine->setTransactionGroups(new Collection([$group]));
|
||||||
$engine->setTrigger(Webhook::TRIGGER_STORE_TRANSACTION);
|
$engine->setTrigger(Webhook::TRIGGER_STORE_TRANSACTION);
|
||||||
|
@@ -23,7 +23,7 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Handlers\Events;
|
namespace FireflyIII\Handlers\Events;
|
||||||
|
|
||||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
use FireflyIII\Events\UpdatedTransactionGroup;
|
||||||
use FireflyIII\Generator\Webhook\WebhookMessageGenerator;
|
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
||||||
use FireflyIII\Models\Account;
|
use FireflyIII\Models\Account;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
@@ -122,7 +122,8 @@ class UpdatedGroupEventHandler
|
|||||||
Log::debug('UpdatedGroupEventHandler:triggerWebhooks');
|
Log::debug('UpdatedGroupEventHandler:triggerWebhooks');
|
||||||
$group = $updatedGroupEvent->transactionGroup;
|
$group = $updatedGroupEvent->transactionGroup;
|
||||||
$user = $group->user;
|
$user = $group->user;
|
||||||
$engine = new WebhookMessageGenerator;
|
/** @var MessageGeneratorInterface $engine */
|
||||||
|
$engine = app(MessageGeneratorInterface::class);
|
||||||
$engine->setUser($user);
|
$engine->setUser($user);
|
||||||
$engine->setTransactionGroups(new Collection([$group]));
|
$engine->setTransactionGroups(new Collection([$group]));
|
||||||
$engine->setTrigger(Webhook::TRIGGER_UPDATE_TRANSACTION);
|
$engine->setTrigger(Webhook::TRIGGER_UPDATE_TRANSACTION);
|
||||||
|
@@ -23,8 +23,10 @@ namespace FireflyIII\Handlers\Events;
|
|||||||
|
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use FireflyIII\Helpers\Webhook\SignatureGeneratorInterface;
|
||||||
use FireflyIII\Models\WebhookAttempt;
|
use FireflyIII\Models\WebhookAttempt;
|
||||||
use FireflyIII\Models\WebhookMessage;
|
use FireflyIII\Models\WebhookMessage;
|
||||||
|
use FireflyIII\Services\Webhook\WebhookSenderInterface;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use GuzzleHttp\Exception\ClientException;
|
use GuzzleHttp\Exception\ClientException;
|
||||||
use JsonException;
|
use JsonException;
|
||||||
@@ -36,97 +38,24 @@ use Log;
|
|||||||
class WebhookEventHandler
|
class WebhookEventHandler
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
*
|
* Will try to send at most 3 messages so the flow doesn't get broken too much.
|
||||||
*/
|
*/
|
||||||
public function sendWebhookMessages(): void
|
public function sendWebhookMessages(): void
|
||||||
{
|
{
|
||||||
$max = (int)config('firefly.webhooks.max_attempts');
|
|
||||||
$max = 0 === $max ? 3 : $max;
|
|
||||||
$messages = WebhookMessage
|
$messages = WebhookMessage
|
||||||
::where('webhook_messages.sent', 0)
|
::where('webhook_messages.sent', 0)
|
||||||
->where('webhook_messages.errored', 0)
|
->where('webhook_messages.errored', 0)
|
||||||
->get(['webhook_messages.*']);
|
->get(['webhook_messages.*'])
|
||||||
Log::debug(sprintf('Found %d webhook message(s) to be send.', $messages->count()));
|
->filter(
|
||||||
/** @var WebhookMessage $message */
|
function (WebhookMessage $message) {
|
||||||
foreach ($messages as $message) {
|
return $message->webhookAttempts()->count() <= 2;
|
||||||
$count = $message->webhookAttempts()->count();
|
|
||||||
if ($count >= 3) {
|
|
||||||
Log::info('No send message.');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// TODO needs its own handler.
|
|
||||||
$this->sendMessageV0($message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
)->splice(0, 3);
|
||||||
|
Log::debug(sprintf('Found %d webhook message(s) ready to be send.', $messages->count()));
|
||||||
|
|
||||||
/**
|
$sender =app(WebhookSenderInterface::class);
|
||||||
* @param WebhookMessage $message
|
$sender->setMessages($messages);
|
||||||
*/
|
$sender->send();
|
||||||
private function sendMessageV0(WebhookMessage $message): void
|
|
||||||
{
|
|
||||||
Log::debug(sprintf('Trying to send webhook message #%d', $message->id));
|
|
||||||
try {
|
|
||||||
$json = json_encode($message->message, JSON_THROW_ON_ERROR);
|
|
||||||
} catch (JsonException $e) {
|
|
||||||
$attempt = new WebhookAttempt;
|
|
||||||
$attempt->webhookMessage()->associate($message);
|
|
||||||
$attempt->status_code = 0;
|
|
||||||
$attempt->logs = sprintf('Json error: %s', $e->getMessage());
|
|
||||||
$attempt->save();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// signature v0 is generated using the following structure:
|
|
||||||
// The signed_payload string is created by concatenating:
|
|
||||||
// The timestamp (as a string)
|
|
||||||
// The character .
|
|
||||||
// The character .
|
|
||||||
// The actual JSON payload (i.e., the request body)
|
|
||||||
$timestamp = time();
|
|
||||||
$payload = sprintf('%s.%s', $timestamp, $json);
|
|
||||||
$signature = hash_hmac('sha3-256', $payload, $message->webhook->secret, false);
|
|
||||||
|
|
||||||
// signature string:
|
|
||||||
// header included in each signed event contains a timestamp and one or more signatures.
|
|
||||||
// The timestamp is prefixed by t=, and each signature is prefixed by a scheme.
|
|
||||||
// Schemes start with v, followed by an integer. Currently, the only valid live signature scheme is v0.
|
|
||||||
$signatureString = sprintf('t=%s,v0=%s', $timestamp, $signature);
|
|
||||||
|
|
||||||
$options = [
|
|
||||||
'body' => $json,
|
|
||||||
'headers' => [
|
|
||||||
'Content-Type' => 'application/json',
|
|
||||||
'Accept' => 'application/json',
|
|
||||||
'Signature' => $signatureString,
|
|
||||||
'connect_timeout' => 3.14,
|
|
||||||
'User-Agent' => sprintf('FireflyIII/%s', config('firefly.version')),
|
|
||||||
'timeout' => 10,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
$client = new Client;
|
|
||||||
$logs = $message->logs ?? [];
|
|
||||||
try {
|
|
||||||
$res = $client->request('POST', $message->webhook->url, $options);
|
|
||||||
$message->sent = true;
|
|
||||||
} catch (ClientException|Exception $e) {
|
|
||||||
Log::error($e->getMessage());
|
|
||||||
Log::error($e->getTraceAsString());
|
|
||||||
$logs[] = sprintf('%s: %s', date('Y-m-d H:i:s'), $e->getMessage());
|
|
||||||
$message->errored = true;
|
|
||||||
$message->sent = false;
|
|
||||||
}
|
|
||||||
$message->save();
|
|
||||||
|
|
||||||
$attempt = new WebhookAttempt;
|
|
||||||
$attempt->webhookMessage()->associate($message);
|
|
||||||
$attempt->status_code = $res->getStatusCode();
|
|
||||||
$attempt->logs = '';
|
|
||||||
$attempt->response = (string)$res->getBody();
|
|
||||||
$attempt->save();
|
|
||||||
|
|
||||||
Log::debug(sprintf('Webhook message #%d was sent. Status code %d', $message->id, $res->getStatusCode()));
|
|
||||||
Log::debug(sprintf('Webhook request body size: %d bytes', strlen($json)));
|
|
||||||
Log::debug(sprintf('Response body: %s', $res->getBody()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
70
app/Helpers/Webhook/Sha3SignatureGenerator.php
Normal file
70
app/Helpers/Webhook/Sha3SignatureGenerator.php
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* Sha3SignatureGenerator.php
|
||||||
|
* Copyright (c) 2020 james@firefly-iii.org
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FireflyIII\Helpers\Webhook;
|
||||||
|
|
||||||
|
use FireflyIII\Models\WebhookMessage;
|
||||||
|
use JsonException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Sha3SignatureGenerator
|
||||||
|
*/
|
||||||
|
class Sha3SignatureGenerator implements SignatureGeneratorInterface
|
||||||
|
{
|
||||||
|
private int $version = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getVersion(): int
|
||||||
|
{
|
||||||
|
return $this->version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function generate(WebhookMessage $message): string
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$json = json_encode($message->message, JSON_THROW_ON_ERROR);
|
||||||
|
} catch (JsonException $e) {
|
||||||
|
// TODO needs FireflyException.
|
||||||
|
return sprintf('t=1,v%d=err-invalid-signature', $this->getVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
// signature v1 is generated using the following structure:
|
||||||
|
// The signed_payload string is created by concatenating:
|
||||||
|
// The timestamp (as a string)
|
||||||
|
// The character .
|
||||||
|
// The character .
|
||||||
|
// The actual JSON payload (i.e., the request body)
|
||||||
|
$timestamp = time();
|
||||||
|
$payload = sprintf('%s.%s', $timestamp, $json);
|
||||||
|
$signature = hash_hmac('sha3-256', $payload, $message->webhook->secret, false);
|
||||||
|
|
||||||
|
// signature string:
|
||||||
|
// header included in each signed event contains a timestamp and one or more signatures.
|
||||||
|
// The timestamp is prefixed by t=, and each signature is prefixed by a scheme.
|
||||||
|
// Schemes start with v, followed by an integer. Currently, the only valid live signature scheme is v1.
|
||||||
|
return sprintf('t=%s,v%d=%s', $timestamp, $this->getVersion(), $signature);
|
||||||
|
}
|
||||||
|
}
|
44
app/Helpers/Webhook/SignatureGeneratorInterface.php
Normal file
44
app/Helpers/Webhook/SignatureGeneratorInterface.php
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* SignatureGeneratorInterface.php
|
||||||
|
* Copyright (c) 2020 james@firefly-iii.org
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FireflyIII\Helpers\Webhook;
|
||||||
|
|
||||||
|
use FireflyIII\Models\WebhookMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface SignatureGeneratorInterface
|
||||||
|
*/
|
||||||
|
interface SignatureGeneratorInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return the version of this signature generator.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getVersion(): int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param WebhookMessage $message
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function generate(WebhookMessage $message): string;
|
||||||
|
}
|
@@ -36,6 +36,8 @@ use FireflyIII\Helpers\Report\PopupReport;
|
|||||||
use FireflyIII\Helpers\Report\PopupReportInterface;
|
use FireflyIII\Helpers\Report\PopupReportInterface;
|
||||||
use FireflyIII\Helpers\Report\ReportHelper;
|
use FireflyIII\Helpers\Report\ReportHelper;
|
||||||
use FireflyIII\Helpers\Report\ReportHelperInterface;
|
use FireflyIII\Helpers\Report\ReportHelperInterface;
|
||||||
|
use FireflyIII\Helpers\Webhook\Sha3SignatureGenerator;
|
||||||
|
use FireflyIII\Helpers\Webhook\SignatureGeneratorInterface;
|
||||||
use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepository;
|
use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepository;
|
||||||
use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface;
|
use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Telemetry\TelemetryRepository;
|
use FireflyIII\Repositories\Telemetry\TelemetryRepository;
|
||||||
@@ -50,6 +52,8 @@ use FireflyIII\Services\FireflyIIIOrg\Update\UpdateRequest;
|
|||||||
use FireflyIII\Services\FireflyIIIOrg\Update\UpdateRequestInterface;
|
use FireflyIII\Services\FireflyIIIOrg\Update\UpdateRequestInterface;
|
||||||
use FireflyIII\Services\Password\PwndVerifierV2;
|
use FireflyIII\Services\Password\PwndVerifierV2;
|
||||||
use FireflyIII\Services\Password\Verifier;
|
use FireflyIII\Services\Password\Verifier;
|
||||||
|
use FireflyIII\Services\Webhook\StandardWebhookSender;
|
||||||
|
use FireflyIII\Services\Webhook\WebhookSenderInterface;
|
||||||
use FireflyIII\Support\Amount;
|
use FireflyIII\Support\Amount;
|
||||||
use FireflyIII\Support\ExpandedForm;
|
use FireflyIII\Support\ExpandedForm;
|
||||||
use FireflyIII\Support\FireflyConfig;
|
use FireflyIII\Support\FireflyConfig;
|
||||||
@@ -226,6 +230,11 @@ class FireflyServiceProvider extends ServiceProvider
|
|||||||
$this->app->bind(UpdateRequestInterface::class, UpdateRequest::class);
|
$this->app->bind(UpdateRequestInterface::class, UpdateRequest::class);
|
||||||
$this->app->bind(TelemetryRepositoryInterface::class, TelemetryRepository::class);
|
$this->app->bind(TelemetryRepositoryInterface::class, TelemetryRepository::class);
|
||||||
|
|
||||||
|
// webhooks:
|
||||||
|
$this->app->bind(SignatureGeneratorInterface::class,Sha3SignatureGenerator::class);
|
||||||
|
$this->app->bind(WebhookSenderInterface::class, StandardWebhookSender::class);
|
||||||
|
|
||||||
|
|
||||||
// password verifier thing
|
// password verifier thing
|
||||||
$this->app->bind(Verifier::class, PwndVerifierV2::class);
|
$this->app->bind(Verifier::class, PwndVerifierV2::class);
|
||||||
|
|
||||||
|
138
app/Services/Webhook/StandardWebhookSender.php
Normal file
138
app/Services/Webhook/StandardWebhookSender.php
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* StandardWebhookSender.php
|
||||||
|
* Copyright (c) 2020 james@firefly-iii.org
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FireflyIII\Services\Webhook;
|
||||||
|
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Helpers\Webhook\SignatureGeneratorInterface;
|
||||||
|
use FireflyIII\Models\WebhookAttempt;
|
||||||
|
use FireflyIII\Models\WebhookMessage;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Exception\ClientException;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Log;
|
||||||
|
use JsonException;
|
||||||
|
/**
|
||||||
|
* Class StandardWebhookSender
|
||||||
|
*/
|
||||||
|
class StandardWebhookSender implements WebhookSenderInterface
|
||||||
|
{
|
||||||
|
private Collection $messages;
|
||||||
|
private int $version = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getVersion(): int
|
||||||
|
{
|
||||||
|
return $this->version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function setMessages(Collection $messages): void
|
||||||
|
{
|
||||||
|
$this->messages = $messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function send(): void
|
||||||
|
{
|
||||||
|
/** @var WebhookMessage $message */
|
||||||
|
foreach ($this->messages as $message) {
|
||||||
|
try {
|
||||||
|
$this->sendMessage($message);
|
||||||
|
} catch (FireflyException $e) {
|
||||||
|
// TODO log attempt and make WebhookAttempt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param WebhookMessage $message
|
||||||
|
*
|
||||||
|
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||||
|
*/
|
||||||
|
private function sendMessage(WebhookMessage $message): void
|
||||||
|
{
|
||||||
|
// have the signature generator generate a signature. If it fails, the error thrown will
|
||||||
|
// end up in send() to be caught.
|
||||||
|
$signatureGenerator = app(SignatureGeneratorInterface::class);
|
||||||
|
$signature = $signatureGenerator->generate($message);
|
||||||
|
|
||||||
|
Log::debug(sprintf('Trying to send webhook message #%d', $message->id));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$json = json_encode($message->message, JSON_THROW_ON_ERROR);
|
||||||
|
} catch (JsonException $e) {
|
||||||
|
// TODO throw Firefly Exception
|
||||||
|
// $attempt = new WebhookAttempt;
|
||||||
|
// $attempt->webhookMessage()->associate($message);
|
||||||
|
// $attempt->status_code = 0;
|
||||||
|
// $attempt->logs = sprintf('Json error: %s', $e->getMessage());
|
||||||
|
// $attempt->save();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$options = [
|
||||||
|
'body' => $json,
|
||||||
|
'headers' => [
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
'Accept' => 'application/json',
|
||||||
|
'Signature' => $signature,
|
||||||
|
'connect_timeout' => 3.14,
|
||||||
|
'User-Agent' => sprintf('FireflyIII/%s', config('firefly.version')),
|
||||||
|
'timeout' => 10,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$client = new Client;
|
||||||
|
//$logs = $message->logs ?? [];
|
||||||
|
try {
|
||||||
|
$res = $client->request('POST', $message->webhook->url, $options);
|
||||||
|
$message->sent = true;
|
||||||
|
} catch (ClientException|Exception $e) {
|
||||||
|
Log::error($e->getMessage());
|
||||||
|
Log::error($e->getTraceAsString());
|
||||||
|
//$logs[] = sprintf('%s: %s', date('Y-m-d H:i:s'), $e->getMessage());
|
||||||
|
$message->errored = true;
|
||||||
|
$message->sent = false;
|
||||||
|
}
|
||||||
|
$message->save();
|
||||||
|
|
||||||
|
// $attempt = new WebhookAttempt;
|
||||||
|
// $attempt->webhookMessage()->associate($message);
|
||||||
|
// $attempt->status_code = $res->getStatusCode();
|
||||||
|
// $attempt->logs = '';
|
||||||
|
// $attempt->response = (string)$res->getBody();
|
||||||
|
// $attempt->save();
|
||||||
|
|
||||||
|
Log::debug(sprintf('Webhook message #%d was sent. Status code %d', $message->id, $res->getStatusCode()));
|
||||||
|
Log::debug(sprintf('Webhook request body size: %d bytes', strlen($json)));
|
||||||
|
Log::debug(sprintf('Response body: %s', $res->getBody()));
|
||||||
|
|
||||||
|
//$sender
|
||||||
|
|
||||||
|
//$this->sendMessageV0($message);
|
||||||
|
}
|
||||||
|
}
|
46
app/Services/Webhook/WebhookSenderInterface.php
Normal file
46
app/Services/Webhook/WebhookSenderInterface.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* WebhookSenderInterface.php
|
||||||
|
* Copyright (c) 2020 james@firefly-iii.org
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FireflyIII\Services\Webhook;
|
||||||
|
|
||||||
|
use FireflyIII\Models\WebhookMessage;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface WebhookSenderInterface
|
||||||
|
*/
|
||||||
|
interface WebhookSenderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getVersion(): int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $messages
|
||||||
|
*/
|
||||||
|
public function setMessages(Collection $messages): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function send(): void;
|
||||||
|
}
|
Reference in New Issue
Block a user