From 90100e3867bdd052c2b2814312cfdeb28e8e31bc Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:49:38 +0000 Subject: [PATCH] feat: use geosms --- app/config/variables.php | 2 +- app/init.php | 22 ----- composer.json | 2 +- composer.lock | 38 ++++----- src/Appwrite/Platform/Workers/Messaging.php | 91 ++++++++++++++++----- 5 files changed, 93 insertions(+), 62 deletions(-) diff --git a/app/config/variables.php b/app/config/variables.php index 533a85a840..a04f99bc9c 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -441,7 +441,7 @@ return [ 'variables' => [ [ 'name' => '_APP_SMS_PROVIDER', - 'description' => "Provider used for delivering SMS for Phone authentication. Use the following format: 'sms://[USER]:[SECRET]@[PROVIDER]'.\n\nEnsure `[USER]` and `[SECRET]` are URL encoded if they contain any non-alphanumeric characters.\n\nAvailable providers are twilio, text-magic, telesign, msg91, and vonage.", + 'description' => "Provider used for delivering SMS for Phone authentication. Use the following format: 'sms://[USER]:[SECRET]@[PROVIDER]'.\n\nEnsure `[USER]` and `[SECRET]` are URL encoded if they contain any non-alphanumeric characters.\n\nAvailable providers are twilio, text-magic, telesign, msg91 and vonage.\n\nWhen using the using multiple providers, GEOSMS is enabled. With GEOSMS, the first adapter is used as the default adapter for calling codes, and subsequent adapters can add the `local` parameter to specify the calling code for which they are used: e.g. `sms://[user]:[pass]@twilio?local=default,sms://[user]:[pass]@msg91?local=91`", 'introduction' => '0.15.0', 'default' => '', 'required' => false, diff --git a/app/init.php b/app/init.php index a32ab08344..bdd61f62f7 100644 --- a/app/init.php +++ b/app/init.php @@ -47,13 +47,7 @@ use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Structure; use Utopia\Locale\Locale; use Utopia\DSN\DSN; -use Utopia\Messaging\Adapters\SMS\Mock; use Appwrite\GraphQL\Promises\Adapter\Swoole; -use Utopia\Messaging\Adapters\SMS\Msg91; -use Utopia\Messaging\Adapters\SMS\Telesign; -use Utopia\Messaging\Adapters\SMS\TextMagic; -use Utopia\Messaging\Adapters\SMS\Twilio; -use Utopia\Messaging\Adapters\SMS\Vonage; use Utopia\Registry\Registry; use Utopia\Storage\Device; use Utopia\Storage\Device\Backblaze; @@ -1341,22 +1335,6 @@ App::setResource('passwordsDictionary', function ($register) { return $register->get('passwordsDictionary'); }, ['register']); -App::setResource('sms', function () { - $dsn = new DSN(App::getEnv('_APP_SMS_PROVIDER')); - $user = $dsn->getUser(); - $secret = $dsn->getPassword(); - - return match ($dsn->getHost()) { - 'mock' => new Mock($user, $secret), // used for tests - 'twilio' => new Twilio($user, $secret), - 'text-magic' => new TextMagic($user, $secret), - 'telesign' => new Telesign($user, $secret), - 'msg91' => new Msg91($user, $secret), - 'vonage' => new Vonage($user, $secret), - default => null - }; -}); - App::setResource('servers', function () { $platforms = Config::getParam('platforms'); $server = $platforms[APP_PLATFORM_SERVER]; diff --git a/composer.json b/composer.json index 94b1d8f41f..dd9bdb02c3 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,7 @@ "utopia-php/image": "0.5.*", "utopia-php/locale": "0.4.*", "utopia-php/logger": "0.3.*", - "utopia-php/messaging": "0.2.*", + "utopia-php/messaging": "0.3.*", "utopia-php/migration": "0.3.*", "utopia-php/orchestration": "0.9.*", "utopia-php/platform": "0.5.*", diff --git a/composer.lock b/composer.lock index 7507243862..cb25fd447d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "35dcde03d0eb9a0d27de653b9fa038d4", + "content-hash": "615a0644ab34fb1809aba7d8e4217c85", "packages": [ { "name": "adhocore/jwt", @@ -1353,16 +1353,16 @@ }, { "name": "utopia-php/framework", - "version": "0.33.1", + "version": "0.33.2", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "b745607aa1875554a0ad52e28f6db918da1ce11c" + "reference": "b1423ca3e3b61c6c4c2e619d2cb80672809a19f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/b745607aa1875554a0ad52e28f6db918da1ce11c", - "reference": "b745607aa1875554a0ad52e28f6db918da1ce11c", + "url": "https://api.github.com/repos/utopia-php/http/zipball/b1423ca3e3b61c6c4c2e619d2cb80672809a19f3", + "reference": "b1423ca3e3b61c6c4c2e619d2cb80672809a19f3", "shasum": "" }, "require": { @@ -1392,9 +1392,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.1" + "source": "https://github.com/utopia-php/http/tree/0.33.2" }, - "time": "2024-01-17T16:48:32+00:00" + "time": "2024-01-31T10:35:59+00:00" }, { "name": "utopia-php/image", @@ -1554,16 +1554,16 @@ }, { "name": "utopia-php/messaging", - "version": "0.2.0", + "version": "0.3.0", "source": { "type": "git", "url": "https://github.com/utopia-php/messaging.git", - "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43" + "reference": "d488223876f88f97bb76fd6681fed0df80558f62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/2d0f474a106bb1da285f85e105c29b46085d3a43", - "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/d488223876f88f97bb76fd6681fed0df80558f62", + "reference": "d488223876f88f97bb76fd6681fed0df80558f62", "shasum": "" }, "require": { @@ -1596,9 +1596,9 @@ ], "support": { "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.2.0" + "source": "https://github.com/utopia-php/messaging/tree/0.3.0" }, - "time": "2023-09-14T20:48:42+00:00" + "time": "2023-11-14T21:02:37+00:00" }, { "name": "utopia-php/migration", @@ -2471,16 +2471,16 @@ }, { "name": "doctrine/deprecations", - "version": "1.1.2", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", - "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", "shasum": "" }, "require": { @@ -2512,9 +2512,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.2" + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" }, - "time": "2023-09-27T20:04:15+00:00" + "time": "2024-01-30T19:34:25+00:00" }, { "name": "doctrine/instantiator", diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index 92f9e8fdf4..512b5864ef 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -7,21 +7,20 @@ use Utopia\App; use Utopia\CLI\Console; use Utopia\DSN\DSN; use Utopia\Messaging\Messages\SMS; +use Utopia\Messaging\Adapters\SMS as SMSAdapter; use Utopia\Messaging\Adapters\SMS\Mock; use Utopia\Messaging\Adapters\SMS\Msg91; use Utopia\Messaging\Adapters\SMS\Telesign; use Utopia\Messaging\Adapters\SMS\TextMagic; use Utopia\Messaging\Adapters\SMS\Twilio; use Utopia\Messaging\Adapters\SMS\Vonage; +use Utopia\Messaging\Adapters\SMS\GEOSMS; use Utopia\Platform\Action; use Utopia\Queue\Message; class Messaging extends Action { - private ?DSN $dsn = null; - private string $user = ''; - private string $secret = ''; - private string $provider = ''; + private array $dsns = []; public static function getName(): string { @@ -33,11 +32,14 @@ class Messaging extends Action */ public function __construct() { - $this->provider = App::getEnv('_APP_SMS_PROVIDER', ''); - if (!empty($this->provider)) { - $this->dsn = new DSN($this->provider); - $this->user = $this->dsn->getUser(); - $this->secret = $this->dsn->getPassword(); + $providers = App::getEnv('_APP_SMS_PROVIDER', ''); + + if (!empty($providers)) { + $providers = explode(',', $providers); + + foreach ($providers as $provider) { + $this->dsns[] = new DSN($provider); + } } $this @@ -82,21 +84,16 @@ class Messaging extends Action return; } - $sms = match ($this->dsn->getHost()) { - 'mock' => new Mock($this->user, $this->secret), // used for tests - 'twilio' => new Twilio($this->user, $this->secret), - 'text-magic' => new TextMagic($this->user, $this->secret), - 'telesign' => new Telesign($this->user, $this->secret), - 'msg91' => new Msg91($this->user, $this->secret), - 'vonage' => new Vonage($this->user, $this->secret), - default => null - }; if (empty(App::getEnv('_APP_SMS_PROVIDER'))) { Console::error('Skipped sms processing. No Phone provider has been set.'); return; } + $sms = count($this->dsns) > 1 + ? self::createGEOSMSAdapter($this->dsns) + : self::createAdapterFromDSN($this->dsns[0]); + $from = App::getEnv('_APP_SMS_FROM'); if (empty($from)) { @@ -116,4 +113,60 @@ class Messaging extends Action throw new Exception('Error sending message: ' . $error->getMessage(), 500); } } -} + + protected static function createAdapterFromDSN(DSN $dsn): SMSAdapter + { + $from = empty($dsn->getParam('from', '')) ? null : $dsn->getParam('from', ''); + + switch ($dsn->getHost()) { + case 'mock': + return new Mock($dsn->getUser(), $dsn->getPassword()); + case 'msg91': + $adapter = new Msg91($dsn->getUser(), $dsn->getPassword()); + $template = $dsn->getParam('template', App::getEnv('_APP_SMS_FROM', '')); + if (!empty($template)) { + $adapter->setTemplate($template); + } + return $adapter; + case 'telesign': + return new Telesign($dsn->getUser(), $dsn->getPassword()); + case 'textmagic': + case 'text-magic': + return new TextMagic($dsn->getUser(), $dsn->getPassword(), $from); + case 'twilio': + return new Twilio($dsn->getUser(), $dsn->getPassword(), $from); + case 'vonage': + return new Vonage($dsn->getUser(), $dsn->getPassword(), $from); + default: + throw new \Exception('Unknown SMS provider: ' . $dsn->getHost()); + } + } + + protected static function createGEOSMSAdapter(array $dsns): GEOSMS + { + $defaultAdapter = self::createAdapterFromDSN($dsns[0]); + $geosms = new GEOSMS($defaultAdapter); + $localDSNs = array_slice($dsns, 1); + + /** @var DSN $localDSN */ + foreach ($localDSNs as $localDSN) { + $localAdapter = null; + try { + $localAdapter = self::createAdapterFromDSN($localDSN); + } catch (\Exception) { + Console::warning('Unable to create adapter: ' . $localDSN->getHost()); + continue; + } + + $callingCode = $localDSN->getParam('local', ''); + if (empty($callingCode)) { + Console::warning('Unable to register adapter: ' . $localDSN->getHost() . '. Missing `local` parameter.'); + continue; + } + + $geosms->setLocal($callingCode, $localAdapter); + } + + return $geosms; + } +} \ No newline at end of file