Pint linter ran

This commit is contained in:
wess
2023-08-23 13:12:43 -04:00
parent eba91ae55a
commit bf5f0a8419
382 changed files with 10227 additions and 10339 deletions
+15 -16
View File
@@ -1,27 +1,27 @@
<?php
require_once __DIR__ . '/init.php';
require_once __DIR__ . '/controllers/general.php';
require_once __DIR__.'/init.php';
require_once __DIR__.'/controllers/general.php';
use Appwrite\Event\Func;
use Appwrite\Platform\Appwrite;
use Utopia\CLI\CLI;
use Utopia\Database\Validator\Authorization;
use Utopia\Platform\Service;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Cache\Adapter\Sharding;
use Utopia\Cache\Cache;
use Utopia\CLI\CLI;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Logger\Log;
use Utopia\Platform\Service;
use Utopia\Pools\Group;
use Utopia\Registry\Registry;
Authorization::disable();
CLI::setResource('register', fn()=>$register);
CLI::setResource('register', fn () => $register);
CLI::setResource('cache', function ($pools) {
$list = Config::getParam('pools-cache', []);
@@ -31,8 +31,7 @@ CLI::setResource('cache', function ($pools) {
$adapters[] = $pools
->get($value)
->pop()
->getResource()
;
->getResource();
}
return new Cache(new Sharding($adapters));
@@ -64,7 +63,7 @@ CLI::setResource('dbForConsole', function ($pools, $cache) {
$collections = Config::getParam('collections', [])['console'];
$last = \array_key_last($collections);
if (!($dbForConsole->exists($dbForConsole->getDefaultDatabase(), $last))) { /** TODO cache ready variable using registry */
if (! ($dbForConsole->exists($dbForConsole->getDefaultDatabase(), $last))) { /** TODO cache ready variable using registry */
throw new Exception('Tables not ready yet.');
}
@@ -76,8 +75,8 @@ CLI::setResource('dbForConsole', function ($pools, $cache) {
}
} while ($attempts < $maxAttempts);
if (!$ready) {
throw new Exception("Console is not ready yet. Please try again later.");
if (! $ready) {
throw new Exception('Console is not ready yet. Please try again later.');
}
return $dbForConsole;
@@ -95,7 +94,8 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole,
if (isset($databases[$databaseName])) {
$database = $databases[$databaseName];
$database->setNamespace('_' . $project->getInternalId());
$database->setNamespace('_'.$project->getInternalId());
return $database;
}
@@ -108,7 +108,7 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole,
$databases[$databaseName] = $database;
$database->setNamespace('_' . $project->getInternalId());
$database->setNamespace('_'.$project->getInternalId());
return $database;
};
@@ -116,7 +116,6 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole,
return $getProjectDB;
}, ['pools', 'dbForConsole', 'cache']);
CLI::setResource('queueForFunctions', function (Group $pools) {
return new Func($pools->get('queue')->pop()->getResource());
}, ['pools']);
@@ -149,7 +148,7 @@ CLI::setResource('logError', function (Registry $register) {
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);
Console::info('Usage stats log pushed with status code: ' . $responseCode);
Console::info('Usage stats log pushed with status code: '.$responseCode);
}
Console::warning("Failed: {$error->getMessage()}");
+7 -7
View File
@@ -69,7 +69,7 @@ return [ // Ordered by ABC.
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false
'mock' => false,
],
'box' => [
'name' => 'Box',
@@ -79,7 +79,7 @@ return [ // Ordered by ABC.
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false
'mock' => false,
],
'dailymotion' => [
'name' => 'Dailymotion',
@@ -89,7 +89,7 @@ return [ // Ordered by ABC.
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false
'mock' => false,
],
'discord' => [
'name' => 'Discord',
@@ -229,7 +229,7 @@ return [ // Ordered by ABC.
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false
'mock' => false,
],
'paypalSandbox' => [
'name' => 'PayPal Sandbox',
@@ -239,7 +239,7 @@ return [ // Ordered by ABC.
'sandbox' => true,
'form' => false,
'beta' => false,
'mock' => false
'mock' => false,
],
'podio' => [
'name' => 'Podio',
@@ -289,7 +289,7 @@ return [ // Ordered by ABC.
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false
'mock' => false,
],
'tradeshift' => [
'name' => 'Tradeshift',
@@ -329,7 +329,7 @@ return [ // Ordered by ABC.
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false
'mock' => false,
],
'yahoo' => [
'name' => 'Yahoo',
+14 -15
View File
@@ -2,21 +2,20 @@
return [
// Codes based on: https://github.com/matomo-org/device-detector/blob/master/Parser/Client/Browser.php
'aa' => ['name' => 'Avant Browser', 'path' => __DIR__ . '/browsers/avant.png'],
'an' => ['name' => 'Android WebView Beta', 'path' => __DIR__ . '/browsers/android-webview-beta.png'],
'ch' => ['name' => 'Google Chrome', 'path' => __DIR__ . '/browsers/chrome.png'],
'ci' => ['name' => 'Google Chrome (iOS)', 'path' => __DIR__ . '/browsers/chrome.png'],
'cm' => ['name' => 'Google Chrome (Mobile)', 'path' => __DIR__ . '/browsers/chrome.png'],
'cr' => ['name' => 'Chromium', 'path' => __DIR__ . '/browsers/chromium.png'],
'ff' => ['name' => 'Mozilla Firefox', 'path' => __DIR__ . '/browsers/firefox.png'],
'sf' => ['name' => 'Safari', 'path' => __DIR__ . '/browsers/safari.png'],
'mf' => ['name' => 'Mobile Safari', 'path' => __DIR__ . '/browsers/safari.png'],
'ps' => ['name' => 'Microsoft Edge', 'path' => __DIR__ . '/browsers/edge.png'],
'oi' => ['name' => 'Microsoft Edge (iOS)', 'path' => __DIR__ . '/browsers/edge.png'],
'om' => ['name' => 'Opera Mini', 'path' => __DIR__ . '/browsers/opera-mini.png'],
'op' => ['name' => 'Opera', 'path' => __DIR__ . '/browsers/opera.png'],
'on' => ['name' => 'Opera (Next)', 'path' => __DIR__ . '/browsers/opera.png'],
'aa' => ['name' => 'Avant Browser', 'path' => __DIR__.'/browsers/avant.png'],
'an' => ['name' => 'Android WebView Beta', 'path' => __DIR__.'/browsers/android-webview-beta.png'],
'ch' => ['name' => 'Google Chrome', 'path' => __DIR__.'/browsers/chrome.png'],
'ci' => ['name' => 'Google Chrome (iOS)', 'path' => __DIR__.'/browsers/chrome.png'],
'cm' => ['name' => 'Google Chrome (Mobile)', 'path' => __DIR__.'/browsers/chrome.png'],
'cr' => ['name' => 'Chromium', 'path' => __DIR__.'/browsers/chromium.png'],
'ff' => ['name' => 'Mozilla Firefox', 'path' => __DIR__.'/browsers/firefox.png'],
'sf' => ['name' => 'Safari', 'path' => __DIR__.'/browsers/safari.png'],
'mf' => ['name' => 'Mobile Safari', 'path' => __DIR__.'/browsers/safari.png'],
'ps' => ['name' => 'Microsoft Edge', 'path' => __DIR__.'/browsers/edge.png'],
'oi' => ['name' => 'Microsoft Edge (iOS)', 'path' => __DIR__.'/browsers/edge.png'],
'om' => ['name' => 'Opera Mini', 'path' => __DIR__.'/browsers/opera-mini.png'],
'op' => ['name' => 'Opera', 'path' => __DIR__.'/browsers/opera.png'],
'on' => ['name' => 'Opera (Next)', 'path' => __DIR__.'/browsers/opera.png'],
/*
'36' => '360 Phone Browser',
+17 -17
View File
@@ -1,20 +1,20 @@
<?php
return [
'amex' => ['name' => 'American Express', 'path' => __DIR__ . '/credit-cards/amex.png'],
'argencard' => ['name' => 'Argencard', 'path' => __DIR__ . '/credit-cards/argencard.png'],
'cabal' => ['name' => 'Cabal', 'path' => __DIR__ . '/credit-cards/cabal.png'],
'censosud' => ['name' => 'Consosud', 'path' => __DIR__ . '/credit-cards/consosud.png'],
'diners' => ['name' => 'Diners Club', 'path' => __DIR__ . '/credit-cards/diners.png'],
'discover' => ['name' => 'Discover', 'path' => __DIR__ . '/credit-cards/discover.png'],
'elo' => ['name' => 'Elo', 'path' => __DIR__ . '/credit-cards/elo.png'],
'hipercard' => ['name' => 'Hipercard', 'path' => __DIR__ . '/credit-cards/hipercard.png'],
'jcb' => ['name' => 'JCB', 'path' => __DIR__ . '/credit-cards/jcb.png'],
'mastercard' => ['name' => 'Mastercard', 'path' => __DIR__ . '/credit-cards/mastercard.png'],
'naranja' => ['name' => 'Naranja', 'path' => __DIR__ . '/credit-cards/naranja.png'],
'targeta-shopping' => ['name' => 'Tarjeta Shopping', 'path' => __DIR__ . '/credit-cards/tarjeta-shopping.png'],
'union-china-pay' => ['name' => 'Union China Pay', 'path' => __DIR__ . '/credit-cards/union-china-pay.png'],
'visa' => ['name' => 'Visa', 'path' => __DIR__ . '/credit-cards/visa.png'],
'mir' => ['name' => 'MIR', 'path' => __DIR__ . '/credit-cards/mir.png'],
'maestro' => ['name' => 'Maestro', 'path' => __DIR__ . '/credit-cards/maestro.png']
];
'amex' => ['name' => 'American Express', 'path' => __DIR__.'/credit-cards/amex.png'],
'argencard' => ['name' => 'Argencard', 'path' => __DIR__.'/credit-cards/argencard.png'],
'cabal' => ['name' => 'Cabal', 'path' => __DIR__.'/credit-cards/cabal.png'],
'censosud' => ['name' => 'Consosud', 'path' => __DIR__.'/credit-cards/consosud.png'],
'diners' => ['name' => 'Diners Club', 'path' => __DIR__.'/credit-cards/diners.png'],
'discover' => ['name' => 'Discover', 'path' => __DIR__.'/credit-cards/discover.png'],
'elo' => ['name' => 'Elo', 'path' => __DIR__.'/credit-cards/elo.png'],
'hipercard' => ['name' => 'Hipercard', 'path' => __DIR__.'/credit-cards/hipercard.png'],
'jcb' => ['name' => 'JCB', 'path' => __DIR__.'/credit-cards/jcb.png'],
'mastercard' => ['name' => 'Mastercard', 'path' => __DIR__.'/credit-cards/mastercard.png'],
'naranja' => ['name' => 'Naranja', 'path' => __DIR__.'/credit-cards/naranja.png'],
'targeta-shopping' => ['name' => 'Tarjeta Shopping', 'path' => __DIR__.'/credit-cards/tarjeta-shopping.png'],
'union-china-pay' => ['name' => 'Union China Pay', 'path' => __DIR__.'/credit-cards/union-china-pay.png'],
'visa' => ['name' => 'Visa', 'path' => __DIR__.'/credit-cards/visa.png'],
'mir' => ['name' => 'MIR', 'path' => __DIR__.'/credit-cards/mir.png'],
'maestro' => ['name' => 'Maestro', 'path' => __DIR__.'/credit-cards/maestro.png'],
];
+194 -194
View File
@@ -1,198 +1,198 @@
<?php
return [
'af' => ['name' => 'Afghanistan', 'path' => __DIR__ . '/flags/af.png'],
'ao' => ['name' => 'Angola', 'path' => __DIR__ . '/flags/ao.png'],
'al' => ['name' => 'Albania', 'path' => __DIR__ . '/flags/al.png'],
'ad' => ['name' => 'Andorra', 'path' => __DIR__ . '/flags/ad.png'],
'ae' => ['name' => 'United Arab Emirates', 'path' => __DIR__ . '/flags/ae.png'],
'ar' => ['name' => 'Argentina', 'path' => __DIR__ . '/flags/ar.png'],
'am' => ['name' => 'Armenia', 'path' => __DIR__ . '/flags/am.png'],
'ag' => ['name' => 'Antigua and Barbuda', 'path' => __DIR__ . '/flags/ag.png'],
'au' => ['name' => 'Australia', 'path' => __DIR__ . '/flags/au.png'],
'at' => ['name' => 'Austria', 'path' => __DIR__ . '/flags/at.png'],
'az' => ['name' => 'Azerbaijan', 'path' => __DIR__ . '/flags/az.png'],
'bi' => ['name' => 'Burundi', 'path' => __DIR__ . '/flags/bi.png'],
'be' => ['name' => 'Belgium', 'path' => __DIR__ . '/flags/be.png'],
'bj' => ['name' => 'Benin', 'path' => __DIR__ . '/flags/bj.png'],
'bf' => ['name' => 'Burkina Faso', 'path' => __DIR__ . '/flags/bf.png'],
'bd' => ['name' => 'Bangladesh', 'path' => __DIR__ . '/flags/bd.png'],
'bg' => ['name' => 'Bulgaria', 'path' => __DIR__ . '/flags/bg.png'],
'bh' => ['name' => 'Bahrain', 'path' => __DIR__ . '/flags/bh.png'],
'bs' => ['name' => 'Bahamas', 'path' => __DIR__ . '/flags/bs.png'],
'ba' => ['name' => 'Bosnia and Herzegovina', 'path' => __DIR__ . '/flags/ba.png'],
'by' => ['name' => 'Belarus', 'path' => __DIR__ . '/flags/by.png'],
'bz' => ['name' => 'Belize', 'path' => __DIR__ . '/flags/bz.png'],
'bo' => ['name' => 'Bolivia', 'path' => __DIR__ . '/flags/bo.png'],
'br' => ['name' => 'Brazil', 'path' => __DIR__ . '/flags/br.png'],
'bb' => ['name' => 'Barbados', 'path' => __DIR__ . '/flags/bb.png'],
'bn' => ['name' => 'Brunei Darussalam', 'path' => __DIR__ . '/flags/bn.png'],
'bt' => ['name' => 'Bhutan', 'path' => __DIR__ . '/flags/bt.png'],
'bw' => ['name' => 'Botswana', 'path' => __DIR__ . '/flags/bw.png'],
'cf' => ['name' => 'Central African Republic', 'path' => __DIR__ . '/flags/cf.png'],
'ca' => ['name' => 'Canada', 'path' => __DIR__ . '/flags/ca.png'],
'ch' => ['name' => 'Switzerland', 'path' => __DIR__ . '/flags/ch.png'],
'cl' => ['name' => 'Chile', 'path' => __DIR__ . '/flags/cl.png'],
'cn' => ['name' => 'China', 'path' => __DIR__ . '/flags/cn.png'],
'ci' => ['name' => 'Côte d\'Ivoire', 'path' => __DIR__ . '/flags/ci.png'],
'cm' => ['name' => 'Cameroon', 'path' => __DIR__ . '/flags/cm.png'],
'cd' => ['name' => 'Democratic Republic of the Congo', 'path' => __DIR__ . '/flags/cd.png'],
'cg' => ['name' => 'Republic of the Congo', 'path' => __DIR__ . '/flags/cg.png'],
'co' => ['name' => 'Colombia', 'path' => __DIR__ . '/flags/co.png'],
'km' => ['name' => 'Comoros', 'path' => __DIR__ . '/flags/km.png'],
'cv' => ['name' => 'Cape Verde', 'path' => __DIR__ . '/flags/cv.png'],
'cr' => ['name' => 'Costa Rica', 'path' => __DIR__ . '/flags/cr.png'],
'cu' => ['name' => 'Cuba', 'path' => __DIR__ . '/flags/cu.png'],
'cy' => ['name' => 'Cyprus', 'path' => __DIR__ . '/flags/cy.png'],
'cz' => ['name' => 'Czech Republic', 'path' => __DIR__ . '/flags/cz.png'],
'de' => ['name' => 'Germany', 'path' => __DIR__ . '/flags/de.png'],
'dj' => ['name' => 'Djibouti', 'path' => __DIR__ . '/flags/dj.png'],
'dm' => ['name' => 'Dominica', 'path' => __DIR__ . '/flags/dm.png'],
'dk' => ['name' => 'Denmark', 'path' => __DIR__ . '/flags/dk.png'],
'do' => ['name' => 'Dominican Republic', 'path' => __DIR__ . '/flags/do.png'],
'dz' => ['name' => 'Algeria', 'path' => __DIR__ . '/flags/dz.png'],
'ec' => ['name' => 'Ecuador', 'path' => __DIR__ . '/flags/ec.png'],
'eg' => ['name' => 'Egypt', 'path' => __DIR__ . '/flags/eg.png'],
'er' => ['name' => 'Eritrea', 'path' => __DIR__ . '/flags/er.png'],
'es' => ['name' => 'Spain', 'path' => __DIR__ . '/flags/es.png'],
'ee' => ['name' => 'Estonia', 'path' => __DIR__ . '/flags/ee.png'],
'et' => ['name' => 'Ethiopia', 'path' => __DIR__ . '/flags/et.png'],
'fi' => ['name' => 'Finland', 'path' => __DIR__ . '/flags/fi.png'],
'fj' => ['name' => 'Fiji', 'path' => __DIR__ . '/flags/fj.png'],
'fr' => ['name' => 'France', 'path' => __DIR__ . '/flags/fr.png'],
'fm' => ['name' => 'Micronesia (Federated States of)', 'path' => __DIR__ . '/flags/fm.png'],
'ga' => ['name' => 'Gabon', 'path' => __DIR__ . '/flags/ga.png'],
'gb' => ['name' => 'United Kingdom', 'path' => __DIR__ . '/flags/gb.png'],
'ge' => ['name' => 'Georgia', 'path' => __DIR__ . '/flags/ge.png'],
'gh' => ['name' => 'Ghana', 'path' => __DIR__ . '/flags/gh.png'],
'gn' => ['name' => 'Guinea', 'path' => __DIR__ . '/flags/gn.png'],
'gm' => ['name' => 'Gambia', 'path' => __DIR__ . '/flags/gm.png'],
'gw' => ['name' => 'Guinea-Bissau', 'path' => __DIR__ . '/flags/gw.png'],
'gq' => ['name' => 'Equatorial Guinea', 'path' => __DIR__ . '/flags/gq.png'],
'gr' => ['name' => 'Greece', 'path' => __DIR__ . '/flags/gr.png'],
'gd' => ['name' => 'Grenada', 'path' => __DIR__ . '/flags/gd.png'],
'gt' => ['name' => 'Guatemala', 'path' => __DIR__ . '/flags/gt.png'],
'gy' => ['name' => 'Guyana', 'path' => __DIR__ . '/flags/gy.png'],
'hn' => ['name' => 'Honduras', 'path' => __DIR__ . '/flags/hn.png'],
'hr' => ['name' => 'Croatia', 'path' => __DIR__ . '/flags/hr.png'],
'ht' => ['name' => 'Haiti', 'path' => __DIR__ . '/flags/ht.png'],
'hu' => ['name' => 'Hungary', 'path' => __DIR__ . '/flags/hu.png'],
'id' => ['name' => 'Indonesia', 'path' => __DIR__ . '/flags/id.png'],
'in' => ['name' => 'India', 'path' => __DIR__ . '/flags/in.png'],
'ie' => ['name' => 'Ireland', 'path' => __DIR__ . '/flags/ie.png'],
'ir' => ['name' => 'Iran (Islamic Republic of)', 'path' => __DIR__ . '/flags/ir.png'],
'iq' => ['name' => 'Iraq', 'path' => __DIR__ . '/flags/iq.png'],
'is' => ['name' => 'Iceland', 'path' => __DIR__ . '/flags/is.png'],
'il' => ['name' => 'Israel', 'path' => __DIR__ . '/flags/il.png'],
'it' => ['name' => 'Italy', 'path' => __DIR__ . '/flags/it.png'],
'jm' => ['name' => 'Jamaica', 'path' => __DIR__ . '/flags/jm.png'],
'jo' => ['name' => 'Jordan', 'path' => __DIR__ . '/flags/jo.png'],
'jp' => ['name' => 'Japan', 'path' => __DIR__ . '/flags/jp.png'],
'kz' => ['name' => 'Kazakhstan', 'path' => __DIR__ . '/flags/kz.png'],
'ke' => ['name' => 'Kenya', 'path' => __DIR__ . '/flags/ke.png'],
'kg' => ['name' => 'Kyrgyzstan', 'path' => __DIR__ . '/flags/kg.png'],
'kh' => ['name' => 'Cambodia', 'path' => __DIR__ . '/flags/kh.png'],
'ki' => ['name' => 'Kiribati', 'path' => __DIR__ . '/flags/ki.png'],
'kn' => ['name' => 'Saint Kitts and Nevis', 'path' => __DIR__ . '/flags/kn.png'],
'kr' => ['name' => 'South Korea', 'path' => __DIR__ . '/flags/kr.png'],
'kw' => ['name' => 'Kuwait', 'path' => __DIR__ . '/flags/kw.png'],
'la' => ['name' => 'Lao People\'s Democratic Republic', 'path' => __DIR__ . '/flags/la.png'],
'lb' => ['name' => 'Lebanon', 'path' => __DIR__ . '/flags/lb.png'],
'lr' => ['name' => 'Liberia', 'path' => __DIR__ . '/flags/lr.png'],
'ly' => ['name' => 'Libya', 'path' => __DIR__ . '/flags/ly.png'],
'lc' => ['name' => 'Saint Lucia', 'path' => __DIR__ . '/flags/lc.png'],
'li' => ['name' => 'Liechtenstein', 'path' => __DIR__ . '/flags/li.png'],
'lk' => ['name' => 'Sri Lanka', 'path' => __DIR__ . '/flags/lk.png'],
'ls' => ['name' => 'Lesotho', 'path' => __DIR__ . '/flags/ls.png'],
'lt' => ['name' => 'Lithuania', 'path' => __DIR__ . '/flags/lt.png'],
'lu' => ['name' => 'Luxembourg', 'path' => __DIR__ . '/flags/lu.png'],
'lv' => ['name' => 'Latvia', 'path' => __DIR__ . '/flags/lv.png'],
'ma' => ['name' => 'Morocco', 'path' => __DIR__ . '/flags/ma.png'],
'mc' => ['name' => 'Monaco', 'path' => __DIR__ . '/flags/mc.png'],
'md' => ['name' => 'Moldova', 'path' => __DIR__ . '/flags/md.png'],
'mg' => ['name' => 'Madagascar', 'path' => __DIR__ . '/flags/mg.png'],
'mv' => ['name' => 'Maldives', 'path' => __DIR__ . '/flags/mv.png'],
'mx' => ['name' => 'Mexico', 'path' => __DIR__ . '/flags/mx.png'],
'mh' => ['name' => 'Marshall Islands', 'path' => __DIR__ . '/flags/mh.png'],
'mk' => ['name' => 'North Macedonia', 'path' => __DIR__ . '/flags/mk.png'],
'ml' => ['name' => 'Mali', 'path' => __DIR__ . '/flags/ml.png'],
'mt' => ['name' => 'Malta', 'path' => __DIR__ . '/flags/mt.png'],
'mm' => ['name' => 'Myanmar', 'path' => __DIR__ . '/flags/mm.png'],
'me' => ['name' => 'Montenegro', 'path' => __DIR__ . '/flags/me.png'],
'mn' => ['name' => 'Mongolia', 'path' => __DIR__ . '/flags/mn.png'],
'mz' => ['name' => 'Mozambique', 'path' => __DIR__ . '/flags/mz.png'],
'mr' => ['name' => 'Mauritania', 'path' => __DIR__ . '/flags/mr.png'],
'mu' => ['name' => 'Mauritius', 'path' => __DIR__ . '/flags/mu.png'],
'mw' => ['name' => 'Malawi', 'path' => __DIR__ . '/flags/mw.png'],
'my' => ['name' => 'Malaysia', 'path' => __DIR__ . '/flags/my.png'],
'na' => ['name' => 'Namibia', 'path' => __DIR__ . '/flags/na.png'],
'ne' => ['name' => 'Niger', 'path' => __DIR__ . '/flags/ne.png'],
'ng' => ['name' => 'Nigeria', 'path' => __DIR__ . '/flags/ng.png'],
'ni' => ['name' => 'Nicaragua', 'path' => __DIR__ . '/flags/ni.png'],
'nl' => ['name' => 'Netherlands', 'path' => __DIR__ . '/flags/nl.png'],
'no' => ['name' => 'Norway', 'path' => __DIR__ . '/flags/no.png'],
'np' => ['name' => 'Nepal', 'path' => __DIR__ . '/flags/np.png'],
'nr' => ['name' => 'Nauru', 'path' => __DIR__ . '/flags/nr.png'],
'nz' => ['name' => 'New Zealand', 'path' => __DIR__ . '/flags/nz.png'],
'om' => ['name' => 'Oman', 'path' => __DIR__ . '/flags/om.png'],
'pk' => ['name' => 'Pakistan', 'path' => __DIR__ . '/flags/pk.png'],
'pa' => ['name' => 'Panama', 'path' => __DIR__ . '/flags/pa.png'],
'pe' => ['name' => 'Peru', 'path' => __DIR__ . '/flags/pe.png'],
'ph' => ['name' => 'Philippines', 'path' => __DIR__ . '/flags/ph.png'],
'pw' => ['name' => 'Palau', 'path' => __DIR__ . '/flags/pw.png'],
'pg' => ['name' => 'Papua New Guinea', 'path' => __DIR__ . '/flags/pg.png'],
'pl' => ['name' => 'Poland', 'path' => __DIR__ . '/flags/pl.png'],
'kp' => ['name' => 'North Korea', 'path' => __DIR__ . '/flags/kp.png'],
'pt' => ['name' => 'Portugal', 'path' => __DIR__ . '/flags/pt.png'],
'py' => ['name' => 'Paraguay', 'path' => __DIR__ . '/flags/py.png'],
'qa' => ['name' => 'Qatar', 'path' => __DIR__ . '/flags/qa.png'],
'ro' => ['name' => 'Romania', 'path' => __DIR__ . '/flags/ro.png'],
'ru' => ['name' => 'Russia', 'path' => __DIR__ . '/flags/ru.png'],
'rw' => ['name' => 'Rwanda', 'path' => __DIR__ . '/flags/rw.png'],
'sa' => ['name' => 'Saudi Arabia', 'path' => __DIR__ . '/flags/sa.png'],
'sd' => ['name' => 'Sudan', 'path' => __DIR__ . '/flags/sd.png'],
'sn' => ['name' => 'Senegal', 'path' => __DIR__ . '/flags/sn.png'],
'sg' => ['name' => 'Singapore', 'path' => __DIR__ . '/flags/sg.png'],
'sb' => ['name' => 'Solomon Islands', 'path' => __DIR__ . '/flags/sb.png'],
'sl' => ['name' => 'Sierra Leone', 'path' => __DIR__ . '/flags/sl.png'],
'sv' => ['name' => 'El Salvador', 'path' => __DIR__ . '/flags/sv.png'],
'sm' => ['name' => 'San Marino', 'path' => __DIR__ . '/flags/sm.png'],
'so' => ['name' => 'Somalia', 'path' => __DIR__ . '/flags/so.png'],
'rs' => ['name' => 'Serbia', 'path' => __DIR__ . '/flags/rs.png'],
'ss' => ['name' => 'South Sudan', 'path' => __DIR__ . '/flags/ss.png'],
'st' => ['name' => 'Sao Tome and Principe', 'path' => __DIR__ . '/flags/st.png'],
'sr' => ['name' => 'Suriname', 'path' => __DIR__ . '/flags/sr.png'],
'sk' => ['name' => 'Slovakia', 'path' => __DIR__ . '/flags/sk.png'],
'si' => ['name' => 'Slovenia', 'path' => __DIR__ . '/flags/si.png'],
'se' => ['name' => 'Sweden', 'path' => __DIR__ . '/flags/se.png'],
'sz' => ['name' => 'Eswatini', 'path' => __DIR__ . '/flags/sz.png'],
'sc' => ['name' => 'Seychelles', 'path' => __DIR__ . '/flags/sc.png'],
'sy' => ['name' => 'Syria', 'path' => __DIR__ . '/flags/sy.png'],
'td' => ['name' => 'Chad', 'path' => __DIR__ . '/flags/td.png'],
'tg' => ['name' => 'Togo', 'path' => __DIR__ . '/flags/tg.png'],
'th' => ['name' => 'Thailand', 'path' => __DIR__ . '/flags/th.png'],
'tj' => ['name' => 'Tajikistan', 'path' => __DIR__ . '/flags/tj.png'],
'tm' => ['name' => 'Turkmenistan', 'path' => __DIR__ . '/flags/tm.png'],
'tl' => ['name' => 'Timor-Leste', 'path' => __DIR__ . '/flags/tl.png'],
'to' => ['name' => 'Tonga', 'path' => __DIR__ . '/flags/to.png'],
'tt' => ['name' => 'Trinidad and Tobago', 'path' => __DIR__ . '/flags/tt.png'],
'tn' => ['name' => 'Tunisia', 'path' => __DIR__ . '/flags/tn.png'],
'tr' => ['name' => 'Turkey', 'path' => __DIR__ . '/flags/tr.png'],
'tv' => ['name' => 'Tuvalu', 'path' => __DIR__ . '/flags/tv.png'],
'tz' => ['name' => 'Tanzania', 'path' => __DIR__ . '/flags/tz.png'],
'ug' => ['name' => 'Uganda', 'path' => __DIR__ . '/flags/ug.png'],
'ua' => ['name' => 'Ukraine', 'path' => __DIR__ . '/flags/ua.png'],
'uy' => ['name' => 'Uruguay', 'path' => __DIR__ . '/flags/uy.png'],
'us' => ['name' => 'United States', 'path' => __DIR__ . '/flags/us.png'],
'uz' => ['name' => 'Uzbekistan', 'path' => __DIR__ . '/flags/uz.png'],
'va' => ['name' => 'Vatican City', 'path' => __DIR__ . '/flags/va.png'],
'vc' => ['name' => 'Saint Vincent and the Grenadines', 'path' => __DIR__ . '/flags/vc.png'],
've' => ['name' => 'Venezuela', 'path' => __DIR__ . '/flags/ve.png'],
'vn' => ['name' => 'Vietnam', 'path' => __DIR__ . '/flags/vn.png'],
'vu' => ['name' => 'Vanuatu', 'path' => __DIR__ . '/flags/vu.png'],
'ws' => ['name' => 'Samoa', 'path' => __DIR__ . '/flags/ws.png'],
'ye' => ['name' => 'Yemen', 'path' => __DIR__ . '/flags/ye.png'],
'za' => ['name' => 'South Africa', 'path' => __DIR__ . '/flags/za.png'],
'zm' => ['name' => 'Zambia', 'path' => __DIR__ . '/flags/zm.png'],
'zw' => ['name' => 'Zimbabwe', 'path' => __DIR__ . '/flags/zw.png'],
'af' => ['name' => 'Afghanistan', 'path' => __DIR__.'/flags/af.png'],
'ao' => ['name' => 'Angola', 'path' => __DIR__.'/flags/ao.png'],
'al' => ['name' => 'Albania', 'path' => __DIR__.'/flags/al.png'],
'ad' => ['name' => 'Andorra', 'path' => __DIR__.'/flags/ad.png'],
'ae' => ['name' => 'United Arab Emirates', 'path' => __DIR__.'/flags/ae.png'],
'ar' => ['name' => 'Argentina', 'path' => __DIR__.'/flags/ar.png'],
'am' => ['name' => 'Armenia', 'path' => __DIR__.'/flags/am.png'],
'ag' => ['name' => 'Antigua and Barbuda', 'path' => __DIR__.'/flags/ag.png'],
'au' => ['name' => 'Australia', 'path' => __DIR__.'/flags/au.png'],
'at' => ['name' => 'Austria', 'path' => __DIR__.'/flags/at.png'],
'az' => ['name' => 'Azerbaijan', 'path' => __DIR__.'/flags/az.png'],
'bi' => ['name' => 'Burundi', 'path' => __DIR__.'/flags/bi.png'],
'be' => ['name' => 'Belgium', 'path' => __DIR__.'/flags/be.png'],
'bj' => ['name' => 'Benin', 'path' => __DIR__.'/flags/bj.png'],
'bf' => ['name' => 'Burkina Faso', 'path' => __DIR__.'/flags/bf.png'],
'bd' => ['name' => 'Bangladesh', 'path' => __DIR__.'/flags/bd.png'],
'bg' => ['name' => 'Bulgaria', 'path' => __DIR__.'/flags/bg.png'],
'bh' => ['name' => 'Bahrain', 'path' => __DIR__.'/flags/bh.png'],
'bs' => ['name' => 'Bahamas', 'path' => __DIR__.'/flags/bs.png'],
'ba' => ['name' => 'Bosnia and Herzegovina', 'path' => __DIR__.'/flags/ba.png'],
'by' => ['name' => 'Belarus', 'path' => __DIR__.'/flags/by.png'],
'bz' => ['name' => 'Belize', 'path' => __DIR__.'/flags/bz.png'],
'bo' => ['name' => 'Bolivia', 'path' => __DIR__.'/flags/bo.png'],
'br' => ['name' => 'Brazil', 'path' => __DIR__.'/flags/br.png'],
'bb' => ['name' => 'Barbados', 'path' => __DIR__.'/flags/bb.png'],
'bn' => ['name' => 'Brunei Darussalam', 'path' => __DIR__.'/flags/bn.png'],
'bt' => ['name' => 'Bhutan', 'path' => __DIR__.'/flags/bt.png'],
'bw' => ['name' => 'Botswana', 'path' => __DIR__.'/flags/bw.png'],
'cf' => ['name' => 'Central African Republic', 'path' => __DIR__.'/flags/cf.png'],
'ca' => ['name' => 'Canada', 'path' => __DIR__.'/flags/ca.png'],
'ch' => ['name' => 'Switzerland', 'path' => __DIR__.'/flags/ch.png'],
'cl' => ['name' => 'Chile', 'path' => __DIR__.'/flags/cl.png'],
'cn' => ['name' => 'China', 'path' => __DIR__.'/flags/cn.png'],
'ci' => ['name' => 'Côte d\'Ivoire', 'path' => __DIR__.'/flags/ci.png'],
'cm' => ['name' => 'Cameroon', 'path' => __DIR__.'/flags/cm.png'],
'cd' => ['name' => 'Democratic Republic of the Congo', 'path' => __DIR__.'/flags/cd.png'],
'cg' => ['name' => 'Republic of the Congo', 'path' => __DIR__.'/flags/cg.png'],
'co' => ['name' => 'Colombia', 'path' => __DIR__.'/flags/co.png'],
'km' => ['name' => 'Comoros', 'path' => __DIR__.'/flags/km.png'],
'cv' => ['name' => 'Cape Verde', 'path' => __DIR__.'/flags/cv.png'],
'cr' => ['name' => 'Costa Rica', 'path' => __DIR__.'/flags/cr.png'],
'cu' => ['name' => 'Cuba', 'path' => __DIR__.'/flags/cu.png'],
'cy' => ['name' => 'Cyprus', 'path' => __DIR__.'/flags/cy.png'],
'cz' => ['name' => 'Czech Republic', 'path' => __DIR__.'/flags/cz.png'],
'de' => ['name' => 'Germany', 'path' => __DIR__.'/flags/de.png'],
'dj' => ['name' => 'Djibouti', 'path' => __DIR__.'/flags/dj.png'],
'dm' => ['name' => 'Dominica', 'path' => __DIR__.'/flags/dm.png'],
'dk' => ['name' => 'Denmark', 'path' => __DIR__.'/flags/dk.png'],
'do' => ['name' => 'Dominican Republic', 'path' => __DIR__.'/flags/do.png'],
'dz' => ['name' => 'Algeria', 'path' => __DIR__.'/flags/dz.png'],
'ec' => ['name' => 'Ecuador', 'path' => __DIR__.'/flags/ec.png'],
'eg' => ['name' => 'Egypt', 'path' => __DIR__.'/flags/eg.png'],
'er' => ['name' => 'Eritrea', 'path' => __DIR__.'/flags/er.png'],
'es' => ['name' => 'Spain', 'path' => __DIR__.'/flags/es.png'],
'ee' => ['name' => 'Estonia', 'path' => __DIR__.'/flags/ee.png'],
'et' => ['name' => 'Ethiopia', 'path' => __DIR__.'/flags/et.png'],
'fi' => ['name' => 'Finland', 'path' => __DIR__.'/flags/fi.png'],
'fj' => ['name' => 'Fiji', 'path' => __DIR__.'/flags/fj.png'],
'fr' => ['name' => 'France', 'path' => __DIR__.'/flags/fr.png'],
'fm' => ['name' => 'Micronesia (Federated States of)', 'path' => __DIR__.'/flags/fm.png'],
'ga' => ['name' => 'Gabon', 'path' => __DIR__.'/flags/ga.png'],
'gb' => ['name' => 'United Kingdom', 'path' => __DIR__.'/flags/gb.png'],
'ge' => ['name' => 'Georgia', 'path' => __DIR__.'/flags/ge.png'],
'gh' => ['name' => 'Ghana', 'path' => __DIR__.'/flags/gh.png'],
'gn' => ['name' => 'Guinea', 'path' => __DIR__.'/flags/gn.png'],
'gm' => ['name' => 'Gambia', 'path' => __DIR__.'/flags/gm.png'],
'gw' => ['name' => 'Guinea-Bissau', 'path' => __DIR__.'/flags/gw.png'],
'gq' => ['name' => 'Equatorial Guinea', 'path' => __DIR__.'/flags/gq.png'],
'gr' => ['name' => 'Greece', 'path' => __DIR__.'/flags/gr.png'],
'gd' => ['name' => 'Grenada', 'path' => __DIR__.'/flags/gd.png'],
'gt' => ['name' => 'Guatemala', 'path' => __DIR__.'/flags/gt.png'],
'gy' => ['name' => 'Guyana', 'path' => __DIR__.'/flags/gy.png'],
'hn' => ['name' => 'Honduras', 'path' => __DIR__.'/flags/hn.png'],
'hr' => ['name' => 'Croatia', 'path' => __DIR__.'/flags/hr.png'],
'ht' => ['name' => 'Haiti', 'path' => __DIR__.'/flags/ht.png'],
'hu' => ['name' => 'Hungary', 'path' => __DIR__.'/flags/hu.png'],
'id' => ['name' => 'Indonesia', 'path' => __DIR__.'/flags/id.png'],
'in' => ['name' => 'India', 'path' => __DIR__.'/flags/in.png'],
'ie' => ['name' => 'Ireland', 'path' => __DIR__.'/flags/ie.png'],
'ir' => ['name' => 'Iran (Islamic Republic of)', 'path' => __DIR__.'/flags/ir.png'],
'iq' => ['name' => 'Iraq', 'path' => __DIR__.'/flags/iq.png'],
'is' => ['name' => 'Iceland', 'path' => __DIR__.'/flags/is.png'],
'il' => ['name' => 'Israel', 'path' => __DIR__.'/flags/il.png'],
'it' => ['name' => 'Italy', 'path' => __DIR__.'/flags/it.png'],
'jm' => ['name' => 'Jamaica', 'path' => __DIR__.'/flags/jm.png'],
'jo' => ['name' => 'Jordan', 'path' => __DIR__.'/flags/jo.png'],
'jp' => ['name' => 'Japan', 'path' => __DIR__.'/flags/jp.png'],
'kz' => ['name' => 'Kazakhstan', 'path' => __DIR__.'/flags/kz.png'],
'ke' => ['name' => 'Kenya', 'path' => __DIR__.'/flags/ke.png'],
'kg' => ['name' => 'Kyrgyzstan', 'path' => __DIR__.'/flags/kg.png'],
'kh' => ['name' => 'Cambodia', 'path' => __DIR__.'/flags/kh.png'],
'ki' => ['name' => 'Kiribati', 'path' => __DIR__.'/flags/ki.png'],
'kn' => ['name' => 'Saint Kitts and Nevis', 'path' => __DIR__.'/flags/kn.png'],
'kr' => ['name' => 'South Korea', 'path' => __DIR__.'/flags/kr.png'],
'kw' => ['name' => 'Kuwait', 'path' => __DIR__.'/flags/kw.png'],
'la' => ['name' => 'Lao People\'s Democratic Republic', 'path' => __DIR__.'/flags/la.png'],
'lb' => ['name' => 'Lebanon', 'path' => __DIR__.'/flags/lb.png'],
'lr' => ['name' => 'Liberia', 'path' => __DIR__.'/flags/lr.png'],
'ly' => ['name' => 'Libya', 'path' => __DIR__.'/flags/ly.png'],
'lc' => ['name' => 'Saint Lucia', 'path' => __DIR__.'/flags/lc.png'],
'li' => ['name' => 'Liechtenstein', 'path' => __DIR__.'/flags/li.png'],
'lk' => ['name' => 'Sri Lanka', 'path' => __DIR__.'/flags/lk.png'],
'ls' => ['name' => 'Lesotho', 'path' => __DIR__.'/flags/ls.png'],
'lt' => ['name' => 'Lithuania', 'path' => __DIR__.'/flags/lt.png'],
'lu' => ['name' => 'Luxembourg', 'path' => __DIR__.'/flags/lu.png'],
'lv' => ['name' => 'Latvia', 'path' => __DIR__.'/flags/lv.png'],
'ma' => ['name' => 'Morocco', 'path' => __DIR__.'/flags/ma.png'],
'mc' => ['name' => 'Monaco', 'path' => __DIR__.'/flags/mc.png'],
'md' => ['name' => 'Moldova', 'path' => __DIR__.'/flags/md.png'],
'mg' => ['name' => 'Madagascar', 'path' => __DIR__.'/flags/mg.png'],
'mv' => ['name' => 'Maldives', 'path' => __DIR__.'/flags/mv.png'],
'mx' => ['name' => 'Mexico', 'path' => __DIR__.'/flags/mx.png'],
'mh' => ['name' => 'Marshall Islands', 'path' => __DIR__.'/flags/mh.png'],
'mk' => ['name' => 'North Macedonia', 'path' => __DIR__.'/flags/mk.png'],
'ml' => ['name' => 'Mali', 'path' => __DIR__.'/flags/ml.png'],
'mt' => ['name' => 'Malta', 'path' => __DIR__.'/flags/mt.png'],
'mm' => ['name' => 'Myanmar', 'path' => __DIR__.'/flags/mm.png'],
'me' => ['name' => 'Montenegro', 'path' => __DIR__.'/flags/me.png'],
'mn' => ['name' => 'Mongolia', 'path' => __DIR__.'/flags/mn.png'],
'mz' => ['name' => 'Mozambique', 'path' => __DIR__.'/flags/mz.png'],
'mr' => ['name' => 'Mauritania', 'path' => __DIR__.'/flags/mr.png'],
'mu' => ['name' => 'Mauritius', 'path' => __DIR__.'/flags/mu.png'],
'mw' => ['name' => 'Malawi', 'path' => __DIR__.'/flags/mw.png'],
'my' => ['name' => 'Malaysia', 'path' => __DIR__.'/flags/my.png'],
'na' => ['name' => 'Namibia', 'path' => __DIR__.'/flags/na.png'],
'ne' => ['name' => 'Niger', 'path' => __DIR__.'/flags/ne.png'],
'ng' => ['name' => 'Nigeria', 'path' => __DIR__.'/flags/ng.png'],
'ni' => ['name' => 'Nicaragua', 'path' => __DIR__.'/flags/ni.png'],
'nl' => ['name' => 'Netherlands', 'path' => __DIR__.'/flags/nl.png'],
'no' => ['name' => 'Norway', 'path' => __DIR__.'/flags/no.png'],
'np' => ['name' => 'Nepal', 'path' => __DIR__.'/flags/np.png'],
'nr' => ['name' => 'Nauru', 'path' => __DIR__.'/flags/nr.png'],
'nz' => ['name' => 'New Zealand', 'path' => __DIR__.'/flags/nz.png'],
'om' => ['name' => 'Oman', 'path' => __DIR__.'/flags/om.png'],
'pk' => ['name' => 'Pakistan', 'path' => __DIR__.'/flags/pk.png'],
'pa' => ['name' => 'Panama', 'path' => __DIR__.'/flags/pa.png'],
'pe' => ['name' => 'Peru', 'path' => __DIR__.'/flags/pe.png'],
'ph' => ['name' => 'Philippines', 'path' => __DIR__.'/flags/ph.png'],
'pw' => ['name' => 'Palau', 'path' => __DIR__.'/flags/pw.png'],
'pg' => ['name' => 'Papua New Guinea', 'path' => __DIR__.'/flags/pg.png'],
'pl' => ['name' => 'Poland', 'path' => __DIR__.'/flags/pl.png'],
'kp' => ['name' => 'North Korea', 'path' => __DIR__.'/flags/kp.png'],
'pt' => ['name' => 'Portugal', 'path' => __DIR__.'/flags/pt.png'],
'py' => ['name' => 'Paraguay', 'path' => __DIR__.'/flags/py.png'],
'qa' => ['name' => 'Qatar', 'path' => __DIR__.'/flags/qa.png'],
'ro' => ['name' => 'Romania', 'path' => __DIR__.'/flags/ro.png'],
'ru' => ['name' => 'Russia', 'path' => __DIR__.'/flags/ru.png'],
'rw' => ['name' => 'Rwanda', 'path' => __DIR__.'/flags/rw.png'],
'sa' => ['name' => 'Saudi Arabia', 'path' => __DIR__.'/flags/sa.png'],
'sd' => ['name' => 'Sudan', 'path' => __DIR__.'/flags/sd.png'],
'sn' => ['name' => 'Senegal', 'path' => __DIR__.'/flags/sn.png'],
'sg' => ['name' => 'Singapore', 'path' => __DIR__.'/flags/sg.png'],
'sb' => ['name' => 'Solomon Islands', 'path' => __DIR__.'/flags/sb.png'],
'sl' => ['name' => 'Sierra Leone', 'path' => __DIR__.'/flags/sl.png'],
'sv' => ['name' => 'El Salvador', 'path' => __DIR__.'/flags/sv.png'],
'sm' => ['name' => 'San Marino', 'path' => __DIR__.'/flags/sm.png'],
'so' => ['name' => 'Somalia', 'path' => __DIR__.'/flags/so.png'],
'rs' => ['name' => 'Serbia', 'path' => __DIR__.'/flags/rs.png'],
'ss' => ['name' => 'South Sudan', 'path' => __DIR__.'/flags/ss.png'],
'st' => ['name' => 'Sao Tome and Principe', 'path' => __DIR__.'/flags/st.png'],
'sr' => ['name' => 'Suriname', 'path' => __DIR__.'/flags/sr.png'],
'sk' => ['name' => 'Slovakia', 'path' => __DIR__.'/flags/sk.png'],
'si' => ['name' => 'Slovenia', 'path' => __DIR__.'/flags/si.png'],
'se' => ['name' => 'Sweden', 'path' => __DIR__.'/flags/se.png'],
'sz' => ['name' => 'Eswatini', 'path' => __DIR__.'/flags/sz.png'],
'sc' => ['name' => 'Seychelles', 'path' => __DIR__.'/flags/sc.png'],
'sy' => ['name' => 'Syria', 'path' => __DIR__.'/flags/sy.png'],
'td' => ['name' => 'Chad', 'path' => __DIR__.'/flags/td.png'],
'tg' => ['name' => 'Togo', 'path' => __DIR__.'/flags/tg.png'],
'th' => ['name' => 'Thailand', 'path' => __DIR__.'/flags/th.png'],
'tj' => ['name' => 'Tajikistan', 'path' => __DIR__.'/flags/tj.png'],
'tm' => ['name' => 'Turkmenistan', 'path' => __DIR__.'/flags/tm.png'],
'tl' => ['name' => 'Timor-Leste', 'path' => __DIR__.'/flags/tl.png'],
'to' => ['name' => 'Tonga', 'path' => __DIR__.'/flags/to.png'],
'tt' => ['name' => 'Trinidad and Tobago', 'path' => __DIR__.'/flags/tt.png'],
'tn' => ['name' => 'Tunisia', 'path' => __DIR__.'/flags/tn.png'],
'tr' => ['name' => 'Turkey', 'path' => __DIR__.'/flags/tr.png'],
'tv' => ['name' => 'Tuvalu', 'path' => __DIR__.'/flags/tv.png'],
'tz' => ['name' => 'Tanzania', 'path' => __DIR__.'/flags/tz.png'],
'ug' => ['name' => 'Uganda', 'path' => __DIR__.'/flags/ug.png'],
'ua' => ['name' => 'Ukraine', 'path' => __DIR__.'/flags/ua.png'],
'uy' => ['name' => 'Uruguay', 'path' => __DIR__.'/flags/uy.png'],
'us' => ['name' => 'United States', 'path' => __DIR__.'/flags/us.png'],
'uz' => ['name' => 'Uzbekistan', 'path' => __DIR__.'/flags/uz.png'],
'va' => ['name' => 'Vatican City', 'path' => __DIR__.'/flags/va.png'],
'vc' => ['name' => 'Saint Vincent and the Grenadines', 'path' => __DIR__.'/flags/vc.png'],
've' => ['name' => 'Venezuela', 'path' => __DIR__.'/flags/ve.png'],
'vn' => ['name' => 'Vietnam', 'path' => __DIR__.'/flags/vn.png'],
'vu' => ['name' => 'Vanuatu', 'path' => __DIR__.'/flags/vu.png'],
'ws' => ['name' => 'Samoa', 'path' => __DIR__.'/flags/ws.png'],
'ye' => ['name' => 'Yemen', 'path' => __DIR__.'/flags/ye.png'],
'za' => ['name' => 'South Africa', 'path' => __DIR__.'/flags/za.png'],
'zm' => ['name' => 'Zambia', 'path' => __DIR__.'/flags/zm.png'],
'zw' => ['name' => 'Zimbabwe', 'path' => __DIR__.'/flags/zw.png'],
];
+3 -3
View File
@@ -2,9 +2,9 @@
return [
// Codes based on: https://github.com/matomo-org/device-detector/blob/master/Parser/Client/Browser.php
'AND' => __DIR__ . '/os/android.png',
'ATV' => __DIR__ . '/os/apple-tv.png',
'COS' => __DIR__ . '/os/chrome-os.png',
'AND' => __DIR__.'/os/android.png',
'ATV' => __DIR__.'/os/apple-tv.png',
'COS' => __DIR__.'/os/chrome-os.png',
/*
'AIX' => 'AIX',
+23 -25
View File
@@ -16,7 +16,6 @@ $auth = Config::getParam('auth', []);
* attributes => list of attributes
* indexes => list of indexes
*/
$commonCollections = [
'users' => [
'$collection' => ID::custom(Database::METADATA),
@@ -253,7 +252,7 @@ $commonCollections = [
'default' => null,
'array' => false,
'filters' => ['datetime'],
]
],
],
'indexes' => [
[
@@ -410,7 +409,7 @@ $commonCollections = [
'default' => null,
'array' => false,
'filters' => [],
]
],
],
'indexes' => [
[
@@ -1278,7 +1277,7 @@ $commonCollections = [
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
]
],
],
'stats' => [
@@ -1454,7 +1453,7 @@ $commonCollections = [
'default' => null,
'array' => false,
'filters' => ['json', 'encrypt'],
]
],
],
'indexes' => [
[
@@ -1470,7 +1469,7 @@ $commonCollections = [
'attributes' => ['name'],
'lengths' => [128],
'orders' => [Database::ORDER_ASC],
]
],
],
],
@@ -1811,7 +1810,7 @@ $commonCollections = [
'attributes' => ['topicInternalId'],
'lengths' => [],
'orders' => [],
]
],
],
],
@@ -1924,7 +1923,7 @@ $commonCollections = [
'orders' => [],
],
],
]
],
];
$projectCollections = array_merge([
@@ -2667,7 +2666,7 @@ $projectCollections = array_merge([
'default' => false,
'array' => false,
'filters' => [],
]
],
],
'indexes' => [
[
@@ -2858,7 +2857,7 @@ $projectCollections = array_merge([
'default' => '',
'array' => false,
'filters' => [],
]
],
],
'indexes' => [
[
@@ -2867,7 +2866,7 @@ $projectCollections = array_merge([
'attributes' => ['deploymentId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
]
],
],
],
@@ -3102,7 +3101,7 @@ $projectCollections = array_merge([
'required' => true,
'default' => null,
'array' => false,
'filters' => [ 'encrypt' ]
'filters' => ['encrypt'],
],
[
'$id' => ID::custom('search'),
@@ -3186,7 +3185,7 @@ $projectCollections = array_merge([
'array' => false,
'filters' => [],
],
],
],
'indexes' => [
[
'$id' => '_key_accessedAt',
@@ -3308,7 +3307,7 @@ $projectCollections = array_merge([
'default' => null,
'array' => false,
'filters' => [],
]
],
],
'indexes' => [
[
@@ -3338,7 +3337,7 @@ $projectCollections = array_merge([
'attributes' => ['search'],
'lengths' => [],
'orders' => [],
]
],
],
],
], $commonCollections);
@@ -3748,7 +3747,7 @@ $consoleCollections = array_merge([
[
'$id' => ID::custom('_key_region_resourceType_resourceUpdatedAt'),
'type' => Database::INDEX_KEY,
'attributes' => ['region', 'resourceType','resourceUpdatedAt'],
'attributes' => ['region', 'resourceType', 'resourceUpdatedAt'],
'lengths' => [],
'orders' => [],
],
@@ -3843,7 +3842,7 @@ $consoleCollections = array_merge([
'default' => null,
'array' => false,
'filters' => [],
]
],
],
'indexes' => [
[
@@ -4185,7 +4184,7 @@ $consoleCollections = array_merge([
'attributes' => ['projectInternalId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
]
],
],
],
@@ -4312,7 +4311,7 @@ $consoleCollections = array_merge([
'default' => null,
'array' => false,
'filters' => [], //TODO: use json filter
]
],
],
'indexes' => [
[
@@ -4322,7 +4321,7 @@ $consoleCollections = array_merge([
'lengths' => [],
'orders' => [Database::ORDER_DESC],
],
]
],
],
], $commonCollections);
@@ -4588,7 +4587,7 @@ $bucketCollections = [
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
]
],
],
];
@@ -4711,15 +4710,14 @@ $dbCollections = [
'orders' => [Database::ORDER_ASC],
],
],
]
],
];
$collections = [
'projects' => $projectCollections,
'console' => $consoleCollections,
'console' => $consoleCollections,
'buckets' => $bucketCollections,
'databases' => $dbCollections
'databases' => $dbCollections,
];
return $collections;
+4 -4
View File
@@ -299,7 +299,7 @@ return [
Exception::AVATAR_SET_NOT_FOUND => [
'name' => Exception::AVATAR_SET_NOT_FOUND,
'description' => 'The requested avatar set could not be found.',
'code' => 404
'code' => 404,
],
Exception::AVATAR_NOT_FOUND => [
'name' => Exception::AVATAR_NOT_FOUND,
@@ -426,13 +426,13 @@ return [
Exception::DATABASE_NOT_FOUND => [
'name' => Exception::DATABASE_NOT_FOUND,
'description' => 'Database not found',
'code' => 404
'code' => 404,
],
Exception::DATABASE_ALREADY_EXISTS => [
'name' => Exception::DATABASE_ALREADY_EXISTS,
'description' => 'Database already exists',
'code' => 409
'code' => 409,
],
/** Collections */
@@ -702,5 +702,5 @@ return [
'name' => Exception::PROVIDER_INCORRECT_TYPE,
'description' => 'Provider with the request ID is of incorrect type: ',
'code' => 400,
]
],
];
+33 -33
View File
@@ -19,7 +19,7 @@ return [
'$description' => 'This event triggers when a session for a user is created.',
],
'delete' => [
'$description' => 'This event triggers when a session for a user is deleted.'
'$description' => 'This event triggers when a session for a user is deleted.',
],
],
'recovery' => [
@@ -30,7 +30,7 @@ return [
'$description' => 'This event triggers when a recovery token for a user is created.',
],
'update' => [
'$description' => 'This event triggers when a recovery token for a user is validated.'
'$description' => 'This event triggers when a recovery token for a user is validated.',
],
],
'verification' => [
@@ -41,7 +41,7 @@ return [
'$description' => 'This event triggers when a verification token for a user is created.',
],
'update' => [
'$description' => 'This event triggers when a verification token for a user is validated.'
'$description' => 'This event triggers when a verification token for a user is validated.',
],
],
'targets' => [
@@ -59,7 +59,7 @@ return [
],
],
'create' => [
'$description' => 'This event triggers when a user is created.'
'$description' => 'This event triggers when a user is created.',
],
'delete' => [
'$description' => 'This event triggers when a user is deleted.',
@@ -81,7 +81,7 @@ return [
'prefs' => [
'$description' => 'This event triggers when a user\'s preferences is updated.',
],
]
],
],
'databases' => [
'$model' => Response::MODEL_DATABASE,
@@ -99,10 +99,10 @@ return [
'$description' => 'This event triggers when a document is created.',
],
'delete' => [
'$description' => 'This event triggers when a document is deleted.'
'$description' => 'This event triggers when a document is deleted.',
],
'update' => [
'$description' => 'This event triggers when a document is updated.'
'$description' => 'This event triggers when a document is updated.',
],
],
'indexes' => [
@@ -113,8 +113,8 @@ return [
'$description' => 'This event triggers when an index is created.',
],
'delete' => [
'$description' => 'This event triggers when an index is deleted.'
]
'$description' => 'This event triggers when an index is deleted.',
],
],
'attributes' => [
'$model' => Response::MODEL_ATTRIBUTE,
@@ -124,28 +124,28 @@ return [
'$description' => 'This event triggers when an attribute is created.',
],
'delete' => [
'$description' => 'This event triggers when an attribute is deleted.'
]
'$description' => 'This event triggers when an attribute is deleted.',
],
],
'create' => [
'$description' => 'This event triggers when a collection is created.'
'$description' => 'This event triggers when a collection is created.',
],
'delete' => [
'$description' => 'This event triggers when a collection is deleted.',
],
'update' => [
'$description' => 'This event triggers when a collection is updated.',
]
],
],
'create' => [
'$description' => 'This event triggers when a database is created.'
'$description' => 'This event triggers when a database is created.',
],
'delete' => [
'$description' => 'This event triggers when a database is deleted.',
],
'update' => [
'$description' => 'This event triggers when a database is updated.',
]
],
],
'buckets' => [
'$model' => Response::MODEL_BUCKET,
@@ -159,21 +159,21 @@ return [
'$description' => 'This event triggers when a file is created.',
],
'delete' => [
'$description' => 'This event triggers when a file is deleted.'
'$description' => 'This event triggers when a file is deleted.',
],
'update' => [
'$description' => 'This event triggers when a file is updated.'
'$description' => 'This event triggers when a file is updated.',
],
],
'create' => [
'$description' => 'This event triggers when a bucket is created.'
'$description' => 'This event triggers when a bucket is created.',
],
'delete' => [
'$description' => 'This event triggers when a bucket is deleted.',
],
'update' => [
'$description' => 'This event triggers when a bucket is updated.',
]
],
],
'teams' => [
'$model' => Response::MODEL_TEAM,
@@ -187,17 +187,17 @@ return [
'$description' => 'This event triggers when a membership is created.',
],
'delete' => [
'$description' => 'This event triggers when a membership is deleted.'
'$description' => 'This event triggers when a membership is deleted.',
],
'update' => [
'$description' => 'This event triggers when a membership is updated.',
'status' => [
'$description' => 'This event triggers when a team memberships status is updated.'
]
'$description' => 'This event triggers when a team memberships status is updated.',
],
],
],
'create' => [
'$description' => 'This event triggers when a team is created.'
'$description' => 'This event triggers when a team is created.',
],
'delete' => [
'$description' => 'This event triggers when a team is deleted.',
@@ -207,7 +207,7 @@ return [
'prefs' => [
'$description' => 'This event triggers when a team\'s preferences are updated.',
],
]
],
],
'functions' => [
'$model' => Response::MODEL_FUNCTION,
@@ -221,10 +221,10 @@ return [
'$description' => 'This event triggers when a deployment is created.',
],
'delete' => [
'$description' => 'This event triggers when a deployment is deleted.'
'$description' => 'This event triggers when a deployment is deleted.',
],
'update' => [
'$description' => 'This event triggers when a deployment is updated.'
'$description' => 'This event triggers when a deployment is updated.',
],
],
'executions' => [
@@ -235,21 +235,21 @@ return [
'$description' => 'This event triggers when an execution is created.',
],
'delete' => [
'$description' => 'This event triggers when an execution is deleted.'
'$description' => 'This event triggers when an execution is deleted.',
],
'update' => [
'$description' => 'This event triggers when an execution is updated.'
'$description' => 'This event triggers when an execution is updated.',
],
],
'create' => [
'$description' => 'This event triggers when a function is created.'
'$description' => 'This event triggers when a function is created.',
],
'delete' => [
'$description' => 'This event triggers when a function is deleted.',
],
'update' => [
'$description' => 'This event triggers when a function is updated.',
]
],
],
'messages' => [
'$model' => Response::MODEL_MESSAGE,
@@ -269,7 +269,7 @@ return [
'$description' => 'This event triggers when a provider is updated.',
],
'delete' => [
'$description' => 'This event triggers when a provider is deleted.'
'$description' => 'This event triggers when a provider is deleted.',
],
],
'topics' => [
@@ -280,7 +280,7 @@ return [
'$description' => 'This event triggers when a provider is created.',
],
'delete' => [
'$description' => 'This event triggers when a provider is deleted.'
'$description' => 'This event triggers when a provider is deleted.',
],
'subscribers' => [
'$model' => Response::MODEL_SUBSCRIBER,
@@ -290,7 +290,7 @@ return [
'$description' => 'This event triggers when a subscriber is created.',
],
'delete' => [
'$description' => 'This event triggers when a subscriber is deleted.'
'$description' => 'This event triggers when a subscriber is deleted.',
],
],
],
+262 -263
View File
@@ -6,532 +6,531 @@
*
* Source:
* https://www.andiamo.co.uk/resources/iso-language-codes/
*
*/
return [
[
"code" => "af",
"name" => "Afrikaans",
'code' => 'af',
'name' => 'Afrikaans',
],
[
"code" => "ar-ae",
"name" => "Arabic (U.A.E.)",
'code' => 'ar-ae',
'name' => 'Arabic (U.A.E.)',
],
[
"code" => "ar-bh",
"name" => "Arabic (Bahrain)",
'code' => 'ar-bh',
'name' => 'Arabic (Bahrain)',
],
[
"code" => "ar-dz",
"name" => "Arabic (Algeria)",
'code' => 'ar-dz',
'name' => 'Arabic (Algeria)',
],
[
"code" => "ar-eg",
"name" => "Arabic (Egypt)",
'code' => 'ar-eg',
'name' => 'Arabic (Egypt)',
],
[
"code" => "ar-iq",
"name" => "Arabic (Iraq)",
'code' => 'ar-iq',
'name' => 'Arabic (Iraq)',
],
[
"code" => "ar-jo",
"name" => "Arabic (Jordan)",
'code' => 'ar-jo',
'name' => 'Arabic (Jordan)',
],
[
"code" => "ar-kw",
"name" => "Arabic (Kuwait)",
'code' => 'ar-kw',
'name' => 'Arabic (Kuwait)',
],
[
"code" => "ar-lb",
"name" => "Arabic (Lebanon)",
'code' => 'ar-lb',
'name' => 'Arabic (Lebanon)',
],
[
"code" => "ar-ly",
"name" => "Arabic (Libya)",
'code' => 'ar-ly',
'name' => 'Arabic (Libya)',
],
[
"code" => "ar-ma",
"name" => "Arabic (Morocco)",
'code' => 'ar-ma',
'name' => 'Arabic (Morocco)',
],
[
"code" => "ar-om",
"name" => "Arabic (Oman)",
'code' => 'ar-om',
'name' => 'Arabic (Oman)',
],
[
"code" => "ar-qa",
"name" => "Arabic (Qatar)",
'code' => 'ar-qa',
'name' => 'Arabic (Qatar)',
],
[
"code" => "ar-sa",
"name" => "Arabic (Saudi Arabia)",
'code' => 'ar-sa',
'name' => 'Arabic (Saudi Arabia)',
],
[
"code" => "ar-sy",
"name" => "Arabic (Syria)",
'code' => 'ar-sy',
'name' => 'Arabic (Syria)',
],
[
"code" => "ar-tn",
"name" => "Arabic (Tunisia)",
'code' => 'ar-tn',
'name' => 'Arabic (Tunisia)',
],
[
"code" => "ar-ye",
"name" => "Arabic (Yemen)",
'code' => 'ar-ye',
'name' => 'Arabic (Yemen)',
],
[
"code" => "as",
"name" => "Assamese",
'code' => 'as',
'name' => 'Assamese',
],
[
"code" => "az",
"name" => "Azerbaijani",
'code' => 'az',
'name' => 'Azerbaijani',
],
[
"code" => "be",
"name" => "Belarusian",
'code' => 'be',
'name' => 'Belarusian',
],
[
"code" => "bg",
"name" => "Bulgarian",
'code' => 'bg',
'name' => 'Bulgarian',
],
[
"code" => "bh",
"name" => "Bihari",
'code' => 'bh',
'name' => 'Bihari',
],
[
"code" => "bn",
"name" => "Bengali",
'code' => 'bn',
'name' => 'Bengali',
],
[
"code" => "bs",
"name" => "Bosnian",
'code' => 'bs',
'name' => 'Bosnian',
],
[
"code" => "ca",
"name" => "Catalan",
'code' => 'ca',
'name' => 'Catalan',
],
[
"code" => "cs",
"name" => "Czech",
'code' => 'cs',
'name' => 'Czech',
],
[
"code" => "cy",
"name" => "Welsh",
'code' => 'cy',
'name' => 'Welsh',
],
[
"code" => "da",
"name" => "Danish",
'code' => 'da',
'name' => 'Danish',
],
[
"code" => "de",
"name" => "German (Standard)",
'code' => 'de',
'name' => 'German (Standard)',
],
[
"code" => "de-at",
"name" => "German (Austria)",
'code' => 'de-at',
'name' => 'German (Austria)',
],
[
"code" => "de-ch",
"name" => "German (Switzerland)",
'code' => 'de-ch',
'name' => 'German (Switzerland)',
],
[
"code" => "de-li",
"name" => "German (Liechtenstein)",
'code' => 'de-li',
'name' => 'German (Liechtenstein)',
],
[
"code" => "de-lu",
"name" => "German (Luxembourg)",
'code' => 'de-lu',
'name' => 'German (Luxembourg)',
],
[
"code" => "el",
"name" => "Greek",
'code' => 'el',
'name' => 'Greek',
],
[
"code" => "en",
"name" => "English",
'code' => 'en',
'name' => 'English',
],
[
"code" => "en-au",
"name" => "English (Australia)",
'code' => 'en-au',
'name' => 'English (Australia)',
],
[
"code" => "en-bz",
"name" => "English (Belize)",
'code' => 'en-bz',
'name' => 'English (Belize)',
],
[
"code" => "en-ca",
"name" => "English (Canada)",
'code' => 'en-ca',
'name' => 'English (Canada)',
],
[
"code" => "en-gb",
"name" => "English (United Kingdom)",
'code' => 'en-gb',
'name' => 'English (United Kingdom)',
],
[
"code" => "en-ie",
"name" => "English (Ireland)",
'code' => 'en-ie',
'name' => 'English (Ireland)',
],
[
"code" => "en-jm",
"name" => "English (Jamaica)",
'code' => 'en-jm',
'name' => 'English (Jamaica)',
],
[
"code" => "en-nz",
"name" => "English (New Zealand)",
'code' => 'en-nz',
'name' => 'English (New Zealand)',
],
[
"code" => "en-tt",
"name" => "English (Trinidad)",
'code' => 'en-tt',
'name' => 'English (Trinidad)',
],
[
"code" => "en-us",
"name" => "English (United States)",
'code' => 'en-us',
'name' => 'English (United States)',
],
[
"code" => "en-za",
"name" => "English (South Africa)",
'code' => 'en-za',
'name' => 'English (South Africa)',
],
[
"code" => "eo",
"name" => "Esperanto",
'code' => 'eo',
'name' => 'Esperanto',
],
[
"code" => "es",
"name" => "Spanish (Spain)",
'code' => 'es',
'name' => 'Spanish (Spain)',
],
[
"code" => "es-ar",
"name" => "Spanish (Argentina)",
'code' => 'es-ar',
'name' => 'Spanish (Argentina)',
],
[
"code" => "es-bo",
"name" => "Spanish (Bolivia)",
'code' => 'es-bo',
'name' => 'Spanish (Bolivia)',
],
[
"code" => "es-cl",
"name" => "Spanish (Chile)",
'code' => 'es-cl',
'name' => 'Spanish (Chile)',
],
[
"code" => "es-co",
"name" => "Spanish (Colombia)",
'code' => 'es-co',
'name' => 'Spanish (Colombia)',
],
[
"code" => "es-cr",
"name" => "Spanish (Costa Rica)",
'code' => 'es-cr',
'name' => 'Spanish (Costa Rica)',
],
[
"code" => "es-do",
"name" => "Spanish (Dominican Republic)",
'code' => 'es-do',
'name' => 'Spanish (Dominican Republic)',
],
[
"code" => "es-ec",
"name" => "Spanish (Ecuador)",
'code' => 'es-ec',
'name' => 'Spanish (Ecuador)',
],
[
"code" => "es-gt",
"name" => "Spanish (Guatemala)",
'code' => 'es-gt',
'name' => 'Spanish (Guatemala)',
],
[
"code" => "es-hn",
"name" => "Spanish (Honduras)",
'code' => 'es-hn',
'name' => 'Spanish (Honduras)',
],
[
"code" => "es-mx",
"name" => "Spanish (Mexico)",
'code' => 'es-mx',
'name' => 'Spanish (Mexico)',
],
[
"code" => "es-ni",
"name" => "Spanish (Nicaragua)",
'code' => 'es-ni',
'name' => 'Spanish (Nicaragua)',
],
[
"code" => "es-pa",
"name" => "Spanish (Panama)",
'code' => 'es-pa',
'name' => 'Spanish (Panama)',
],
[
"code" => "es-pe",
"name" => "Spanish (Peru)",
'code' => 'es-pe',
'name' => 'Spanish (Peru)',
],
[
"code" => "es-pr",
"name" => "Spanish (Puerto Rico)",
'code' => 'es-pr',
'name' => 'Spanish (Puerto Rico)',
],
[
"code" => "es-py",
"name" => "Spanish (Paraguay)",
'code' => 'es-py',
'name' => 'Spanish (Paraguay)',
],
[
"code" => "es-sv",
"name" => "Spanish (El Salvador)",
'code' => 'es-sv',
'name' => 'Spanish (El Salvador)',
],
[
"code" => "es-uy",
"name" => "Spanish (Uruguay)",
'code' => 'es-uy',
'name' => 'Spanish (Uruguay)',
],
[
"code" => "es-ve",
"name" => "Spanish (Venezuela)",
'code' => 'es-ve',
'name' => 'Spanish (Venezuela)',
],
[
"code" => "et",
"name" => "Estonian",
'code' => 'et',
'name' => 'Estonian',
],
[
"code" => "eu",
"name" => "Basque",
'code' => 'eu',
'name' => 'Basque',
],
[
"code" => "fa",
"name" => "Farsi",
'code' => 'fa',
'name' => 'Farsi',
],
[
"code" => "fi",
"name" => "Finnish",
'code' => 'fi',
'name' => 'Finnish',
],
[
"code" => "fo",
"name" => "Faeroese",
'code' => 'fo',
'name' => 'Faeroese',
],
[
"code" => "fr",
"name" => "French (Standard)",
'code' => 'fr',
'name' => 'French (Standard)',
],
[
"code" => "fr-be",
"name" => "French (Belgium)",
'code' => 'fr-be',
'name' => 'French (Belgium)',
],
[
"code" => "fr-ca",
"name" => "French (Canada)",
'code' => 'fr-ca',
'name' => 'French (Canada)',
],
[
"code" => "fr-ch",
"name" => "French (Switzerland)",
'code' => 'fr-ch',
'name' => 'French (Switzerland)',
],
[
"code" => "fr-lu",
"name" => "French (Luxembourg)",
'code' => 'fr-lu',
'name' => 'French (Luxembourg)',
],
[
"code" => "ga",
"name" => "Irish",
'code' => 'ga',
'name' => 'Irish',
],
[
"code" => "gd",
"name" => "Gaelic (Scotland)",
'code' => 'gd',
'name' => 'Gaelic (Scotland)',
],
[
"code" => "he",
"name" => "Hebrew",
'code' => 'he',
'name' => 'Hebrew',
],
[
"code" => "hi",
"name" => "Hindi",
'code' => 'hi',
'name' => 'Hindi',
],
[
"code" => "hr",
"name" => "Croatian",
'code' => 'hr',
'name' => 'Croatian',
],
[
"code" => "hu",
"name" => "Hungarian",
'code' => 'hu',
'name' => 'Hungarian',
],
[
"code" => "id",
"name" => "Indonesian",
'code' => 'id',
'name' => 'Indonesian',
],
[
"code" => "is",
"name" => "Icelandic",
'code' => 'is',
'name' => 'Icelandic',
],
[
"code" => "it",
"name" => "Italian (Standard)",
'code' => 'it',
'name' => 'Italian (Standard)',
],
[
"code" => "it-ch",
"name" => "Italian (Switzerland)",
'code' => 'it-ch',
'name' => 'Italian (Switzerland)',
],
[
"code" => "ja",
"name" => "Japanese",
'code' => 'ja',
'name' => 'Japanese',
],
[
"code" => "ji",
"name" => "Yiddish",
'code' => 'ji',
'name' => 'Yiddish',
],
[
"code" => "ko",
"name" => "Korean",
'code' => 'ko',
'name' => 'Korean',
],
[
"code" => "ku",
"name" => "Kurdish",
'code' => 'ku',
'name' => 'Kurdish',
],
[
"code" => "lt",
"name" => "Lithuanian",
'code' => 'lt',
'name' => 'Lithuanian',
],
[
"code" => "lv",
"name" => "Latvian",
'code' => 'lv',
'name' => 'Latvian',
],
[
"code" => "mk",
"name" => "Macedonian (FYROM)",
'code' => 'mk',
'name' => 'Macedonian (FYROM)',
],
[
"code" => "ml",
"name" => "Malayalam",
'code' => 'ml',
'name' => 'Malayalam',
],
[
"code" => "ms",
"name" => "Malaysian",
'code' => 'ms',
'name' => 'Malaysian',
],
[
"code" => "mt",
"name" => "Maltese",
'code' => 'mt',
'name' => 'Maltese',
],
[
"code" => "nb",
"name" => "Norwegian (Bokmål)",
'code' => 'nb',
'name' => 'Norwegian (Bokmål)',
],
[
"code" => "ne",
"name" => "Nepali",
'code' => 'ne',
'name' => 'Nepali',
],
[
"code" => "nl",
"name" => "Dutch (Standard)",
'code' => 'nl',
'name' => 'Dutch (Standard)',
],
[
"code" => "nl-be",
"name" => "Dutch (Belgium)",
'code' => 'nl-be',
'name' => 'Dutch (Belgium)',
],
[
"code" => "nn",
"name" => "Norwegian (Nynorsk)",
'code' => 'nn',
'name' => 'Norwegian (Nynorsk)',
],
[
"code" => "no",
"name" => "Norwegian",
'code' => 'no',
'name' => 'Norwegian',
],
[
"code" => "pa",
"name" => "Punjabi",
'code' => 'pa',
'name' => 'Punjabi',
],
[
"code" => "pl",
"name" => "Polish",
'code' => 'pl',
'name' => 'Polish',
],
[
"code" => "pt",
"name" => "Portuguese (Portugal)",
'code' => 'pt',
'name' => 'Portuguese (Portugal)',
],
[
"code" => "pt-br",
"name" => "Portuguese (Brazil)",
'code' => 'pt-br',
'name' => 'Portuguese (Brazil)',
],
[
"code" => "rm",
"name" => "Rhaeto-Romanic",
'code' => 'rm',
'name' => 'Rhaeto-Romanic',
],
[
"code" => "ro",
"name" => "Romanian",
'code' => 'ro',
'name' => 'Romanian',
],
[
"code" => "ro-md",
"name" => "Romanian (Republic of Moldova)",
'code' => 'ro-md',
'name' => 'Romanian (Republic of Moldova)',
],
[
"code" => "ru",
"name" => "Russian",
'code' => 'ru',
'name' => 'Russian',
],
[
"code" => "ru-md",
"name" => "Russian (Republic of Moldova)",
'code' => 'ru-md',
'name' => 'Russian (Republic of Moldova)',
],
[
"code" => "sb",
"name" => "Sorbian",
'code' => 'sb',
'name' => 'Sorbian',
],
[
"code" => "sk",
"name" => "Slovak",
'code' => 'sk',
'name' => 'Slovak',
],
[
"code" => "sl",
"name" => "Slovenian",
'code' => 'sl',
'name' => 'Slovenian',
],
[
"code" => "sq",
"name" => "Albanian",
'code' => 'sq',
'name' => 'Albanian',
],
[
"code" => "sr",
"name" => "Serbian",
'code' => 'sr',
'name' => 'Serbian',
],
[
"code" => "sv",
"name" => "Swedish",
'code' => 'sv',
'name' => 'Swedish',
],
[
"code" => "sv-fi",
"name" => "Swedish (Finland)",
'code' => 'sv-fi',
'name' => 'Swedish (Finland)',
],
[
"code" => "th",
"name" => "Thai",
'code' => 'th',
'name' => 'Thai',
],
[
"code" => "tn",
"name" => "Tswana",
'code' => 'tn',
'name' => 'Tswana',
],
[
"code" => "tr",
"name" => "Turkish",
'code' => 'tr',
'name' => 'Turkish',
],
[
"code" => "ts",
"name" => "Tsonga",
'code' => 'ts',
'name' => 'Tsonga',
],
[
"code" => "ua",
"name" => "Ukrainian",
'code' => 'ua',
'name' => 'Ukrainian',
],
[
"code" => "ur",
"name" => "Urdu",
'code' => 'ur',
'name' => 'Urdu',
],
[
"code" => "ve",
"name" => "Venda",
'code' => 've',
'name' => 'Venda',
],
[
"code" => "vi",
"name" => "Vietnamese",
'code' => 'vi',
'name' => 'Vietnamese',
],
[
"code" => "xh",
"name" => "Xhosa",
'code' => 'xh',
'name' => 'Xhosa',
],
[
"code" => "zh-cn",
"name" => "Chinese (PRC)",
'code' => 'zh-cn',
'name' => 'Chinese (PRC)',
],
[
"code" => "zh-hk",
"name" => "Chinese (Hong Kong)",
'code' => 'zh-hk',
'name' => 'Chinese (Hong Kong)',
],
[
"code" => "zh-sg",
"name" => "Chinese (Singapore)",
'code' => 'zh-sg',
'name' => 'Chinese (Singapore)',
],
[
"code" => "zh-tw",
"name" => "Chinese (Taiwan)",
'code' => 'zh-tw',
'name' => 'Chinese (Taiwan)',
],
[
"code" => "zu",
"name" => "Zulu",
'code' => 'zu',
'name' => 'Zulu',
],
];
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -10,6 +10,6 @@ return [
'sms' => [
'verification',
'login',
'invitation'
]
'invitation',
],
];
+293 -293
View File
@@ -1,300 +1,300 @@
<?php
return [
'email' => [
'mailchimp' => [
'name' => 'Mailchimp',
'developers' => 'https://mailchimp.com/developer/marketing/api/',
'icon' => 'icon-mailchimp',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
'email' => [
'mailchimp' => [
'name' => 'Mailchimp',
'developers' => 'https://mailchimp.com/developer/marketing/api/',
'icon' => 'icon-mailchimp',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'mailgun' => [
'name' => 'Mailgun',
'developers' => 'https://documentation.mailgun.com/',
'icon' => 'icon-mailgun',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'mailjet' => [
'name' => 'Mailjet',
'developers' => 'https://dev.mailjet.com/',
'icon' => 'icon-mailjet',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'postmark' => [
'name' => 'Postmark',
'developers' => 'https://postmarkapp.com/developer',
'icon' => 'icon-postmark',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'sendgrid' => [
'name' => 'Sendgrid',
'developers' => 'https://docs.sendgrid.com/api-reference/how-to-use-the-sendgrid-v3-api/',
'icon' => 'icon-sendgrid',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'sendinblue' => [
'name' => 'SendinBlue',
'developers' => 'https://developers.sendinblue.com/',
'icon' => 'icon-sendinblue',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'mailslurp' => [
'name' => 'MailSlurp',
'developers' => 'https://www.mailslurp.com/docs/',
'icon' => 'icon-mailslurp',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'elasticemail' => [
'name' => 'ElasticEmail',
'developers' => 'https://api.elasticemail.com/public/help',
'icon' => 'icon-elasticemail',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'ses' => [
'name' => 'SES',
'developers' => 'https://docs.aws.amazon.com/ses/latest/APIReference/',
'icon' => 'icon-ses',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
],
'mailgun' => [
'name' => 'Mailgun',
'developers' => 'https://documentation.mailgun.com/',
'icon' => 'icon-mailgun',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
'sms' => [
'africastalking' => [
'name' => 'Africa\'s Talking',
'developers' => 'https://developers.africastalking.com/',
'icon' => 'icon-africastalking',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'clickatell' => [
'name' => 'Clickatell',
'developers' => 'https://www.clickatell.com/developers/api-docs/',
'icon' => 'icon-clickatell',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'infobip' => [
'name' => 'Infobip',
'developers' => 'https://www.infobip.com/docs/',
'icon' => 'icon-infobip',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'msg91' => [
'name' => 'Msg91',
'developers' => 'https://docs.msg91.com/reference/overview',
'icon' => 'icon-msg91',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'plivo' => [
'name' => 'Plivo',
'developers' => 'https://developers.plivo.com/',
'icon' => 'icon-plivo',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'sinch' => [
'name' => 'Sinch',
'developers' => 'https://developers.sinch.com/',
'icon' => 'icon-sinch',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'sms77' => [
'name' => 'Sms77',
'developers' => 'https://sms77.io/docs/gateway/',
'icon' => 'icon-sms77',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'telesign' => [
'name' => 'Telesign',
'developers' => 'https://developer.telesign.com/enterprise/docs',
'icon' => 'icon-telesign',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'textmagic' => [
'name' => 'TextMagic',
'developers' => 'https://www.textmagic.com/docs/api/',
'icon' => 'icon-twilio',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'twilio' => [
'name' => 'Twilio',
'developers' => 'https://www.twilio.com/docs/sms',
'icon' => 'icon-twilio',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'twilio-notify' => [
'name' => 'Twilio Notify',
'developers' => 'https://www.twilio.com/docs/notify',
'icon' => 'icon-twilio',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'vonage' => [
'name' => 'Vonage',
'developers' => 'https://developer.nexmo.com/',
'icon' => 'icon-vonage',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
],
'mailjet' => [
'name' => 'Mailjet',
'developers' => 'https://dev.mailjet.com/',
'icon' => 'icon-mailjet',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
'push' => [
'apns' => [
'name' => 'APNS',
'developers' => 'https://developer.apple.com/documentation/usernotifications',
'icon' => 'icon-apns',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'fcm' => [
'name' => 'FCM',
'developers' => 'https://firebase.google.com/docs/cloud-messaging',
'icon' => 'icon-fcm',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'one_signal' => [
'name' => 'OneSignal',
'developers' => 'https://documentation.onesignal.com/docs',
'icon' => 'icon-onesignal',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'pushbullet' => [
'name' => 'PushBullet',
'developers' => 'https://docs.pushbullet.com/',
'icon' => 'icon-pushbullet',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'pusher' => [
'name' => 'Pusher',
'developers' => 'https://pusher.com/docs',
'icon' => 'icon-pusher',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'pushwoosh' => [
'name' => 'Pushwoosh',
'developers' => 'https://www.pushwoosh.com/docs/',
'icon' => 'icon-pushwoosh',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'urban_airship' => [
'name' => 'Urban Airship',
'developers' => 'https://docs.airship.com/api/',
'icon' => 'icon-urbanairship',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'web_push' => [
'name' => 'WebPush',
'developers' => 'https://developer.mozilla.org/en-US/docs/Web/API/Push_API',
'icon' => 'icon-webpush',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
],
'postmark' => [
'name' => 'Postmark',
'developers' => 'https://postmarkapp.com/developer',
'icon' => 'icon-postmark',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'sendgrid' => [
'name' => 'Sendgrid',
'developers' => 'https://docs.sendgrid.com/api-reference/how-to-use-the-sendgrid-v3-api/',
'icon' => 'icon-sendgrid',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'sendinblue' => [
'name' => 'SendinBlue',
'developers' => 'https://developers.sendinblue.com/',
'icon' => 'icon-sendinblue',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'mailslurp' => [
'name' => 'MailSlurp',
'developers' => 'https://www.mailslurp.com/docs/',
'icon' => 'icon-mailslurp',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'elasticemail' => [
'name' => 'ElasticEmail',
'developers' => 'https://api.elasticemail.com/public/help',
'icon' => 'icon-elasticemail',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'ses' => [
'name' => 'SES',
'developers' => 'https://docs.aws.amazon.com/ses/latest/APIReference/',
'icon' => 'icon-ses',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
],
'sms' => [
'africastalking' => [
'name' => 'Africa\'s Talking',
'developers' => 'https://developers.africastalking.com/',
'icon' => 'icon-africastalking',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'clickatell' => [
'name' => 'Clickatell',
'developers' => 'https://www.clickatell.com/developers/api-docs/',
'icon' => 'icon-clickatell',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'infobip' => [
'name' => 'Infobip',
'developers' => 'https://www.infobip.com/docs/',
'icon' => 'icon-infobip',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'msg91' => [
'name' => 'Msg91',
'developers' => 'https://docs.msg91.com/reference/overview',
'icon' => 'icon-msg91',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'plivo' => [
'name' => 'Plivo',
'developers' => 'https://developers.plivo.com/',
'icon' => 'icon-plivo',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'sinch' => [
'name' => 'Sinch',
'developers' => 'https://developers.sinch.com/',
'icon' => 'icon-sinch',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'sms77' => [
'name' => 'Sms77',
'developers' => 'https://sms77.io/docs/gateway/',
'icon' => 'icon-sms77',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'telesign' => [
'name' => 'Telesign',
'developers' => 'https://developer.telesign.com/enterprise/docs',
'icon' => 'icon-telesign',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'textmagic' => [
'name' => 'TextMagic',
'developers' => 'https://www.textmagic.com/docs/api/',
'icon' => 'icon-twilio',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'twilio' => [
'name' => 'Twilio',
'developers' => 'https://www.twilio.com/docs/sms',
'icon' => 'icon-twilio',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'twilio-notify' => [
'name' => 'Twilio Notify',
'developers' => 'https://www.twilio.com/docs/notify',
'icon' => 'icon-twilio',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'vonage' => [
'name' => 'Vonage',
'developers' => 'https://developer.nexmo.com/',
'icon' => 'icon-vonage',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
],
'push' => [
'apns' => [
'name' => 'APNS',
'developers' => 'https://developer.apple.com/documentation/usernotifications',
'icon' => 'icon-apns',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'fcm' => [
'name' => 'FCM',
'developers' => 'https://firebase.google.com/docs/cloud-messaging',
'icon' => 'icon-fcm',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'one_signal' => [
'name' => 'OneSignal',
'developers' => 'https://documentation.onesignal.com/docs',
'icon' => 'icon-onesignal',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'pushbullet' => [
'name' => 'PushBullet',
'developers' => 'https://docs.pushbullet.com/',
'icon' => 'icon-pushbullet',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'pusher' => [
'name' => 'Pusher',
'developers' => 'https://pusher.com/docs',
'icon' => 'icon-pusher',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'pushwoosh' => [
'name' => 'Pushwoosh',
'developers' => 'https://www.pushwoosh.com/docs/',
'icon' => 'icon-pushwoosh',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'urban_airship' => [
'name' => 'Urban Airship',
'developers' => 'https://docs.airship.com/api/',
'icon' => 'icon-urbanairship',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'web_push' => [
'name' => 'WebPush',
'developers' => 'https://developer.mozilla.org/en-US/docs/Web/API/Push_API',
'icon' => 'icon-webpush',
'enabled' => false,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
]
];
+22 -22
View File
@@ -24,7 +24,7 @@ return [
'hidden' => false,
'family' => APP_PLATFORM_CLIENT,
'prism' => 'javascript',
'source' => \realpath(__DIR__ . '/../sdks/client-web'),
'source' => \realpath(__DIR__.'/../sdks/client-web'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-web.git',
'gitRepoName' => 'sdk-for-web',
'gitUserName' => 'appwrite',
@@ -58,7 +58,7 @@ return [
'source' => 'https://github.com/appwrite/todo-with-svelte',
'url' => 'https://appwrite-todo-with-svelte.vercel.app/',
],
]
],
],
[
'key' => 'flutter',
@@ -72,7 +72,7 @@ return [
'hidden' => false,
'family' => APP_PLATFORM_CLIENT,
'prism' => 'dart',
'source' => \realpath(__DIR__ . '/../sdks/client-flutter'),
'source' => \realpath(__DIR__.'/../sdks/client-flutter'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-flutter.git',
'gitRepoName' => 'sdk-for-flutter',
'gitUserName' => 'appwrite',
@@ -90,7 +90,7 @@ return [
'hidden' => false,
'family' => APP_PLATFORM_CLIENT,
'prism' => 'swift',
'source' => \realpath(__DIR__ . '/../sdks/client-apple'),
'source' => \realpath(__DIR__.'/../sdks/client-apple'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-apple.git',
'gitRepoName' => 'sdk-for-apple',
'gitUserName' => 'appwrite',
@@ -125,7 +125,7 @@ return [
'hidden' => false,
'family' => APP_PLATFORM_CLIENT,
'prism' => 'kotlin',
'source' => \realpath(__DIR__ . '/../sdks/client-android'),
'source' => \realpath(__DIR__.'/../sdks/client-android'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-android.git',
'gitRepoName' => 'sdk-for-android',
'gitUserName' => 'appwrite',
@@ -147,7 +147,7 @@ return [
'hidden' => true,
'family' => APP_PLATFORM_CLIENT,
'prism' => 'graphql',
'source' => \realpath(__DIR__ . '/../sdks/client-graphql'),
'source' => \realpath(__DIR__.'/../sdks/client-graphql'),
'gitUrl' => '',
'gitRepoName' => '',
'gitUserName' => '',
@@ -166,7 +166,7 @@ return [
'hidden' => true,
'family' => APP_PLATFORM_CLIENT,
'prism' => 'http',
'source' => \realpath(__DIR__ . '/../sdks/client-rest'),
'source' => \realpath(__DIR__.'/../sdks/client-rest'),
'gitUrl' => '',
'gitRepoName' => '',
'gitUserName' => '',
@@ -194,7 +194,7 @@ return [
'hidden' => true,
'family' => APP_PLATFORM_CONSOLE,
'prism' => 'javascript',
'source' => \realpath(__DIR__ . '/../sdks/console-web'),
'source' => \realpath(__DIR__.'/../sdks/console-web'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-console.git',
'gitBranch' => 'dev',
'gitRepoName' => 'sdk-for-console',
@@ -212,7 +212,7 @@ return [
'hidden' => false,
'family' => APP_PLATFORM_CONSOLE,
'prism' => 'bash',
'source' => \realpath(__DIR__ . '/../sdks/console-cli'),
'source' => \realpath(__DIR__.'/../sdks/console-cli'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-cli.git',
'gitRepoName' => 'sdk-for-cli',
'gitUserName' => 'appwrite',
@@ -240,7 +240,7 @@ return [
'hidden' => false,
'family' => APP_PLATFORM_SERVER,
'prism' => 'javascript',
'source' => \realpath(__DIR__ . '/../sdks/server-nodejs'),
'source' => \realpath(__DIR__.'/../sdks/server-nodejs'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-node.git',
'gitRepoName' => 'sdk-for-node',
'gitUserName' => 'appwrite',
@@ -258,7 +258,7 @@ return [
'hidden' => false,
'family' => APP_PLATFORM_SERVER,
'prism' => 'typescript',
'source' => \realpath(__DIR__ . '/../sdks/server-deno'),
'source' => \realpath(__DIR__.'/../sdks/server-deno'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-deno.git',
'gitRepoName' => 'sdk-for-deno',
'gitUserName' => 'appwrite',
@@ -276,7 +276,7 @@ return [
'hidden' => false,
'family' => APP_PLATFORM_SERVER,
'prism' => 'php',
'source' => \realpath(__DIR__ . '/../sdks/server-php'),
'source' => \realpath(__DIR__.'/../sdks/server-php'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-php.git',
'gitRepoName' => 'sdk-for-php',
'gitUserName' => 'appwrite',
@@ -294,7 +294,7 @@ return [
'hidden' => false,
'family' => APP_PLATFORM_SERVER,
'prism' => 'python',
'source' => \realpath(__DIR__ . '/../sdks/server-python'),
'source' => \realpath(__DIR__.'/../sdks/server-python'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-python.git',
'gitRepoName' => 'sdk-for-python',
'gitUserName' => 'appwrite',
@@ -312,7 +312,7 @@ return [
'hidden' => false,
'family' => APP_PLATFORM_SERVER,
'prism' => 'ruby',
'source' => \realpath(__DIR__ . '/../sdks/server-ruby'),
'source' => \realpath(__DIR__.'/../sdks/server-ruby'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-ruby.git',
'gitRepoName' => 'sdk-for-ruby',
'gitUserName' => 'appwrite',
@@ -330,7 +330,7 @@ return [
'hidden' => false,
'family' => APP_PLATFORM_SERVER,
'prism' => 'go',
'source' => \realpath(__DIR__ . '/../sdks/server-go'),
'source' => \realpath(__DIR__.'/../sdks/server-go'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-go.git',
'gitRepoName' => 'sdk-for-go',
'gitUserName' => 'appwrite',
@@ -348,7 +348,7 @@ return [
'hidden' => false,
'family' => APP_PLATFORM_SERVER,
'prism' => 'java',
'source' => \realpath(__DIR__ . '/../sdks/server-java'),
'source' => \realpath(__DIR__.'/../sdks/server-java'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-java.git',
'gitRepoName' => 'sdk-for-java',
'gitUserName' => 'appwrite',
@@ -366,7 +366,7 @@ return [
'hidden' => false,
'family' => APP_PLATFORM_SERVER,
'prism' => 'csharp',
'source' => \realpath(__DIR__ . '/../sdks/server-dotnet'),
'source' => \realpath(__DIR__.'/../sdks/server-dotnet'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-dotnet.git',
'gitRepoName' => 'sdk-for-dotnet',
'gitUserName' => 'appwrite',
@@ -384,7 +384,7 @@ return [
'hidden' => false,
'family' => APP_PLATFORM_SERVER,
'prism' => 'dart',
'source' => \realpath(__DIR__ . '/../sdks/server-dart'),
'source' => \realpath(__DIR__.'/../sdks/server-dart'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-dart.git',
'gitRepoName' => 'sdk-for-dart',
'gitUserName' => 'appwrite',
@@ -402,7 +402,7 @@ return [
'hidden' => false,
'family' => APP_PLATFORM_SERVER,
'prism' => 'kotlin',
'source' => \realpath(__DIR__ . '/../sdks/server-kotlin'),
'source' => \realpath(__DIR__.'/../sdks/server-kotlin'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-kotlin.git',
'gitRepoName' => 'sdk-for-kotlin',
'gitUserName' => 'appwrite',
@@ -424,7 +424,7 @@ return [
'hidden' => false,
'family' => APP_PLATFORM_SERVER,
'prism' => 'swift',
'source' => \realpath(__DIR__ . '/../sdks/server-swift'),
'source' => \realpath(__DIR__.'/../sdks/server-swift'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-swift.git',
'gitRepoName' => 'sdk-for-swift',
'gitUserName' => 'appwrite',
@@ -442,7 +442,7 @@ return [
'hidden' => true,
'family' => APP_PLATFORM_SERVER,
'prism' => 'graphql',
'source' => \realpath(__DIR__ . '/../sdks/server-graphql'),
'source' => \realpath(__DIR__.'/../sdks/server-graphql'),
'gitUrl' => '',
'gitRepoName' => '',
'gitUserName' => '',
@@ -461,7 +461,7 @@ return [
'hidden' => true,
'family' => APP_PLATFORM_SERVER,
'prism' => 'http',
'source' => \realpath(__DIR__ . '/../sdks/server-rest'),
'source' => \realpath(__DIR__.'/../sdks/server-rest'),
'gitUrl' => '',
'gitRepoName' => '',
'gitUserName' => '',
+1 -1
View File
@@ -5,5 +5,5 @@ return [
'name' => 'Default',
'default' => true,
'disabled' => false,
]
],
];
+2 -2
View File
@@ -25,7 +25,7 @@ $member = [
'providers.write',
'providers.read',
'messages.write',
'messages.read'
'messages.read',
];
$admins = [
@@ -64,7 +64,7 @@ $admins = [
'providers.write',
'providers.read',
'messages.write',
'messages.read'
'messages.read',
];
return [
+1 -1
View File
@@ -4,8 +4,8 @@
* List of Appwrite Cloud Functions supported runtimes
*/
use Utopia\App;
use Appwrite\Runtimes\Runtimes;
use Utopia\App;
$runtimes = new Runtimes('v2');
+2 -2
View File
@@ -37,7 +37,7 @@ return [ // List of publicly visible scopes
'indexes.write' => [
'description' => 'Access to create, update, and delete your project\'s database collection\'s indexes',
],
'documents.read' => [
'documents.read' => [
'description' => 'Access to read your project\'s database documents',
],
'documents.write' => [
@@ -111,5 +111,5 @@ return [ // List of publicly visible scopes
],
'targets.write' => [
'description' => 'Access to create, update, and delete your project\'s targets',
]
],
];
+3 -3
View File
@@ -66,8 +66,8 @@ return [
'optional' => true,
'icon' => '/images/services/databases.png',
'globalAttributes' => [
'databaseId'
]
'databaseId',
],
],
'locale' => [
'key' => 'locale',
@@ -237,5 +237,5 @@ return [
'tests' => false,
'optional' => true,
'icon' => '/images/services/messaging.png',
]
],
];
+20 -20
View File
@@ -1,24 +1,24 @@
<?php
return [
'24h' => [
'period' => '1h',
'limit' => 24,
'factor' => 3600,
],
'7d' => [
'period' => '1d',
'limit' => 7,
'factor' => 86400,
],
'30d' => [
'period' => '1d',
'limit' => 30,
'factor' => 86400,
],
'90d' => [
'period' => '1d',
'limit' => 90,
'factor' => 86400,
],
'24h' => [
'period' => '1h',
'limit' => 24,
'factor' => 3600,
],
'7d' => [
'period' => '1d',
'limit' => 7,
'factor' => 86400,
],
'30d' => [
'period' => '1d',
'limit' => 30,
'factor' => 86400,
],
'90d' => [
'period' => '1d',
'limit' => 90,
'factor' => 86400,
],
];
+98 -98
View File
@@ -14,7 +14,7 @@ return [
'default' => 'production',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_LOCALE',
@@ -23,7 +23,7 @@ return [
'default' => 'en',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_OPTIONS_ABUSE',
@@ -32,7 +32,7 @@ return [
'default' => 'enabled',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_OPTIONS_FORCE_HTTPS',
@@ -41,7 +41,7 @@ return [
'default' => 'disabled',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_OPENSSL_KEY_V1',
@@ -50,7 +50,7 @@ return [
'default' => 'your-secret-key',
'required' => true,
'question' => 'Choose a secret API key, make sure to make a backup of your key in a secure location',
'filter' => 'token'
'filter' => 'token',
],
[
'name' => '_APP_DOMAIN',
@@ -59,7 +59,7 @@ return [
'default' => 'localhost',
'required' => true,
'question' => 'Enter your Appwrite hostname',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_DOMAIN_TARGET',
@@ -67,8 +67,8 @@ return [
'introduction' => '',
'default' => 'localhost',
'required' => true,
'question' => 'Enter a DNS A record hostname to serve as a CNAME for your custom domains.' . PHP_EOL . 'You can use the same value as used for the Appwrite hostname.',
'filter' => 'domainTarget'
'question' => 'Enter a DNS A record hostname to serve as a CNAME for your custom domains.'.PHP_EOL.'You can use the same value as used for the Appwrite hostname.',
'filter' => 'domainTarget',
],
[
'name' => '_APP_CONSOLE_WHITELIST_ROOT',
@@ -77,7 +77,7 @@ return [
'default' => 'enabled',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_CONSOLE_WHITELIST_EMAILS',
@@ -86,7 +86,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
// [
// 'name' => '_APP_CONSOLE_WHITELIST_DOMAINS',
@@ -103,7 +103,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_CONSOLE_ROOT_SESSION',
@@ -112,7 +112,7 @@ return [
'default' => 'disabled',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_SYSTEM_EMAIL_NAME',
@@ -121,7 +121,7 @@ return [
'default' => 'Appwrite',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_SYSTEM_EMAIL_ADDRESS',
@@ -130,7 +130,7 @@ return [
'default' => 'team@appwrite.io',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_SYSTEM_RESPONSE_FORMAT',
@@ -139,7 +139,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_SYSTEM_SECURITY_EMAIL_ADDRESS',
@@ -148,7 +148,7 @@ return [
'default' => 'certs@appwrite.io',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_USAGE_STATS',
@@ -157,7 +157,7 @@ return [
'default' => 'enabled',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_LOGGING_PROVIDER',
@@ -166,7 +166,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_LOGGING_CONFIG',
@@ -175,7 +175,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_USAGE_AGGREGATION_INTERVAL',
@@ -184,7 +184,7 @@ return [
'default' => '30',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_USAGE_TIMESERIES_INTERVAL',
@@ -193,7 +193,7 @@ return [
'default' => '30',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_USAGE_DATABASE_INTERVAL',
@@ -202,7 +202,7 @@ return [
'default' => '900',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_WORKER_PER_CORE',
@@ -211,7 +211,7 @@ return [
'default' => 6,
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_CONSOLE_INVITES',
@@ -220,7 +220,7 @@ return [
'default' => 'enabled',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
],
],
@@ -235,7 +235,7 @@ return [
'default' => 'redis',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_REDIS_PORT',
@@ -244,7 +244,7 @@ return [
'default' => '6379',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_REDIS_USER',
@@ -253,7 +253,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_REDIS_PASS',
@@ -262,7 +262,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
],
],
@@ -277,7 +277,7 @@ return [
'default' => 'mariadb',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_DB_PORT',
@@ -286,7 +286,7 @@ return [
'default' => '3306',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_DB_SCHEMA',
@@ -295,7 +295,7 @@ return [
'default' => 'appwrite',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_DB_USER',
@@ -304,7 +304,7 @@ return [
'default' => 'user',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_DB_PASS',
@@ -313,7 +313,7 @@ return [
'default' => 'password',
'required' => false,
'question' => '',
'filter' => 'password'
'filter' => 'password',
],
[
'name' => '_APP_DB_ROOT_PASS',
@@ -322,7 +322,7 @@ return [
'default' => 'rootsecretpassword',
'required' => false,
'question' => '',
'filter' => 'password'
'filter' => 'password',
],
[
'name' => '_APP_CONNECTIONS_MAX',
@@ -331,26 +331,26 @@ return [
'default' => 251,
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
// [
// 'name' => '_APP_CONNECTIONS_DB_PROJECT',
// 'description' => 'A list of comma-separated key value pairs representing Project DBs where key is the database name and value is the DSN connection string.',
// 'introduction' => 'TBD',
// 'default' => 'db_fra1_01=mysql://user:password@mariadb:3306/appwrite',
// 'required' => true,
// 'question' => '',
// 'filter' => ''
// ],
// [
// 'name' => '_APP_CONNECTIONS_DB_CONSOLE',
// 'description' => 'A key value pair representing the Console DB where key is the database name and value is the DSN connection string.',
// 'introduction' => 'TBD',
// 'default' => 'db_fra1_01=mysql://user:password@mariadb:3306/appwrite',
// 'required' => true,
// 'question' => '',
// 'filter' => ''
// ]
// [
// 'name' => '_APP_CONNECTIONS_DB_PROJECT',
// 'description' => 'A list of comma-separated key value pairs representing Project DBs where key is the database name and value is the DSN connection string.',
// 'introduction' => 'TBD',
// 'default' => 'db_fra1_01=mysql://user:password@mariadb:3306/appwrite',
// 'required' => true,
// 'question' => '',
// 'filter' => ''
// ],
// [
// 'name' => '_APP_CONNECTIONS_DB_CONSOLE',
// 'description' => 'A key value pair representing the Console DB where key is the database name and value is the DSN connection string.',
// 'introduction' => 'TBD',
// 'default' => 'db_fra1_01=mysql://user:password@mariadb:3306/appwrite',
// 'required' => true,
// 'question' => '',
// 'filter' => ''
// ]
],
],
[
@@ -364,7 +364,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_SMTP_PORT',
@@ -373,7 +373,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_SMTP_SECURE',
@@ -382,7 +382,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_SMTP_USERNAME',
@@ -391,7 +391,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_SMTP_PASSWORD',
@@ -400,7 +400,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
],
],
@@ -415,7 +415,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_SMS_FROM',
@@ -424,7 +424,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
],
],
@@ -439,7 +439,7 @@ return [
'default' => '30000000',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_STORAGE_PREVIEW_LIMIT',
@@ -448,7 +448,7 @@ return [
'default' => '20000000',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_STORAGE_ANTIVIRUS',
@@ -457,7 +457,7 @@ return [
'default' => 'disabled',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_STORAGE_ANTIVIRUS_HOST',
@@ -466,7 +466,7 @@ return [
'default' => 'clamav',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_STORAGE_ANTIVIRUS_PORT',
@@ -475,7 +475,7 @@ return [
'default' => '3310',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_CONNECTIONS_STORAGE',
@@ -666,7 +666,7 @@ return [
'default' => '30000000',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_FUNCTIONS_TIMEOUT',
@@ -675,7 +675,7 @@ return [
'default' => '900',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_FUNCTIONS_BUILD_TIMEOUT',
@@ -684,7 +684,7 @@ return [
'default' => '900',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_FUNCTIONS_CONTAINERS',
@@ -693,7 +693,7 @@ return [
'default' => '10',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_FUNCTIONS_CPUS',
@@ -702,7 +702,7 @@ return [
'default' => '0',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_FUNCTIONS_MEMORY',
@@ -711,7 +711,7 @@ return [
'default' => '0',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_FUNCTIONS_MEMORY_SWAP',
@@ -720,16 +720,16 @@ return [
'default' => '0',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_FUNCTIONS_RUNTIMES',
'description' => "This option allows you to enable or disable runtime environments for cloud functions. Disable unused runtimes to save disk space.\n\nTo enable cloud function runtimes, pass a list of enabled environments separated by a comma.\n\nCurrently, supported environments are: " . \implode(', ', \array_keys(Config::getParam('runtimes'))),
'description' => "This option allows you to enable or disable runtime environments for cloud functions. Disable unused runtimes to save disk space.\n\nTo enable cloud function runtimes, pass a list of enabled environments separated by a comma.\n\nCurrently, supported environments are: ".\implode(', ', \array_keys(Config::getParam('runtimes'))),
'introduction' => '0.8.0',
'default' => 'node-16.0,php-8.0,python-3.9,ruby-3.0',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_EXECUTOR_SECRET',
@@ -738,7 +738,7 @@ return [
'default' => 'your-secret-key',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_EXECUTOR_HOST',
@@ -747,7 +747,7 @@ return [
'default' => 'http://appwrite-executor/v1',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_EXECUTOR_RUNTIME_NETWORK',
@@ -756,7 +756,7 @@ return [
'default' => 'appwrite_runtimes',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_FUNCTIONS_ENVS',
@@ -765,7 +765,7 @@ return [
'default' => 'node-16.0,php-7.4,python-3.9,ruby-3.0',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_FUNCTIONS_INACTIVE_THRESHOLD',
@@ -774,7 +774,7 @@ return [
'default' => '60',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => 'DOCKERHUB_PULL_USERNAME',
@@ -783,7 +783,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => 'DOCKERHUB_PULL_PASSWORD',
@@ -792,7 +792,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => 'DOCKERHUB_PULL_EMAIL',
@@ -801,7 +801,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => 'OPEN_RUNTIMES_NETWORK',
@@ -810,7 +810,7 @@ return [
'default' => 'appwrite_runtimes',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_FUNCTIONS_RUNTIMES_NETWORK',
@@ -819,7 +819,7 @@ return [
'default' => 'runtimes',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_DOCKER_HUB_USERNAME',
@@ -828,7 +828,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_DOCKER_HUB_PASSWORD',
@@ -837,7 +837,7 @@ return [
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_FUNCTIONS_MAINTENANCE_INTERVAL',
@@ -846,7 +846,7 @@ return [
'default' => '60',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
],
],
@@ -861,7 +861,7 @@ return [
'default' => '86400',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_MAINTENANCE_RETENTION_CACHE',
@@ -870,7 +870,7 @@ return [
'default' => '2592000',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_MAINTENANCE_RETENTION_EXECUTION',
@@ -879,7 +879,7 @@ return [
'default' => '1209600',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_MAINTENANCE_RETENTION_AUDIT',
@@ -888,7 +888,7 @@ return [
'default' => '1209600',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_MAINTENANCE_RETENTION_ABUSE',
@@ -897,7 +897,7 @@ return [
'default' => '86400',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_MAINTENANCE_RETENTION_USAGE_HOURLY',
@@ -906,7 +906,7 @@ return [
'default' => '8640000',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_MAINTENANCE_RETENTION_SCHEDULES',
@@ -915,8 +915,8 @@ return [
'default' => '86400',
'required' => false,
'question' => '',
'filter' => ''
]
'filter' => '',
],
],
],
[
@@ -930,7 +930,7 @@ return [
'default' => '10',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_GRAPHQL_MAX_COMPLEXITY',
@@ -939,7 +939,7 @@ return [
'default' => '250',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
[
'name' => '_APP_GRAPHQL_MAX_DEPTH',
@@ -948,7 +948,7 @@ return [
'default' => '3',
'required' => false,
'question' => '',
'filter' => ''
'filter' => '',
],
],
],
File diff suppressed because it is too large Load Diff
+82 -87
View File
@@ -1,7 +1,6 @@
<?php
use Appwrite\Extend\Exception;
use Utopia\Validator\URL;
use Appwrite\URL\URL as URLParse;
use Appwrite\Utopia\Response;
use chillerlan\QRCode\QRCode;
@@ -21,23 +20,23 @@ use Utopia\Validator\Boolean;
use Utopia\Validator\HexColor;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\URL;
use Utopia\Validator\WhiteList;
$avatarCallback = function (string $type, string $code, int $width, int $height, int $quality, Response $response) {
$code = \strtolower($code);
$type = \strtolower($type);
$set = Config::getParam('avatar-' . $type, []);
$set = Config::getParam('avatar-'.$type, []);
if (empty($set)) {
throw new Exception(Exception::AVATAR_SET_NOT_FOUND);
}
if (!\array_key_exists($code, $set)) {
if (! \array_key_exists($code, $set)) {
throw new Exception(Exception::AVATAR_NOT_FOUND);
}
if (!\extension_loaded('imagick')) {
if (! \extension_loaded('imagick')) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
}
@@ -45,8 +44,8 @@ $avatarCallback = function (string $type, string $code, int $width, int $height,
$path = $set[$code]['path'];
$type = 'png';
if (!\is_readable($path)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'File not readable in ' . $path);
if (! \is_readable($path)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'File not readable in '.$path);
}
$image = new Image(\file_get_contents($path));
@@ -54,7 +53,7 @@ $avatarCallback = function (string $type, string $code, int $width, int $height,
$output = (empty($output)) ? $type : $output;
$data = $image->output($output, $quality);
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30).' GMT')
->setContentType('image/png')
->file($data);
unset($image);
@@ -83,12 +82,12 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
$accessTokenExpiry = $gitHubSession->getAttribute('providerAccessTokenExpiry');
$refreshToken = $gitHubSession->getAttribute('providerRefreshToken');
$appId = $project->getAttribute('authProviders', [])[$provider . 'Appid'] ?? '';
$appSecret = $project->getAttribute('authProviders', [])[$provider . 'Secret'] ?? '{}';
$appId = $project->getAttribute('authProviders', [])[$provider.'Appid'] ?? '';
$appSecret = $project->getAttribute('authProviders', [])[$provider.'Secret'] ?? '{}';
$className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider);
$className = 'Appwrite\\Auth\\OAuth2\\'.\ucfirst($provider);
if (!\class_exists($className)) {
if (! \class_exists($className)) {
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
@@ -105,13 +104,13 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
$verificationId = $oauth2->getUserID($accessToken);
if (empty($verificationId)) {
throw new \Exception("Locked tokens."); // Race codition, handeled in catch
throw new \Exception('Locked tokens.'); // Race codition, handeled in catch
}
$gitHubSession
->setAttribute('providerAccessToken', $accessToken)
->setAttribute('providerRefreshToken', $refreshToken)
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$oauth2->getAccessTokenExpiry('')));
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int) $oauth2->getAccessTokenExpiry('')));
Authorization::skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession));
@@ -150,7 +149,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
return [
'name' => $githubUser,
'id' => $githubId
'id' => $githubId,
];
} catch (Exception $error) {
if ($logger) {
@@ -177,7 +176,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);
Console::info('GitHub error log pushed with status code: ' . $responseCode);
Console::info('GitHub error log pushed with status code: '.$responseCode);
}
Console::warning("Failed: {$error->getMessage()}");
@@ -202,12 +201,12 @@ App::get('/v1/avatars/credit-cards/:code')
->label('sdk.description', '/docs/references/avatars/get-credit-card.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE_PNG)
->param('code', '', new WhiteList(\array_keys(Config::getParam('avatar-credit-cards'))), 'Credit Card Code. Possible values: ' . \implode(', ', \array_keys(Config::getParam('avatar-credit-cards'))) . '.')
->param('code', '', new WhiteList(\array_keys(Config::getParam('avatar-credit-cards'))), 'Credit Card Code. Possible values: '.\implode(', ', \array_keys(Config::getParam('avatar-credit-cards'))).'.')
->param('width', 100, new Range(0, 2000), 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->inject('response')
->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('credit-cards', $code, $width, $height, $quality, $response));
->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('credit-cards', $code, $width, $height, $quality, $response));
App::get('/v1/avatars/browsers/:code')
->desc('Get Browser Icon')
@@ -267,18 +266,17 @@ App::get('/v1/avatars/image')
->param('height', 400, new Range(0, 2000), 'Resize preview image height, Pass an integer between 0 to 2000. Defaults to 400.', true)
->inject('response')
->action(function (string $url, int $width, int $height, Response $response) {
$quality = 80;
$output = 'png';
$type = 'png';
if (!\extension_loaded('imagick')) {
if (! \extension_loaded('imagick')) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
}
$fetch = @\file_get_contents($url, false);
if (!$fetch) {
if (! $fetch) {
throw new Exception(Exception::AVATAR_IMAGE_NOT_FOUND);
}
@@ -293,7 +291,7 @@ App::get('/v1/avatars/image')
$data = $image->output($output, $quality);
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30).' GMT')
->setContentType('image/png')
->file($data);
unset($image);
@@ -315,14 +313,13 @@ App::get('/v1/avatars/favicon')
->param('url', '', new URL(['http', 'https']), 'Website URL which you want to fetch the favicon from.')
->inject('response')
->action(function (string $url, Response $response) {
$width = 56;
$height = 56;
$quality = 80;
$output = 'png';
$type = 'png';
if (!\extension_loaded('imagick')) {
if (! \extension_loaded('imagick')) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
}
@@ -344,7 +341,7 @@ App::get('/v1/avatars/favicon')
\curl_close($curl);
if (!$html) {
if (! $html) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}
@@ -395,7 +392,7 @@ App::get('/v1/avatars/favicon')
if (empty($outputHref) || empty($outputExt)) {
$default = \parse_url($url);
$outputHref = $default['scheme'] . '://' . $default['host'] . '/favicon.ico';
$outputHref = $default['scheme'].'://'.$default['host'].'/favicon.ico';
$outputExt = 'ico';
}
@@ -406,14 +403,14 @@ App::get('/v1/avatars/favicon')
throw new Exception(Exception::AVATAR_ICON_NOT_FOUND, 'Favicon not found');
}
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30).' GMT')
->setContentType('image/x-icon')
->file($data);
}
$fetch = @\file_get_contents($outputHref, false);
if (!$fetch) {
if (! $fetch) {
throw new Exception(Exception::AVATAR_ICON_NOT_FOUND);
}
@@ -423,7 +420,7 @@ App::get('/v1/avatars/favicon')
$data = $image->output($output, $quality);
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30).' GMT')
->setContentType('image/png')
->file($data);
unset($image);
@@ -446,7 +443,6 @@ App::get('/v1/avatars/qr')
->param('download', false, new Boolean(true), 'Return resulting image with \'Content-Disposition: attachment \' headers for the browser to start downloading it. Pass 0 for no header, or 1 for otherwise. Default value is set to 0.', true)
->inject('response')
->action(function (string $text, int $size, int $margin, bool $download, Response $response) {
$download = ($download === '1' || $download === 'true' || $download === 1 || $download === true);
$options = new QROptions([
'addQuietzone' => true,
@@ -464,7 +460,7 @@ App::get('/v1/avatars/qr')
$image->crop((int) $size, (int) $size);
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->setContentType('image/png')
->send($image->output('png', 9));
});
@@ -488,17 +484,16 @@ App::get('/v1/avatars/initials')
->inject('response')
->inject('user')
->action(function (string $name, int $width, int $height, string $background, Response $response, Document $user) {
$themes = [
['background' => '#FFA1CE'], // Default (Pink)
['background' => '#FDC584'], // Orange
['background' => '#94DBD1'], // Green
['background' => '#A1C4FF'], // Blue
['background' => '#FFA1CE'], // Pink
['background' => '#CBB1FC'] // Purple
['background' => '#CBB1FC'], // Purple
];
$name = (!empty($name)) ? $name : $user->getAttribute('name', $user->getAttribute('email', ''));
$name = (! empty($name)) ? $name : $user->getAttribute('name', $user->getAttribute('email', ''));
$words = \explode(' ', \strtoupper($name));
// if there is no space, try to split by `_` underscore
$words = (count($words) == 1) ? \explode('_', \strtoupper($name)) : $words;
@@ -520,7 +515,7 @@ App::get('/v1/avatars/initials')
// Wrap rand value to avoid out of range
$rand = ($rand > \count($themes) - 1) ? $rand % \count($themes) : $rand;
$background = (!empty($background)) ? '#' . $background : $themes[$rand]['background'];
$background = (! empty($background)) ? '#'.$background : $themes[$rand]['background'];
$image = new \Imagick();
$punch = new \Imagick();
@@ -529,8 +524,8 @@ App::get('/v1/avatars/initials')
$punch->newImage($width, $height, 'transparent');
$draw->setFont(__DIR__ . "/../../assets/fonts/poppins-v9-latin-500.ttf");
$image->setFont(__DIR__ . "/../../assets/fonts/poppins-v9-latin-500.ttf");
$draw->setFont(__DIR__.'/../../assets/fonts/poppins-v9-latin-500.ttf');
$image->setFont(__DIR__.'/../../assets/fonts/poppins-v9-latin-500.ttf');
$draw->setFillColor(new ImagickPixel('black'));
$draw->setFontSize($fontSize);
@@ -542,13 +537,13 @@ App::get('/v1/avatars/initials')
$punch->negateImage(true, Imagick::CHANNEL_ALPHA);
$image->newImage($width, $height, $background);
$image->setImageFormat("png");
$image->setImageFormat('png');
$image->compositeImage($punch, Imagick::COMPOSITE_COPYOPACITY, 0, 0);
//$image->setImageCompressionQuality(9 - round(($quality / 100) * 9));
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->setContentType('image/png')
->file($image->getImageBlob());
});
@@ -582,7 +577,7 @@ App::get('/v1/cards/cloud')
throw new Exception(Exception::USER_NOT_FOUND);
}
if (!$mock) {
if (! $mock) {
$name = $user->getAttribute('name', 'Anonymous');
$email = $user->getAttribute('email', '');
$createdAt = new \DateTime($user->getCreatedAt());
@@ -602,9 +597,9 @@ App::get('/v1/cards/cloud')
$createdAt = new \DateTime($employees[$email]['memberSince'] ?? '');
}
if (!$isEmployee && !empty($githubName)) {
if (! $isEmployee && ! empty($githubName)) {
$employeeGitHub = \array_search(\strtolower($githubName), \array_map(fn ($employee) => \strtolower($employee['gitHub']) ?? '', $employees));
if (!empty($employeeGitHub)) {
if (! empty($employeeGitHub)) {
$isEmployee = true;
$employeeNumber = $isEmployee ? $employees[$employeeGitHub]['spot'] : '';
$createdAt = new \DateTime($employees[$employeeGitHub]['memberSince'] ?? '');
@@ -645,20 +640,20 @@ App::get('/v1/cards/cloud')
$isGolden = $isEmployee || $isHero || $isContributor;
$isPlatinum = $isGolden ? false : $isPlatinum;
$memberSince = \strtoupper('Member since ' . $createdAt->format('M') . ' ' . $createdAt->format('d') . ', ' . $createdAt->format('o'));
$memberSince = \strtoupper('Member since '.$createdAt->format('M').' '.$createdAt->format('d').', '.$createdAt->format('o'));
$imagePath = $isGolden ? 'front-golden.png' : ($isPlatinum ? 'front-platinum.png' : 'front.png');
$baseImage = new \Imagick(__DIR__ . '/../../../public/images/cards/cloud/' . $imagePath);
$baseImage = new \Imagick(__DIR__.'/../../../public/images/cards/cloud/'.$imagePath);
if ($isEmployee) {
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/employee.png');
$image = new Imagick(__DIR__.'/../../../public/images/cards/cloud/employee.png');
$image->setGravity(Imagick::GRAVITY_CENTER);
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 793, 35);
$text = new \ImagickDraw();
$text->setTextAlignment(Imagick::ALIGN_CENTER);
$text->setFont(__DIR__ . '/../../../public/fonts/Inter-Bold.ttf');
$text->setFont(__DIR__.'/../../../public/fonts/Inter-Bold.ttf');
$text->setFillColor(new \ImagickPixel('#FFFADF'));
$text->setFontSize(\strlen($employeeNumber) <= 2 ? 54 : 48);
$text->setFontWeight(700);
@@ -666,7 +661,7 @@ App::get('/v1/cards/cloud')
$hashtag = new \ImagickDraw();
$hashtag->setTextAlignment(Imagick::ALIGN_CENTER);
$hashtag->setFont(__DIR__ . '/../../../public/fonts/Inter-Bold.ttf');
$hashtag->setFont(__DIR__.'/../../../public/fonts/Inter-Bold.ttf');
$hashtag->setFillColor(new \ImagickPixel('#FFFADF'));
$hashtag->setFontSize(28);
$hashtag->setFontWeight(700);
@@ -689,25 +684,25 @@ App::get('/v1/cards/cloud')
}
if ($isContributor) {
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/contributor.png');
$image = new Imagick(__DIR__.'/../../../public/images/cards/cloud/contributor.png');
$image->setGravity(Imagick::GRAVITY_CENTER);
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 793, 34);
}
if ($isHero) {
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/hero.png');
$image = new Imagick(__DIR__.'/../../../public/images/cards/cloud/hero.png');
$image->setGravity(Imagick::GRAVITY_CENTER);
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 793, 34);
}
setlocale(LC_ALL, "en_US.utf8");
setlocale(LC_ALL, 'en_US.utf8');
// $name = \iconv("utf-8", "ascii//TRANSLIT", $name);
// $memberSince = \iconv("utf-8", "ascii//TRANSLIT", $memberSince);
// $githubName = \iconv("utf-8", "ascii//TRANSLIT", $githubName);
$text = new \ImagickDraw();
$text->setTextAlignment(Imagick::ALIGN_CENTER);
$text->setFont(__DIR__ . '/../../../public/fonts/Poppins-Bold.ttf');
$text->setFont(__DIR__.'/../../../public/fonts/Poppins-Bold.ttf');
$text->setFillColor(new \ImagickPixel('#FFFFFF'));
if (\strlen($name) > 32) {
@@ -726,17 +721,17 @@ App::get('/v1/cards/cloud')
$text = new \ImagickDraw();
$text->setTextAlignment(Imagick::ALIGN_CENTER);
$text->setFont(__DIR__ . '/../../../public/fonts/Inter-SemiBold.ttf');
$text->setFont(__DIR__.'/../../../public/fonts/Inter-SemiBold.ttf');
$text->setFillColor(new \ImagickPixel($isGolden || $isPlatinum ? '#FFFFFF' : '#FFB9CC'));
$text->setFontSize(27);
$text->setFontWeight(600);
$text->setTextKerning(1.08);
$baseImage->annotateImage($text, 512, 541, 0, \strtoupper($memberSince));
if (!empty($githubName)) {
if (! empty($githubName)) {
$text = new \ImagickDraw();
$text->setTextAlignment(Imagick::ALIGN_CENTER);
$text->setFont(__DIR__ . '/../../../public/fonts/Inter-Regular.ttf');
$text->setFont(__DIR__.'/../../../public/fonts/Inter-Regular.ttf');
$text->setFillColor(new \ImagickPixel('#FFFFFF'));
$text->setFontSize($scalingDown ? 28 : 32);
$text->setFontWeight(400);
@@ -744,18 +739,18 @@ App::get('/v1/cards/cloud')
$baseImage->annotateImage($text, 512 + 20 + 4, 373 + ($scalingDown ? 2 : 0), 0, $githubName);
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/github.png');
$image = new Imagick(__DIR__.'/../../../public/images/cards/cloud/github.png');
$image->setGravity(Imagick::GRAVITY_CENTER);
$precisionFix = 5;
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 512 - ($metrics['textWidth'] / 2) - 20 - 4, 373 - ($metrics['textHeight'] - $precisionFix));
}
if (!empty($width) || !empty($height)) {
if (! empty($width) || ! empty($height)) {
$baseImage->resizeImage($width, $height, Imagick::FILTER_LANCZOS, 1);
}
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->setContentType('image/png')
->file($baseImage->getImageBlob());
});
@@ -789,7 +784,7 @@ App::get('/v1/cards/cloud-back')
throw new Exception(Exception::USER_NOT_FOUND);
}
if (!$mock) {
if (! $mock) {
$userId = $user->getId();
$email = $user->getAttribute('email', '');
@@ -809,31 +804,31 @@ App::get('/v1/cards/cloud-back')
$isPlatinum = $mock === 'platinum';
}
$userId = 'UID ' . $userId;
$userId = 'UID '.$userId;
$isPlatinum = $isGolden ? false : $isPlatinum;
$imagePath = $isGolden ? 'back-golden.png' : ($isPlatinum ? 'back-platinum.png' : 'back.png');
$baseImage = new \Imagick(__DIR__ . '/../../../public/images/cards/cloud/' . $imagePath);
$baseImage = new \Imagick(__DIR__.'/../../../public/images/cards/cloud/'.$imagePath);
setlocale(LC_ALL, "en_US.utf8");
setlocale(LC_ALL, 'en_US.utf8');
// $userId = \iconv("utf-8", "ascii//TRANSLIT", $userId);
$text = new \ImagickDraw();
$text->setTextAlignment(Imagick::ALIGN_CENTER);
$text->setFont(__DIR__ . '/../../../public/fonts/SourceCodePro-Regular.ttf');
$text->setFont(__DIR__.'/../../../public/fonts/SourceCodePro-Regular.ttf');
$text->setFillColor(new \ImagickPixel($isGolden ? '#664A1E' : ($isPlatinum ? '#555555' : '#E8E9F0')));
$text->setFontSize(28);
$text->setFontWeight(400);
$baseImage->annotateImage($text, 512, 596, 0, $userId);
if (!empty($width) || !empty($height)) {
if (! empty($width) || ! empty($height)) {
$baseImage->resizeImage($width, $height, Imagick::FILTER_LANCZOS, 1);
}
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->setContentType('image/png')
->file($baseImage->getImageBlob());
});
@@ -867,7 +862,7 @@ App::get('/v1/cards/cloud-og')
throw new Exception(Exception::USER_NOT_FOUND);
}
if (!$mock) {
if (! $mock) {
$internalId = $user->getInternalId();
$bgVariation = $internalId % 3 === 0 ? '1' : ($internalId % 3 === 1 ? '2' : '3');
$cardVariation = $internalId % 3 === 0 ? '1' : ($internalId % 3 === 1 ? '2' : '3');
@@ -891,9 +886,9 @@ App::get('/v1/cards/cloud-og')
$createdAt = new \DateTime($employees[$email]['memberSince'] ?? '');
}
if (!$isEmployee && !empty($githubName)) {
if (! $isEmployee && ! empty($githubName)) {
$employeeGitHub = \array_search(\strtolower($githubName), \array_map(fn ($employee) => \strtolower($employee['gitHub']) ?? '', $employees));
if (!empty($employeeGitHub)) {
if (! empty($employeeGitHub)) {
$isEmployee = true;
$employeeNumber = $isEmployee ? $employees[$employeeGitHub]['spot'] : '';
$createdAt = new \DateTime($employees[$employeeGitHub]['memberSince'] ?? '');
@@ -940,17 +935,17 @@ App::get('/v1/cards/cloud-og')
$isGolden = $isEmployee || $isHero || $isContributor;
$isPlatinum = $isGolden ? false : $isPlatinum;
$memberSince = \strtoupper('Member since ' . $createdAt->format('M') . ' ' . $createdAt->format('d') . ', ' . $createdAt->format('o'));
$memberSince = \strtoupper('Member since '.$createdAt->format('M').' '.$createdAt->format('d').', '.$createdAt->format('o'));
$baseImage = new \Imagick(__DIR__ . "/../../../public/images/cards/cloud/og-background{$bgVariation}.png");
$baseImage = new \Imagick(__DIR__."/../../../public/images/cards/cloud/og-background{$bgVariation}.png");
$cardType = $isGolden ? '-golden' : ($isPlatinum ? '-platinum' : '');
$image = new Imagick(__DIR__ . "/../../../public/images/cards/cloud/og-card{$cardType}{$cardVariation}.png");
$image = new Imagick(__DIR__."/../../../public/images/cards/cloud/og-card{$cardType}{$cardVariation}.png");
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 1008 / 2 - $image->getImageWidth() / 2, 1008 / 2 - $image->getImageHeight() / 2);
$imageLogo = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/og-background-logo.png');
$imageShadow = new Imagick(__DIR__ . "/../../../public/images/cards/cloud/og-shadow{$cardType}.png");
$imageLogo = new Imagick(__DIR__.'/../../../public/images/cards/cloud/og-background-logo.png');
$imageShadow = new Imagick(__DIR__."/../../../public/images/cards/cloud/og-shadow{$cardType}.png");
if ($cardVariation === '1') {
$baseImage->compositeImage($imageLogo, Imagick::COMPOSITE_OVER, 32, 1008 - $imageLogo->getImageHeight() - 32);
$baseImage->compositeImage($imageShadow, Imagick::COMPOSITE_OVER, -450, 700);
@@ -964,19 +959,19 @@ App::get('/v1/cards/cloud-og')
if ($isEmployee) {
$file = $cardVariation === '3' ? 'employee-skew.png' : 'employee.png';
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/' . $file);
$image = new Imagick(__DIR__.'/../../../public/images/cards/cloud/'.$file);
$image->setGravity(Imagick::GRAVITY_CENTER);
$hashtag = new \ImagickDraw();
$hashtag->setTextAlignment(Imagick::ALIGN_LEFT);
$hashtag->setFont(__DIR__ . '/../../../public/fonts/Inter-Bold.ttf');
$hashtag->setFont(__DIR__.'/../../../public/fonts/Inter-Bold.ttf');
$hashtag->setFillColor(new \ImagickPixel('#FFFADF'));
$hashtag->setFontSize(20);
$hashtag->setFontWeight(700);
$text = new \ImagickDraw();
$text->setTextAlignment(Imagick::ALIGN_LEFT);
$text->setFont(__DIR__ . '/../../../public/fonts/Inter-Bold.ttf');
$text->setFont(__DIR__.'/../../../public/fonts/Inter-Bold.ttf');
$text->setFillColor(new \ImagickPixel('#FFFADF'));
$text->setFontSize(\strlen($employeeNumber) <= 1 ? 36 : 28);
$text->setFontWeight(700);
@@ -1050,7 +1045,7 @@ App::get('/v1/cards/cloud-og')
if ($isContributor) {
$file = $cardVariation === '3' ? 'contributor-skew.png' : 'contributor.png';
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/' . $file);
$image = new Imagick(__DIR__.'/../../../public/images/cards/cloud/'.$file);
$image->setGravity(Imagick::GRAVITY_CENTER);
if ($cardVariation === '1') {
@@ -1068,7 +1063,7 @@ App::get('/v1/cards/cloud-og')
if ($isHero) {
$file = $cardVariation === '3' ? 'hero-skew.png' : 'hero.png';
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/' . $file);
$image = new Imagick(__DIR__.'/../../../public/images/cards/cloud/'.$file);
$image->setGravity(Imagick::GRAVITY_CENTER);
if ($cardVariation === '1') {
@@ -1084,14 +1079,14 @@ App::get('/v1/cards/cloud-og')
}
}
setlocale(LC_ALL, "en_US.utf8");
setlocale(LC_ALL, 'en_US.utf8');
// $name = \iconv("utf-8", "ascii//TRANSLIT", $name);
// $memberSince = \iconv("utf-8", "ascii//TRANSLIT", $memberSince);
// $githubName = \iconv("utf-8", "ascii//TRANSLIT", $githubName);
$textName = new \ImagickDraw();
$textName->setTextAlignment(Imagick::ALIGN_CENTER);
$textName->setFont(__DIR__ . '/../../../public/fonts/Poppins-Bold.ttf');
$textName->setFont(__DIR__.'/../../../public/fonts/Poppins-Bold.ttf');
$textName->setFillColor(new \ImagickPixel('#FFFFFF'));
if (\strlen($name) > 32) {
@@ -1128,7 +1123,7 @@ App::get('/v1/cards/cloud-og')
$textMember = new \ImagickDraw();
$textMember->setTextAlignment(Imagick::ALIGN_CENTER);
$textMember->setFont(__DIR__ . '/../../../public/fonts/Inter-Medium.ttf');
$textMember->setFont(__DIR__.'/../../../public/fonts/Inter-Medium.ttf');
$textMember->setFillColor(new \ImagickPixel($isGolden || $isPlatinum ? '#FFFFFF' : '#FFB9CC'));
$textMember->setFontWeight(500);
$textMember->setTextKerning(1.12);
@@ -1158,10 +1153,10 @@ App::get('/v1/cards/cloud-og')
$baseImage->drawImage($textMember);
}
if (!empty($githubName)) {
if (! empty($githubName)) {
$text = new \ImagickDraw();
$text->setTextAlignment(Imagick::ALIGN_LEFT);
$text->setFont(__DIR__ . '/../../../public/fonts/Inter-Regular.ttf');
$text->setFont(__DIR__.'/../../../public/fonts/Inter-Regular.ttf');
$text->setFillColor(new \ImagickPixel('#FFFFFF'));
$text->setFontSize($scalingDown ? 16 : 20);
$text->setFontWeight(400);
@@ -1172,7 +1167,7 @@ App::get('/v1/cards/cloud-og')
$group = new Imagick();
$groupWidth = $metrics['textWidth'] + 32 + 4;
$group->newImage($groupWidth, $metrics['textHeight'] + 10, '#00000000');
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/github.png');
$image = new Imagick(__DIR__.'/../../../public/images/cards/cloud/github.png');
$image->setGravity(Imagick::GRAVITY_CENTER);
$image->resizeImage(32, 32, Imagick::FILTER_LANCZOS, 1);
$precisionFix = -1;
@@ -1190,7 +1185,7 @@ App::get('/v1/cards/cloud-og')
$group = new Imagick();
$groupWidth = $metrics['textWidth'] + 32 + 4;
$group->newImage($groupWidth, $metrics['textHeight'] + 10, '#00000000');
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/github.png');
$image = new Imagick(__DIR__.'/../../../public/images/cards/cloud/github.png');
$image->setGravity(Imagick::GRAVITY_CENTER);
$image->resizeImage(32, 32, Imagick::FILTER_LANCZOS, 1);
$precisionFix = -1;
@@ -1210,7 +1205,7 @@ App::get('/v1/cards/cloud-og')
$text->annotation(320 + 15 + 2, 640, $githubName);
$metrics = $baseImage->queryFontMetrics($text, $githubName);
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/github-skew.png');
$image = new Imagick(__DIR__.'/../../../public/images/cards/cloud/github-skew.png');
$image->setGravity(Imagick::GRAVITY_CENTER);
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 512 - ($metrics['textWidth'] / 2), 518 + \strlen($githubName) * 1.3);
@@ -1218,12 +1213,12 @@ App::get('/v1/cards/cloud-og')
}
}
if (!empty($width) || !empty($height)) {
if (! empty($width) || ! empty($height)) {
$baseImage->resizeImage($width, $height, Imagick::FILTER_LANCZOS, 1);
}
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->setContentType('image/png')
->file($baseImage->getImageBlob());
});
-3
View File
@@ -15,7 +15,6 @@ App::init()
}
});
App::get('/v1/console/variables')
->desc('Get Variables')
->groups(['api', 'projects'])
@@ -29,7 +28,6 @@ App::get('/v1/console/variables')
->label('sdk.response.model', Response::MODEL_CONSOLE_VARIABLES)
->inject('response')
->action(function (Response $response) {
$variables = new Document([
'_APP_DOMAIN_TARGET' => App::getEnv('_APP_DOMAIN_TARGET'),
'_APP_STORAGE_LIMIT' => +App::getEnv('_APP_STORAGE_LIMIT'),
@@ -40,7 +38,6 @@ App::get('/v1/console/variables')
$response->dynamic($variables, Response::MODEL_CONSOLE_VARIABLES);
});
App::post('/v1/console/assistant')
->desc('Ask Query')
->groups(['api', 'assistant'])
File diff suppressed because it is too large Load Diff
+96 -111
View File
@@ -9,40 +9,40 @@ use Appwrite\Event\Func;
use Appwrite\Event\Usage;
use Appwrite\Event\Validator\Event as ValidatorEvent;
use Appwrite\Extend\Exception;
use Appwrite\Task\Validator\Cron;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries\Deployments;
use Appwrite\Utopia\Database\Validator\Queries\Executions;
use Appwrite\Utopia\Database\Validator\Queries\Functions;
use Appwrite\Utopia\Response;
use Executor\Executor;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Roles;
use Utopia\Database\Validator\UID;
use Utopia\Storage\Device;
use Utopia\Storage\Validator\File;
use Utopia\Storage\Validator\FileExt;
use Utopia\Storage\Validator\FileSize;
use Utopia\Storage\Validator\Upload;
use Appwrite\Utopia\Response;
use Utopia\Swoole\Request;
use Appwrite\Task\Validator\Cron;
use Appwrite\Utopia\Database\Validator\Queries\Deployments;
use Appwrite\Utopia\Database\Validator\Queries\Executions;
use Appwrite\Utopia\Database\Validator\Queries\Functions;
use Utopia\App;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\DateTime;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Text;
use Utopia\Validator\Range;
use Utopia\Validator\WhiteList;
use Utopia\Config\Config;
use Executor\Executor;
use Utopia\CLI\Console;
use Utopia\Database\Validator\Roles;
use Utopia\Validator\Boolean;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
include_once __DIR__ . '/../shared/api.php';
include_once __DIR__.'/../shared/api.php';
App::post('/v1/functions')
->groups(['api', 'functions'])
@@ -60,9 +60,9 @@ App::post('/v1/functions')
->label('sdk.response.model', Response::MODEL_FUNCTION)
->param('functionId', '', new CustomId(), 'Function ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution roles. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true)
->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution roles. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' roles are allowed, each 64 characters long.', true)
->param('runtime', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Execution runtime.')
->param('events', [], new ArrayList(new ValidatorEvent(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.', true)
->param('events', [], new ArrayList(new ValidatorEvent(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' events are allowed.', true)
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Function maximum execution time in seconds.', true)
->param('enabled', true, new Boolean(), 'Is function enabled?', true)
@@ -73,7 +73,6 @@ App::post('/v1/functions')
->inject('events')
->inject('dbForConsole')
->action(function (string $functionId, string $name, array $execute, string $runtime, array $events, string $schedule, int $timeout, bool $enabled, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance, Database $dbForConsole) {
$functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
$function = $dbForProject->createDocument('functions', new Document([
'$id' => $functionId,
@@ -87,18 +86,18 @@ App::post('/v1/functions')
'schedule' => $schedule,
'scheduleInternalId' => '',
'timeout' => $timeout,
'search' => implode(' ', [$functionId, $name, $runtime])
'search' => implode(' ', [$functionId, $name, $runtime]),
]));
$schedule = Authorization::skip(
fn() => $dbForConsole->createDocument('schedules', new Document([
fn () => $dbForConsole->createDocument('schedules', new Document([
'region' => App::getEnv('_APP_REGION', 'default'), // Todo replace with projects region
'resourceType' => 'function',
'resourceId' => $function->getId(),
'resourceInternalId' => $function->getInternalId(),
'resourceUpdatedAt' => DateTime::now(),
'projectId' => $project->getId(),
'schedule' => $function->getAttribute('schedule'),
'schedule' => $function->getAttribute('schedule'),
'active' => false,
]))
);
@@ -125,15 +124,14 @@ App::get('/v1/functions')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FUNCTION_LIST)
->param('queries', [], new Functions(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Functions::ALLOWED_ATTRIBUTES), true)
->param('queries', [], new Functions(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' queries are allowed, each '.APP_LIMIT_ARRAY_ELEMENT_SIZE.' characters long. You may filter on the following attributes: '.implode(', ', Functions::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
->action(function (array $queries, string $search, Response $response, Database $dbForProject) {
$queries = Query::parseQueries($queries);
if (!empty($search)) {
if (! empty($search)) {
$queries[] = Query::search('search', $search);
}
@@ -173,17 +171,17 @@ App::get('/v1/functions/runtimes')
->label('sdk.response.model', Response::MODEL_RUNTIME_LIST)
->inject('response')
->action(function (Response $response) {
$runtimes = Config::getParam('runtimes');
$runtimes = array_map(function ($key) use ($runtimes) {
$runtimes[$key]['$id'] = $key;
return $runtimes[$key];
}, array_keys($runtimes));
$response->dynamic(new Document([
'total' => count($runtimes),
'runtimes' => $runtimes
'runtimes' => $runtimes,
]), Response::MODEL_RUNTIME_LIST);
});
@@ -226,7 +224,6 @@ App::get('/v1/functions/:functionId/usage')
->inject('response')
->inject('dbForProject')
->action(function (string $functionId, string $range, Response $response, Database $dbForProject) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
@@ -270,18 +267,18 @@ App::get('/v1/functions/:functionId/usage')
'1d' => 'Y-m-d\T00:00:00.000P',
};
foreach ($metrics as $metric) {
$usage[$metric] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric][] = [
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
foreach ($metrics as $metric) {
$usage[$metric] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric][] = [
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
}
}
}
$response->dynamic(new Document([
'range' => $range,
@@ -309,7 +306,6 @@ App::get('/v1/functions/usage')
->inject('response')
->inject('dbForProject')
->action(function (string $range, Response $response, Database $dbForProject) {
$periods = Config::getParam('usage', []);
$stats = $usage = [];
$days = $periods[$range];
@@ -348,18 +344,18 @@ App::get('/v1/functions/usage')
'1d' => 'Y-m-d\T00:00:00.000P',
};
foreach ($metrics as $metric) {
$usage[$metric] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric][] = [
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
foreach ($metrics as $metric) {
$usage[$metric] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric][] = [
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
}
}
}
$response->dynamic(new Document([
'range' => $range,
'functionsTotal' => $usage[$metrics[0]],
@@ -389,8 +385,8 @@ App::put('/v1/functions/:functionId')
->label('sdk.response.model', Response::MODEL_FUNCTION)
->param('functionId', '', new UID(), 'Function ID.')
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution roles. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true)
->param('events', [], new ArrayList(new ValidatorEvent(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.', true)
->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution roles. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' roles are allowed, each 64 characters long.', true)
->param('events', [], new ArrayList(new ValidatorEvent(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' events are allowed.', true)
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Maximum execution time in seconds.', true)
->param('enabled', true, new Boolean(), 'Is function enabled?', true)
@@ -401,7 +397,6 @@ App::put('/v1/functions/:functionId')
->inject('events')
->inject('dbForConsole')
->action(function (string $functionId, string $name, array $execute, array $events, string $schedule, int $timeout, bool $enabled, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance, Database $dbForConsole) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
@@ -424,7 +419,7 @@ App::put('/v1/functions/:functionId')
$schedule
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('schedule', $function->getAttribute('schedule'))
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
->setAttribute('active', ! empty($function->getAttribute('schedule')) && ! empty($function->getAttribute('deployment')));
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
$eventsInstance->setParam('functionId', $function->getId());
@@ -454,7 +449,6 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
->inject('events')
->inject('dbForConsole')
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $events, Database $dbForConsole) {
$function = $dbForProject->getDocument('functions', $functionId);
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
$build = $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''));
@@ -484,7 +478,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
$schedule
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('schedule', $function->getAttribute('schedule'))
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
->setAttribute('active', ! empty($function->getAttribute('schedule')) && ! empty($function->getAttribute('deployment')));
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
$events
@@ -515,14 +509,13 @@ App::delete('/v1/functions/:functionId')
->inject('project')
->inject('dbForConsole')
->action(function (string $functionId, Response $response, Database $dbForProject, Delete $deletes, Event $events, Document $project, Database $dbForConsole) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
if (!$dbForProject->deleteDocument('functions', $function->getId())) {
if (! $dbForProject->deleteDocument('functions', $function->getId())) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove function from DB');
}
@@ -570,7 +563,6 @@ App::post('/v1/functions/:functionId/deployments')
->inject('deviceLocal')
->inject('dbForConsole')
->action(function (string $functionId, string $entrypoint, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $events, Document $project, Device $deviceFunctions, Device $deviceLocal, Database $dbForConsole) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
@@ -597,7 +589,7 @@ App::post('/v1/functions/:functionId/deployments')
$fileTmpName = (\is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name'];
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
if (!$fileExt->isValid($file['name'])) { // Check if file type is allowed
if (! $fileExt->isValid($file['name'])) { // Check if file type is allowed
throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED);
}
@@ -606,7 +598,7 @@ App::post('/v1/functions/:functionId/deployments')
$chunk = 1;
$chunks = 1;
if (!empty($contentRange)) {
if (! empty($contentRange)) {
$start = $request->getContentRangeStart();
$end = $request->getContentRangeEnd();
$fileSize = $request->getContentRangeSize();
@@ -627,21 +619,21 @@ App::post('/v1/functions/:functionId/deployments')
}
}
if (!$fileSizeValidator->isValid($fileSize)) { // Check if file size is exceeding allowed limit
if (! $fileSizeValidator->isValid($fileSize)) { // Check if file size is exceeding allowed limit
throw new Exception(Exception::STORAGE_INVALID_FILE_SIZE);
}
if (!$upload->isValid($fileTmpName)) {
if (! $upload->isValid($fileTmpName)) {
throw new Exception(Exception::STORAGE_INVALID_FILE);
}
// Save to storage
$fileSize ??= $deviceLocal->getFileSize($fileTmpName);
$path = $deviceFunctions->getPath($deploymentId . '.' . \pathinfo($fileName, PATHINFO_EXTENSION));
$path = $deviceFunctions->getPath($deploymentId.'.'.\pathinfo($fileName, PATHINFO_EXTENSION));
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
$metadata = ['content_type' => $deviceLocal->getFileMimeType($fileTmpName)];
if (!$deployment->isEmpty()) {
if (! $deployment->isEmpty()) {
$chunks = $deployment->getAttribute('chunksTotal', 1);
$metadata = $deployment->getAttribute('metadata', []);
if ($chunk === -1) {
@@ -663,7 +655,7 @@ App::post('/v1/functions/:functionId/deployments')
$activeDeployments = $dbForProject->find('deployments', [
Query::equal('activate', [true]),
Query::equal('resourceId', [$functionId]),
Query::equal('resourceType', ['functions'])
Query::equal('resourceType', ['functions']),
]);
foreach ($activeDeployments as $activeDeployment) {
@@ -755,12 +747,11 @@ App::get('/v1/functions/:functionId/deployments')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DEPLOYMENT_LIST)
->param('functionId', '', new UID(), 'Function ID.')
->param('queries', [], new Deployments(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Deployments::ALLOWED_ATTRIBUTES), true)
->param('queries', [], new Deployments(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' queries are allowed, each '.APP_LIMIT_ARRAY_ELEMENT_SIZE.' characters long. You may filter on the following attributes: '.implode(', ', Deployments::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
@@ -769,7 +760,7 @@ App::get('/v1/functions/:functionId/deployments')
$queries = Query::parseQueries($queries);
if (!empty($search)) {
if (! empty($search)) {
$queries[] = Query::search('search', $search);
}
@@ -827,7 +818,6 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId')
->inject('response')
->inject('dbForProject')
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
@@ -873,7 +863,6 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
->inject('events')
->inject('deviceFunctions')
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Delete $deletes, Event $events, Device $deviceFunctions) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
@@ -889,7 +878,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
}
if ($deviceFunctions->delete($deployment->getAttribute('path', ''))) {
if (!$dbForProject->deleteDocument('deployments', $deployment->getId())) {
if (! $dbForProject->deleteDocument('deployments', $deployment->getId())) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove deployment from DB');
}
}
@@ -933,7 +922,6 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
->inject('project')
->inject('events')
->action(function (string $functionId, string $deploymentId, string $buildId, Response $response, Database $dbForProject, Document $project, Event $events) {
$function = $dbForProject->getDocument('functions', $functionId);
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
@@ -998,11 +986,10 @@ App::post('/v1/functions/:functionId/executions')
->inject('queueForFunctions')
->inject('queueForUsage')
->action(function (string $functionId, string $data, bool $async, Response $response, Document $project, Database $dbForProject, Document $user, Event $events, string $mode, Func $queueForFunctions, Usage $queueForUsage) {
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
if ($function->isEmpty() || !$function->getAttribute('enabled')) {
if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) {
if ($function->isEmpty() || ! $function->getAttribute('enabled')) {
if (! ($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
}
@@ -1012,7 +999,7 @@ App::post('/v1/functions/:functionId/executions')
$runtime = (isset($runtimes[$function->getAttribute('runtime', '')])) ? $runtimes[$function->getAttribute('runtime', '')] : null;
if (\is_null($runtime)) {
throw new Exception(Exception::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported');
throw new Exception(Exception::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "'.$function->getAttribute('runtime', '').'" is not supported');
}
$deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', '')));
@@ -1037,7 +1024,7 @@ App::post('/v1/functions/:functionId/executions')
$validator = new Authorization('execute');
if (!$validator->isValid($function->getAttribute('execute'))) { // Check if user has write access to execute function
if (! $validator->isValid($function->getAttribute('execute'))) { // Check if user has write access to execute function
throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription());
}
@@ -1046,7 +1033,7 @@ App::post('/v1/functions/:functionId/executions')
/** @var Document $execution */
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', new Document([
'$id' => $executionId,
'$permissions' => !$user->isEmpty() ? [Permission::read(Role::user($user->getId()))] : [],
'$permissions' => ! $user->isEmpty() ? [Permission::read(Role::user($user->getId()))] : [],
'functionInternalId' => $function->getInternalId(),
'functionId' => $function->getId(),
'deploymentInternalId' => $deployment->getInternalId(),
@@ -1061,7 +1048,7 @@ App::post('/v1/functions/:functionId/executions')
])));
$jwt = ''; // initialize
if (!$user->isEmpty()) { // If userId exists, generate a JWT for function
if (! $user->isEmpty()) { // If userId exists, generate a JWT for function
$sessions = $user->getAttribute('sessions', []);
$current = new Document();
@@ -1072,7 +1059,7 @@ App::post('/v1/functions/:functionId/executions')
}
}
if (!$current->isEmpty()) {
if (! $current->isEmpty()) {
$jwtObj = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$jwt = $jwtObj->encode([
'userId' => $user->getId(),
@@ -1104,6 +1091,7 @@ App::post('/v1/functions/:functionId/executions')
$vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) {
$carry[$var->getAttribute('key')] = $var->getAttribute('value') ?? '';
return $carry;
}, []);
@@ -1145,13 +1133,13 @@ App::post('/v1/functions/:functionId/executions')
* Sync execution compute usage from
*/
$queueForUsage
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($executionResponse['duration'] * 1000))// per project
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($executionResponse['duration'] * 1000))// per function
;
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int) ($executionResponse['duration'] * 1000))// per project
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int) ($executionResponse['duration'] * 1000))// per function
;
} catch (\Throwable $th) {
$interval = (new \DateTime())->diff(new \DateTime($execution->getCreatedAt()));
$execution
->setAttribute('duration', (float)$interval->format('%s.%f'))
->setAttribute('duration', (float) $interval->format('%s.%f'))
->setAttribute('status', 'failed')
->setAttribute('statusCode', $th->getCode())
->setAttribute('stderr', $th->getMessage());
@@ -1164,7 +1152,7 @@ App::post('/v1/functions/:functionId/executions')
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
if (!$isPrivilegedUser && !$isAppUser) {
if (! $isPrivilegedUser && ! $isAppUser) {
$execution->setAttribute('stdout', '');
$execution->setAttribute('stderr', '');
}
@@ -1186,24 +1174,23 @@ App::get('/v1/functions/:functionId/executions')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_EXECUTION_LIST)
->param('functionId', '', new UID(), 'Function ID.')
->param('queries', [], new Executions(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Executions::ALLOWED_ATTRIBUTES), true)
->param('queries', [], new Executions(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' queries are allowed, each '.APP_LIMIT_ARRAY_ELEMENT_SIZE.' characters long. You may filter on the following attributes: '.implode(', ', Executions::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
->inject('mode')
->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) {
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
if ($function->isEmpty() || !$function->getAttribute('enabled')) {
if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) {
if ($function->isEmpty() || ! $function->getAttribute('enabled')) {
if (! ($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
}
$queries = Query::parseQueries($queries);
if (!empty($search)) {
if (! empty($search)) {
$queries[] = Query::search('search', $search);
}
@@ -1233,10 +1220,11 @@ App::get('/v1/functions/:functionId/executions')
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
if (!$isPrivilegedUser && !$isAppUser) {
if (! $isPrivilegedUser && ! $isAppUser) {
$results = array_map(function ($execution) {
$execution->setAttribute('stdout', '');
$execution->setAttribute('stderr', '');
return $execution;
}, $results);
}
@@ -1264,11 +1252,10 @@ App::get('/v1/functions/:functionId/executions/:executionId')
->inject('dbForProject')
->inject('mode')
->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, string $mode) {
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
if ($function->isEmpty() || !$function->getAttribute('enabled')) {
if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) {
if ($function->isEmpty() || ! $function->getAttribute('enabled')) {
if (! ($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
}
@@ -1286,7 +1273,7 @@ App::get('/v1/functions/:functionId/executions/:executionId')
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
if (!$isPrivilegedUser && !$isAppUser) {
if (! $isPrivilegedUser && ! $isAppUser) {
$execution->setAttribute('stdout', '');
$execution->setAttribute('stderr', '');
}
@@ -1310,7 +1297,7 @@ App::post('/v1/functions/:functionId/variables')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_VARIABLE)
->param('functionId', '', new UID(), 'Function unique ID.', false)
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false)
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: '.Database::LENGTH_KEY.' chars.', false)
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', false)
->inject('response')
->inject('dbForProject')
@@ -1348,7 +1335,7 @@ App::post('/v1/functions/:functionId/variables')
$schedule
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('schedule', $function->getAttribute('schedule'))
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
->setAttribute('active', ! empty($function->getAttribute('schedule')) && ! empty($function->getAttribute('deployment')));
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
$dbForProject->deleteCachedDocument('functions', $function->getId());
@@ -1357,7 +1344,7 @@ App::post('/v1/functions/:functionId/variables')
$schedule
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('schedule', $function->getAttribute('schedule'))
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
->setAttribute('active', ! empty($function->getAttribute('schedule')) && ! empty($function->getAttribute('deployment')));
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
$response
@@ -1447,7 +1434,6 @@ App::put('/v1/functions/:functionId/variables/:variableId')
->inject('dbForProject')
->inject('dbForConsole')
->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject, Database $dbForConsole) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
@@ -1466,8 +1452,7 @@ App::put('/v1/functions/:functionId/variables/:variableId')
$variable
->setAttribute('key', $key)
->setAttribute('value', $value ?? $variable->getAttribute('value'))
->setAttribute('search', implode(' ', [$variableId, $function->getId(), $key]))
;
->setAttribute('search', implode(' ', [$variableId, $function->getId(), $key]));
try {
$dbForProject->updateDocument('variables', $variable->getId(), $variable);
@@ -1479,7 +1464,7 @@ App::put('/v1/functions/:functionId/variables/:variableId')
$schedule
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('schedule', $function->getAttribute('schedule'))
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
->setAttribute('active', ! empty($function->getAttribute('schedule')) && ! empty($function->getAttribute('deployment')));
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
$dbForProject->deleteCachedDocument('functions', $function->getId());
@@ -1488,7 +1473,7 @@ App::put('/v1/functions/:functionId/variables/:variableId')
$schedule
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('schedule', $function->getAttribute('schedule'))
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
->setAttribute('active', ! empty($function->getAttribute('schedule')) && ! empty($function->getAttribute('deployment')));
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
$response->dynamic($variable, Response::MODEL_VARIABLE);
@@ -1533,7 +1518,7 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
$schedule
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('schedule', $function->getAttribute('schedule'))
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
->setAttribute('active', ! empty($function->getAttribute('schedule')) && ! empty($function->getAttribute('deployment')));
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
$dbForProject->deleteCachedDocument('functions', $function->getId());
@@ -1542,7 +1527,7 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
$schedule
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('schedule', $function->getAttribute('schedule'))
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
->setAttribute('active', ! empty($function->getAttribute('schedule')) && ! empty($function->getAttribute('deployment')));
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
$response->noContent();
+12 -11
View File
@@ -42,11 +42,11 @@ App::get('/v1/graphql')
'query' => $query,
];
if (!empty($operationName)) {
if (! empty($operationName)) {
$query['operationName'] = $operationName;
}
if (!empty($variables)) {
if (! empty($variables)) {
$query['variables'] = \json_decode($variables, true);
}
@@ -150,10 +150,11 @@ App::post('/v1/graphql')
/**
* Execute a GraphQL request
*
* @param GQLSchema $schema
* @param Adapter $promiseAdapter
* @param array $query
* @param GQLSchema $schema
* @param Adapter $promiseAdapter
* @param array $query
* @return array
*
* @throws Exception
*/
function execute(
@@ -165,7 +166,7 @@ function execute(
$maxComplexity = App::getEnv('_APP_GRAPHQL_MAX_COMPLEXITY', 250);
$maxDepth = App::getEnv('_APP_GRAPHQL_MAX_DEPTH', 3);
if (!empty($query) && !isset($query[0])) {
if (! empty($query) && ! isset($query[0])) {
$query = [$query];
}
if (empty($query)) {
@@ -224,7 +225,7 @@ function execute(
/**
* Parse an "application/graphql" type request
*
* @param Request $request
* @param Request $request
* @return array
*/
function parseGraphql(Request $request): array
@@ -235,8 +236,8 @@ function parseGraphql(Request $request): array
/**
* Parse an "multipart/form-data" type request
*
* @param array $query
* @param Request $request
* @param array $query
* @param Request $request
* @return array
*/
function parseMultipart(array $query, Request $request): array
@@ -248,7 +249,7 @@ function parseMultipart(array $query, Request $request): array
foreach ($locations as $location) {
$items = &$operations;
foreach (\explode('.', $location) as $key) {
if (!isset($items[$key]) || !\is_array($items[$key])) {
if (! isset($items[$key]) || ! \is_array($items[$key])) {
$items[$key] = [];
}
$items = &$items[$key];
@@ -276,7 +277,7 @@ function parseMultipart(array $query, Request $request): array
function processResult($result, $debugFlags): array
{
// Only one query, return the result
if (!isset($result[1])) {
if (! isset($result[1])) {
return $result[0]->toArray($debugFlags);
}
+43 -55
View File
@@ -28,11 +28,10 @@ App::get('/v1/health')
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
->inject('response')
->action(function (Response $response) {
$output = [
'name' => 'http',
'status' => 'pass',
'ping' => 0
'ping' => 0,
];
$response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS);
@@ -47,7 +46,7 @@ App::get('/v1/health/version')
->label('sdk.response.model', Response::MODEL_HEALTH_VERSION)
->inject('response')
->action(function (Response $response) {
$response->dynamic(new Document([ 'version' => APP_VERSION_STABLE ]), Response::MODEL_HEALTH_VERSION);
$response->dynamic(new Document(['version' => APP_VERSION_STABLE]), Response::MODEL_HEALTH_VERSION);
});
App::get('/v1/health/db')
@@ -64,7 +63,6 @@ App::get('/v1/health/db')
->inject('response')
->inject('pools')
->action(function (Response $response, Group $pools) {
$output = [];
$configs = [
@@ -81,22 +79,22 @@ App::get('/v1/health/db')
if ($adapter->ping()) {
$output[] = new Document([
'name' => $key . " ($database)",
'name' => $key." ($database)",
'status' => 'pass',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000),
]);
} else {
$output[] = new Document([
'name' => $key . " ($database)",
'name' => $key." ($database)",
'status' => 'fail',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000),
]);
}
} catch (\Throwable $th) {
$output[] = new Document([
'name' => $key . " ($database)",
'name' => $key." ($database)",
'status' => 'fail',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000),
]);
}
}
@@ -122,7 +120,6 @@ App::get('/v1/health/cache')
->inject('response')
->inject('pools')
->action(function (Response $response, Group $pools) {
$output = [];
$configs = [
@@ -138,22 +135,22 @@ App::get('/v1/health/cache')
if ($adapter->ping()) {
$output[] = new Document([
'name' => $key . " ($database)",
'name' => $key." ($database)",
'status' => 'pass',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000),
]);
} else {
$output[] = new Document([
'name' => $key . " ($database)",
'name' => $key." ($database)",
'status' => 'fail',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000),
]);
}
} catch (\Throwable $th) {
$output[] = new Document([
'name' => $key . " ($database)",
'name' => $key." ($database)",
'status' => 'fail',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000),
]);
}
}
@@ -179,7 +176,6 @@ App::get('/v1/health/queue')
->inject('response')
->inject('pools')
->action(function (Response $response, Group $pools) {
$output = [];
$configs = [
@@ -195,22 +191,22 @@ App::get('/v1/health/queue')
if ($adapter->ping()) {
$output[] = new Document([
'name' => $key . " ($database)",
'name' => $key." ($database)",
'status' => 'pass',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000),
]);
} else {
$output[] = new Document([
'name' => $key . " ($database)",
'name' => $key." ($database)",
'status' => 'fail',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000),
]);
}
} catch (\Throwable $th) {
$output[] = new Document([
'name' => $key . " ($database)",
'name' => $key." ($database)",
'status' => 'fail',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000),
]);
}
}
@@ -236,7 +232,6 @@ App::get('/v1/health/pubsub')
->inject('response')
->inject('pools')
->action(function (Response $response, Group $pools) {
$output = [];
$configs = [
@@ -252,22 +247,22 @@ App::get('/v1/health/pubsub')
if ($adapter->ping()) {
$output[] = new Document([
'name' => $key . " ($database)",
'name' => $key." ($database)",
'status' => 'pass',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000),
]);
} else {
$output[] = new Document([
'name' => $key . " ($database)",
'name' => $key." ($database)",
'status' => 'fail',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000),
]);
}
} catch (\Throwable $th) {
$output[] = new Document([
'name' => $key . " ($database)",
'name' => $key." ($database)",
'status' => 'fail',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000),
]);
}
}
@@ -292,7 +287,6 @@ App::get('/v1/health/time')
->label('sdk.response.model', Response::MODEL_HEALTH_TIME)
->inject('response')
->action(function (Response $response) {
/*
* Code from: @see https://www.beliefmedia.com.au/query-ntp-time-server
*/
@@ -305,7 +299,7 @@ App::get('/v1/health/time')
\socket_connect($sock, $host, 123);
/* Send request */
$msg = "\010" . \str_repeat("\0", 47);
$msg = "\010".\str_repeat("\0", 47);
\socket_send($sock, $msg, \strlen($msg), 0);
@@ -330,7 +324,7 @@ App::get('/v1/health/time')
$output = [
'remoteTime' => $timestamp,
'localTime' => \time(),
'diff' => $diff
'diff' => $diff,
];
$response->dynamic(new Document($output), Response::MODEL_HEALTH_TIME);
@@ -349,8 +343,7 @@ App::get('/v1/health/queue/webhooks')
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
->inject('response')
->action(function (Response $response) {
$response->dynamic(new Document([ 'size' => Resque::size(Event::WEBHOOK_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE);
$response->dynamic(new Document(['size' => Resque::size(Event::WEBHOOK_QUEUE_NAME)]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
App::get('/v1/health/queue/logs')
@@ -366,8 +359,7 @@ App::get('/v1/health/queue/logs')
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
->inject('response')
->action(function (Response $response) {
$response->dynamic(new Document([ 'size' => Resque::size(Event::AUDITS_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE);
$response->dynamic(new Document(['size' => Resque::size(Event::AUDITS_QUEUE_NAME)]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
App::get('/v1/health/queue/certificates')
@@ -383,8 +375,7 @@ App::get('/v1/health/queue/certificates')
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
->inject('response')
->action(function (Response $response) {
$response->dynamic(new Document([ 'size' => Resque::size(Event::CERTIFICATES_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE);
$response->dynamic(new Document(['size' => Resque::size(Event::CERTIFICATES_QUEUE_NAME)]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
App::get('/v1/health/queue/functions')
@@ -402,7 +393,7 @@ App::get('/v1/health/queue/functions')
->inject('response')
->action(function (Connection $queue, Response $response) {
$client = new Client(Event::FUNCTIONS_QUEUE_NAME, $queue);
$response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE);
$response->dynamic(new Document(['size' => $client->sumProcessingJobs()]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
App::get('/v1/health/storage/local')
@@ -418,31 +409,30 @@ App::get('/v1/health/storage/local')
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
->inject('response')
->action(function (Response $response) {
$checkStart = \microtime(true);
foreach (
[
'Uploads' => APP_STORAGE_UPLOADS,
'Cache' => APP_STORAGE_CACHE,
'Config' => APP_STORAGE_CONFIG,
'Certs' => APP_STORAGE_CERTIFICATES
'Uploads' => APP_STORAGE_UPLOADS,
'Cache' => APP_STORAGE_CACHE,
'Config' => APP_STORAGE_CONFIG,
'Certs' => APP_STORAGE_CERTIFICATES,
] as $key => $volume
) {
$device = new Local($volume);
if (!\is_readable($device->getRoot())) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Device ' . $key . ' dir is not readable');
if (! \is_readable($device->getRoot())) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Device '.$key.' dir is not readable');
}
if (!\is_writable($device->getRoot())) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Device ' . $key . ' dir is not writable');
if (! \is_writable($device->getRoot())) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Device '.$key.' dir is not writable');
}
}
$output = [
'status' => 'pass',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000),
];
$response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS);
@@ -461,10 +451,9 @@ App::get('/v1/health/anti-virus')
->label('sdk.response.model', Response::MODEL_HEALTH_ANTIVIRUS)
->inject('response')
->action(function (Response $response) {
$output = [
'status' => '',
'version' => ''
'version' => '',
];
if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'disabled') { // Check if scans are enabled
@@ -499,7 +488,6 @@ App::get('/v1/health/stats') // Currently only used internally
->inject('register')
->inject('deviceFiles')
->action(function (Response $response, Registry $register, Device $deviceFiles) {
$cache = $register->get('cache');
$cacheStats = $cache->info();
@@ -507,7 +495,7 @@ App::get('/v1/health/stats') // Currently only used internally
$response
->json([
'storage' => [
'used' => Storage::human($deviceFiles->getDirectorySize($deviceFiles->getRoot() . '/')),
'used' => Storage::human($deviceFiles->getDirectorySize($deviceFiles->getRoot().'/')),
'partitionTotal' => Storage::human($deviceFiles->getPartitionTotalSpace()),
'partitionFree' => Storage::human($deviceFiles->getPartitionFreeSpace()),
],
+13 -14
View File
@@ -1,7 +1,7 @@
<?php
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use MaxMind\Db\Reader;
use Utopia\App;
use Utopia\Config\Config;
@@ -40,8 +40,8 @@ App::get('/v1/locale')
if ($record) {
$output['countryCode'] = $record['country']['iso_code'];
$output['country'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
$output['continent'] = $locale->getText('continents.' . strtolower($record['continent']['code']), $locale->getText('locale.country.unknown'));
$output['country'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
$output['continent'] = $locale->getText('continents.'.strtolower($record['continent']['code']), $locale->getText('locale.country.unknown'));
$output['continentCode'] = $record['continent']['code'];
$output['eu'] = (\in_array($record['country']['iso_code'], $eu)) ? true : false;
@@ -62,9 +62,9 @@ App::get('/v1/locale')
}
$response
->addHeader('Cache-Control', 'public, max-age=' . $time)
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time) . ' GMT') // 45 days cache
;
->addHeader('Cache-Control', 'public, max-age='.$time)
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time).' GMT') // 45 days cache
;
$response->dynamic(new Document($output), Response::MODEL_LOCALE);
});
@@ -111,7 +111,7 @@ App::get('/v1/locale/countries')
foreach ($list as $value) {
$output[] = new Document([
'name' => $locale->getText('countries.' . strtolower($value)),
'name' => $locale->getText('countries.'.strtolower($value)),
'code' => $value,
]);
}
@@ -143,9 +143,9 @@ App::get('/v1/locale/countries/eu')
$output = [];
foreach ($eu as $code) {
if ($locale->getText('countries.' . strtolower($code), false) !== false) {
if ($locale->getText('countries.'.strtolower($code), false) !== false) {
$output[] = new Document([
'name' => $locale->getText('countries.' . strtolower($code)),
'name' => $locale->getText('countries.'.strtolower($code)),
'code' => $code,
]);
}
@@ -180,11 +180,11 @@ App::get('/v1/locale/countries/phones')
\asort($list);
foreach ($list as $code => $name) {
if ($locale->getText('countries.' . strtolower($code), false) !== false) {
if ($locale->getText('countries.'.strtolower($code), false) !== false) {
$output[] = new Document([
'code' => '+' . $list[$code],
'code' => '+'.$list[$code],
'countryCode' => $code,
'countryName' => $locale->getText('countries.' . strtolower($code)),
'countryName' => $locale->getText('countries.'.strtolower($code)),
]);
}
}
@@ -212,7 +212,7 @@ App::get('/v1/locale/continents')
foreach ($list as $value) {
$output[] = new Document([
'name' => $locale->getText('continents.' . strtolower($value)),
'name' => $locale->getText('continents.'.strtolower($value)),
'code' => $value,
]);
}
@@ -246,7 +246,6 @@ App::get('/v1/locale/currencies')
$response->dynamic(new Document(['currencies' => $list, 'total' => \count($list)]), Response::MODEL_CURRENCY_LIST);
});
App::get('/v1/locale/languages')
->desc('List Languages')
->groups(['api', 'locale'])
+394 -394
View File
@@ -25,35 +25,35 @@ App::get('/v1/messaging/providers')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROVIDER_LIST)
->param('queries', [], new Providers(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Providers::ALLOWED_ATTRIBUTES), true)
->param('queries', [], new Providers(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' queries are allowed, each '.APP_LIMIT_ARRAY_ELEMENT_SIZE.' characters long. You may filter on the following attributes: '.implode(', ', Providers::ALLOWED_ATTRIBUTES), true)
->inject('dbForProject')
->inject('response')
->action(function (array $queries, Database $dbForProject, Response $response) {
$queries = Query::parseQueries($queries);
$queries = Query::parseQueries($queries);
// Get cursor document if there was a cursor query
$cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
$cursor = reset($cursor);
// Get cursor document if there was a cursor query
$cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
$cursor = reset($cursor);
if ($cursor) {
$providerId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->find('providers', [
Query::equal('$id', [$providerId]),
Query::limit(1),
]));
if ($cursor) {
$providerId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->find('providers', [
Query::equal('$id', [$providerId]),
Query::limit(1),
]));
if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Provider '{$providerId}' for the 'cursor' value not found.");
}
if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Provider '{$providerId}' for the 'cursor' value not found.");
}
$cursor->setValue($cursorDocument[0]);
}
$cursor->setValue($cursorDocument[0]);
}
$filterQueries = Query::groupByType($queries)['filters'];
$response->dynamic(new Document([
'total' => $dbForProject->count('providers', $filterQueries, APP_LIMIT_COUNT),
'indexes' => $dbForProject->find('providers', $queries),
]), Response::MODEL_PROVIDER_LIST);
$filterQueries = Query::groupByType($queries)['filters'];
$response->dynamic(new Document([
'total' => $dbForProject->count('providers', $filterQueries, APP_LIMIT_COUNT),
'indexes' => $dbForProject->find('providers', $queries),
]), Response::MODEL_PROVIDER_LIST);
});
App::get('/v1/messaging/providers/:id')
@@ -71,13 +71,13 @@ App::get('/v1/messaging/providers/:id')
->inject('dbForProject')
->inject('response')
->action(function (string $id, Database $dbForProject, Response $response) {
$provider = $dbForProject->getDocument('providers', $id);
$provider = $dbForProject->getDocument('providers', $id);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$response->dynamic($provider, Response::MODEL_PROVIDER);
$response->dynamic($provider, Response::MODEL_PROVIDER);
});
/**
@@ -101,18 +101,18 @@ App::post('/v1/messaging/providers/mailgun')
->inject('dbForProject')
->inject('response')
->action(function (string $name, string $apiKey, string $domain, Database $dbForProject, Response $response) {
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'mailgun',
'type' => 'email',
'credentials' => [
'apiKey' => $apiKey,
'domain' => $domain,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'mailgun',
'type' => 'email',
'credentials' => [
'apiKey' => $apiKey,
'domain' => $domain,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::patch('/v1/messaging/providers/:id/mailgun')
@@ -134,39 +134,39 @@ App::patch('/v1/messaging/providers/:id/mailgun')
->inject('dbForProject')
->inject('response')
->action(function (string $id, string $name, string $apiKey, string $domain, Database $dbForProject, Response $response) {
$provider = $dbForProject->getDocument('providers', $id);
$provider = $dbForProject->getDocument('providers', $id);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($providerAttr !== 'mailgun') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE . $providerAttr);
}
if ($providerAttr !== 'mailgun') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE.$providerAttr);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($apiKey || $domain) {
// Check if all five variables are present
if ($apiKey && $domain) {
$provider->setAttribute('credentials', [
'apiKey' => $apiKey,
'domain' => $domain
]);
} else {
// Not all credential params are present
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
}
}
if ($apiKey || $domain) {
// Check if all five variables are present
if ($apiKey && $domain) {
$provider->setAttribute('credentials', [
'apiKey' => $apiKey,
'domain' => $domain,
]);
} else {
// Not all credential params are present
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
}
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$response
->dynamic($provider, Response::MODEL_PROVIDER);
$response
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::post('/v1/messaging/providers/sendgrid')
@@ -186,17 +186,17 @@ App::post('/v1/messaging/providers/sendgrid')
->inject('dbForProject')
->inject('response')
->action(function (string $name, string $apiKey, Database $dbForProject, Response $response) {
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'sendgrid',
'type' => 'email',
'credentials' => [
'apiKey' => $apiKey,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'sendgrid',
'type' => 'email',
'credentials' => [
'apiKey' => $apiKey,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::patch('/v1/messaging/providers/:id/sendgrid')
@@ -217,32 +217,32 @@ App::patch('/v1/messaging/providers/:id/sendgrid')
->inject('dbForProject')
->inject('response')
->action(function (string $id, string $name, string $apiKey, Database $dbForProject, Response $response) {
$provider = $dbForProject->getDocument('providers', $id);
$provider = $dbForProject->getDocument('providers', $id);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($providerAttr !== 'sendgrid') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE . $providerAttr);
}
if ($providerAttr !== 'sendgrid') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE.$providerAttr);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($apiKey) {
$provider->setAttribute('credentials', [
'apiKey' => $apiKey
]);
}
if ($apiKey) {
$provider->setAttribute('credentials', [
'apiKey' => $apiKey,
]);
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$response
->dynamic($provider, Response::MODEL_PROVIDER);
$response
->dynamic($provider, Response::MODEL_PROVIDER);
});
/**
@@ -266,18 +266,18 @@ App::post('/v1/messaging/providers/msg91')
->inject('dbForProject')
->inject('response')
->action(function (string $name, string $senderId, string $authKey, Database $dbForProject, Response $response) {
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'msg91',
'type' => 'sms',
'credentials' => [
'senderId' => $senderId,
'authKey' => $authKey,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'msg91',
'type' => 'sms',
'credentials' => [
'senderId' => $senderId,
'authKey' => $authKey,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::patch('/v1/messaging/providers/:id/msg91')
@@ -299,39 +299,39 @@ App::patch('/v1/messaging/providers/:id/msg91')
->inject('dbForProject')
->inject('response')
->action(function (string $id, string $name, string $senderId, string $authKey, Database $dbForProject, Response $response) {
$provider = $dbForProject->getDocument('providers', $id);
$provider = $dbForProject->getDocument('providers', $id);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($providerAttr !== 'msg91') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE . $providerAttr);
}
if ($providerAttr !== 'msg91') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE.$providerAttr);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($senderId || $authKey) {
// Check if all five variables are present
if ($senderId && $authKey) {
$provider->setAttribute('credentials', [
'senderId' => $senderId,
'authKey' => $authKey
]);
} else {
// Not all credential params are present
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
}
}
if ($senderId || $authKey) {
// Check if all five variables are present
if ($senderId && $authKey) {
$provider->setAttribute('credentials', [
'senderId' => $senderId,
'authKey' => $authKey,
]);
} else {
// Not all credential params are present
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
}
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$response
->dynamic($provider, Response::MODEL_PROVIDER);
$response
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::post('/v1/messaging/providers/telesign')
@@ -352,18 +352,18 @@ App::post('/v1/messaging/providers/telesign')
->inject('dbForProject')
->inject('response')
->action(function (string $name, string $username, string $password, Database $dbForProject, Response $response) {
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'telesign',
'type' => 'sms',
'credentials' => [
'username' => $username,
'password' => $password,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'telesign',
'type' => 'sms',
'credentials' => [
'username' => $username,
'password' => $password,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::patch('/v1/messaging/providers/:id/telesign')
@@ -385,39 +385,39 @@ App::patch('/v1/messaging/providers/:id/telesign')
->inject('dbForProject')
->inject('response')
->action(function (string $id, string $name, string $username, string $password, Database $dbForProject, Response $response) {
$provider = $dbForProject->getDocument('providers', $id);
$provider = $dbForProject->getDocument('providers', $id);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($providerAttr !== 'telesign') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE . $providerAttr);
}
if ($providerAttr !== 'telesign') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE.$providerAttr);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($username || $password) {
// Check if all five variables are present
if ($username && $password) {
$provider->setAttribute('credentials', [
'username' => $username,
'password' => $password
]);
} else {
// Not all credential params are present
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
}
}
if ($username || $password) {
// Check if all five variables are present
if ($username && $password) {
$provider->setAttribute('credentials', [
'username' => $username,
'password' => $password,
]);
} else {
// Not all credential params are present
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
}
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$response
->dynamic($provider, Response::MODEL_PROVIDER);
$response
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::post('/v1/messaging/providers/textmagic')
@@ -438,18 +438,18 @@ App::post('/v1/messaging/providers/textmagic')
->inject('dbForProject')
->inject('response')
->action(function (string $name, string $username, string $apiKey, Database $dbForProject, Response $response) {
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'text-magic',
'type' => 'sms',
'credentials' => [
'username' => $username,
'apiKey' => $apiKey,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'text-magic',
'type' => 'sms',
'credentials' => [
'username' => $username,
'apiKey' => $apiKey,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::patch('/v1/messaging/providers/:id/textmagic')
@@ -471,39 +471,39 @@ App::patch('/v1/messaging/providers/:id/textmagic')
->inject('dbForProject')
->inject('response')
->action(function (string $id, string $name, string $username, string $apiKey, Database $dbForProject, Response $response) {
$provider = $dbForProject->getDocument('providers', $id);
$provider = $dbForProject->getDocument('providers', $id);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($providerAttr !== 'text-magic') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE . $providerAttr);
}
if ($providerAttr !== 'text-magic') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE.$providerAttr);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($username || $apiKey) {
// Check if all five variables are present
if ($username && $apiKey) {
$provider->setAttribute('credentials', [
'username' => $username,
'apiKey' => $apiKey
]);
} else {
// Not all credential params are present
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
}
}
if ($username || $apiKey) {
// Check if all five variables are present
if ($username && $apiKey) {
$provider->setAttribute('credentials', [
'username' => $username,
'apiKey' => $apiKey,
]);
} else {
// Not all credential params are present
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
}
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$response
->dynamic($provider, Response::MODEL_PROVIDER);
$response
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::post('/v1/messaging/providers/twilio')
@@ -524,18 +524,18 @@ App::post('/v1/messaging/providers/twilio')
->inject('dbForProject')
->inject('response')
->action(function (string $name, string $accountSid, string $authToken, Database $dbForProject, Response $response) {
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'twilio',
'type' => 'sms',
'credentials' => [
'accountSid' => $accountSid,
'authToken' => $authToken,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'twilio',
'type' => 'sms',
'credentials' => [
'accountSid' => $accountSid,
'authToken' => $authToken,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::patch('/v1/messaging/providers/:id/twilio')
@@ -557,39 +557,39 @@ App::patch('/v1/messaging/providers/:id/twilio')
->inject('dbForProject')
->inject('response')
->action(function (string $id, string $name, string $accountSid, string $authToken, Database $dbForProject, Response $response) {
$provider = $dbForProject->getDocument('providers', $id);
$provider = $dbForProject->getDocument('providers', $id);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($providerAttr !== 'twilio') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE . $providerAttr);
}
if ($providerAttr !== 'twilio') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE.$providerAttr);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($accountSid || $authToken) {
// Check if all five variables are present
if ($accountSid && $authToken) {
$provider->setAttribute('credentials', [
'accountSid' => $accountSid,
'authToken' => $authToken,
]);
} else {
// Not all credential params are present
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
}
}
if ($accountSid || $authToken) {
// Check if all five variables are present
if ($accountSid && $authToken) {
$provider->setAttribute('credentials', [
'accountSid' => $accountSid,
'authToken' => $authToken,
]);
} else {
// Not all credential params are present
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
}
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$response
->dynamic($provider, Response::MODEL_PROVIDER);
$response
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::post('/v1/messaging/providers/vonage')
@@ -610,18 +610,18 @@ App::post('/v1/messaging/providers/vonage')
->inject('dbForProject')
->inject('response')
->action(function (string $name, string $apiKey, string $apiSecret, Database $dbForProject, Response $response) {
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'vonage',
'type' => 'sms',
'credentials' => [
'apiKey' => $apiKey,
'apiSecret' => $apiSecret,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'vonage',
'type' => 'sms',
'credentials' => [
'apiKey' => $apiKey,
'apiSecret' => $apiSecret,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::patch('/v1/messaging/providers/:id/vonage')
@@ -643,39 +643,39 @@ App::patch('/v1/messaging/providers/:id/vonage')
->inject('dbForProject')
->inject('response')
->action(function (string $id, string $name, string $apiKey, string $apiSecret, Database $dbForProject, Response $response) {
$provider = $dbForProject->getDocument('providers', $id);
$provider = $dbForProject->getDocument('providers', $id);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($providerAttr !== 'vonage') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE . $providerAttr);
}
if ($providerAttr !== 'vonage') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE.$providerAttr);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($apiKey || $apiSecret) {
// Check if all five variables are present
if ($apiKey && $apiSecret) {
$provider->setAttribute('credentials', [
'apiKey' => $apiKey,
'apiSecret' => $apiSecret,
]);
} else {
// Not all credential params are present
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
}
}
if ($apiKey || $apiSecret) {
// Check if all five variables are present
if ($apiKey && $apiSecret) {
$provider->setAttribute('credentials', [
'apiKey' => $apiKey,
'apiSecret' => $apiSecret,
]);
} else {
// Not all credential params are present
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
}
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$response
->dynamic($provider, Response::MODEL_PROVIDER);
$response
->dynamic($provider, Response::MODEL_PROVIDER);
});
/**
@@ -698,17 +698,17 @@ App::post('/v1/messaging/providers/fcm')
->inject('dbForProject')
->inject('response')
->action(function (string $name, string $serverKey, Database $dbForProject, Response $response) {
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'fcm',
'type' => 'push',
'credentials' => [
'serverKey' => $serverKey,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'fcm',
'type' => 'push',
'credentials' => [
'serverKey' => $serverKey,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::patch('/v1/messaging/providers/:id/fcm')
@@ -729,30 +729,30 @@ App::patch('/v1/messaging/providers/:id/fcm')
->inject('dbForProject')
->inject('response')
->action(function (string $id, string $name, string $serverKey, Database $dbForProject, Response $response) {
$provider = $dbForProject->getDocument('providers', $id);
$provider = $dbForProject->getDocument('providers', $id);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($providerAttr !== 'fcm') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE . $providerAttr);
}
if ($providerAttr !== 'fcm') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE.$providerAttr);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($serverKey) {
$provider->setAttribute('credentials', ['serverKey' => $serverKey]);
}
if ($serverKey) {
$provider->setAttribute('credentials', ['serverKey' => $serverKey]);
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$response
->dynamic($provider, Response::MODEL_PROVIDER);
$response
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::post('/v1/messaging/providers/apns')
@@ -776,21 +776,21 @@ App::post('/v1/messaging/providers/apns')
->inject('dbForProject')
->inject('response')
->action(function (string $name, string $authKey, string $authKeyId, string $teamId, string $bundleId, string $endpoint, Database $dbForProject, Response $response) {
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'apns',
'type' => 'push',
'credentials' => [
'authKey' => $authKey,
'authKeyId' => $authKeyId,
'teamId' => $teamId,
'bundleId' => $bundleId,
'endpoint' => $endpoint,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
$provider = $dbForProject->createDocument('providers', new Document([
'name' => $name,
'provider' => 'apns',
'type' => 'push',
'credentials' => [
'authKey' => $authKey,
'authKeyId' => $authKeyId,
'teamId' => $teamId,
'bundleId' => $bundleId,
'endpoint' => $endpoint,
],
]));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::patch('/v1/messaging/providers/:id/apns')
@@ -815,42 +815,42 @@ App::patch('/v1/messaging/providers/:id/apns')
->inject('dbForProject')
->inject('response')
->action(function (string $id, string $name, string $authKey, string $authKeyId, string $teamId, string $bundleId, string $endpoint, Database $dbForProject, Response $response) {
$provider = $dbForProject->getDocument('providers', $id);
$provider = $dbForProject->getDocument('providers', $id);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($providerAttr !== 'apns') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE . $providerAttr);
}
if ($providerAttr !== 'apns') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE.$providerAttr);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($name) {
$provider->setAttribute('name', $name);
}
if ($authKey || $authKeyId || $teamId || $bundleId || $endpoint) {
// Check if all five variables are present
if ($authKey && $authKeyId && $teamId && $bundleId && $endpoint) {
$provider->setAttribute('credentials', [
'authKey' => $authKey,
'authKeyId' => $authKeyId,
'teamId' => $teamId,
'bundleId' => $bundleId,
'endpoint' => $endpoint,
]);
} else {
// Not all credential params are present
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
}
}
if ($authKey || $authKeyId || $teamId || $bundleId || $endpoint) {
// Check if all five variables are present
if ($authKey && $authKeyId && $teamId && $bundleId && $endpoint) {
$provider->setAttribute('credentials', [
'authKey' => $authKey,
'authKeyId' => $authKeyId,
'teamId' => $teamId,
'bundleId' => $bundleId,
'endpoint' => $endpoint,
]);
} else {
// Not all credential params are present
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
}
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$response
->dynamic($provider, Response::MODEL_PROVIDER);
$response
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::delete('/v1/messaging/providers/:id')
@@ -869,16 +869,16 @@ App::delete('/v1/messaging/providers/:id')
->inject('dbForProject')
->inject('response')
->action(function (string $id, Database $dbForProject, Response $response) {
$provider = $dbForProject->getDocument('providers', $id);
$provider = $dbForProject->getDocument('providers', $id);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$dbForProject->deleteDocument('providers', $provider->getId());
$dbForProject->deleteCachedDocument('providers', $provider->getId());
$dbForProject->deleteDocument('providers', $provider->getId());
$response->noContent();
$response->noContent();
});
App::post('/v1/messaging/messages/email')
@@ -904,30 +904,30 @@ App::post('/v1/messaging/messages/email')
->inject('events')
->inject('response')
->action(function (string $providerId, string $to, string $subject, string $content, string $from, string $html, DateTime $deliveryTime, Database $dbForProject, Event $eventsInstance, Response $response) {
$provider = $dbForProject->getDocument('providers', $providerId);
$provider = $dbForProject->getDocument('providers', $providerId);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$message = $dbForProject->createDocument('messages', new Document([
'providerId' => $provider->getId(),
'providerInternalId' => $provider->getInternalId(),
'to' => $to,
'data' => [
'subject' => $subject,
'content' => $content,
],
'deliveryTime' => $deliveryTime,
'deliveryError' => null,
'deliveredTo' => null,
'delivered' => false,
'search' => null,
]));
$message = $dbForProject->createDocument('messages', new Document([
'providerId' => $provider->getId(),
'providerInternalId' => $provider->getInternalId(),
'to' => $to,
'data' => [
'subject' => $subject,
'content' => $content,
],
'deliveryTime' => $deliveryTime,
'deliveryError' => null,
'deliveredTo' => null,
'delivered' => false,
'search' => null,
]));
$eventsInstance->setParam('messageId', $message->getId());
$eventsInstance->setParam('messageId', $message->getId());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_MESSAGE);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_MESSAGE);
});
+20 -20
View File
@@ -28,7 +28,7 @@ use Utopia\Validator\Text;
use Utopia\Validator\URL;
use Utopia\Validator\WhiteList;
include_once __DIR__ . '/../shared/api.php';
include_once __DIR__.'/../shared/api.php';
App::post('/v1/migrations/appwrite')
->groups(['api', 'migrations'])
@@ -110,7 +110,7 @@ App::post('/v1/migrations/firebase/oauth')
$firebase = new OAuth2Firebase(
App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''),
App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''),
$request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect'
$request->getProtocol().'://'.$request->getHostname().'/v1/migrations/firebase/redirect'
);
$identity = $dbForConsole->findOne('identities', [
@@ -141,7 +141,7 @@ App::post('/v1/migrations/firebase/oauth')
$identity = $identity
->setAttribute('providerAccessToken', $accessToken)
->setAttribute('providerRefreshToken', $refreshToken)
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$firebase->getAccessTokenExpiry('')));
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int) $firebase->getAccessTokenExpiry('')));
$dbForConsole->updateDocument('identities', $identity->getId(), $identity);
}
@@ -167,7 +167,7 @@ App::post('/v1/migrations/firebase/oauth')
'resources' => $resources,
'statusCounters' => '{}',
'resourceData' => '{}',
'errors' => []
'errors' => [],
]));
$events->setParam('migrationId', $migration->getId());
@@ -368,14 +368,14 @@ App::get('/v1/migrations')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MIGRATION_LIST)
->param('queries', [], new Migrations(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Migrations::ALLOWED_ATTRIBUTES), true)
->param('queries', [], new Migrations(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' queries are allowed, each '.APP_LIMIT_ARRAY_ELEMENT_SIZE.' characters long. You may filter on the following attributes: '.implode(', ', Migrations::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
->action(function (array $queries, string $search, Response $response, Database $dbForProject) {
$queries = Query::parseQueries($queries);
if (!empty($search)) {
if (! empty($search)) {
$queries[] = Query::search('search', $search);
}
@@ -505,7 +505,7 @@ App::get('/v1/migrations/firebase/report/oauth')
$firebase = new OAuth2Firebase(
App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''),
App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''),
$request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect'
$request->getProtocol().'://'.$request->getHostname().'/v1/migrations/firebase/redirect'
);
$identity = $dbForConsole->findOne('identities', [
@@ -536,7 +536,7 @@ App::get('/v1/migrations/firebase/report/oauth')
$identity = $identity
->setAttribute('providerAccessToken', $accessToken)
->setAttribute('providerRefreshToken', $refreshToken)
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$firebase->getAccessTokenExpiry('')));
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int) $firebase->getAccessTokenExpiry('')));
$dbForConsole->updateDocument('identities', $identity->getId(), $identity);
}
@@ -597,7 +597,7 @@ App::get('/v1/migrations/firebase/connect')
$oauth2 = new OAuth2Firebase(
App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''),
App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''),
$request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect'
$request->getProtocol().'://'.$request->getHostname().'/v1/migrations/firebase/redirect'
);
$url = $oauth2->getLoginURL();
@@ -611,7 +611,7 @@ App::get('/v1/migrations/firebase/redirect')
->desc('Capture and receive data on Firebase authorization')
->groups(['api', 'migrations'])
->label('scope', 'public')
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('error', __DIR__.'/../../views/general/error.phtml')
->param('code', '', new Text(2048), 'OAuth2 code.', true)
->inject('user')
->inject('project')
@@ -635,7 +635,7 @@ App::get('/v1/migrations/firebase/redirect')
$project = $dbForConsole->getDocument('projects', $projectId);
if (empty($redirect)) {
$redirect = $request->getProtocol() . '://' . $request->getHostname() . '/console/project-$projectId/settings/migrations';
$redirect = $request->getProtocol().'://'.$request->getHostname().'/console/project-$projectId/settings/migrations';
}
if ($project->isEmpty()) {
@@ -648,11 +648,11 @@ App::get('/v1/migrations/firebase/redirect')
}
// OAuth Authroization
if (!empty($code)) {
if (! empty($code)) {
$oauth2 = new OAuth2Firebase(
App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''),
App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''),
$request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect'
$request->getProtocol().'://'.$request->getHostname().'/v1/migrations/firebase/redirect'
);
$accessToken = $oauth2->getAccessToken($code);
@@ -678,17 +678,17 @@ App::get('/v1/migrations/firebase/redirect')
Query::equal('providerEmail', [$email]),
]);
if ($identity !== false && !$identity->isEmpty()) {
if ($identity !== false && ! $identity->isEmpty()) {
if ($identity->getAttribute('userInternalId', '') !== $user->getInternalId()) {
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
}
}
if ($identity !== false && !$identity->isEmpty()) {
if ($identity !== false && ! $identity->isEmpty()) {
$identity = $identity
->setAttribute('providerAccessToken', $accessToken)
->setAttribute('providerRefreshToken', $refreshToken)
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$accessTokenExpiry));
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int) $accessTokenExpiry));
$dbForConsole->updateDocument('identities', $identity->getId(), $identity);
} else {
@@ -706,7 +706,7 @@ App::get('/v1/migrations/firebase/redirect')
'providerEmail' => $email,
'providerAccessToken' => $accessToken,
'providerRefreshToken' => $refreshToken,
'providerAccessTokenExpiry' => DateTime::addSeconds(new \DateTime(), (int)$accessTokenExpiry),
'providerAccessTokenExpiry' => DateTime::addSeconds(new \DateTime(), (int) $accessTokenExpiry),
]));
}
} else {
@@ -738,7 +738,7 @@ App::get('/v1/migrations/firebase/projects')
$firebase = new OAuth2Firebase(
App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''),
App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''),
$request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect'
$request->getProtocol().'://'.$request->getHostname().'/v1/migrations/firebase/redirect'
);
$identity = $dbForConsole->findOne('identities', [
@@ -781,7 +781,7 @@ App::get('/v1/migrations/firebase/projects')
$identity = $identity
->setAttribute('providerAccessToken', $accessToken)
->setAttribute('providerRefreshToken', $refreshToken)
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$firebase->getAccessTokenExpiry('')));
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int) $firebase->getAccessTokenExpiry('')));
$dbForConsole->updateDocument('identities', $identity->getId(), $identity);
}
@@ -964,7 +964,7 @@ App::delete('/v1/migrations/:migrationId')
throw new Exception(Exception::MIGRATION_NOT_FOUND);
}
if (!$dbForProject->deleteDocument('migrations', $migration->getId())) {
if (! $dbForProject->deleteDocument('migrations', $migration->getId())) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove migration from DB', 500);
}
+12 -16
View File
@@ -4,7 +4,6 @@ use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
@@ -24,7 +23,6 @@ App::get('/v1/project/usage')
->inject('response')
->inject('dbForProject')
->action(function (string $range, Response $response, Database $dbForProject) {
$periods = Config::getParam('usage', []);
$stats = $usage = [];
$days = $periods[$range];
@@ -37,7 +35,7 @@ App::get('/v1/project/usage')
METRIC_DATABASES,
METRIC_USERS,
METRIC_BUCKETS,
METRIC_FILES_STORAGE
METRIC_FILES_STORAGE,
];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
@@ -60,25 +58,23 @@ App::get('/v1/project/usage')
}
});
$format = match ($days['period']) {
'1h' => 'Y-m-d\TH:00:00.000P',
'1d' => 'Y-m-d\T00:00:00.000P',
};
foreach ($metrics as $metric) {
$usage[$metric] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric][] = [
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
foreach ($metrics as $metric) {
$usage[$metric] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric][] = [
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
}
}
}
$response->dynamic(new Document([
'range' => $range,
+65 -113
View File
@@ -1,19 +1,22 @@
<?php
use Appwrite\Auth\Auth;
use Appwrite\Auth\Validator\Password;
use Appwrite\Event\Certificate;
use Appwrite\Event\Delete;
use Appwrite\Event\Validator\Event;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\CNAME;
use Utopia\Validator\Domain as DomainValidator;
use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\Origin;
use Utopia\Validator\URL;
use Appwrite\Template\Template;
use Appwrite\Utopia\Database\Validator\ProjectId;
use Appwrite\Utopia\Database\Validator\Queries\Projects;
use Appwrite\Utopia\Response;
use PHPMailer\PHPMailer\PHPMailer;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\Cache\Cache;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
@@ -21,26 +24,22 @@ use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Query;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\UID;
use Utopia\Domains\Domain;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Appwrite\Utopia\Database\Validator\Queries\Projects;
use Utopia\Cache\Cache;
use Utopia\Locale\Locale;
use Utopia\Pools\Group;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean;
use Utopia\Validator\Domain as DomainValidator;
use Utopia\Validator\Hostname;
use Utopia\Validator\Integer;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\URL;
use Utopia\Validator\WhiteList;
use Appwrite\Template\Template;
use Utopia\Locale\Locale;
use PHPMailer\PHPMailer\PHPMailer;
App::init()
->groups(['projects'])
@@ -64,7 +63,7 @@ App::post('/v1/projects')
->param('projectId', '', new ProjectId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, and hyphen. Can\'t start with a special char. Max length is 36 chars.')
->param('name', null, new Text(128), 'Project name. Max length: 128 chars.')
->param('teamId', '', new UID(), 'Team unique ID.')
->param('region', App::getEnv('_APP_REGION', 'default'), new Whitelist(array_keys(array_filter(Config::getParam('regions'), fn($config) => !$config['disabled']))), 'Project Region.', true)
->param('region', App::getEnv('_APP_REGION', 'default'), new Whitelist(array_keys(array_filter(Config::getParam('regions'), fn ($config) => ! $config['disabled']))), 'Project Region.', true)
->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true)
->param('logo', '', new Text(1024), 'Project logo.', true)
->param('url', '', new URL(), 'Project URL.', true)
@@ -79,8 +78,6 @@ App::post('/v1/projects')
->inject('cache')
->inject('pools')
->action(function (string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForConsole, Cache $cache, Group $pools) {
$team = $dbForConsole->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -164,7 +161,7 @@ App::post('/v1/projects')
'domains' => null,
'auths' => $auths,
'search' => implode(' ', [$projectId, $name]),
'database' => $database
'database' => $database,
]));
} catch (Duplicate $th) {
throw new Exception(Exception::PROJECT_ALREADY_EXISTS);
@@ -201,7 +198,7 @@ App::post('/v1/projects')
'array' => $attribute['array'],
'filters' => $attribute['filters'],
'default' => $attribute['default'] ?? null,
'format' => $attribute['format'] ?? ''
'format' => $attribute['format'] ?? '',
]);
}
@@ -232,15 +229,14 @@ App::get('/v1/projects')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT_LIST)
->param('queries', [], new Projects(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Projects::ALLOWED_ATTRIBUTES), true)
->param('queries', [], new Projects(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' queries are allowed, each '.APP_LIMIT_ARRAY_ELEMENT_SIZE.' characters long. You may filter on the following attributes: '.implode(', ', Projects::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForConsole')
->action(function (array $queries, string $search, Response $response, Database $dbForConsole) {
$queries = Query::parseQueries($queries);
if (!empty($search)) {
if (! empty($search)) {
$queries[] = Query::search('search', $search);
}
@@ -281,7 +277,6 @@ App::get('/v1/projects/:projectId')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -315,7 +310,6 @@ App::patch('/v1/projects/:projectId')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $name, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -353,7 +347,6 @@ App::patch('/v1/projects/:projectId/team')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $teamId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
$team = $dbForConsole->getDocument('teams', $teamId);
@@ -389,12 +382,11 @@ App::patch('/v1/projects/:projectId/service')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('service', '', new WhiteList(array_keys(array_filter(Config::getParam('services'), fn($element) => $element['optional'])), true), 'Service name.')
->param('service', '', new WhiteList(array_keys(array_filter(Config::getParam('services'), fn ($element) => $element['optional'])), true), 'Service name.')
->param('status', null, new Boolean(), 'Service status.')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $service, bool $status, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -424,14 +416,13 @@ App::patch('/v1/projects/:projectId/service/all')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, bool $status, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$allServices = array_keys(array_filter(Config::getParam('services'), fn($element) => $element['optional']));
$allServices = array_keys(array_filter(Config::getParam('services'), fn ($element) => $element['optional']));
$services = [];
foreach ($allServices as $service) {
@@ -461,7 +452,6 @@ App::patch('/v1/projects/:projectId/oauth2')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $provider, ?string $appId, ?string $secret, ?bool $enabled, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -471,15 +461,15 @@ App::patch('/v1/projects/:projectId/oauth2')
$providers = $project->getAttribute('authProviders', []);
if ($appId !== null) {
$providers[$provider . 'Appid'] = $appId;
$providers[$provider.'Appid'] = $appId;
}
if ($secret !== null) {
$providers[$provider . 'Secret'] = $secret;
$providers[$provider.'Secret'] = $secret;
}
if ($enabled !== null) {
$providers[$provider . 'Enabled'] = $enabled;
$providers[$provider.'Enabled'] = $enabled;
}
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('authProviders', $providers));
@@ -502,7 +492,6 @@ App::patch('/v1/projects/:projectId/auth/limit')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -533,7 +522,6 @@ App::patch('/v1/projects/:projectId/auth/duration')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, int $duration, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -560,12 +548,11 @@ App::patch('/v1/projects/:projectId/auth/:method')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('method', '', new WhiteList(\array_keys(Config::getParam('auth')), true), 'Auth Method. Possible values: ' . implode(',', \array_keys(Config::getParam('auth'))), false)
->param('method', '', new WhiteList(\array_keys(Config::getParam('auth')), true), 'Auth Method. Possible values: '.implode(',', \array_keys(Config::getParam('auth'))), false)
->param('status', false, new Boolean(true), 'Set the status of this auth method.')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $method, bool $status, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
$auth = Config::getParam('auth')[$method] ?? [];
$authKey = $auth['key'] ?? '';
@@ -594,11 +581,10 @@ App::patch('/v1/projects/:projectId/auth/password-history')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('limit', 0, new Range(0, APP_LIMIT_USER_PASSWORD_HISTORY), 'Set the max number of passwords to store in user history. User can\'t choose a new password that is already stored in the password history list. Max number of passwords allowed in history is' . APP_LIMIT_USER_PASSWORD_HISTORY . '. Default value is 0')
->param('limit', 0, new Range(0, APP_LIMIT_USER_PASSWORD_HISTORY), 'Set the max number of passwords to store in user history. User can\'t choose a new password that is already stored in the password history list. Max number of passwords allowed in history is'.APP_LIMIT_USER_PASSWORD_HISTORY.'. Default value is 0')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -629,7 +615,6 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, bool $enabled, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -660,7 +645,6 @@ App::patch('/v1/projects/:projectId/auth/personal-data')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, bool $enabled, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -687,11 +671,10 @@ App::patch('/v1/projects/:projectId/auth/max-sessions')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('limit', false, new Range(1, APP_LIMIT_USER_SESSIONS_MAX), 'Set the max number of users allowed in this project. Value allowed is between 1-' . APP_LIMIT_USER_SESSIONS_MAX . '. Default is ' . APP_LIMIT_USER_SESSIONS_DEFAULT)
->param('limit', false, new Range(1, APP_LIMIT_USER_SESSIONS_MAX), 'Set the max number of users allowed in this project. Value allowed is between 1-'.APP_LIMIT_USER_SESSIONS_MAX.'. Default is '.APP_LIMIT_USER_SESSIONS_DEFAULT)
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -730,10 +713,9 @@ App::delete('/v1/projects/:projectId')
$deletes
->setType(DELETE_TYPE_DOCUMENT)
->setDocument($project)
;
->setDocument($project);
if (!$dbForConsole->deleteDocument('projects', $projectId)) {
if (! $dbForConsole->deleteDocument('projects', $projectId)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove project from DB');
}
@@ -754,7 +736,7 @@ App::post('/v1/projects/:projectId/webhooks')
->label('sdk.response.model', Response::MODEL_WEBHOOK)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.')
->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' events are allowed.')
->param('url', null, new URL(['http', 'https']), 'Webhook URL.')
->param('security', false, new Boolean(true), 'Certificate verification, false for disabled or true for enabled.')
->param('httpUser', '', new Text(256), 'Webhook HTTP user. Max length: 256 chars.', true)
@@ -762,7 +744,6 @@ App::post('/v1/projects/:projectId/webhooks')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $name, array $events, string $url, bool $security, string $httpUser, string $httpPass, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -812,7 +793,6 @@ App::get('/v1/projects/:projectId/webhooks')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -845,7 +825,6 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -877,7 +856,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
->param('projectId', '', new UID(), 'Project unique ID.')
->param('webhookId', '', new UID(), 'Webhook unique ID.')
->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.')
->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' events are allowed.')
->param('url', null, new URL(['http', 'https']), 'Webhook URL.')
->param('security', false, new Boolean(true), 'Certificate verification, false for disabled or true for enabled.')
->param('httpUser', '', new Text(256), 'Webhook HTTP user. Max length: 256 chars.', true)
@@ -885,7 +864,6 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $webhookId, string $name, array $events, string $url, bool $security, string $httpUser, string $httpPass, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -909,8 +887,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
->setAttribute('url', $url)
->setAttribute('security', $security)
->setAttribute('httpUser', $httpUser)
->setAttribute('httpPass', $httpPass)
;
->setAttribute('httpPass', $httpPass);
$dbForConsole->updateDocument('webhooks', $webhook->getId(), $webhook);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
@@ -933,7 +910,6 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -971,7 +947,6 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1008,12 +983,11 @@ App::post('/v1/projects/:projectId/keys')
->label('sdk.response.model', Response::MODEL_KEY)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' scopes are allowed.')
->param('expire', null, new DatetimeValidator(), 'Expiration time in ISO 8601 format. Use null for unlimited expiration.', true)
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1060,7 +1034,6 @@ App::get('/v1/projects/:projectId/keys')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1093,7 +1066,6 @@ App::get('/v1/projects/:projectId/keys/:keyId')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1125,12 +1097,11 @@ App::put('/v1/projects/:projectId/keys/:keyId')
->param('projectId', '', new UID(), 'Project unique ID.')
->param('keyId', '', new UID(), 'Key unique ID.')
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' events are allowed.')
->param('expire', null, new DatetimeValidator(), 'Expiration time in ISO 8601 format. Use null for unlimited expiration.', true)
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1149,8 +1120,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
$key
->setAttribute('name', $name)
->setAttribute('scopes', $scopes)
->setAttribute('expire', $expire)
;
->setAttribute('expire', $expire);
$dbForConsole->updateDocument('keys', $key->getId(), $key);
@@ -1173,7 +1143,6 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1236,7 +1205,7 @@ App::post('/v1/projects/:projectId/platforms')
'name' => $name,
'key' => $key,
'store' => $store,
'hostname' => $hostname
'hostname' => $hostname,
]);
$platform = $dbForConsole->createDocument('platforms', $platform);
@@ -1262,7 +1231,6 @@ App::get('/v1/projects/:projectId/platforms')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1295,7 +1263,6 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1352,8 +1319,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
->setAttribute('name', $name)
->setAttribute('key', $key)
->setAttribute('store', $store)
->setAttribute('hostname', $hostname)
;
->setAttribute('hostname', $hostname);
$dbForConsole->updateDocument('platforms', $platform->getId(), $platform);
@@ -1376,7 +1342,6 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1416,7 +1381,6 @@ App::post('/v1/projects/:projectId/domains')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $domain, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1428,17 +1392,17 @@ App::post('/v1/projects/:projectId/domains')
}
$document = $dbForConsole->findOne('domains', [
Query::equal('domain', [$domain])
Query::equal('domain', [$domain]),
]);
if ($document && !$document->isEmpty()) {
if ($document && ! $document->isEmpty()) {
throw new Exception(Exception::DOMAIN_ALREADY_EXISTS);
}
$target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', ''));
if (!$target->isKnown() || $target->isTest()) {
throw new Exception(Exception::DOMAIN_TARGET_INVALID, 'Unreachable CNAME target (' . $target->get() . '). Please check the _APP_DOMAIN_TARGET environment variable of your Appwrite server.');
if (! $target->isKnown() || $target->isTest()) {
throw new Exception(Exception::DOMAIN_TARGET_INVALID, 'Unreachable CNAME target ('.$target->get().'). Please check the _APP_DOMAIN_TARGET environment variable of your Appwrite server.');
}
$domain = new Domain($domain);
@@ -1483,7 +1447,6 @@ App::get('/v1/projects/:projectId/domains')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1516,7 +1479,6 @@ App::get('/v1/projects/:projectId/domains/:domainId')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1550,7 +1512,6 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1568,8 +1529,8 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
$target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', ''));
if (!$target->isKnown() || $target->isTest()) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.');
if (! $target->isKnown() || $target->isTest()) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unreachable CNAME target ('.$target->get().'), please use a domain with a public suffix.');
}
if ($domain->getAttribute('verification') === true) {
@@ -1578,11 +1539,10 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
$validator = new CNAME($target->get()); // Verify Domain with DNS records
if (!$validator->isValid($domain->getAttribute('domain', ''))) {
if (! $validator->isValid($domain->getAttribute('domain', ''))) {
throw new Exception(Exception::DOMAIN_VERIFICATION_FAILED);
}
$dbForConsole->updateDocument('domains', $domain->getId(), $domain->setAttribute('verification', true));
$dbForConsole->deleteCachedDocument('projects', $project->getId());
@@ -1610,7 +1570,6 @@ App::delete('/v1/projects/:projectId/domains/:domainId')
->inject('dbForConsole')
->inject('deletes')
->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole, Delete $deletes) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1663,7 +1622,6 @@ App::patch('/v1/projects/:projectId/smtp')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, bool $enabled, string $sender, string $host, int $port, string $username, string $password, string $secure, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1681,7 +1639,7 @@ App::patch('/v1/projects/:projectId/smtp')
$mail->SMTPAutoTLS = false;
$valid = $mail->SmtpConnect();
if (!$valid) {
if (! $valid) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED);
}
@@ -1712,11 +1670,10 @@ App::get('/v1/projects/:projectId/templates/sms/:type/:locale')
->label('sdk.response.model', Response::MODEL_SMS_TEMPLATE)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? []), 'Template type')
->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1724,11 +1681,11 @@ App::get('/v1/projects/:projectId/templates/sms/:type/:locale')
}
$templates = $project->getAttribute('templates', []);
$template = $templates['sms.' . $type . '-' . $locale] ?? null;
$template = $templates['sms.'.$type.'-'.$locale] ?? null;
if (is_null($template)) {
$template = [
'message' => Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl')->render(),
'message' => Template::fromFile(__DIR__.'/../../config/locale/templates/sms-base.tpl')->render(),
];
}
@@ -1750,11 +1707,10 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
->label('sdk.response.model', Response::MODEL_EMAIL_TEMPLATE)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? []), 'Template type')
->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1762,11 +1718,11 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
}
$templates = $project->getAttribute('templates', []);
$template = $templates['email.' . $type . '-' . $locale] ?? null;
$template = $templates['email.'.$type.'-'.$locale] ?? null;
$localeObj = new Locale($locale);
if (is_null($template)) {
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-base.tpl');
$message = Template::fromFile(__DIR__.'/../../config/locale/templates/email-base.tpl');
$message = $message
->setParam('{{hello}}', $localeObj->getText("emails.{$type}.hello"))
->setParam('{{name}}', '')
@@ -1781,12 +1737,12 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
->render();
$from = $project->isEmpty() || $project->getId() === 'console' ? '' : \sprintf($localeObj->getText('emails.sender'), $project->getAttribute('name'));
$from = empty($from) ? \urldecode(App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')) : $from;
$from = empty($from) ? \urldecode(App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server')) : $from;
$template = [
'message' => $message,
'subject' => $localeObj->getText('emails.' . $type . '.subject'),
'subject' => $localeObj->getText('emails.'.$type.'.subject'),
'senderEmail' => App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', ''),
'senderName' => $from
'senderName' => $from,
];
}
@@ -1808,12 +1764,11 @@ App::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
->label('sdk.response.model', Response::MODEL_SMS_TEMPLATE)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? []), 'Template type')
->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->param('message', '', new Text(0), 'Template message')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $type, string $locale, string $message, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1821,8 +1776,8 @@ App::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
}
$templates = $project->getAttribute('templates', []);
$templates['sms.' . $type . '-' . $locale] = [
'message' => $message
$templates['sms.'.$type.'-'.$locale] = [
'message' => $message,
];
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('templates', $templates));
@@ -1846,7 +1801,7 @@ App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? []), 'Template type')
->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->param('senderName', '', new Text(255), 'Name of the email sender')
->param('senderEmail', '', new Email(), 'Email of the sender')
->param('subject', '', new Text(255), 'Email Subject')
@@ -1855,7 +1810,6 @@ App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $type, string $locale, string $senderName, string $senderEmail, string $subject, string $message, string $replyTo, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1863,12 +1817,12 @@ App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
}
$templates = $project->getAttribute('templates', []);
$templates['email.' . $type . '-' . $locale] = [
$templates['email.'.$type.'-'.$locale] = [
'senderName' => $senderName,
'senderEmail' => $senderEmail,
'subject' => $subject,
'replyTo' => $replyTo,
'message' => $message
'message' => $message,
];
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('templates', $templates));
@@ -1880,7 +1834,7 @@ App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
'senderEmail' => $senderEmail,
'subject' => $subject,
'replyTo' => $replyTo,
'message' => $message
'message' => $message,
]), Response::MODEL_EMAIL_TEMPLATE);
});
@@ -1896,11 +1850,10 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
->label('sdk.response.model', Response::MODEL_SMS_TEMPLATE)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? []), 'Template type')
->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1908,20 +1861,20 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
}
$templates = $project->getAttribute('templates', []);
$template = $templates['sms.' . $type . '-' . $locale] ?? null;
$template = $templates['sms.'.$type.'-'.$locale] ?? null;
if (is_null($template)) {
throw new Exception(Exception::PROJECT_TEMPLATE_DEFAULT_DELETION);
}
unset($template['sms.' . $type . '-' . $locale]);
unset($template['sms.'.$type.'-'.$locale]);
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('templates', $templates));
$response->dynamic(new Document([
'type' => $type,
'locale' => $locale,
'message' => $template['message']
'message' => $template['message'],
]), Response::MODEL_SMS_TEMPLATE);
});
@@ -1937,11 +1890,10 @@ App::delete('/v1/projects/:projectId/templates/email/:type/:locale')
->label('sdk.response.model', Response::MODEL_EMAIL_TEMPLATE)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? []), 'Template type')
->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
@@ -1949,13 +1901,13 @@ App::delete('/v1/projects/:projectId/templates/email/:type/:locale')
}
$templates = $project->getAttribute('templates', []);
$template = $templates['email.' . $type . '-' . $locale] ?? null;
$template = $templates['email.'.$type.'-'.$locale] ?? null;
if (is_null($template)) {
throw new Exception(Exception::PROJECT_TEMPLATE_DEFAULT_DELETION);
}
unset($templates['email.' . $type . '-' . $locale]);
unset($templates['email.'.$type.'-'.$locale]);
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('templates', $templates));
@@ -1966,6 +1918,6 @@ App::delete('/v1/projects/:projectId/templates/email/:type/:locale')
'senderEmail' => $template['senderEmail'],
'subject' => $template['subject'],
'replyTo' => $template['replyTo'],
'message' => $template['message']
'message' => $template['message'],
]), Response::MODEL_EMAIL_TEMPLATE);
});
+140 -166
View File
@@ -4,28 +4,27 @@ use Appwrite\Auth\Auth;
use Appwrite\ClamAV\Network;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Extend\Exception;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries\Buckets;
use Appwrite\Utopia\Database\Validator\Queries\Files;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\DateTime;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Authorization as AuthorizationException;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\Structure as StructureException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Query;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Permissions;
use Utopia\Database\Validator\UID;
use Appwrite\Extend\Exception;
use Appwrite\Utopia\Database\Validator\Queries\Buckets;
use Appwrite\Utopia\Database\Validator\Queries\Files;
use Utopia\Image\Image;
use Utopia\Storage\Compression\Algorithms\GZIP;
use Utopia\Storage\Compression\Algorithms\Zstd;
@@ -35,14 +34,13 @@ use Utopia\Storage\Validator\File;
use Utopia\Storage\Validator\FileExt;
use Utopia\Storage\Validator\FileSize;
use Utopia\Storage\Validator\Upload;
use Utopia\Swoole\Request;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean;
use Utopia\Validator\HexColor;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
use Utopia\DSN\DSN;
use Utopia\Swoole\Request;
App::post('/v1/storage/buckets')
->desc('Create bucket')
@@ -63,16 +61,15 @@ App::post('/v1/storage/buckets')
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, no user is granted with any permissions. [Learn more about permissions](/docs/permissions).', true)
->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](/docs/permissions).', true)
->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true)
->param('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0), new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self-hosted setups you can change the max limit by changing the `_APP_STORAGE_LIMIT` environment variable. [Learn more about storage environment variables](/docs/environment-variables#storage)', true)
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
->param('compression', COMPRESSION_TYPE_NONE, new WhiteList([COMPRESSION_TYPE_NONE, COMPRESSION_TYPE_GZIP, COMPRESSION_TYPE_ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . COMPRESSION_TYPE_NONE . ', [' . COMPRESSION_TYPE_GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . COMPRESSION_TYPE_ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true)
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true)
->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true)
->param('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0), new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is '.Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0), 0).'. For self-hosted setups you can change the max limit by changing the `_APP_STORAGE_LIMIT` environment variable. [Learn more about storage environment variables](/docs/environment-variables#storage)', true)
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' extensions are allowed, each 64 characters long.', true)
->param('compression', COMPRESSION_TYPE_NONE, new WhiteList([COMPRESSION_TYPE_NONE, COMPRESSION_TYPE_GZIP, COMPRESSION_TYPE_ZSTD]), 'Compression algorithm choosen for compression. Can be one of '.COMPRESSION_TYPE_NONE.', ['.COMPRESSION_TYPE_GZIP.'](https://en.wikipedia.org/wiki/Gzip), or ['.COMPRESSION_TYPE_ZSTD.'](https://en.wikipedia.org/wiki/Zstd), For file size above '.Storage::human(APP_STORAGE_READ_BUFFER, 0).' compression is skipped even if it\'s enabled', true)
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above '.Storage::human(APP_STORAGE_READ_BUFFER, 0).' encryption is skipped even if it\'s enabled', true)
->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above '.Storage::human(APP_LIMIT_ANTIVIRUS, 0).' AntiVirus scanning is skipped even if it\'s enabled', true)
->inject('response')
->inject('dbForProject')
->inject('events')
->action(function (string $bucketId, string $name, ?array $permissions, bool $fileSecurity, bool $enabled, int $maximumFileSize, array $allowedFileExtensions, string $compression, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Event $events) {
$bucketId = $bucketId === 'unique()' ? ID::unique() : $bucketId;
// Map aggregate permissions into the multiple permissions they represent.
@@ -97,7 +94,7 @@ App::post('/v1/storage/buckets')
'array' => $attribute['array'],
'filters' => $attribute['filters'],
'default' => $attribute['default'] ?? null,
'format' => $attribute['format'] ?? ''
'format' => $attribute['format'] ?? '',
]);
}
@@ -128,14 +125,13 @@ App::post('/v1/storage/buckets')
$bucket = $dbForProject->getDocument('buckets', $bucketId);
$dbForProject->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes, permissions: $permissions ?? [], documentSecurity: $fileSecurity);
$dbForProject->createCollection('bucket_'.$bucket->getInternalId(), $attributes, $indexes, permissions: $permissions ?? [], documentSecurity: $fileSecurity);
} catch (Duplicate) {
throw new Exception(Exception::STORAGE_BUCKET_ALREADY_EXISTS);
}
$events
->setParam('bucketId', $bucket->getId())
;
->setParam('bucketId', $bucket->getId());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -153,15 +149,14 @@ App::get('/v1/storage/buckets')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_BUCKET_LIST)
->param('queries', [], new Buckets(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Buckets::ALLOWED_ATTRIBUTES), true)
->param('queries', [], new Buckets(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' queries are allowed, each '.APP_LIMIT_ARRAY_ELEMENT_SIZE.' characters long. You may filter on the following attributes: '.implode(', ', Buckets::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
->action(function (array $queries, string $search, Response $response, Database $dbForProject) {
$queries = Query::parseQueries($queries);
if (!empty($search)) {
if (! empty($search)) {
$queries[] = Query::search('search', $search);
}
@@ -203,7 +198,6 @@ App::get('/v1/storage/buckets/:bucketId')
->inject('response')
->inject('dbForProject')
->action(function (string $bucketId, Response $response, Database $dbForProject) {
$bucket = $dbForProject->getDocument('buckets', $bucketId);
if ($bucket->isEmpty()) {
@@ -232,11 +226,11 @@ App::put('/v1/storage/buckets/:bucketId')
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](/docs/permissions).', true)
->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](/docs/permissions).', true)
->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true)
->param('maximumFileSize', null, new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self hosted version you can change the limit by changing _APP_STORAGE_LIMIT environment variable. [Learn more about storage environment variables](/docs/environment-variables#storage)', true)
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
->param('compression', COMPRESSION_TYPE_NONE, new WhiteList([COMPRESSION_TYPE_NONE, COMPRESSION_TYPE_GZIP, COMPRESSION_TYPE_ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . COMPRESSION_TYPE_NONE . ', [' . COMPRESSION_TYPE_GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . COMPRESSION_TYPE_ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true)
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true)
->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true)
->param('maximumFileSize', null, new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is '.Storage::human((int) App::getEnv('_APP_STORAGE_LIMIT', 0), 0).'. For self hosted version you can change the limit by changing _APP_STORAGE_LIMIT environment variable. [Learn more about storage environment variables](/docs/environment-variables#storage)', true)
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' extensions are allowed, each 64 characters long.', true)
->param('compression', COMPRESSION_TYPE_NONE, new WhiteList([COMPRESSION_TYPE_NONE, COMPRESSION_TYPE_GZIP, COMPRESSION_TYPE_ZSTD]), 'Compression algorithm choosen for compression. Can be one of '.COMPRESSION_TYPE_NONE.', ['.COMPRESSION_TYPE_GZIP.'](https://en.wikipedia.org/wiki/Gzip), or ['.COMPRESSION_TYPE_ZSTD.'](https://en.wikipedia.org/wiki/Zstd), For file size above '.Storage::human(APP_STORAGE_READ_BUFFER, 0).' compression is skipped even if it\'s enabled', true)
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above '.Storage::human(APP_STORAGE_READ_BUFFER, 0).' encryption is skipped even if it\'s enabled', true)
->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above '.Storage::human(APP_LIMIT_ANTIVIRUS, 0).' AntiVirus scanning is skipped even if it\'s enabled', true)
->inject('response')
->inject('dbForProject')
->inject('events')
@@ -271,11 +265,10 @@ App::put('/v1/storage/buckets/:bucketId')
->setAttribute('encryption', $encryption)
->setAttribute('compression', $compression)
->setAttribute('antivirus', $antivirus));
$dbForProject->updateCollection('bucket_' . $bucket->getInternalId(), $permissions, $fileSecurity);
$dbForProject->updateCollection('bucket_'.$bucket->getInternalId(), $permissions, $fileSecurity);
$events
->setParam('bucketId', $bucket->getId())
;
->setParam('bucketId', $bucket->getId());
$response->dynamic($bucket, Response::MODEL_BUCKET);
});
@@ -305,7 +298,7 @@ App::delete('/v1/storage/buckets/:bucketId')
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
if (!$dbForProject->deleteDocument('buckets', $bucketId)) {
if (! $dbForProject->deleteDocument('buckets', $bucketId)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove bucket from DB');
}
@@ -315,8 +308,7 @@ App::delete('/v1/storage/buckets/:bucketId')
$events
->setParam('bucketId', $bucket->getId())
->setPayload($response->output($bucket, Response::MODEL_BUCKET))
;
->setPayload($response->output($bucket, Response::MODEL_BUCKET));
$response->noContent();
});
@@ -354,15 +346,14 @@ App::post('/v1/storage/buckets/:bucketId/files')
->inject('deviceFiles')
->inject('deviceLocal')
->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $events, string $mode, Device $deviceFiles, Device $deviceLocal) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
if ($bucket->isEmpty() || (! $bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$validator = new Authorization(Database::PERMISSION_CREATE);
if (!$validator->isValid($bucket->getCreate())) {
if (! $validator->isValid($bucket->getCreate())) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
@@ -378,7 +369,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
// Add permissions for current the user if none were provided.
if (\is_null($permissions)) {
$permissions = [];
if (!empty($user->getId())) {
if (! empty($user->getId())) {
foreach ($allowedPermissions as $permission) {
$permissions[] = (new Permission($permission, 'user', $user->getId()))->toString();
}
@@ -387,7 +378,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
// Users can only manage their own roles, API keys and Admin users can manage any
$roles = Authorization::getRoles();
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
if (! Auth::isAppUser($roles) && ! Auth::isPrivilegedUser($roles)) {
foreach (Database::PERMISSIONS as $type) {
foreach ($permissions as $permission) {
$permission = Permission::parse($permission);
@@ -399,8 +390,8 @@ App::post('/v1/storage/buckets/:bucketId/files')
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
if (!Authorization::isRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
if (! Authorization::isRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: ('.\implode(', ', $roles).')');
}
}
}
@@ -411,7 +402,6 @@ App::post('/v1/storage/buckets/:bucketId/files')
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Maximum bucket file size is larger than _APP_STORAGE_LIMIT');
}
$file = $request->getFiles('file');
// GraphQL multipart spec adds files with index keys
@@ -433,7 +423,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
$chunk = 1;
$chunks = 1;
if (!empty($contentRange)) {
if (! empty($contentRange)) {
$start = $request->getContentRangeStart();
$end = $request->getContentRangeEnd();
$fileSize = $request->getContentRangeSize();
@@ -444,7 +434,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
}
$idValidator = new UID();
if (!$idValidator->isValid($request->getHeader('x-appwrite-id'))) {
if (! $idValidator->isValid($request->getHeader('x-appwrite-id'))) {
throw new Exception(Exception::STORAGE_INVALID_APPWRITE_ID);
}
@@ -465,30 +455,30 @@ App::post('/v1/storage/buckets/:bucketId/files')
// Check if file type is allowed
$allowedFileExtensions = $bucket->getAttribute('allowedFileExtensions', []);
$fileExt = new FileExt($allowedFileExtensions);
if (!empty($allowedFileExtensions) && !$fileExt->isValid($fileName)) {
if (! empty($allowedFileExtensions) && ! $fileExt->isValid($fileName)) {
throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED, 'File extension not allowed');
}
// Check if file size is exceeding allowed limit
$fileSizeValidator = new FileSize($maximumFileSize);
if (!$fileSizeValidator->isValid($fileSize)) {
if (! $fileSizeValidator->isValid($fileSize)) {
throw new Exception(Exception::STORAGE_INVALID_FILE_SIZE, 'File size not allowed');
}
$upload = new Upload();
if (!$upload->isValid($fileTmpName)) {
if (! $upload->isValid($fileTmpName)) {
throw new Exception(Exception::STORAGE_INVALID_FILE);
}
// Save to storage
$fileSize ??= $deviceLocal->getFileSize($fileTmpName);
$path = $deviceFiles->getPath($fileId . '.' . \pathinfo($fileName, PATHINFO_EXTENSION));
$path = str_ireplace($deviceFiles->getRoot(), $deviceFiles->getRoot() . DIRECTORY_SEPARATOR . $bucket->getId(), $path); // Add bucket id to path after root
$path = $deviceFiles->getPath($fileId.'.'.\pathinfo($fileName, PATHINFO_EXTENSION));
$path = str_ireplace($deviceFiles->getRoot(), $deviceFiles->getRoot().DIRECTORY_SEPARATOR.$bucket->getId(), $path); // Add bucket id to path after root
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
$file = $dbForProject->getDocument('bucket_'.$bucket->getInternalId(), $fileId);
$metadata = ['content_type' => $deviceLocal->getFileMimeType($fileTmpName)];
if (!$file->isEmpty()) {
if (! $file->isEmpty()) {
$chunks = $file->getAttribute('chunksTotal', 1);
$metadata = $file->getAttribute('metadata', []);
if ($chunk === -1) {
@@ -508,7 +498,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
(int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310)
);
if (!$antivirus->fileScan($path)) {
if (! $antivirus->fileScan($path)) {
$deviceFiles->delete($path);
throw new Exception(Exception::STORAGE_INVALID_FILE);
}
@@ -542,8 +532,8 @@ App::post('/v1/storage/buckets/:bucketId/files')
$data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag);
}
if (!empty($data)) {
if (!$deviceFiles->write($path, $data, $mimeType)) {
if (! empty($data)) {
if (! $deviceFiles->write($path, $data, $mimeType)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to save file');
}
}
@@ -587,7 +577,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
'metadata' => $metadata,
]);
$file = $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
$file = $dbForProject->createDocument('bucket_'.$bucket->getInternalId(), $doc);
} else {
$file = $file
->setAttribute('$permissions', $permissions)
@@ -602,7 +592,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
->setAttribute('metadata', $metadata)
->setAttribute('chunksUploaded', $chunksUploaded);
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
$file = $dbForProject->updateDocument('bucket_'.$bucket->getInternalId(), $fileId, $file);
}
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
@@ -633,13 +623,13 @@ App::post('/v1/storage/buckets/:bucketId/files')
'metadata' => $metadata,
]);
$file = $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
$file = $dbForProject->createDocument('bucket_'.$bucket->getInternalId(), $doc);
} else {
$file = $file
->setAttribute('chunksUploaded', $chunksUploaded)
->setAttribute('metadata', $metadata);
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
$file = $dbForProject->updateDocument('bucket_'.$bucket->getInternalId(), $fileId, $file);
}
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
@@ -653,8 +643,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
$events
->setParam('bucketId', $bucket->getId())
->setParam('fileId', $file->getId())
->setContext('bucket', $bucket)
;
->setContext('bucket', $bucket);
$metadata = null; // was causing leaks as it was passed by reference
@@ -676,29 +665,28 @@ App::get('/v1/storage/buckets/:bucketId/files')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FILE_LIST)
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('queries', [], new Files(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Files::ALLOWED_ATTRIBUTES), true)
->param('queries', [], new Files(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' queries are allowed, each '.APP_LIMIT_ARRAY_ELEMENT_SIZE.' characters long. You may filter on the following attributes: '.implode(', ', Files::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
->inject('mode')
->action(function (string $bucketId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
if ($bucket->isEmpty() || (! $bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
if (!$fileSecurity && !$valid) {
if (! $fileSecurity && ! $valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$queries = Query::parseQueries($queries);
if (!empty($search)) {
if (! empty($search)) {
$queries[] = Query::search('search', $search);
}
@@ -709,10 +697,10 @@ App::get('/v1/storage/buckets/:bucketId/files')
/** @var Query $cursor */
$fileId = $cursor->getValue();
if ($fileSecurity && !$valid) {
$cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
if ($fileSecurity && ! $valid) {
$cursorDocument = $dbForProject->getDocument('bucket_'.$bucket->getInternalId(), $fileId);
} else {
$cursorDocument = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('bucket_'.$bucket->getInternalId(), $fileId));
}
if ($cursorDocument->isEmpty()) {
@@ -724,12 +712,12 @@ App::get('/v1/storage/buckets/:bucketId/files')
$filterQueries = Query::groupByType($queries)['filters'];
if ($fileSecurity && !$valid) {
$files = $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries);
$total = $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT);
if ($fileSecurity && ! $valid) {
$files = $dbForProject->find('bucket_'.$bucket->getInternalId(), $queries);
$total = $dbForProject->count('bucket_'.$bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT);
} else {
$files = Authorization::skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries));
$total = Authorization::skip(fn () => $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT));
$files = Authorization::skip(fn () => $dbForProject->find('bucket_'.$bucket->getInternalId(), $queries));
$total = Authorization::skip(fn () => $dbForProject->count('bucket_'.$bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT));
}
$response->dynamic(new Document([
@@ -756,24 +744,23 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
->inject('dbForProject')
->inject('mode')
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, string $mode) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
if ($bucket->isEmpty() || (! $bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
if (!$fileSecurity && !$valid) {
if (! $fileSecurity && ! $valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($fileSecurity && !$valid) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
if ($fileSecurity && ! $valid) {
$file = $dbForProject->getDocument('bucket_'.$bucket->getInternalId(), $fileId);
} else {
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_'.$bucket->getInternalId(), $fileId));
}
if ($file->isEmpty()) {
@@ -802,7 +789,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
->param('fileId', '', new UID(), 'File ID')
->param('width', 0, new Range(0, 4000), 'Resize preview image width, Pass an integer between 0 to 4000.', true)
->param('height', 0, new Range(0, 4000), 'Resize preview image height, Pass an integer between 0 to 4000.', true)
->param('gravity', Image::GRAVITY_CENTER, new WhiteList(Image::getGravityTypes()), 'Image crop gravity. Can be one of ' . implode(",", Image::getGravityTypes()), true)
->param('gravity', Image::GRAVITY_CENTER, new WhiteList(Image::getGravityTypes()), 'Image crop gravity. Can be one of '.implode(',', Image::getGravityTypes()), true)
->param('quality', 100, new Range(0, 100), 'Preview image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->param('borderWidth', 0, new Range(0, 100), 'Preview image border in pixels. Pass an integer between 0 to 100. Defaults to 0.', true)
->param('borderColor', '', new HexColor(), 'Preview image border color. Use a valid HEX color, no # is needed for prefix.', true)
@@ -819,21 +806,20 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
->inject('deviceFiles')
->inject('deviceLocal')
->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Request $request, Response $response, Document $project, Database $dbForProject, string $mode, Device $deviceFiles, Device $deviceLocal) {
if (!\extension_loaded('imagick')) {
if (! \extension_loaded('imagick')) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
}
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
if ($bucket->isEmpty() || (! $bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
if (!$fileSecurity && !$valid) {
if (! $fileSecurity && ! $valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
@@ -845,10 +831,10 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$outputs = Config::getParam('storage-outputs');
$fileLogos = Config::getParam('storage-logos');
if ($fileSecurity && !$valid) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
if ($fileSecurity && ! $valid) {
$file = $dbForProject->getDocument('bucket_'.$bucket->getInternalId(), $fileId);
} else {
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_'.$bucket->getInternalId(), $fileId));
}
if ($file->isEmpty()) {
@@ -860,8 +846,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$algorithm = $file->getAttribute('algorithm', 'none');
$cipher = $file->getAttribute('openSSLCipher');
$mime = $file->getAttribute('mimeType');
if (!\in_array($mime, $inputs) || $file->getAttribute('sizeActual') > (int) App::getEnv('_APP_STORAGE_PREVIEW_LIMIT', 20000000)) {
if (!\in_array($mime, $inputs)) {
if (! \in_array($mime, $inputs) || $file->getAttribute('sizeActual') > (int) App::getEnv('_APP_STORAGE_PREVIEW_LIMIT', 20000000)) {
if (! \in_array($mime, $inputs)) {
$path = (\array_key_exists($mime, $fileLogos)) ? $fileLogos[$mime] : $fileLogos['default'];
} else {
// it was an image but the file size exceeded the limit
@@ -875,7 +861,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$deviceFiles = $deviceLocal;
}
if (!$deviceFiles->exists($path)) {
if (! $deviceFiles->exists($path)) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
@@ -885,14 +871,13 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$output = empty($type) ? (array_search($mime, $outputs) ?? 'jpg') : $type;
}
$source = $deviceFiles->read($path);
if (!empty($cipher)) { // Decrypt
if (! empty($cipher)) { // Decrypt
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('openSSLCipher'),
App::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
App::getEnv('_APP_OPENSSL_KEY_V'.$file->getAttribute('openSSLVersion')),
0,
\hex2bin($file->getAttribute('openSSLIV')),
\hex2bin($file->getAttribute('openSSLTag'))
@@ -914,23 +899,23 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$image->crop((int) $width, (int) $height, $gravity);
if (!empty($opacity) || $opacity === 0) {
if (! empty($opacity) || $opacity === 0) {
$image->setOpacity($opacity);
}
if (!empty($background)) {
$image->setBackground('#' . $background);
if (! empty($background)) {
$image->setBackground('#'.$background);
}
if (!empty($borderWidth)) {
$image->setBorder($borderWidth, '#' . $borderColor);
if (! empty($borderWidth)) {
$image->setBorder($borderWidth, '#'.$borderColor);
}
if (!empty($borderRadius)) {
if (! empty($borderRadius)) {
$image->setBorderRadius($borderRadius);
}
if (!empty($rotation)) {
if (! empty($rotation)) {
$image->setRotation(($rotation + 360) % 360);
}
@@ -939,10 +924,9 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$contentType = (\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg'];
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30).' GMT')
->setContentType($contentType)
->file($data)
;
->file($data);
unset($image);
});
@@ -967,24 +951,23 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
->inject('mode')
->inject('deviceFiles')
->action(function (string $bucketId, string $fileId, Request $request, Response $response, Database $dbForProject, string $mode, Device $deviceFiles) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
if ($bucket->isEmpty() || (! $bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
if (!$fileSecurity && !$valid) {
if (! $fileSecurity && ! $valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($fileSecurity && !$valid) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
if ($fileSecurity && ! $valid) {
$file = $dbForProject->getDocument('bucket_'.$bucket->getInternalId(), $fileId);
} else {
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_'.$bucket->getInternalId(), $fileId));
}
if ($file->isEmpty()) {
@@ -993,21 +976,20 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
$path = $file->getAttribute('path', '');
if (!$deviceFiles->exists($path)) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path);
if (! $deviceFiles->exists($path)) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in '.$path);
}
$response
->setContentType($file->getAttribute('mimeType'))
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->addHeader('X-Peak', \memory_get_peak_usage())
->addHeader('Content-Disposition', 'attachment; filename="' . $file->getAttribute('name', '') . '"')
;
->addHeader('Content-Disposition', 'attachment; filename="'.$file->getAttribute('name', '').'"');
$size = $file->getAttribute('sizeOriginal', 0);
$rangeHeader = $request->getHeader('range');
if (!empty($rangeHeader)) {
if (! empty($rangeHeader)) {
$start = $request->getRangeStart();
$end = $request->getRangeEnd();
$unit = $request->getRangeUnit();
@@ -1022,18 +1004,18 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
$response
->addHeader('Accept-Ranges', 'bytes')
->addHeader('Content-Range', 'bytes ' . $start . '-' . $end . '/' . $size)
->addHeader('Content-Range', 'bytes '.$start.'-'.$end.'/'.$size)
->addHeader('Content-Length', $end - $start + 1)
->setStatusCode(Response::STATUS_CODE_PARTIALCONTENT);
}
$source = '';
if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt
if (! empty($file->getAttribute('openSSLCipher'))) { // Decrypt
$source = $deviceFiles->read($path);
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('openSSLCipher'),
App::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
App::getEnv('_APP_OPENSSL_KEY_V'.$file->getAttribute('openSSLVersion')),
0,
\hex2bin($file->getAttribute('openSSLIV')),
\hex2bin($file->getAttribute('openSSLTag'))
@@ -1057,14 +1039,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
break;
}
if (!empty($source)) {
if (!empty($rangeHeader)) {
if (! empty($source)) {
if (! empty($rangeHeader)) {
$response->send(substr($source, $start, ($end - $start + 1)));
}
$response->send($source);
}
if (!empty($rangeHeader)) {
if (! empty($rangeHeader)) {
$response->send($deviceFiles->read($path, $start, ($end - $start + 1)));
}
@@ -1105,24 +1087,23 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
->inject('mode')
->inject('deviceFiles')
->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, string $mode, Device $deviceFiles) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
if ($bucket->isEmpty() || (! $bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
if (!$fileSecurity && !$valid) {
if (! $fileSecurity && ! $valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($fileSecurity && !$valid) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
if ($fileSecurity && ! $valid) {
$file = $dbForProject->getDocument('bucket_'.$bucket->getInternalId(), $fileId);
} else {
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_'.$bucket->getInternalId(), $fileId));
}
if ($file->isEmpty()) {
@@ -1133,8 +1114,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
$path = $file->getAttribute('path', '');
if (!$deviceFiles->exists($path)) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path);
if (! $deviceFiles->exists($path)) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in '.$path);
}
$contentType = 'text/plain';
@@ -1147,15 +1128,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
->setContentType($contentType)
->addHeader('Content-Security-Policy', 'script-src none;')
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('X-Peak', \memory_get_peak_usage())
;
->addHeader('Content-Disposition', 'inline; filename="'.$file->getAttribute('name', '').'"')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->addHeader('X-Peak', \memory_get_peak_usage());
$size = $file->getAttribute('sizeOriginal', 0);
$rangeHeader = $request->getHeader('range');
if (!empty($rangeHeader)) {
if (! empty($rangeHeader)) {
$start = $request->getRangeStart();
$end = $request->getRangeEnd();
$unit = $request->getRangeUnit();
@@ -1176,12 +1156,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
}
$source = '';
if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt
if (! empty($file->getAttribute('openSSLCipher'))) { // Decrypt
$source = $deviceFiles->read($path);
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('openSSLCipher'),
App::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
App::getEnv('_APP_OPENSSL_KEY_V'.$file->getAttribute('openSSLVersion')),
0,
\hex2bin($file->getAttribute('openSSLIV')),
\hex2bin($file->getAttribute('openSSLTag'))
@@ -1205,14 +1185,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
break;
}
if (!empty($source)) {
if (!empty($rangeHeader)) {
if (! empty($source)) {
if (! empty($rangeHeader)) {
$response->send(substr($source, $start, ($end - $start + 1)));
}
$response->send($source);
}
if (!empty($rangeHeader)) {
if (! empty($rangeHeader)) {
$response->send($deviceFiles->read($path, $start, ($end - $start + 1)));
}
@@ -1262,22 +1242,21 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
->inject('mode')
->inject('events')
->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $events) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
if ($bucket->isEmpty() || (! $bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttributes('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_UPDATE);
$valid = $validator->isValid($bucket->getUpdate());
if (!$fileSecurity && !$valid) {
if (! $fileSecurity && ! $valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
// Read permission should not be required for update
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_'.$bucket->getInternalId(), $fileId));
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
@@ -1292,7 +1271,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
// Users can only manage their own roles, API keys and Admin users can manage any
$roles = Authorization::getRoles();
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles) && !\is_null($permissions)) {
if (! Auth::isAppUser($roles) && ! Auth::isPrivilegedUser($roles) && ! \is_null($permissions)) {
foreach (Database::PERMISSIONS as $type) {
foreach ($permissions as $permission) {
$permission = Permission::parse($permission);
@@ -1304,8 +1283,8 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
if (!Authorization::isRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
if (! Authorization::isRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: ('.\implode(', ', $roles).')');
}
}
}
@@ -1317,25 +1296,24 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
$file->setAttribute('$permissions', $permissions);
if (!is_null($name)) {
if (! is_null($name)) {
$file->setAttribute('name', $name);
}
if ($fileSecurity && !$valid) {
if ($fileSecurity && ! $valid) {
try {
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
$file = $dbForProject->updateDocument('bucket_'.$bucket->getInternalId(), $fileId, $file);
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
} else {
$file = Authorization::skip(fn() => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file));
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_'.$bucket->getInternalId(), $fileId, $file));
}
$events
->setParam('bucketId', $bucket->getId())
->setParam('fileId', $file->getId())
->setContext('bucket', $bucket)
;
->setContext('bucket', $bucket);
$response->dynamic($file, Response::MODEL_FILE);
});
@@ -1368,26 +1346,26 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $events, string $mode, Device $deviceFiles, Delete $deletes) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
if ($bucket->isEmpty() || (! $bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttributes('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_DELETE);
$valid = $validator->isValid($bucket->getDelete());
if (!$fileSecurity && !$valid) {
if (! $fileSecurity && ! $valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
// Read permission should not be required for delete
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_'.$bucket->getInternalId(), $fileId));
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
// Make sure we don't delete the file before the document permission check occurs
if ($fileSecurity && !$valid && !$validator->isValid($file->getDelete())) {
if ($fileSecurity && ! $valid && ! $validator->isValid($file->getDelete())) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
@@ -1404,20 +1382,19 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
if ($deviceDeleted) {
$deletes
->setType(DELETE_TYPE_CACHE_BY_RESOURCE)
->setResource('file/' . $fileId)
;
->setResource('file/'.$fileId);
if ($fileSecurity && !$valid) {
if ($fileSecurity && ! $valid) {
try {
$deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId);
$deleted = $dbForProject->deleteDocument('bucket_'.$bucket->getInternalId(), $fileId);
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
} else {
$deleted = Authorization::skip(fn() => $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId));
$deleted = Authorization::skip(fn () => $dbForProject->deleteDocument('bucket_'.$bucket->getInternalId(), $fileId));
}
if (!$deleted) {
if (! $deleted) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove file from DB');
}
} else {
@@ -1428,8 +1405,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
->setParam('bucketId', $bucket->getId())
->setParam('fileId', $file->getId())
->setContext('bucket', $bucket)
->setPayload($response->output($file, Response::MODEL_FILE))
;
->setPayload($response->output($file, Response::MODEL_FILE));
$response->noContent();
});
@@ -1448,7 +1424,6 @@ App::get('/v1/storage/usage')
->inject('response')
->inject('dbForProject')
->action(function (string $range, Response $response, Database $dbForProject) {
$periods = Config::getParam('usage', []);
$stats = $usage = [];
$days = $periods[$range];
@@ -1518,7 +1493,6 @@ App::get('/v1/storage/:bucketId/usage')
->inject('response')
->inject('dbForProject')
->action(function (string $bucketId, string $range, Response $response, Database $dbForProject) {
$bucket = $dbForProject->getDocument('buckets', $bucketId);
if ($bucket->isEmpty()) {
+76 -99
View File
@@ -9,14 +9,10 @@ use Appwrite\Event\Mail;
use Appwrite\Event\Phone as EventPhone;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Utopia\Validator\Host;
use Appwrite\Template\Template;
use Appwrite\Utopia\Database\Validator\CustomId;
use Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\Memberships;
use Appwrite\Utopia\Database\Validator\Queries\Teams;
use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use MaxMind\Db\Reader;
@@ -24,20 +20,24 @@ use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Authorization as AuthorizationException;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Query;
use Utopia\Database\DateTime;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Queries;
use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\UID;
use Utopia\Locale\Locale;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\Host;
use Utopia\Validator\Text;
App::post('/v1/teams')
@@ -56,20 +56,19 @@ App::post('/v1/teams')
->label('sdk.response.model', Response::MODEL_TEAM)
->param('teamId', '', new CustomId(), 'Team ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('name', null, new Text(128), 'Team name. Max length: 128 chars.')
->param('roles', ['owner'], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of strings. Use this param to set the roles in the team for the user who created it. The default role is **owner**. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', true)
->param('roles', ['owner'], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of strings. Use this param to set the roles in the team for the user who created it. The default role is **owner**. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' roles are allowed, each 32 characters long.', true)
->inject('response')
->inject('user')
->inject('dbForProject')
->inject('events')
->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $events) {
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAppUser = Auth::isAppUser(Authorization::getRoles());
$teamId = $teamId == 'unique()' ? ID::unique() : $teamId;
try {
$team = Authorization::skip(fn() => $dbForProject->createDocument('teams', new Document([
$team = Authorization::skip(fn () => $dbForProject->createDocument('teams', new Document([
'$id' => $teamId,
'$permissions' => [
Permission::read(Role::team($teamId)),
@@ -85,8 +84,8 @@ App::post('/v1/teams')
throw new Exception(Exception::TEAM_ALREADY_EXISTS);
}
if (!$isPrivilegedUser && !$isAppUser) { // Don't add user on server mode
if (!\in_array('owner', $roles)) {
if (! $isPrivilegedUser && ! $isAppUser) { // Don't add user on server mode
if (! \in_array('owner', $roles)) {
$roles[] = 'owner';
}
@@ -110,7 +109,7 @@ App::post('/v1/teams')
'joined' => DateTime::now(),
'confirm' => true,
'secret' => '',
'search' => implode(' ', [$membershipId, $user->getId()])
'search' => implode(' ', [$membershipId, $user->getId()]),
]);
$membership = $dbForProject->createDocument('memberships', $membership);
@@ -119,7 +118,7 @@ App::post('/v1/teams')
$events->setParam('teamId', $team->getId());
if (!empty($user->getId())) {
if (! empty($user->getId())) {
$events->setParam('userId', $user->getId());
}
@@ -140,15 +139,14 @@ App::get('/v1/teams')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEAM_LIST)
->label('sdk.offline.model', '/teams')
->param('queries', [], new Teams(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Teams::ALLOWED_ATTRIBUTES), true)
->param('queries', [], new Teams(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' queries are allowed, each '.APP_LIMIT_ARRAY_ELEMENT_SIZE.' characters long. You may filter on the following attributes: '.implode(', ', Teams::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
->action(function (array $queries, string $search, Response $response, Database $dbForProject) {
$queries = Query::parseQueries($queries);
if (!empty($search)) {
if (! empty($search)) {
$queries[] = Query::search('search', $search);
}
@@ -195,7 +193,6 @@ App::get('/v1/teams/:teamId')
->inject('response')
->inject('dbForProject')
->action(function (string $teamId, Response $response, Database $dbForProject) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -221,7 +218,6 @@ App::get('/v1/teams/:teamId/prefs')
->inject('response')
->inject('dbForProject')
->action(function (string $teamId, Response $response, Database $dbForProject) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -256,7 +252,6 @@ App::put('/v1/teams/:teamId')
->inject('dbForProject')
->inject('events')
->action(function (string $teamId, string $name, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $events) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -298,7 +293,6 @@ App::put('/v1/teams/:teamId/prefs')
->inject('dbForProject')
->inject('events')
->action(function (string $teamId, array $prefs, Response $response, Database $dbForProject, Event $events) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -331,14 +325,13 @@ App::delete('/v1/teams/:teamId')
->inject('events')
->inject('deletes')
->action(function (string $teamId, Response $response, Database $dbForProject, Event $events, Delete $deletes) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
throw new Exception(Exception::TEAM_NOT_FOUND);
}
if (!$dbForProject->deleteDocument('teams', $teamId)) {
if (! $dbForProject->deleteDocument('teams', $teamId)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove team from DB');
}
@@ -348,8 +341,7 @@ App::delete('/v1/teams/:teamId')
$events
->setParam('teamId', $team->getId())
->setPayload($response->output($team, Response::MODEL_TEAM))
;
->setPayload($response->output($team, Response::MODEL_TEAM));
$response->noContent();
});
@@ -375,8 +367,8 @@ App::post('/v1/teams/:teamId/memberships')
->param('email', '', new Email(), 'Email of the new team member.', true)
->param('userId', '', new UID(), 'ID of the user to be added to a team.', true)
->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
->param('roles', [], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.')
->param('url', '', fn($clients) => new Host($clients), 'URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) // TODO add our own built-in confirm page
->param('roles', [], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' roles are allowed, each 32 characters long.')
->param('url', '', fn ($clients) => new Host($clients), 'URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) // TODO add our own built-in confirm page
->param('name', '', new Text(128), 'Name of the new team member. Max length: 128 chars.', true)
->inject('response')
->inject('project')
@@ -387,14 +379,13 @@ App::post('/v1/teams/:teamId/memberships')
->inject('messaging')
->inject('events')
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $mails, EventPhone $messaging, Event $events) {
if (empty($userId) && empty($email) && empty($phone)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'At least one of userId, email, or phone is required');
}
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAppUser = Auth::isAppUser(Authorization::getRoles());
if (!$isPrivilegedUser && !$isAppUser && empty(App::getEnv('_APP_SMTP_HOST'))) {
if (! $isPrivilegedUser && ! $isAppUser && empty(App::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED);
}
@@ -405,28 +396,28 @@ App::post('/v1/teams/:teamId/memberships')
if ($team->isEmpty()) {
throw new Exception(Exception::TEAM_NOT_FOUND);
}
if (!empty($userId)) {
if (! empty($userId)) {
$invitee = $dbForProject->getDocument('users', $userId);
if ($invitee->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND, 'User with given userId doesn\'t exist.', 404);
}
if (!empty($email) && $invitee->getAttribute('email', '') !== $email) {
if (! empty($email) && $invitee->getAttribute('email', '') !== $email) {
throw new Exception(Exception::USER_ALREADY_EXISTS, 'Given userId and email doesn\'t match', 409);
}
if (!empty($phone) && $invitee->getAttribute('phone', '') !== $phone) {
if (! empty($phone) && $invitee->getAttribute('phone', '') !== $phone) {
throw new Exception(Exception::USER_ALREADY_EXISTS, 'Given userId and phone doesn\'t match', 409);
}
$email = $invitee->getAttribute('email', '');
$phone = $invitee->getAttribute('phone', '');
$name = empty($name) ? $invitee->getAttribute('name', '') : $name;
} elseif (!empty($email)) {
} elseif (! empty($email)) {
$invitee = $dbForProject->findOne('users', [Query::equal('email', [$email])]); // Get user by email address
if (!empty($invitee) && !empty($phone) && $invitee->getAttribute('phone', '') !== $phone) {
if (! empty($invitee) && ! empty($phone) && $invitee->getAttribute('phone', '') !== $phone) {
throw new Exception(Exception::USER_ALREADY_EXISTS, 'Given email and phone doesn\'t match', 409);
}
} elseif (!empty($phone)) {
} elseif (! empty($phone)) {
$invitee = $dbForProject->findOne('users', [Query::equal('phone', [$phone])]);
if (!empty($invitee) && !empty($email) && $invitee->getAttribute('email', '') !== $email) {
if (! empty($invitee) && ! empty($email) && $invitee->getAttribute('email', '') !== $email) {
throw new Exception(Exception::USER_ALREADY_EXISTS, 'Given phone and email doesn\'t match', 409);
}
}
@@ -446,13 +437,13 @@ App::post('/v1/teams/:teamId/memberships')
$identityWithMatchingEmail = $dbForProject->findOne('identities', [
Query::equal('providerEmail', [$email]),
]);
if ($identityWithMatchingEmail !== false && !$identityWithMatchingEmail->isEmpty()) {
if ($identityWithMatchingEmail !== false && ! $identityWithMatchingEmail->isEmpty()) {
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
}
try {
$userId = ID::unique();
$invitee = Authorization::skip(fn() => $dbForProject->createDocument('users', new Document([
$invitee = Authorization::skip(fn () => $dbForProject->createDocument('users', new Document([
'$id' => $userId,
'$permissions' => [
Permission::read(Role::any()),
@@ -480,16 +471,16 @@ App::post('/v1/teams/:teamId/memberships')
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $name])
'search' => implode(' ', [$userId, $email, $name]),
])));
} catch (Duplicate $th) {
throw new Exception(Exception::USER_ALREADY_EXISTS);
}
}
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
$isOwner = Authorization::isRole('team:'.$team->getId().'/owner');
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
if (! $isOwner && ! $isPrivilegedUser && ! $isAppUser) { // Not owner, not admin, not app (server)
throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to send invitations for this team');
}
@@ -514,17 +505,17 @@ App::post('/v1/teams/:teamId/memberships')
'joined' => ($isPrivilegedUser || $isAppUser) ? DateTime::now() : null,
'confirm' => ($isPrivilegedUser || $isAppUser),
'secret' => Auth::hash($secret),
'search' => implode(' ', [$membershipId, $invitee->getId()])
'search' => implode(' ', [$membershipId, $invitee->getId()]),
]);
if ($isPrivilegedUser || $isAppUser) { // Allow admin to create membership
try {
$membership = Authorization::skip(fn() => $dbForProject->createDocument('memberships', $membership));
$membership = Authorization::skip(fn () => $dbForProject->createDocument('memberships', $membership));
} catch (Duplicate $th) {
throw new Exception(Exception::TEAM_INVITE_ALREADY_EXISTS);
}
$team->setAttribute('total', $team->getAttribute('total', 0) + 1);
$team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team));
$team = Authorization::skip(fn () => $dbForProject->updateDocument('teams', $team->getId(), $team));
$dbForProject->deleteCachedDocument('users', $invitee->getId());
} else {
@@ -537,16 +528,16 @@ App::post('/v1/teams/:teamId/memberships')
$url = Template::parseURL($url);
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['membershipId' => $membership->getId(), 'userId' => $invitee->getId(), 'secret' => $secret, 'teamId' => $teamId]);
$url = Template::unParseURL($url);
if (!empty($email)) {
if (! empty($email)) {
$projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]');
$from = $project->isEmpty() || $project->getId() === 'console' ? '' : \sprintf($locale->getText('emails.sender'), $projectName);
$body = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-base.tpl');
$subject = \sprintf($locale->getText("emails.invitation.subject"), $team->getAttribute('name'), $projectName);
$body = Template::fromFile(__DIR__.'/../../config/locale/templates/email-base.tpl');
$subject = \sprintf($locale->getText('emails.invitation.subject'), $team->getAttribute('name'), $projectName);
$smtpEnabled = $project->getAttribute('smtp', [])['enabled'] ?? false;
$customTemplate = $project->getAttribute('templates', [])['email.invitation-' . $locale->default] ?? [];
if ($smtpEnabled && !empty($customTemplate)) {
$customTemplate = $project->getAttribute('templates', [])['email.invitation-'.$locale->default] ?? [];
if ($smtpEnabled && ! empty($customTemplate)) {
$body = $customTemplate['message'];
$subject = $customTemplate['subject'];
$from = $customTemplate['senderName'];
@@ -557,13 +548,13 @@ App::post('/v1/teams/:teamId/memberships')
$body
->setParam('{{subject}}', $subject)
->setParam('{{hello}}', $locale->getText("emails.invitation.hello"))
->setParam('{{hello}}', $locale->getText('emails.invitation.hello'))
->setParam('{{name}}', $user->getAttribute('name'))
->setParam('{{body}}', $locale->getText("emails.invitation.body"))
->setParam('{{body}}', $locale->getText('emails.invitation.body'))
->setParam('{{redirect}}', $url)
->setParam('{{footer}}', $locale->getText("emails.invitation.footer"))
->setParam('{{thanks}}', $locale->getText("emails.invitation.thanks"))
->setParam('{{signature}}', $locale->getText("emails.invitation.signature"))
->setParam('{{footer}}', $locale->getText('emails.invitation.footer'))
->setParam('{{thanks}}', $locale->getText('emails.invitation.thanks'))
->setParam('{{signature}}', $locale->getText('emails.invitation.signature'))
->setParam('{{project}}', $projectName)
->setParam('{{direction}}', $locale->getText('settings.direction'))
->setParam('{{bg-body}}', '#f7f7f7')
@@ -578,13 +569,12 @@ App::post('/v1/teams/:teamId/memberships')
->setFrom($from)
->setRecipient($invitee->getAttribute('email'))
->setName($invitee->getAttribute('name'))
->trigger()
;
} elseif (!empty($phone)) {
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl');
->trigger();
} elseif (! empty($phone)) {
$message = Template::fromFile(__DIR__.'/../../config/locale/templates/sms-base.tpl');
$customTemplate = $project->getAttribute('templates', [])['sms.invitation-' . $locale->default] ?? [];
if (!empty($customTemplate)) {
$customTemplate = $project->getAttribute('templates', [])['sms.invitation-'.$locale->default] ?? [];
if (! empty($customTemplate)) {
$message = $customTemplate['message'];
}
@@ -600,8 +590,7 @@ App::post('/v1/teams/:teamId/memberships')
$events
->setParam('teamId', $team->getId())
->setParam('membershipId', $membership->getId())
;
->setParam('membershipId', $membership->getId());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -627,12 +616,11 @@ App::get('/v1/teams/:teamId/memberships')
->label('sdk.response.model', Response::MODEL_MEMBERSHIP_LIST)
->label('sdk.offline.model', '/teams/{teamId}/memberships')
->param('teamId', '', new UID(), 'Team ID.')
->param('queries', [], new Memberships(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Memberships::ALLOWED_ATTRIBUTES), true)
->param('queries', [], new Memberships(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' queries are allowed, each '.APP_LIMIT_ARRAY_ELEMENT_SIZE.' characters long. You may filter on the following attributes: '.implode(', ', Memberships::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $teamId, array $queries, string $search, Response $response, Database $dbForProject) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -641,7 +629,7 @@ App::get('/v1/teams/:teamId/memberships')
$queries = Query::parseQueries($queries);
if (!empty($search)) {
if (! empty($search)) {
$queries[] = Query::search('search', $search);
}
@@ -676,7 +664,7 @@ App::get('/v1/teams/:teamId/memberships')
max: APP_LIMIT_COUNT
);
$memberships = array_filter($memberships, fn(Document $membership) => !empty($membership->getAttribute('userId')));
$memberships = array_filter($memberships, fn (Document $membership) => ! empty($membership->getAttribute('userId')));
$memberships = array_map(function ($membership) use ($dbForProject, $team) {
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
@@ -684,8 +672,7 @@ App::get('/v1/teams/:teamId/memberships')
$membership
->setAttribute('teamName', $team->getAttribute('name'))
->setAttribute('userName', $user->getAttribute('name'))
->setAttribute('userEmail', $user->getAttribute('email'))
;
->setAttribute('userEmail', $user->getAttribute('email'));
return $membership;
}, $memberships);
@@ -714,7 +701,6 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
->inject('response')
->inject('dbForProject')
->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -732,8 +718,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
$membership
->setAttribute('teamName', $team->getAttribute('name'))
->setAttribute('userName', $user->getAttribute('name'))
->setAttribute('userEmail', $user->getAttribute('email'))
;
->setAttribute('userEmail', $user->getAttribute('email'));
$response->dynamic($membership, Response::MODEL_MEMBERSHIP);
});
@@ -754,14 +739,13 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->label('sdk.response.model', Response::MODEL_MEMBERSHIP)
->param('teamId', '', new UID(), 'Team ID.')
->param('membershipId', '', new UID(), 'Membership ID.')
->param('roles', [], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings. Use this param to set the user\'s roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.')
->param('roles', [], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings. Use this param to set the user\'s roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' roles are allowed, each 32 characters long.')
->inject('request')
->inject('response')
->inject('user')
->inject('dbForProject')
->inject('events')
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $events) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
throw new Exception(Exception::TEAM_NOT_FOUND);
@@ -779,9 +763,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAppUser = Auth::isAppUser(Authorization::getRoles());
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
$isOwner = Authorization::isRole('team:'.$team->getId().'/owner');
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
if (! $isOwner && ! $isPrivilegedUser && ! $isAppUser) { // Not owner, not admin, not app (server)
throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to modify roles');
}
@@ -848,7 +832,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH);
}
$team = Authorization::skip(fn() => $dbForProject->getDocument('teams', $teamId));
$team = Authorization::skip(fn () => $dbForProject->getDocument('teams', $teamId));
if ($team->isEmpty()) {
throw new Exception(Exception::TEAM_NOT_FOUND);
@@ -859,7 +843,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
}
if ($userId !== $membership->getAttribute('userId')) {
throw new Exception(Exception::TEAM_INVITE_MISMATCH, 'Invite does not belong to current user (' . $user->getAttribute('email') . ')');
throw new Exception(Exception::TEAM_INVITE_MISMATCH, 'Invite does not belong to current user ('.$user->getAttribute('email').')');
}
if ($user->isEmpty()) {
@@ -867,7 +851,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
}
if ($membership->getAttribute('userId') !== $user->getId()) {
throw new Exception(Exception::TEAM_INVITE_MISMATCH, 'Invite does not belong to current user (' . $user->getAttribute('email') . ')');
throw new Exception(Exception::TEAM_INVITE_MISMATCH, 'Invite does not belong to current user ('.$user->getAttribute('email').')');
}
if ($membership->getAttribute('confirm') === true) {
@@ -876,10 +860,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
$membership // Attach user to team
->setAttribute('joined', DateTime::now())
->setAttribute('confirm', true)
;
->setAttribute('confirm', true);
Authorization::skip(fn() => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true)));
Authorization::skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true)));
// Log user in
@@ -917,23 +900,20 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
$dbForProject->deleteCachedDocument('users', $user->getId());
$team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team->setAttribute('total', $team->getAttribute('total', 0) + 1)));
$team = Authorization::skip(fn () => $dbForProject->updateDocument('teams', $team->getId(), $team->setAttribute('total', $team->getAttribute('total', 0) + 1)));
$events
->setParam('teamId', $team->getId())
->setParam('membershipId', $membership->getId())
;
->setParam('membershipId', $membership->getId());
if (!Config::getParam('domainVerification')) {
if (! Config::getParam('domainVerification')) {
$response
->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]))
;
->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]));
}
$response
->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
;
->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'));
$response->dynamic(
$membership
@@ -963,7 +943,6 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
->inject('dbForProject')
->inject('events')
->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $events) {
$membership = $dbForProject->getDocument('memberships', $membershipId);
if ($membership->isEmpty()) {
@@ -998,14 +977,13 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
if ($membership->getAttribute('confirm')) { // Count only confirmed members
$team->setAttribute('total', \max($team->getAttribute('total', 0) - 1, 0));
Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team));
Authorization::skip(fn () => $dbForProject->updateDocument('teams', $team->getId(), $team));
}
$events
->setParam('teamId', $team->getId())
->setParam('membershipId', $membership->getId())
->setPayload($response->output($membership, Response::MODEL_MEMBERSHIP))
;
->setPayload($response->output($membership, Response::MODEL_MEMBERSHIP));
$response->noContent();
});
@@ -1028,7 +1006,6 @@ App::get('/v1/teams/:teamId/logs')
->inject('locale')
->inject('geodb')
->action(function (string $teamId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -1041,13 +1018,13 @@ App::get('/v1/teams/:teamId/logs')
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject);
$resource = 'team/' . $team->getId();
$resource = 'team/'.$team->getId();
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$output = [];
foreach ($logs as $i => &$log) {
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
$log['userAgent'] = (! empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
$detector = new Detector($log['userAgent']);
$detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
@@ -1075,14 +1052,14 @@ App::get('/v1/teams/:teamId/logs')
'clientEngineVersion' => $client['clientEngineVersion'],
'deviceName' => $device['deviceName'],
'deviceBrand' => $device['deviceBrand'],
'deviceModel' => $device['deviceModel']
'deviceModel' => $device['deviceModel'],
]);
$record = $geodb->get($log['ip']);
if ($record) {
$output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
$output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
$output[$i]['countryCode'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
} else {
$output[$i]['countryCode'] = '--';
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
+54 -85
View File
@@ -2,43 +2,43 @@
use Appwrite\Auth\Auth;
use Appwrite\Auth\Validator\Password;
use Appwrite\Auth\Validator\PasswordDictionary;
use Appwrite\Auth\Validator\PasswordHistory;
use Appwrite\Auth\Validator\PersonalData;
use Appwrite\Auth\Validator\Phone;
use Appwrite\Detector\Detector;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Appwrite\Utopia\Database\Validator\CustomId;
use Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\Identities;
use Appwrite\Utopia\Database\Validator\Queries\Users;
use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Appwrite\Utopia\Response;
use MaxMind\Db\Reader;
use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Locale\Locale;
use Appwrite\Extend\Exception;
use Utopia\Database\Document;
use Utopia\Database\DateTime;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Validator\UID;
use Utopia\Database\Database;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Queries;
use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\UID;
use Utopia\Locale\Locale;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\WhiteList;
use Utopia\Validator\Text;
use Utopia\Validator\Boolean;
use MaxMind\Db\Reader;
use Utopia\Validator\Integer;
use Appwrite\Auth\Validator\PasswordHistory;
use Appwrite\Auth\Validator\PasswordDictionary;
use Appwrite\Auth\Validator\PersonalData;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
/** TODO: Remove function when we move to using utopia/platform */
function createUser(string $hash, mixed $hashOptions, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Document $project, Database $dbForProject, Event $events): Document
@@ -46,14 +46,14 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
$hashOptionsObject = (\is_string($hashOptions)) ? \json_decode($hashOptions, true) : $hashOptions; // Cast to JSON array
$passwordHistory = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
if (!empty($email)) {
if (! empty($email)) {
$email = \strtolower($email);
// Makes sure this email is not already used in another identity
$identityWithMatchingEmail = $dbForProject->findOne('identities', [
Query::equal('providerEmail', [$email]),
]);
if ($identityWithMatchingEmail !== false && !$identityWithMatchingEmail->isEmpty()) {
if ($identityWithMatchingEmail !== false && ! $identityWithMatchingEmail->isEmpty()) {
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
}
}
@@ -65,12 +65,12 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
if ($project->getAttribute('auths', [])['personalDataCheck'] ?? false) {
$personalDataValidator = new PersonalData($userId, $email, $name, $phone);
if (!$personalDataValidator->isValid($password)) {
if (! $personalDataValidator->isValid($password)) {
throw new Exception(Exception::USER_PASSWORD_PERSONAL_DATA);
}
}
$password = (!empty($password)) ? ($hash === 'plaintext' ? Auth::passwordHash($password, $hash, $hashOptionsObject) : $password) : null;
$password = (! empty($password)) ? ($hash === 'plaintext' ? Auth::passwordHash($password, $hash, $hashOptionsObject) : $password) : null;
$user = $dbForProject->createDocument('users', new Document([
'$id' => $userId,
'$permissions' => [
@@ -86,7 +86,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
'labels' => [],
'password' => $password,
'passwordHistory' => is_null($password) && $passwordHistory === 0 ? [] : [$password],
'passwordUpdate' => (!empty($password)) ? DateTime::now() : null,
'passwordUpdate' => (! empty($password)) ? DateTime::now() : null,
'hash' => $hash === 'plaintext' ? Auth::DEFAULT_ALGO : $hash,
'hashOptions' => $hash === 'plaintext' ? Auth::DEFAULT_ALGO_OPTIONS : $hashOptionsObject + ['type' => $hash],
'registration' => DateTime::now(),
@@ -96,7 +96,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $phone, $name])
'search' => implode(' ', [$userId, $email, $phone, $name]),
]));
} catch (Duplicate $th) {
throw new Exception(Exception::USER_ALREADY_EXISTS);
@@ -131,7 +131,6 @@ App::post('/v1/users')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, ?string $email, ?string $phone, ?string $password, string $name, Response $response, Document $project, Database $dbForProject, Event $events) {
$user = createUser('plaintext', '{}', $userId, $email, $password, $phone, $name, $project, $dbForProject, $events);
$response
@@ -255,8 +254,8 @@ App::post('/v1/users/sha')
->action(function (string $userId, string $email, string $password, string $passwordVersion, string $name, Response $response, Document $project, Database $dbForProject, Event $events) {
$options = '{}';
if (!empty($passwordVersion)) {
$options = '{"version":"' . $passwordVersion . '"}';
if (! empty($passwordVersion)) {
$options = '{"version":"'.$passwordVersion.'"}';
}
$user = createUser('sha', $options, $userId, $email, $password, null, $name, $project, $dbForProject, $events);
@@ -329,7 +328,7 @@ App::post('/v1/users/scrypt')
'costCpu' => $passwordCpu,
'costMemory' => $passwordMemory,
'costParallel' => $passwordParallel,
'length' => $passwordLength
'length' => $passwordLength,
];
$user = createUser('scrypt', \json_encode($options), $userId, $email, $password, null, $name, $project, $dbForProject, $events);
@@ -365,7 +364,7 @@ App::post('/v1/users/scrypt-modified')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, string $email, string $password, string $passwordSalt, string $passwordSaltSeparator, string $passwordSignerKey, string $name, Response $response, Document $project, Database $dbForProject, Event $events) {
$user = createUser('scryptMod', '{"signerKey":"' . $passwordSignerKey . '","saltSeparator":"' . $passwordSaltSeparator . '","salt":"' . $passwordSalt . '"}', $userId, $email, $password, null, $name, $project, $dbForProject, $events);
$user = createUser('scryptMod', '{"signerKey":"'.$passwordSignerKey.'","saltSeparator":"'.$passwordSaltSeparator.'","salt":"'.$passwordSalt.'"}', $userId, $email, $password, null, $name, $project, $dbForProject, $events);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -406,7 +405,7 @@ App::post('/v1/users/:userId/targets')
$target = $dbForProject->getDocument('targets', $targetId);
if (!$target->isEmpty()) {
if (! $target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
}
@@ -414,7 +413,7 @@ App::post('/v1/users/:userId/targets')
'$id' => $targetId,
// TO DO: what permissions should be given when created a target.
'$permissions' => [
Permission::read(Role::any())
Permission::read(Role::any()),
],
'providerId' => $providerId,
'providerInternalId' => $provider->getInternalId(),
@@ -442,15 +441,14 @@ App::get('/v1/users')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER_LIST)
->param('queries', [], new Users(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Users::ALLOWED_ATTRIBUTES), true)
->param('queries', [], new Users(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' queries are allowed, each '.APP_LIMIT_ARRAY_ELEMENT_SIZE.' characters long. You may filter on the following attributes: '.implode(', ', Users::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
->action(function (array $queries, string $search, Response $response, Database $dbForProject) {
$queries = Query::parseQueries($queries);
if (!empty($search)) {
if (! empty($search)) {
$queries[] = Query::search('search', $search);
}
@@ -492,7 +490,6 @@ App::get('/v1/users/:userId')
->inject('response')
->inject('dbForProject')
->action(function (string $userId, Response $response, Database $dbForProject) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -517,7 +514,6 @@ App::get('/v1/users/:userId/prefs')
->inject('response')
->inject('dbForProject')
->action(function (string $userId, Response $response, Database $dbForProject) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -545,7 +541,6 @@ App::get('/v1/users/:userId/targets/:targetId')
->inject('response')
->inject('dbForProject')
->action(function (string $userId, string $targetId, Response $response, Database $dbForProject) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -577,7 +572,6 @@ App::get('/v1/users/:userId/sessions')
->inject('dbForProject')
->inject('locale')
->action(function (string $userId, Response $response, Database $dbForProject, Locale $locale) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -588,8 +582,7 @@ App::get('/v1/users/:userId/sessions')
foreach ($sessions as $key => $session) {
/** @var Document $session */
$countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
$countryName = $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
$session->setAttribute('countryName', $countryName);
$session->setAttribute('current', false);
@@ -617,7 +610,6 @@ App::get('/v1/users/:userId/memberships')
->inject('response')
->inject('dbForProject')
->action(function (string $userId, Response $response, Database $dbForProject) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -659,7 +651,6 @@ App::get('/v1/users/:userId/logs')
->inject('locale')
->inject('geodb')
->action(function (string $userId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -678,7 +669,7 @@ App::get('/v1/users/:userId/logs')
$output = [];
foreach ($logs as $i => &$log) {
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
$log['userAgent'] = (! empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
$detector = new Detector($log['userAgent']);
$detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
@@ -702,14 +693,14 @@ App::get('/v1/users/:userId/logs')
'clientEngineVersion' => $client['clientEngineVersion'],
'deviceName' => $device['deviceName'],
'deviceBrand' => $device['deviceBrand'],
'deviceModel' => $device['deviceModel']
'deviceModel' => $device['deviceModel'],
]);
$record = $geodb->get($log['ip']);
if ($record) {
$output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
$output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
$output[$i]['countryCode'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
} else {
$output[$i]['countryCode'] = '--';
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
@@ -737,7 +728,6 @@ App::get('/v1/users/:userId/targets')
->inject('response')
->inject('dbForProject')
->action(function (string $userId, Response $response, Database $dbForProject) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -763,15 +753,14 @@ App::get('/v1/users/identities')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_IDENTITY_LIST)
->param('queries', [], new Identities(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Identities::ALLOWED_ATTRIBUTES), true)
->param('queries', [], new Identities(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of '.APP_LIMIT_ARRAY_PARAMS_SIZE.' queries are allowed, each '.APP_LIMIT_ARRAY_ELEMENT_SIZE.' characters long. You may filter on the following attributes: '.implode(', ', Identities::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
->action(function (array $queries, string $search, Response $response, Database $dbForProject) {
$queries = Query::parseQueries($queries);
if (!empty($search)) {
if (! empty($search)) {
$queries[] = Query::search('search', $search);
}
@@ -819,7 +808,6 @@ App::patch('/v1/users/:userId/status')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, bool $status, Response $response, Database $dbForProject, Event $events) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -856,7 +844,6 @@ App::put('/v1/users/:userId/labels')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, array $labels, Response $response, Database $dbForProject, Event $events) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -893,7 +880,6 @@ App::patch('/v1/users/:userId/verification')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, bool $emailVerification, Response $response, Database $dbForProject, Event $events) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -928,7 +914,6 @@ App::patch('/v1/users/:userId/verification/phone')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, bool $phoneVerification, Response $response, Database $dbForProject, Event $events) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -964,7 +949,6 @@ App::patch('/v1/users/:userId/name')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, string $name, Response $response, Database $dbForProject, Event $events) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -1002,7 +986,6 @@ App::patch('/v1/users/:userId/password')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, string $password, Response $response, Document $project, Database $dbForProject, Event $events) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -1011,7 +994,7 @@ App::patch('/v1/users/:userId/password')
if ($project->getAttribute('auths', [])['personalDataCheck'] ?? false) {
$personalDataValidator = new PersonalData($userId, $user->getAttribute('email'), $user->getAttribute('name'), $user->getAttribute('phone'));
if (!$personalDataValidator->isValid($password)) {
if (! $personalDataValidator->isValid($password)) {
throw new Exception(Exception::USER_PASSWORD_PERSONAL_DATA);
}
}
@@ -1022,7 +1005,7 @@ App::patch('/v1/users/:userId/password')
$history = $user->getAttribute('passwordHistory', []);
if ($historyLimit > 0) {
$validator = new PasswordHistory($history, $user->getAttribute('hash'), $user->getAttribute('hashOptions'));
if (!$validator->isValid($password)) {
if (! $validator->isValid($password)) {
throw new Exception(Exception::USER_PASSWORD_RECENTLY_USED);
}
@@ -1065,7 +1048,6 @@ App::patch('/v1/users/:userId/email')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, string $email, Response $response, Database $dbForProject, Event $events) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -1079,15 +1061,13 @@ App::patch('/v1/users/:userId/email')
Query::equal('providerEmail', [$email]),
Query::notEqual('userId', $user->getId()),
]);
if ($identityWithMatchingEmail !== false && !$identityWithMatchingEmail->isEmpty()) {
if ($identityWithMatchingEmail !== false && ! $identityWithMatchingEmail->isEmpty()) {
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
}
$user
->setAttribute('email', $email)
->setAttribute('emailVerification', false)
;
->setAttribute('emailVerification', false);
try {
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
@@ -1120,7 +1100,6 @@ App::patch('/v1/users/:userId/phone')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, string $number, Response $response, Database $dbForProject, Event $events) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -1129,8 +1108,7 @@ App::patch('/v1/users/:userId/phone')
$user
->setAttribute('phone', $number)
->setAttribute('phoneVerification', false)
;
->setAttribute('phoneVerification', false);
try {
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
@@ -1164,7 +1142,6 @@ App::patch('/v1/users/:userId/verification')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, bool $emailVerification, Response $response, Database $dbForProject, Event $events) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -1196,7 +1173,6 @@ App::patch('/v1/users/:userId/prefs')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, array $prefs, Response $response, Database $dbForProject, Event $events) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -1230,7 +1206,6 @@ App::patch('/v1/users/:userId/targets/:targetId/identifier')
->inject('dbForProject')
->inject('events')
->action(function (string $targetId, string $userId, string $identifier, Response $response, Database $dbForProject, Event $events) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -1276,7 +1251,6 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, string $sessionId, Response $response, Database $dbForProject, Event $events) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -1318,7 +1292,6 @@ App::delete('/v1/users/:userId/sessions')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, Response $response, Database $dbForProject, Event $events) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -1361,7 +1334,6 @@ App::delete('/v1/users/:userId')
->inject('events')
->inject('deletes')
->action(function (string $userId, Response $response, Database $dbForProject, Event $events, Delete $deletes) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -1402,7 +1374,6 @@ App::delete('/v1/users/:userId/targets/:targetId')
->inject('dbForProject')
->inject('events')
->action(function (string $targetId, string $userId, Response $response, Database $dbForProject, Event $events) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -1449,7 +1420,6 @@ App::delete('/v1/users/identities/:identityId')
->inject('events')
->inject('deletes')
->action(function (string $identityId, Response $response, Database $dbForProject, Event $events, Delete $deletes) {
$identity = $dbForProject->getDocument('identities', $identityId);
if ($identity->isEmpty()) {
@@ -1476,7 +1446,6 @@ App::get('/v1/users/usage')
->inject('dbForProject')
->inject('register')
->action(function (string $range, Response $response, Database $dbForProject) {
$periods = Config::getParam('usage', []);
$stats = $usage = [];
$days = $periods[$range];
@@ -1509,22 +1478,22 @@ App::get('/v1/users/usage')
'1d' => 'Y-m-d\T00:00:00.000P',
};
foreach ($metrics as $metric) {
$usage[$metric] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric][] = [
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
foreach ($metrics as $metric) {
$usage[$metric] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric][] = [
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
}
}
}
$response->dynamic(new Document([
'range' => $range,
'usersTotal' => $usage[$metrics[0]],
'usersTotal' => $usage[$metrics[0]],
'sessionsTotal' => $usage[$metrics[1]],
]), Response::MODEL_USAGE_USERS);
});
+76 -81
View File
@@ -1,38 +1,38 @@
<?php
require_once __DIR__ . '/../init.php';
require_once __DIR__.'/../init.php';
use Utopia\App;
use Utopia\Database\Helpers\Role;
use Utopia\Locale\Locale;
use Utopia\Logger\Logger;
use Utopia\Logger\Log;
use Utopia\Logger\Log\User;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\View;
use Appwrite\Extend\Exception as AppwriteException;
use Utopia\Config\Config;
use Utopia\Domains\Domain;
use Appwrite\Auth\Auth;
use Appwrite\Event\Certificate;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Network\Validator\Origin;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Request\Filters\V12 as RequestV12;
use Appwrite\Utopia\Request\Filters\V13 as RequestV13;
use Appwrite\Utopia\Request\Filters\V14 as RequestV14;
use Appwrite\Utopia\Request\Filters\V15 as RequestV15;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Filters\V11 as ResponseV11;
use Appwrite\Utopia\Response\Filters\V12 as ResponseV12;
use Appwrite\Utopia\Response\Filters\V13 as ResponseV13;
use Appwrite\Utopia\Response\Filters\V14 as ResponseV14;
use Appwrite\Utopia\Response\Filters\V15 as ResponseV15;
use Appwrite\Utopia\View;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Domains\Domain;
use Utopia\Locale\Locale;
use Utopia\Logger\Log;
use Utopia\Logger\Log\User;
use Utopia\Logger\Logger;
use Utopia\Validator\Hostname;
use Appwrite\Utopia\Request\Filters\V12 as RequestV12;
use Appwrite\Utopia\Request\Filters\V13 as RequestV13;
use Appwrite\Utopia\Request\Filters\V14 as RequestV14;
use Appwrite\Utopia\Request\Filters\V15 as RequestV15;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
@@ -84,12 +84,12 @@ App::init()
$domain = $request->getHostname();
$domains = Config::getParam('domains', []);
if (!array_key_exists($domain, $domains)) {
$domain = new Domain(!empty($domain) ? $domain : '');
if (! array_key_exists($domain, $domains)) {
$domain = new Domain(! empty($domain) ? $domain : '');
if (empty($domain->get()) || !$domain->isKnown() || $domain->isTest()) {
if (empty($domain->get()) || ! $domain->isKnown() || $domain->isTest()) {
$domains[$domain->get()] = false;
Console::warning($domain->get() . ' is not a publicly accessible domain. Skipping SSL certificate generation.');
Console::warning($domain->get().' is not a publicly accessible domain. Skipping SSL certificate generation.');
} elseif (str_starts_with($request->getURI(), '/.well-known/acme-challenge')) {
Console::warning('Skipping SSL certificates generation on ACME challenge.');
} else {
@@ -97,7 +97,7 @@ App::init()
$envDomain = App::getEnv('_APP_DOMAIN', '');
$mainDomain = null;
if (!empty($envDomain) && $envDomain !== 'localhost') {
if (! empty($envDomain) && $envDomain !== 'localhost') {
$mainDomain = $envDomain;
} else {
$domainDocument = $dbForConsole->findOne('domains', [Query::orderAsc('_id')]);
@@ -105,13 +105,13 @@ App::init()
}
if ($mainDomain !== $domain->get()) {
Console::warning($domain->get() . ' is not a main domain. Skipping SSL certificate generation.');
Console::warning($domain->get().' is not a main domain. Skipping SSL certificate generation.');
} else {
$domainDocument = $dbForConsole->findOne('domains', [
Query::equal('domain', [$domain->get()])
Query::equal('domain', [$domain->get()]),
]);
if (!$domainDocument) {
if (! $domainDocument) {
$domainDocument = new Document([
'domain' => $domain->get(),
'tld' => $domain->getSuffix(),
@@ -122,7 +122,7 @@ App::init()
$domainDocument = $dbForConsole->createDocument('domains', $domainDocument);
Console::info('Issuing a TLS certificate for the main domain (' . $domain->get() . ') in a few seconds...');
Console::info('Issuing a TLS certificate for the main domain ('.$domain->get().') in a few seconds...');
(new Certificate())
->setDomain($domainDocument)
@@ -145,7 +145,7 @@ App::init()
throw new AppwriteException(AppwriteException::PROJECT_NOT_FOUND);
}
if (!empty($route->getLabel('sdk.auth', [])) && $project->isEmpty() && ($route->getLabel('scope', '') !== 'public')) {
if (! empty($route->getLabel('sdk.auth', [])) && $project->isEmpty() && ($route->getLabel('scope', '') !== 'public')) {
throw new AppwriteException(AppwriteException::PROJECT_UNKNOWN);
}
@@ -160,14 +160,14 @@ App::init()
$refDomainOrigin = $origin;
}
$refDomain = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $refDomainOrigin . (!empty($port) ? ':' . $port : '');
$refDomain = (! empty($protocol) ? $protocol : $request->getProtocol()).'://'.$refDomainOrigin.(! empty($port) ? ':'.$port : '');
$refDomain = (!$route->getLabel('origin', false)) // This route is publicly accessible
$refDomain = (! $route->getLabel('origin', false)) // This route is publicly accessible
? $refDomain
: (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $origin . (!empty($port) ? ':' . $port : '');
: (! empty($protocol) ? $protocol : $request->getProtocol()).'://'.$origin.(! empty($port) ? ':'.$port : '');
$selfDomain = new Domain($request->getHostname());
$endDomain = new Domain((string)$origin);
$endDomain = new Domain((string) $origin);
Config::setParam(
'domainVerification',
@@ -175,7 +175,7 @@ App::init()
$endDomain->getRegisterable() !== ''
);
$isLocalHost = $request->getHostname() === 'localhost' || $request->getHostname() === 'localhost:' . $request->getPort();
$isLocalHost = $request->getHostname() === 'localhost' || $request->getHostname() === 'localhost:'.$request->getPort();
$isIpAddress = filter_var($request->getHostname(), FILTER_VALIDATE_IP) !== false;
$isConsoleProject = $project->getAttribute('$id', '') === 'console';
@@ -186,8 +186,8 @@ App::init()
$isLocalHost || $isIpAddress
? null
: ($isConsoleProject && $isConsoleRootSession
? '.' . $selfDomain->getRegisterable()
: '.' . $request->getHostname()
? '.'.$selfDomain->getRegisterable()
: '.'.$request->getHostname()
)
);
@@ -231,12 +231,12 @@ App::init()
throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP.');
}
return $response->redirect('https://' . $request->getHostname() . $request->getURI());
return $response->redirect('https://'.$request->getHostname().$request->getURI());
}
}
if ($request->getProtocol() === 'https') {
$response->addHeader('Strict-Transport-Security', 'max-age=' . (60 * 60 * 24 * 126)); // 126 days
$response->addHeader('Strict-Transport-Security', 'max-age='.(60 * 60 * 24 * 126)); // 126 days
}
$response
@@ -246,8 +246,7 @@ App::init()
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma')
->addHeader('Access-Control-Expose-Headers', 'X-Fallback-Cookies')
->addHeader('Access-Control-Allow-Origin', $refDomain)
->addHeader('Access-Control-Allow-Credentials', 'true')
;
->addHeader('Access-Control-Allow-Credentials', 'true');
/*
* Validate Client Domain - Check to avoid CSRF attack
@@ -258,7 +257,7 @@ App::init()
$originValidator = new Origin(\array_merge($project->getAttribute('platforms', []), $console->getAttribute('platforms', [])));
if (
!$originValidator->isValid($origin)
! $originValidator->isValid($origin)
&& \in_array($request->getMethod(), [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH, Request::METHOD_DELETE])
&& $route->getLabel('origin', false) !== '*'
&& empty($request->getHeader('x-appwrite-key', ''))
@@ -298,7 +297,7 @@ App::init()
$authKey = $request->getHeader('x-appwrite-key', '');
if (!empty($authKey)) { // API Key authentication
if (! empty($authKey)) { // API Key authentication
// Check if given key match project API keys
$key = $project->find('secret', $authKey, 'keys');
@@ -310,7 +309,7 @@ App::init()
$user = new Document([
'$id' => '',
'status' => true,
'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(),
'email' => 'app.'.$project->getId().'@service.'.$request->getHostname(),
'password' => '',
'name' => $project->getAttribute('name', 'Untitled'),
]);
@@ -319,8 +318,8 @@ App::init()
$scopes = \array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', []));
$expire = $key->getAttribute('expire');
if (!empty($expire) && $expire < DateTime::formatTz(DateTime::now())) {
throw new AppwriteException(AppwriteException:: PROJECT_KEY_EXPIRED);
if (! empty($expire) && $expire < DateTime::formatTz(DateTime::now())) {
throw new AppwriteException(AppwriteException::PROJECT_KEY_EXPIRED);
}
Authorization::setRole(Auth::USER_ROLE_APPS);
@@ -337,7 +336,7 @@ App::init()
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
if ($sdkValidator->isValid($sdk)) {
$sdks = $key->getAttribute('sdks', []);
if (!in_array($sdk, $sdks)) {
if (! in_array($sdk, $sdks)) {
array_push($sdks, $sdk);
$key->setAttribute('sdks', $sdks);
@@ -357,22 +356,22 @@ App::init()
}
$service = $route->getLabel('sdk.namespace', '');
if (!empty($service)) {
if (! empty($service)) {
if (
array_key_exists($service, $project->getAttribute('services', []))
&& !$project->getAttribute('services', [])[$service]
&& !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles()))
&& ! $project->getAttribute('services', [])[$service]
&& ! (Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles()))
) {
throw new AppwriteException(AppwriteException::GENERAL_SERVICE_DISABLED);
}
}
if (!\in_array($scope, $scopes)) {
if (! \in_array($scope, $scopes)) {
if ($project->isEmpty()) { // Check if permission is denied because project is missing
throw new AppwriteException(AppwriteException::PROJECT_NOT_FOUND);
}
throw new AppwriteException(AppwriteException::GENERAL_UNAUTHORIZED_SCOPE, $user->getAttribute('email', 'User') . ' (role: ' . \strtolower($roles[$role]['label']) . ') missing scope (' . $scope . ')');
throw new AppwriteException(AppwriteException::GENERAL_UNAUTHORIZED_SCOPE, $user->getAttribute('email', 'User').' (role: '.\strtolower($roles[$role]['label']).') missing scope ('.$scope.')');
}
if (false === $user->getAttribute('status')) { // Account is blocked
@@ -388,7 +387,6 @@ App::options()
->inject('request')
->inject('response')
->action(function (Request $request, Response $response) {
$origin = $request->getOrigin();
$response
@@ -410,7 +408,6 @@ App::error()
->inject('logger')
->inject('loggerBreadcrumbs')
->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, array $loggerBreadcrumbs) {
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
$route = $utopia->match($request);
@@ -425,11 +422,11 @@ App::error()
$log = new Utopia\Logger\Log();
if (isset($user) && !$user->isEmpty()) {
if (isset($user) && ! $user->isEmpty()) {
$log->setUser(new User($user->getId()));
}
$log->setNamespace("http");
$log->setNamespace('http');
$log->setServer(\gethostname());
$log->setVersion($version);
$log->setType(Log::TYPE_ERROR);
@@ -442,7 +439,7 @@ App::error()
$log->addTag('code', $error->getCode());
$log->addTag('projectId', $project->getId());
$log->addTag('hostname', $request->getHostname());
$log->addTag('locale', (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', '')));
$log->addTag('locale', (string) $request->getParam('locale', $request->getHeader('x-appwrite-locale', '')));
$log->addExtra('file', $error->getFile());
$log->addExtra('line', $error->getLine());
@@ -450,7 +447,7 @@ App::error()
$log->addExtra('detailedTrace', $error->getTrace());
$log->addExtra('roles', Authorization::getRoles());
$action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD");
$action = $route->getLabel('sdk.namespace', 'UNKNOWN_NAMESPACE').'.'.$route->getLabel('sdk.method', 'UNKNOWN_METHOD');
$log->setAction($action);
$isProduction = App::getEnv('_APP_ENV', 'development') === 'production';
@@ -461,7 +458,7 @@ App::error()
}
$responseCode = $logger->addLog($log);
Console::info('Log pushed with status code: ' . $responseCode);
Console::info('Log pushed with status code: '.$responseCode);
}
}
@@ -472,17 +469,17 @@ App::error()
$trace = $error->getTrace();
if (php_sapi_name() === 'cli') {
Console::error('[Error] Timestamp: ' . date('c', time()));
Console::error('[Error] Timestamp: '.date('c', time()));
if ($route) {
Console::error('[Error] Method: ' . $route->getMethod());
Console::error('[Error] URL: ' . $route->getPath());
Console::error('[Error] Method: '.$route->getMethod());
Console::error('[Error] URL: '.$route->getPath());
}
Console::error('[Error] Type: ' . get_class($error));
Console::error('[Error] Message: ' . $message);
Console::error('[Error] File: ' . $file);
Console::error('[Error] Line: ' . $line);
Console::error('[Error] Type: '.get_class($error));
Console::error('[Error] Message: '.$message);
Console::error('[Error] File: '.$file);
Console::error('[Error] Line: '.$line);
}
/** Handle Utopia Errors */
@@ -503,7 +500,7 @@ App::error()
}
/** Wrap all exceptions inside Appwrite\Extend\Exception */
if (!($error instanceof AppwriteException)) {
if (! ($error instanceof AppwriteException)) {
$error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error);
}
@@ -548,8 +545,7 @@ App::error()
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
->addHeader('Expires', '0')
->addHeader('Pragma', 'no-cache')
->setStatusCode($code)
;
->setStatusCode($code);
$template = ($route) ? $route->getLabel('error', null) : null;
@@ -557,15 +553,14 @@ App::error()
$layout = new View($template);
$layout
->setParam('title', $project->getAttribute('name') . ' - Error')
->setParam('title', $project->getAttribute('name').' - Error')
->setParam('development', App::isDevelopment())
->setParam('projectName', $project->getAttribute('name'))
->setParam('projectURL', $project->getAttribute('url'))
->setParam('message', $error->getMessage())
->setParam('type', $type)
->setParam('code', $code)
->setParam('trace', $trace)
;
->setParam('trace', $trace);
$response->html($layout->render());
}
@@ -582,7 +577,7 @@ App::get('/robots.txt')
->label('docs', false)
->inject('response')
->action(function (Response $response) {
$template = new View(__DIR__ . '/../views/general/robots.phtml');
$template = new View(__DIR__.'/../views/general/robots.phtml');
$response->text($template->render(false));
});
@@ -592,7 +587,7 @@ App::get('/humans.txt')
->label('docs', false)
->inject('response')
->action(function (Response $response) {
$template = new View(__DIR__ . '/../views/general/humans.phtml');
$template = new View(__DIR__.'/../views/general/humans.phtml');
$response->text($template->render(false));
});
@@ -611,43 +606,43 @@ App::get('/.well-known/acme-challenge/*')
...Text::ALPHABET_LOWER,
...Text::ALPHABET_UPPER,
'-',
'_'
'_',
]);
if (!$validator->isValid($token) || \count($uriChunks) !== 4) {
if (! $validator->isValid($token) || \count($uriChunks) !== 4) {
throw new AppwriteException(AppwriteException::GENERAL_ARGUMENT_INVALID, 'Invalid challenge token.');
}
$base = \realpath(APP_STORAGE_CERTIFICATES);
$absolute = \realpath($base . '/.well-known/acme-challenge/' . $token);
$absolute = \realpath($base.'/.well-known/acme-challenge/'.$token);
if (!$base) {
if (! $base) {
throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Storage error');
}
if (!$absolute) {
if (! $absolute) {
throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND, 'Unknown path');
}
if (!\substr($absolute, 0, \strlen($base)) === $base) {
if (! \substr($absolute, 0, \strlen($base)) === $base) {
throw new AppwriteException(AppwriteException::GENERAL_UNAUTHORIZED_SCOPE, 'Invalid path');
}
if (!\file_exists($absolute)) {
if (! \file_exists($absolute)) {
throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND, 'Unknown path');
}
$content = @\file_get_contents($absolute);
if (!$content) {
if (! $content) {
throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Failed to get contents');
}
$response->text($content);
});
include_once __DIR__ . '/shared/api.php';
include_once __DIR__ . '/shared/api/auth.php';
include_once __DIR__.'/shared/api.php';
include_once __DIR__.'/shared/api/auth.php';
foreach (Config::getParam('services', []) as $service) {
include_once $service['controller'];
+27 -42
View File
@@ -3,18 +3,18 @@
global $utopia, $request, $response;
use Appwrite\Extend\Exception;
use Utopia\Database\Document;
use Utopia\Validator\Host;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Integer;
use Utopia\Validator\Text;
use Utopia\Storage\Validator\File;
use Utopia\Validator\WhiteList;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Storage\Validator\File;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Host;
use Utopia\Validator\Integer;
use Utopia\Validator\Nullable;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
App::get('/v1/mock/tests/foo')
->desc('Get Foo')
@@ -220,9 +220,9 @@ App::get('/v1/mock/tests/general/headers')
'x-sdk-version' => $request->getHeader('x-sdk-version'),
];
$res = array_map(function ($key, $value) {
return $key . ': ' . $value;
return $key.': '.$value;
}, array_keys($res), $res);
$res = implode("; ", $res);
$res = implode('; ', $res);
$response->dynamic(new Document(['result' => $res]), Response::MODEL_MOCK);
});
@@ -241,14 +241,12 @@ App::get('/v1/mock/tests/general/download')
->label('sdk.mock', true)
->inject('response')
->action(function (Response $response) {
$response
->setContentType('text/plain')
->addHeader('Content-Disposition', 'attachment; filename="test.txt"')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->addHeader('X-Peak', \memory_get_peak_usage())
->send("GET:/v1/mock/tests/general/download:passed")
;
->send('GET:/v1/mock/tests/general/download:passed');
});
App::post('/v1/mock/tests/general/upload')
@@ -271,14 +269,13 @@ App::post('/v1/mock/tests/general/upload')
->inject('request')
->inject('response')
->action(function (string $x, int $y, array $z, mixed $file, Request $request, Response $response) {
$file = $request->getFiles('file');
$contentRange = $request->getHeader('content-range');
$chunkSize = 5 * 1024 * 1024; // 5MB
if (!empty($contentRange)) {
if (! empty($contentRange)) {
$start = $request->getContentRangeStart();
$end = $request->getContentRangeEnd();
$size = $request->getContentRangeSize();
@@ -293,7 +290,7 @@ App::post('/v1/mock/tests/general/upload')
throw new Exception(Exception::GENERAL_MOCK, 'Invalid content-range header');
}
if ($start === 0 && !empty($id)) {
if ($start === 0 && ! empty($id)) {
throw new Exception(Exception::GENERAL_MOCK, 'First chunked request cannot have id header');
}
@@ -317,7 +314,7 @@ App::post('/v1/mock/tests/general/upload')
$response->json([
'$id' => ID::custom('newfileid'),
'chunksTotal' => (int) ceil($size / ($end + 1 - $start)),
'chunksUploaded' => ceil($start / $chunkSize) + 1
'chunksUploaded' => ceil($start / $chunkSize) + 1,
]);
}
} else {
@@ -330,7 +327,7 @@ App::post('/v1/mock/tests/general/upload')
}
if ($file['size'] !== 38756) {
throw new Exception(Exception::GENERAL_MOCK, 'Wrong file size');
throw new Exception(Exception::GENERAL_MOCK, 'Wrong file size');
}
if (\md5(\file_get_contents($file['tmp_name'])) !== 'd80e7e6999a3eb2ae0d631a96fe135a4') {
@@ -353,7 +350,6 @@ App::get('/v1/mock/tests/general/redirect')
->label('sdk.mock', true)
->inject('response')
->action(function (Response $response) {
$response->redirect('/v1/mock/tests/general/redirect/done');
});
@@ -387,7 +383,6 @@ App::get('/v1/mock/tests/general/set-cookie')
->inject('response')
->inject('request')
->action(function (Response $response, Request $request) {
$response->addCookie('cookieName', 'cookieValue', \time() + 31536000, '/', $request->getHostname(), true, true);
});
@@ -405,7 +400,6 @@ App::get('/v1/mock/tests/general/get-cookie')
->label('sdk.mock', true)
->inject('request')
->action(function (Request $request) {
if ($request->getCookie('cookieName', '') !== 'cookieValue') {
throw new Exception(Exception::GENERAL_MOCK, 'Missing cookie value');
}
@@ -424,7 +418,6 @@ App::get('/v1/mock/tests/general/empty')
->label('sdk.mock', true)
->inject('response')
->action(function (Response $response) {
$response->noContent();
});
@@ -465,9 +458,9 @@ App::get('/v1/mock/tests/general/headers')
'x-sdk-version' => $request->getHeader('x-sdk-version'),
];
$res = array_map(function ($key, $value) {
return $key . ': ' . $value;
return $key.': '.$value;
}, array_keys($res), $res);
$res = implode("; ", $res);
$res = implode('; ', $res);
$response->dynamic(new Document(['result' => $res]), Response::MODEL_MOCK);
});
@@ -518,11 +511,9 @@ App::get('/v1/mock/tests/general/502-error')
->label('sdk.mock', true)
->inject('response')
->action(function (Response $response) {
$response
->setStatusCode(502)
->text('This is a text error')
;
->text('This is a text error');
});
App::get('/v1/mock/tests/general/oauth2')
@@ -537,8 +528,7 @@ App::get('/v1/mock/tests/general/oauth2')
->param('state', '', new Text(1024), 'OAuth2 state.')
->inject('response')
->action(function (string $client_id, string $redirectURI, string $scope, string $state, Response $response) {
$response->redirect($redirectURI . '?' . \http_build_query(['code' => 'abcdef', 'state' => $state]));
$response->redirect($redirectURI.'?'.\http_build_query(['code' => 'abcdef', 'state' => $state]));
});
App::get('/v1/mock/tests/general/oauth2/token')
@@ -555,7 +545,6 @@ App::get('/v1/mock/tests/general/oauth2/token')
->param('refresh_token', '', new Text(100), 'OAuth2 refresh token.', true)
->inject('response')
->action(function (string $client_id, string $client_secret, string $grantType, string $redirectURI, string $code, string $refreshToken, Response $response) {
if ($client_id != '1') {
throw new Exception(Exception::GENERAL_MOCK, 'Invalid client ID');
}
@@ -567,7 +556,7 @@ App::get('/v1/mock/tests/general/oauth2/token')
$responseJson = [
'access_token' => '123456',
'refresh_token' => 'tuvwxyz',
'expires_in' => 14400
'expires_in' => 14400,
];
if ($grantType === 'authorization_code') {
@@ -595,7 +584,6 @@ App::get('/v1/mock/tests/general/oauth2/user')
->param('token', '', new Text(100), 'OAuth2 Access Token.')
->inject('response')
->action(function (string $token, Response $response) {
if ($token != '123456') {
throw new Exception(Exception::GENERAL_MOCK, 'Invalid token');
}
@@ -614,7 +602,6 @@ App::get('/v1/mock/tests/general/oauth2/success')
->label('docs', false)
->inject('response')
->action(function (Response $response) {
$response->json([
'result' => 'success',
]);
@@ -627,7 +614,6 @@ App::get('/v1/mock/tests/general/oauth2/failure')
->label('docs', false)
->inject('response')
->action(function (Response $response) {
$response
->setStatusCode(Response::STATUS_CODE_BAD_REQUEST)
->json([
@@ -641,23 +627,22 @@ App::shutdown()
->inject('response')
->inject('request')
->action(function (App $utopia, Response $response, Request $request) {
$result = [];
$route = $utopia->match($request);
$path = APP_STORAGE_CACHE . '/tests.json';
$tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : [];
$route = $utopia->match($request);
$path = APP_STORAGE_CACHE.'/tests.json';
$tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : [];
if (!\is_array($tests)) {
if (! \is_array($tests)) {
throw new Exception(Exception::GENERAL_MOCK, 'Failed to read results', 500);
}
$result[$route->getMethod() . ':' . $route->getPath()] = true;
$result[$route->getMethod().':'.$route->getPath()] = true;
$tests = \array_merge($tests, $result);
if (!\file_put_contents($path, \json_encode($tests), LOCK_EX)) {
if (! \file_put_contents($path, \json_encode($tests), LOCK_EX)) {
throw new Exception(Exception::GENERAL_MOCK, 'Failed to save results', 500);
}
$response->dynamic(new Document(['result' => $route->getMethod() . ':' . $route->getPath() . ':passed']), Response::MODEL_MOCK);
$response->dynamic(new Document(['result' => $route->getMethod().':'.$route->getPath().':passed']), Response::MODEL_MOCK);
});
+50 -57
View File
@@ -7,14 +7,14 @@ use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Func;
use Appwrite\Event\Mail;
use Appwrite\Extend\Exception;
use Appwrite\Event\Usage;
use Appwrite\Extend\Exception;
use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Request;
use Utopia\App;
use Appwrite\Utopia\Response;
use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\App;
use Utopia\Cache\Adapter\Filesystem;
use Utopia\Cache\Cache;
use Utopia\Database\Database;
@@ -36,7 +36,7 @@ $parseLabel = function (string $label, array $responsePayload, array $requestPar
$replace = $parts[1] ?? '';
$params = match ($namespace) {
'user' => (array)$user,
'user' => (array) $user,
'request' => $requestParams,
default => $responsePayload,
};
@@ -45,11 +45,11 @@ $parseLabel = function (string $label, array $responsePayload, array $requestPar
$label = \str_replace($find, $params[$replace], $label);
}
}
return $label;
};
$databaseListener = function (string $event, Document $document, Document $project, Usage $queueForUsage, Database $dbForProject) {
$value = 1;
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$value = -1;
@@ -81,13 +81,13 @@ $databaseListener = function (string $event, Document $document, Document $proje
->addReduce($document);
}
break;
case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections
case str_starts_with($document->getCollection(), 'database_') && ! str_contains($document->getCollection(), 'collection'): //collections
$parts = explode('_', $document->getCollection());
$databaseInternalId = $parts[1] ?? 0;
$queueForUsage
->addMetric(METRIC_COLLECTIONS, $value) // per project
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value) // per database
;
;
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$queueForUsage
@@ -96,7 +96,7 @@ $databaseListener = function (string $event, Document $document, Document $proje
break;
case str_starts_with($document->getCollection(), 'database_') && str_contains($document->getCollection(), '_collection_'): //documents
$parts = explode('_', $document->getCollection());
$databaseInternalId = $parts[1] ?? 0;
$databaseInternalId = $parts[1] ?? 0;
$collectionInternalId = $parts[3] ?? 0;
$queueForUsage
->addMetric(METRIC_DOCUMENTS, $value) // per project
@@ -132,13 +132,13 @@ $databaseListener = function (string $event, Document $document, Document $proje
->addMetric(METRIC_DEPLOYMENTS, $value) // per project
->addMetric(METRIC_DEPLOYMENTS_STORAGE, $document->getAttribute('size') * $value) // per project
->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_FUNCTION_ID_DEPLOYMENTS), $value)// per function
->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE), $document->getAttribute('size') * $value);// per function
->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE), $document->getAttribute('size') * $value); // per function
break;
case $document->getCollection() === 'executions':
$queueForUsage
->addMetric(METRIC_EXECUTIONS, $value) // per project
->addMetric(str_replace('{functionInternalId}', $document->getAttribute('functionInternalId'), METRIC_FUNCTION_ID_EXECUTIONS), $value);// per function
->addMetric(str_replace('{functionInternalId}', $document->getAttribute('functionInternalId'), METRIC_FUNCTION_ID_EXECUTIONS), $value); // per function
break;
default:
break;
@@ -161,7 +161,6 @@ App::init()
->inject('mode')
->inject('mails')
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $events, Audit $audits, Delete $deletes, EventDatabase $database, Database $dbForProject, Usage $queueForUsage, string $mode, Mail $mails) use ($databaseListener) {
$route = $utopia->match($request);
if ($project->isEmpty() && $route->getLabel('abuse-limit', 0) > 0) { // Abuse limit requires an active project scope
@@ -174,7 +173,7 @@ App::init()
$abuseKeyLabel = $route->getLabel('abuse-key', 'url:{url},ip:{ip}');
$timeLimitArray = [];
$abuseKeyLabel = (!is_array($abuseKeyLabel)) ? [$abuseKeyLabel] : $abuseKeyLabel;
$abuseKeyLabel = (! is_array($abuseKeyLabel)) ? [$abuseKeyLabel] : $abuseKeyLabel;
foreach ($abuseKeyLabel as $abuseKey) {
$timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject);
@@ -182,7 +181,7 @@ App::init()
->setParam('{userId}', $user->getId())
->setParam('{userAgent}', $request->getUserAgent(''))
->setParam('{ip}', $request->getIP())
->setParam('{url}', $request->getHostname() . $route->getPath())
->setParam('{url}', $request->getHostname().$route->getPath())
->setParam('{method}', $request->getMethod());
$timeLimitArray[] = $timeLimit;
}
@@ -195,8 +194,8 @@ App::init()
foreach ($timeLimitArray as $timeLimit) {
foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys
if (!empty($value)) {
$timeLimit->setParam('{param-' . $key . '}', (\is_array($value)) ? \json_encode($value) : $value);
if (! empty($value)) {
$timeLimit->setParam('{param-'.$key.'}', (\is_array($value)) ? \json_encode($value) : $value);
}
}
@@ -210,16 +209,15 @@ App::init()
$response
->addHeader('X-RateLimit-Limit', $limit)
->addHeader('X-RateLimit-Remaining', $remaining)
->addHeader('X-RateLimit-Reset', $time)
;
->addHeader('X-RateLimit-Reset', $time);
}
$enabled = App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled';
if (
$enabled // Abuse is enabled
&& !$isAppUser // User is not API key
&& !$isPrivilegedUser // User is not an admin
&& ! $isAppUser // User is not API key
&& ! $isPrivilegedUser // User is not an admin
&& $abuse->check() // Route is rate-limited
) {
throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED);
@@ -243,7 +241,7 @@ App::init()
->setUser($user);
$smtp = $project->getAttribute('smtp', []);
if (!empty($smtp) && ($smtp['enabled'] ?? false)) {
if (! empty($smtp) && ($smtp['enabled'] ?? false)) {
$mails
->setSmtpHost($smtp['host'] ?? '')
->setSmtpPort($smtp['port'] ?? 25)
@@ -265,14 +263,14 @@ App::init()
$useCache = $route->getLabel('cache', false);
if ($useCache) {
$key = md5($request->getURI() . implode('*', $request->getParams())) . '*' . APP_CACHE_BUSTER;
$key = md5($request->getURI().implode('*', $request->getParams())).'*'.APP_CACHE_BUSTER;
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
new Filesystem(APP_STORAGE_CACHE.DIRECTORY_SEPARATOR.'app-'.$project->getId())
);
$timestamp = 60 * 60 * 24 * 30;
$data = $cache->load($key, $timestamp);
if (!empty($data)) {
if (! empty($data)) {
$data = json_decode($data, true);
$parts = explode('/', $data['resourceType']);
$type = $parts[0] ?? null;
@@ -282,24 +280,24 @@ App::init()
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
if ($bucket->isEmpty() || (! $bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
if (!$fileSecurity && !$valid) {
if (! $fileSecurity && ! $valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$parts = explode('/', $data['resource']);
$fileId = $parts[1] ?? null;
if ($fileSecurity && !$valid) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
if ($fileSecurity && ! $valid) {
$file = $dbForProject->getDocument('bucket_'.$bucket->getInternalId(), $fileId);
} else {
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_'.$bucket->getInternalId(), $fileId));
}
if ($file->isEmpty()) {
@@ -308,11 +306,10 @@ App::init()
}
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $timestamp) . ' GMT')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $timestamp).' GMT')
->addHeader('X-Appwrite-Cache', 'hit')
->setContentType($data['contentType'])
->send(base64_decode($data['payload']))
;
->send(base64_decode($data['payload']));
$route->setIsActive(false);
} else {
@@ -327,7 +324,6 @@ App::init()
->inject('request')
->inject('project')
->action(function (App $utopia, Request $request, Document $project) {
$route = $utopia->match($request);
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
@@ -431,10 +427,9 @@ App::shutdown()
->inject('mode')
->inject('dbForConsole')
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $events, Audit $audits, Delete $deletes, EventDatabase $database, Database $dbForProject, Func $queueForFunctions, Usage $queueForUsage, string $mode, Database $dbForConsole) use ($parseLabel) {
$responsePayload = $response->getPayload();
if (!empty($events->getEvent())) {
if (! empty($events->getEvent())) {
if (empty($events->getPayload())) {
$events->setPayload($responsePayload);
}
@@ -483,7 +478,7 @@ App::shutdown()
roles: $target['roles'],
options: [
'permissionsChanged' => $target['permissionsChanged'],
'userId' => $events->getParam('userId')
'userId' => $events->getParam('userId'),
]
);
}
@@ -496,24 +491,24 @@ App::shutdown()
* Audit labels
*/
$pattern = $route->getLabel('audits.resource', null);
if (!empty($pattern)) {
if (! empty($pattern)) {
$resource = $parseLabel($pattern, $responsePayload, $requestParams, $user);
if (!empty($resource) && $resource !== $pattern) {
if (! empty($resource) && $resource !== $pattern) {
$audits->setResource($resource);
}
}
if (!$user->isEmpty()) {
if (! $user->isEmpty()) {
$audits->setUser($user);
}
if (!empty($audits->getResource()) && !empty($audits->getUser()->getId())) {
if (! empty($audits->getResource()) && ! empty($audits->getUser()->getId())) {
/**
* audits.payload is switched to default true
* in order to auto audit payload for all endpoints
*/
$pattern = $route->getLabel('audits.payload', true);
if (!empty($pattern)) {
if (! empty($pattern)) {
$audits->setPayload($responsePayload);
}
@@ -523,11 +518,11 @@ App::shutdown()
$audits->trigger();
}
if (!empty($deletes->getType())) {
if (! empty($deletes->getType())) {
$deletes->trigger();
}
if (!empty($database->getType())) {
if (! empty($database->getType())) {
$database->trigger();
}
@@ -539,35 +534,35 @@ App::shutdown()
$resource = $resourceType = null;
$data = $response->getPayload();
if (!empty($data['payload'])) {
if (! empty($data['payload'])) {
$pattern = $route->getLabel('cache.resource', null);
if (!empty($pattern)) {
if (! empty($pattern)) {
$resource = $parseLabel($pattern, $responsePayload, $requestParams, $user);
}
$pattern = $route->getLabel('cache.resourceType', null);
if (!empty($pattern)) {
if (! empty($pattern)) {
$resourceType = $parseLabel($pattern, $responsePayload, $requestParams, $user);
}
$key = md5($request->getURI() . implode('*', $request->getParams())) . '*' . APP_CACHE_BUSTER;
$key = md5($request->getURI().implode('*', $request->getParams())).'*'.APP_CACHE_BUSTER;
$data = json_encode([
'resourceType' => $resourceType,
'resource' => $resource,
'contentType' => $response->getContentType(),
'payload' => base64_encode($data['payload']),
]) ;
]);
$signature = md5($data);
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$accessedAt = $cacheLog->getAttribute('accessedAt', '');
$now = DateTime::now();
if ($cacheLog->isEmpty()) {
Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([
'$id' => $key,
'resource' => $resource,
'accessedAt' => $now,
'signature' => $signature,
'$id' => $key,
'resource' => $resource,
'accessedAt' => $now,
'signature' => $signature,
])));
} elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) {
$cacheLog->setAttribute('accessedAt', $now);
@@ -576,20 +571,18 @@ App::shutdown()
if ($signature !== $cacheLog->getAttribute('signature')) {
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
new Filesystem(APP_STORAGE_CACHE.DIRECTORY_SEPARATOR.'app-'.$project->getId())
);
$cache->save($key, $data);
}
}
}
if ($project->getId() !== 'console') {
if ($mode !== APP_MODE_ADMIN) {
$fileSize = 0;
$file = $request->getFiles('file');
if (!empty($file)) {
if (! empty($file)) {
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
}
@@ -607,7 +600,7 @@ App::shutdown()
/**
* Update user last activity
*/
if (!$user->isEmpty()) {
if (! $user->isEmpty()) {
$accessedAt = $user->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_USER_ACCCESS)) > $accessedAt) {
$user->setAttribute('accessedAt', DateTime::now());
+1 -2
View File
@@ -1,9 +1,9 @@
<?php
use Appwrite\Auth\Auth;
use Appwrite\Extend\Exception;
use Appwrite\Utopia\Request;
use Utopia\App;
use Appwrite\Extend\Exception;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
@@ -13,7 +13,6 @@ App::init()
->inject('request')
->inject('project')
->action(function (App $utopia, Request $request, Document $project) {
$route = $utopia->match($request);
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
+12 -12
View File
@@ -11,9 +11,9 @@ App::init()
->action(function (Request $request, Response $response) {
$response
->addHeader('X-Frame-Options', 'SAMEORIGIN') // Avoid console and homepage from showing in iframes
->addHeader('X-XSS-Protection', '1; mode=block; report=/v1/xss?url=' . \urlencode($request->getURI()))
->addHeader('X-XSS-Protection', '1; mode=block; report=/v1/xss?url='.\urlencode($request->getURI()))
->addHeader('X-UA-Compatible', 'IE=Edge') // Deny IE browsers from going into quirks mode
;
;
});
App::get('/console/*')
@@ -30,41 +30,41 @@ App::get('/console/*')
->inject('request')
->inject('response')
->action(function (Request $request, Response $response) {
$fallback = file_get_contents(__DIR__ . '/../../../console/index.html');
$fallback = file_get_contents(__DIR__.'/../../../console/index.html');
// Card SSR
if (\str_starts_with($request->getURI(), '/card')) {
$urlCunks = \explode('/', $request->getURI());
$userId = $urlCunks[\count($urlCunks) - 1] ?? '';
$domain = $request->getProtocol() . '://' . $request->getHostname();
$domain = $request->getProtocol().'://'.$request->getHostname();
if (!empty($userId)) {
$ogImageUrl = $domain . '/v1/cards/cloud-og?userId=' . $userId;
if (! empty($userId)) {
$ogImageUrl = $domain.'/v1/cards/cloud-og?userId='.$userId;
} else {
$ogImageUrl = $domain . '/v1/cards/cloud-og?mock=normal';
$ogImageUrl = $domain.'/v1/cards/cloud-og?mock=normal';
}
$ogTags = [
'<title>Appwrite Cloud Card</title>',
'<meta name="description" content="Appwrite Cloud is now LIVE! Share your Cloud card for a chance to win an exclusive Cloud hoodie!">',
'<meta property="og:url" content="' . $domain . $request->getURI() . '">',
'<meta property="og:url" content="'.$domain.$request->getURI().'">',
'<meta name="og:image:type" content="image/png">',
'<meta name="og:image:width" content="1008">',
'<meta name="og:image:height" content="1008">',
'<meta property="og:type" content="website">',
'<meta property="og:title" content="Appwrite Cloud Card">',
'<meta property="og:description" content="Appwrite Cloud is now LIVE! Share your Cloud card for a chance to win an exclusive Cloud hoodie!">',
'<meta property="og:image" content="' . $ogImageUrl . '">',
'<meta property="og:image" content="'.$ogImageUrl.'">',
'<meta name="twitter:card" content="summary_large_image">',
'<meta property="twitter:domain" content="' . $request->getHostname() . '">',
'<meta property="twitter:url" content="' . $domain . $request->getURI() . '">',
'<meta property="twitter:domain" content="'.$request->getHostname().'">',
'<meta property="twitter:url" content="'.$domain.$request->getURI().'">',
'<meta name="twitter:title" content="Appwrite Cloud Card">',
'<meta name="twitter:image:type" content="image/png">',
'<meta name="twitter:image:width" content="1008">',
'<meta name="twitter:image:height" content="1008">',
'<meta name="twitter:description" content="Appwrite Cloud is now LIVE! Share your Cloud card for a chance to win an exclusive Cloud hoodie!">',
'<meta name="twitter:image" content="' . $ogImageUrl . '">',
'<meta name="twitter:image" content="'.$ogImageUrl.'">',
];
$fallback = \str_replace('<!-- {{CLOUD_OG}} -->', \implode('', $ogTags), $fallback);
+2 -2
View File
@@ -24,14 +24,14 @@ App::get('/versions')
continue;
}
if (isset($language['enabled']) && !$language['enabled']) {
if (isset($language['enabled']) && ! $language['enabled']) {
continue;
}
$platformKey = $platform['key'] ?? '';
$languageKey = $language['key'] ?? '';
$version = $language['version'] ?? '';
$versions[$platformKey . '-' . $languageKey] = $version;
$versions[$platformKey.'-'.$languageKey] = $version;
}
}
+38 -38
View File
@@ -1,30 +1,30 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__.'/../vendor/autoload.php';
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Swoole\Process;
use Swoole\Http\Server;
use Swoole\Http\Request as SwooleRequest;
use Swoole\Http\Response as SwooleResponse;
use Swoole\Http\Server;
use Swoole\Process;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Audit\Audit;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Swoole\Files;
use Appwrite\Utopia\Request;
use Utopia\Logger\Log;
use Utopia\Logger\Log\User;
use Utopia\Pools\Group;
use Utopia\Swoole\Files;
$http = new Server("0.0.0.0", App::getEnv('PORT', 80));
$http = new Server('0.0.0.0', App::getEnv('PORT', 80));
$payloadSize = 6 * (1024 * 1024); // 6MB
$workerNumber = swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6));
@@ -42,7 +42,7 @@ $http
]);
$http->on('WorkerStart', function ($server, $workerId) {
Console::success('Worker ' . ++$workerId . ' started successfully');
Console::success('Worker '.++$workerId.' started successfully');
});
$http->on('BeforeReload', function ($server, $workerId) {
@@ -53,16 +53,16 @@ $http->on('AfterReload', function ($server, $workerId) {
Console::success('Reload completed...');
});
Files::load(__DIR__ . '/../console');
Files::load(__DIR__.'/../console');
include __DIR__ . '/controllers/general.php';
include __DIR__.'/controllers/general.php';
$http->on('start', function (Server $http) use ($payloadSize, $register) {
$app = new App('UTC');
go(function () use ($register, $app) {
$pools = $register->get('pools'); /** @var Group $pools */
App::setResource('pools', fn() => $pools);
App::setResource('pools', fn () => $pools);
// wait for database to be ready
$attempts = 0;
@@ -77,7 +77,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
} catch (\Exception $e) {
Console::warning("Database not ready. Retrying connection ({$attempts})...");
if ($attempts >= $max) {
throw new \Exception('Failed to connect to database: ' . $e->getMessage());
throw new \Exception('Failed to connect to database: '.$e->getMessage());
}
sleep($sleep);
}
@@ -98,7 +98,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
}
if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) {
$adapter = new TimeLimit("", 0, 1, $dbForConsole);
$adapter = new TimeLimit('', 0, 1, $dbForConsole);
$adapter->setup();
}
@@ -109,11 +109,11 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
if (($collection['$collection'] ?? '') !== Database::METADATA) {
continue;
}
if (!$dbForConsole->getCollection($key)->isEmpty()) {
if (! $dbForConsole->getCollection($key)->isEmpty()) {
continue;
}
Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...');
Console::success('[Setup] - Creating collection: '.$collection['$id'].'...');
$attributes = [];
$indexes = [];
@@ -128,7 +128,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
'array' => $attribute['array'],
'filters' => $attribute['filters'],
'default' => $attribute['default'] ?? null,
'format' => $attribute['format'] ?? ''
'format' => $attribute['format'] ?? '',
]);
}
@@ -145,7 +145,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
$dbForConsole->createCollection($key, $attributes, $indexes);
}
if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDefaultDatabase(), 'bucket_1')) {
if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && ! $dbForConsole->exists($dbForConsole->getDefaultDatabase(), 'bucket_1')) {
Console::success('[Setup] - Creating default bucket...');
$dbForConsole->createDocument('buckets', new Document([
'$id' => ID::custom('default'),
@@ -188,7 +188,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
'array' => $attribute['array'],
'filters' => $attribute['filters'],
'default' => $attribute['default'] ?? null,
'format' => $attribute['format'] ?? ''
'format' => $attribute['format'] ?? '',
]);
}
@@ -202,7 +202,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
]);
}
$dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes);
$dbForConsole->createCollection('bucket_'.$bucket->getInternalId(), $attributes, $indexes);
}
$pools->reclaim();
@@ -210,7 +210,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
Console::success('[Setup] - Server database init completed...');
});
Console::success('Server started successfully (max payload is ' . number_format($payloadSize) . ' bytes)');
Console::success('Server started successfully (max payload is '.number_format($payloadSize).' bytes)');
Console::info("Master pid {$http->master_pid}, manager pid {$http->manager_pid}");
// listen ctrl + c
@@ -229,8 +229,8 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
$response
->setContentType(Files::getFileMimeType($request->getURI()))
->addHeader('Cache-Control', 'public, max-age=' . $time)
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time) . ' GMT') // 45 days cache
->addHeader('Cache-Control', 'public, max-age='.$time)
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time).' GMT') // 45 days cache
->send(Files::getFileContents($request->getURI()));
return;
@@ -239,7 +239,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
$app = new App('UTC');
$pools = $register->get('pools');
App::setResource('pools', fn() => $pools);
App::setResource('pools', fn () => $pools);
try {
Authorization::cleanRoles();
@@ -249,7 +249,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
} catch (\Throwable $th) {
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
$logger = $app->getResource("logger");
$logger = $app->getResource('logger');
if ($logger) {
try {
/** @var Utopia\Database\Document $user */
@@ -258,16 +258,16 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
// All good, user is optional information for logger
}
$loggerBreadcrumbs = $app->getResource("loggerBreadcrumbs");
$loggerBreadcrumbs = $app->getResource('loggerBreadcrumbs');
$route = $app->match($request);
$log = new Utopia\Logger\Log();
if (isset($user) && !$user->isEmpty()) {
if (isset($user) && ! $user->isEmpty()) {
$log->setUser(new User($user->getId()));
}
$log->setNamespace("http");
$log->setNamespace('http');
$log->setServer(\gethostname());
$log->setVersion($version);
$log->setType(Log::TYPE_ERROR);
@@ -279,7 +279,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
$log->addTag('code', $th->getCode());
// $log->addTag('projectId', $project->getId()); // TODO: Figure out how to get ProjectID, if it becomes relevant
$log->addTag('hostname', $request->getHostname());
$log->addTag('locale', (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', '')));
$log->addTag('locale', (string) $request->getParam('locale', $request->getHeader('x-appwrite-locale', '')));
$log->addExtra('file', $th->getFile());
$log->addExtra('line', $th->getLine());
@@ -287,7 +287,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
$log->addExtra('detailedTrace', $th->getTrace());
$log->addExtra('roles', Authorization::getRoles());
$action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD");
$action = $route->getLabel('sdk.namespace', 'UNKNOWN_NAMESPACE').'.'.$route->getLabel('sdk.method', 'UNKNOWN_METHOD');
$log->setAction($action);
$isProduction = App::getEnv('_APP_ENV', 'development') === 'production';
@@ -298,18 +298,18 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
}
$responseCode = $logger->addLog($log);
Console::info('Log pushed with status code: ' . $responseCode);
Console::info('Log pushed with status code: '.$responseCode);
}
Console::error('[Error] Type: ' . get_class($th));
Console::error('[Error] Message: ' . $th->getMessage());
Console::error('[Error] File: ' . $th->getFile());
Console::error('[Error] Line: ' . $th->getLine());
Console::error('[Error] Type: '.get_class($th));
Console::error('[Error] Message: '.$th->getMessage());
Console::error('[Error] File: '.$th->getFile());
Console::error('[Error] Line: '.$th->getLine());
$swooleResponse->setStatusCode(500);
$output = ((App::isDevelopment())) ? [
'message' => 'Error: ' . $th->getMessage(),
'message' => 'Error: '.$th->getMessage(),
'code' => 500,
'file' => $th->getFile(),
'line' => $th->getLine(),
+152 -151
View File
@@ -5,11 +5,9 @@
*
* Initializes both Appwrite API entry point, queue workers, and CLI tasks.
* Set configuration, framework resources & app constants
*
*/
if (\file_exists(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
if (\file_exists(__DIR__.'/../vendor/autoload.php')) {
require_once __DIR__.'/../vendor/autoload.php';
}
ini_set('memory_limit', '512M');
@@ -18,41 +16,54 @@ ini_set('display_startup_errors', 1);
ini_set('default_socket_timeout', -1);
error_reporting(E_ALL);
use Appwrite\Event\Usage;
use Appwrite\Extend\Exception;
use Ahc\Jwt\JWT;
use Ahc\Jwt\JWTException;
use Appwrite\Auth\Auth;
use Appwrite\Event\Audit;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Func;
use Appwrite\Event\Mail;
use Appwrite\Event\Phone;
use Appwrite\Event\Delete;
use Appwrite\Event\Usage;
use Appwrite\Extend\Exception;
use Appwrite\GraphQL\Promises\Adapter\Swoole;
use Appwrite\GraphQL\Schema;
use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\Origin;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\URL\URL as AppwriteURL;
use MaxMind\Db\Reader;
use PHPMailer\PHPMailer\PHPMailer;
use Swoole\Database\PDOProxy;
use Utopia\App;
use Utopia\Logger\Logger;
use Utopia\Cache\Adapter\Redis as RedisCache;
use Utopia\Cache\Adapter\Sharding;
use Utopia\Cache\Cache;
use Utopia\Config\Config;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Adapter\MySQL;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Structure;
use Utopia\Locale\Locale;
use Utopia\DSN\DSN;
use Utopia\Locale\Locale;
use Utopia\Logger\Logger;
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\Pools\Group;
use Utopia\Pools\Pool;
use Utopia\Queue;
use Utopia\Queue\Connection;
use Utopia\Registry\Registry;
use Utopia\Storage\Device;
use Utopia\Storage\Device\Backblaze;
@@ -61,22 +72,9 @@ use Utopia\Storage\Device\Linode;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Device\S3;
use Utopia\Storage\Device\Wasabi;
use Utopia\Cache\Adapter\Sharding;
use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Adapter\MySQL;
use Utopia\Pools\Group;
use Utopia\Pools\Pool;
use Ahc\Jwt\JWT;
use Ahc\Jwt\JWTException;
use Appwrite\Event\Func;
use MaxMind\Db\Reader;
use PHPMailer\PHPMailer\PHPMailer;
use Swoole\Database\PDOProxy;
use Utopia\Queue;
use Utopia\Queue\Connection;
use Utopia\Storage\Storage;
use Utopia\Validator\Range;
use Utopia\Validator\IP;
use Utopia\Validator\Range;
use Utopia\Validator\URL;
use Utopia\Validator\WhiteList;
@@ -84,7 +82,7 @@ const APP_NAME = 'Appwrite';
const APP_DOMAIN = 'appwrite.io';
const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address
const APP_EMAIL_SECURITY = ''; // Default security email address
const APP_USERAGENT = APP_NAME . '-Server v%s. Please report abuse at %s';
const APP_USERAGENT = APP_NAME.'-Server v%s. Please report abuse at %s';
const APP_MODE_DEFAULT = 'default';
const APP_MODE_ADMIN = 'admin';
const APP_PAGING_LIMIT = 12;
@@ -162,7 +160,7 @@ const DELETE_TYPE_REALTIME = 'realtime';
const DELETE_TYPE_BUCKETS = 'buckets';
const DELETE_TYPE_SESSIONS = 'sessions';
const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp';
const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource';
const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource';
const DELETE_TYPE_SCHEDULES = 'schedules';
// Compression type
const COMPRESSION_TYPE_NONE = 'none';
@@ -184,7 +182,7 @@ const MAX_OUTPUT_CHUNK_SIZE = 2 * 1024 * 1024; // 2MB
// Usage metrics
const METRIC_TEAMS = 'teams';
const METRIC_USERS = 'users';
const METRIC_SESSIONS = 'sessions';
const METRIC_SESSIONS = 'sessions';
const METRIC_DATABASES = 'databases';
const METRIC_COLLECTIONS = 'collections';
const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections';
@@ -192,28 +190,28 @@ const METRIC_DOCUMENTS = 'documents';
const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents';
const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents';
const METRIC_BUCKETS = 'buckets';
const METRIC_FILES = 'files';
const METRIC_FILES_STORAGE = 'files.storage';
const METRIC_FILES = 'files';
const METRIC_FILES_STORAGE = 'files.storage';
const METRIC_BUCKET_ID_FILES = '{bucketInternalId}.files';
const METRIC_BUCKET_ID_FILES_STORAGE = '{bucketInternalId}.files.storage';
const METRIC_FUNCTIONS = 'functions';
const METRIC_DEPLOYMENTS = 'deployments';
const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage';
const METRIC_BUILDS = 'builds';
const METRIC_BUILDS_STORAGE = 'builds.storage';
const METRIC_BUILDS_COMPUTE = 'builds.compute';
const METRIC_FUNCTION_ID_BUILDS = '{functionInternalId}.builds';
const METRIC_BUCKET_ID_FILES_STORAGE = '{bucketInternalId}.files.storage';
const METRIC_FUNCTIONS = 'functions';
const METRIC_DEPLOYMENTS = 'deployments';
const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage';
const METRIC_BUILDS = 'builds';
const METRIC_BUILDS_STORAGE = 'builds.storage';
const METRIC_BUILDS_COMPUTE = 'builds.compute';
const METRIC_FUNCTION_ID_BUILDS = '{functionInternalId}.builds';
const METRIC_FUNCTION_ID_BUILDS_STORAGE = '{functionInternalId}.builds.storage';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE = '{functionInternalId}.builds.compute';
const METRIC_FUNCTION_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments';
const METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage';
const METRIC_EXECUTIONS = 'executions';
const METRIC_EXECUTIONS_COMPUTE = 'executions.compute';
const METRIC_FUNCTION_ID_EXECUTIONS = '{functionInternalId}.executions';
const METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE = '{functionInternalId}.executions.compute';
const METRIC_NETWORK_REQUESTS = 'network.requests';
const METRIC_NETWORK_INBOUND = 'network.inbound';
const METRIC_NETWORK_OUTBOUND = 'network.outbound';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE = '{functionInternalId}.builds.compute';
const METRIC_FUNCTION_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments';
const METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage';
const METRIC_EXECUTIONS = 'executions';
const METRIC_EXECUTIONS_COMPUTE = 'executions.compute';
const METRIC_FUNCTION_ID_EXECUTIONS = '{functionInternalId}.executions';
const METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE = '{functionInternalId}.executions.compute';
const METRIC_NETWORK_REQUESTS = 'network.requests';
const METRIC_NETWORK_INBOUND = 'network.inbound';
const METRIC_NETWORK_OUTBOUND = 'network.outbound';
$register = new Registry();
@@ -222,42 +220,42 @@ App::setMode(App::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION));
/*
* ENV vars
*/
Config::load('events', __DIR__ . '/config/events.php');
Config::load('auth', __DIR__ . '/config/auth.php');
Config::load('errors', __DIR__ . '/config/errors.php');
Config::load('authProviders', __DIR__ . '/config/authProviders.php');
Config::load('platforms', __DIR__ . '/config/platforms.php');
Config::load('collections', __DIR__ . '/config/collections.php');
Config::load('runtimes', __DIR__ . '/config/runtimes.php');
Config::load('usage', __DIR__ . '/config/usage.php');
Config::load('roles', __DIR__ . '/config/roles.php'); // User roles and scopes
Config::load('scopes', __DIR__ . '/config/scopes.php'); // User roles and scopes
Config::load('services', __DIR__ . '/config/services.php'); // List of services
Config::load('variables', __DIR__ . '/config/variables.php'); // List of env variables
Config::load('regions', __DIR__ . '/config/regions.php'); // List of available regions
Config::load('avatar-browsers', __DIR__ . '/config/avatars/browsers.php');
Config::load('avatar-credit-cards', __DIR__ . '/config/avatars/credit-cards.php');
Config::load('avatar-flags', __DIR__ . '/config/avatars/flags.php');
Config::load('locale-codes', __DIR__ . '/config/locale/codes.php');
Config::load('locale-currencies', __DIR__ . '/config/locale/currencies.php');
Config::load('locale-eu', __DIR__ . '/config/locale/eu.php');
Config::load('locale-languages', __DIR__ . '/config/locale/languages.php');
Config::load('locale-phones', __DIR__ . '/config/locale/phones.php');
Config::load('locale-countries', __DIR__ . '/config/locale/countries.php');
Config::load('locale-continents', __DIR__ . '/config/locale/continents.php');
Config::load('locale-templates', __DIR__ . '/config/locale/templates.php');
Config::load('storage-logos', __DIR__ . '/config/storage/logos.php');
Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php');
Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php');
Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php');
Config::load('messagingProviders', __DIR__ . '/config/messagingProviders.php');
Config::load('events', __DIR__.'/config/events.php');
Config::load('auth', __DIR__.'/config/auth.php');
Config::load('errors', __DIR__.'/config/errors.php');
Config::load('authProviders', __DIR__.'/config/authProviders.php');
Config::load('platforms', __DIR__.'/config/platforms.php');
Config::load('collections', __DIR__.'/config/collections.php');
Config::load('runtimes', __DIR__.'/config/runtimes.php');
Config::load('usage', __DIR__.'/config/usage.php');
Config::load('roles', __DIR__.'/config/roles.php'); // User roles and scopes
Config::load('scopes', __DIR__.'/config/scopes.php'); // User roles and scopes
Config::load('services', __DIR__.'/config/services.php'); // List of services
Config::load('variables', __DIR__.'/config/variables.php'); // List of env variables
Config::load('regions', __DIR__.'/config/regions.php'); // List of available regions
Config::load('avatar-browsers', __DIR__.'/config/avatars/browsers.php');
Config::load('avatar-credit-cards', __DIR__.'/config/avatars/credit-cards.php');
Config::load('avatar-flags', __DIR__.'/config/avatars/flags.php');
Config::load('locale-codes', __DIR__.'/config/locale/codes.php');
Config::load('locale-currencies', __DIR__.'/config/locale/currencies.php');
Config::load('locale-eu', __DIR__.'/config/locale/eu.php');
Config::load('locale-languages', __DIR__.'/config/locale/languages.php');
Config::load('locale-phones', __DIR__.'/config/locale/phones.php');
Config::load('locale-countries', __DIR__.'/config/locale/countries.php');
Config::load('locale-continents', __DIR__.'/config/locale/continents.php');
Config::load('locale-templates', __DIR__.'/config/locale/templates.php');
Config::load('storage-logos', __DIR__.'/config/storage/logos.php');
Config::load('storage-mimes', __DIR__.'/config/storage/mimes.php');
Config::load('storage-inputs', __DIR__.'/config/storage/inputs.php');
Config::load('storage-outputs', __DIR__.'/config/storage/outputs.php');
Config::load('messagingProviders', __DIR__.'/config/messagingProviders.php');
$user = App::getEnv('_APP_REDIS_USER', '');
$pass = App::getEnv('_APP_REDIS_PASS', '');
if (!empty($user) || !empty($pass)) {
Resque::setBackend('redis://' . $user . ':' . $pass . '@' . App::getEnv('_APP_REDIS_HOST', '') . ':' . App::getEnv('_APP_REDIS_PORT', ''));
if (! empty($user) || ! empty($pass)) {
Resque::setBackend('redis://'.$user.':'.$pass.'@'.App::getEnv('_APP_REDIS_HOST', '').':'.App::getEnv('_APP_REDIS_PORT', ''));
} else {
Resque::setBackend(App::getEnv('_APP_REDIS_HOST', '') . ':' . App::getEnv('_APP_REDIS_PORT', ''));
Resque::setBackend(App::getEnv('_APP_REDIS_HOST', '').':'.App::getEnv('_APP_REDIS_PORT', ''));
}
/**
@@ -313,8 +311,7 @@ Database::addFilter(
if (isset($formatOptions['min']) || isset($formatOptions['max'])) {
$attribute
->setAttribute('min', $formatOptions['min'])
->setAttribute('max', $formatOptions['max'])
;
->setAttribute('max', $formatOptions['max']);
}
return $value;
@@ -437,7 +434,7 @@ Database::addFilter(
return null;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn() => $database
return Authorization::skip(fn () => $database
->find('tokens', [
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
@@ -451,7 +448,7 @@ Database::addFilter(
return null;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn() => $database
return Authorization::skip(fn () => $database
->find('memberships', [
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
@@ -493,7 +490,7 @@ Database::addFilter(
return null;
}
$value = json_decode($value, true);
$key = App::getEnv('_APP_OPENSSL_KEY_V' . $value['version']);
$key = App::getEnv('_APP_OPENSSL_KEY_V'.$value['version']);
return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag']));
}
@@ -506,11 +503,11 @@ Database::addFilter(
$user->getId(),
$user->getAttribute('email', ''),
$user->getAttribute('name', ''),
$user->getAttribute('phone', '')
$user->getAttribute('phone', ''),
];
foreach ($user->getAttribute('labels', []) as $label) {
$searchValues[] = 'label:' . $label;
$searchValues[] = 'label:'.$label;
}
$search = implode(' ', \array_filter($searchValues));
@@ -528,7 +525,7 @@ Database::addFilter(
return null;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn() => $database
return Authorization::skip(fn () => $database
->find('targets', [
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
@@ -542,7 +539,7 @@ Database::addFilter(
return null;
},
function (mixed $value, Document $document, Database $database) {
$provider = Authorization::skip(fn() => $database
$provider = Authorization::skip(fn () => $database
->findOne('providers', [
Query::equal('$id', [$document->getAttribute('providerId')]),
Query::select(['type']),
@@ -551,6 +548,7 @@ Database::addFilter(
if ($provider) {
return $provider->getAttribute('type');
}
return null;
}
);
@@ -568,6 +566,7 @@ Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () {
Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) {
$elements = $attribute['formatOptions']['elements'];
return new WhiteList($elements, true);
}, Database::VAR_STRING);
@@ -582,12 +581,14 @@ Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () {
Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) {
$min = $attribute['formatOptions']['min'] ?? -INF;
$max = $attribute['formatOptions']['max'] ?? INF;
return new Range($min, $max, Range::TYPE_INTEGER);
}, Database::VAR_INTEGER);
Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) {
$min = $attribute['formatOptions']['min'] ?? -INF;
$max = $attribute['formatOptions']['max'] ?? INF;
return new Range($min, $max, Range::TYPE_FLOAT);
}, Database::VAR_FLOAT);
@@ -603,12 +604,13 @@ $register->set('logger', function () {
return null;
}
if (!Logger::hasProvider($providerName)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled");
if (! Logger::hasProvider($providerName)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Logging provider not supported. Logging is disabled');
}
$classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName);
$classname = '\\Utopia\\Logger\\Adapter\\'.\ucfirst($providerName);
$adapter = new $classname($providerConfig);
return new Logger($adapter);
});
$register->set('pools', function () {
@@ -677,7 +679,7 @@ $register->set('pools', function () {
throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500);
}
$poolSize = (int)($instanceConnections / $workerCount);
$poolSize = (int) ($instanceConnections / $workerCount);
foreach ($connections as $key => $connection) {
$type = $connection['type'] ?? '';
@@ -688,7 +690,7 @@ $register->set('pools', function () {
$dsns = explode(',', $connection['dsns'] ?? '');
foreach ($dsns as &$dsn) {
$dsn = explode('=', $dsn);
$name = ($multipe) ? $key . '_' . $dsn[0] : $key;
$name = ($multipe) ? $key.'_'.$dsn[0] : $key;
$dsn = $dsn[1] ?? '';
$config[] = $name;
if (empty($dsn)) {
@@ -704,8 +706,8 @@ $register->set('pools', function () {
$dsnScheme = $dsn->getScheme();
$dsnDatabase = $dsn->getPath();
if (!in_array($dsnScheme, $schemes)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme");
if (! in_array($dsnScheme, $schemes)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid console database scheme');
}
/**
@@ -720,21 +722,21 @@ $register->set('pools', function () {
case 'mariadb':
$resource = function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array(
return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, [
PDO::ATTR_TIMEOUT => 3, // Seconds
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_ERRMODE => App::isDevelopment() ? PDO::ERRMODE_WARNING : PDO::ERRMODE_SILENT, // If in production mode, warnings are not displayed
PDO::ATTR_EMULATE_PREPARES => true,
PDO::ATTR_STRINGIFY_FETCHES => true
));
PDO::ATTR_STRINGIFY_FETCHES => true,
]);
});
};
break;
case 'redis':
$resource = function () use ($dsnHost, $dsnPort, $dsnPass) {
$redis = new Redis();
@$redis->pconnect($dsnHost, (int)$dsnPort);
@$redis->pconnect($dsnHost, (int) $dsnPort);
if ($dsnPass) {
$redis->auth($dsnPass);
}
@@ -745,7 +747,7 @@ $register->set('pools', function () {
break;
default:
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid scheme");
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme');
break;
}
@@ -779,7 +781,7 @@ $register->set('pools', function () {
break;
default:
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation.");
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Server error: Missing adapter implementation.');
break;
}
@@ -789,7 +791,7 @@ $register->set('pools', function () {
$group->add($pool);
}
Config::setParam('pools-' . $key, $config);
Config::setParam('pools-'.$key, $config);
}
return $group;
@@ -806,14 +808,14 @@ $register->set('smtp', function () {
$mail->XMailer = 'Appwrite Mailer';
$mail->Host = App::getEnv('_APP_SMTP_HOST', 'smtp');
$mail->Port = App::getEnv('_APP_SMTP_PORT', 25);
$mail->SMTPAuth = (!empty($username) && !empty($password));
$mail->SMTPAuth = (! empty($username) && ! empty($password));
$mail->Username = $username;
$mail->Password = $password;
$mail->SMTPSecure = App::getEnv('_APP_SMTP_SECURE', false);
$mail->SMTPAutoTLS = false;
$mail->CharSet = 'UTF-8';
$from = \urldecode(App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'));
$from = \urldecode(App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server'));
$email = App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$mail->setFrom($email, $from);
@@ -824,12 +826,13 @@ $register->set('smtp', function () {
return $mail;
});
$register->set('geodb', function () {
return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2023-01.mmdb');
return new Reader(__DIR__.'/assets/dbip/dbip-country-lite-2023-01.mmdb');
});
$register->set('passwordsDictionary', function () {
$content = \file_get_contents(__DIR__ . '/assets/security/10k-common-passwords');
$content = \file_get_contents(__DIR__.'/assets/security/10k-common-passwords');
$content = explode("\n", $content);
$content = array_flip($content);
return $content;
});
$register->set('promiseAdapter', function () {
@@ -845,12 +848,12 @@ $locales = Config::getParam('locale-codes', []);
foreach ($locales as $locale) {
$code = $locale['code'];
$path = __DIR__ . '/config/locale/translations/' . $code . '.json';
$path = __DIR__.'/config/locale/translations/'.$code.'.json';
if (!\file_exists($path)) {
$path = __DIR__ . '/config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar`
if (!\file_exists($path)) {
$path = __DIR__ . '/config/locale/translations/en.json'; // if none translation exists, use default from `en.json`
if (! \file_exists($path)) {
$path = __DIR__.'/config/locale/translations/'.\substr($code, 0, 2).'.json'; // if `ar-ae` doesn't exist, look for `ar`
if (! \file_exists($path)) {
$path = __DIR__.'/config/locale/translations/en.json'; // if none translation exists, use default from `en.json`
}
}
@@ -878,20 +881,20 @@ App::setResource('loggerBreadcrumbs', function () {
return [];
});
App::setResource('register', fn() => $register);
App::setResource('locale', fn() => new Locale(App::getEnv('_APP_LOCALE', 'en')));
App::setResource('register', fn () => $register);
App::setResource('locale', fn () => new Locale(App::getEnv('_APP_LOCALE', 'en')));
App::setResource('localeCodes', function () {
return array_map(fn($locale) => $locale['code'], Config::getParam('locale-codes', []));
return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []));
});
// Queues
App::setResource('events', fn() => new Event('', ''));
App::setResource('audits', fn() => new Audit());
App::setResource('mails', fn() => new Mail());
App::setResource('deletes', fn() => new Delete());
App::setResource('database', fn() => new EventDatabase());
App::setResource('messaging', fn() => new Phone());
App::setResource('events', fn () => new Event('', ''));
App::setResource('audits', fn () => new Audit());
App::setResource('mails', fn () => new Mail());
App::setResource('deletes', fn () => new Delete());
App::setResource('database', fn () => new EventDatabase());
App::setResource('messaging', fn () => new Phone());
App::setResource('queue', function (Group $pools) {
return $pools->get('queue')->pop()->getResource();
}, ['pools']);
@@ -917,7 +920,7 @@ App::setResource('clients', function ($request, $console, $project) {
fn ($node) => $node['hostname'],
\array_filter(
$console->getAttribute('platforms', []),
fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && isset($node['hostname']) && !empty($node['hostname']))
fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && isset($node['hostname']) && ! empty($node['hostname']))
)
);
@@ -928,7 +931,7 @@ App::setResource('clients', function ($request, $console, $project) {
fn ($node) => $node['hostname'],
\array_filter(
$project->getAttribute('platforms', []),
fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname']))
fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && ! empty($node['hostname']))
)
)
)
@@ -944,23 +947,22 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Database $dbForConsole */
/** @var string $mode */
Authorization::setDefaultStatus(true);
Auth::setCookieName('a_session_' . $project->getId());
Auth::setCookieName('a_session_'.$project->getId());
$authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
if (APP_MODE_ADMIN === $mode) {
Auth::setCookieName('a_session_' . $console->getId());
Auth::setCookieName('a_session_'.$console->getId());
$authDuration = Auth::TOKEN_EXPIRATION_LOGIN_LONG;
}
$session = Auth::decodeSession(
$request->getCookie(
Auth::$cookieName, // Get sessions
$request->getCookie(Auth::$cookieName . '_legacy', '')
$request->getCookie(Auth::$cookieName.'_legacy', '')
)
);// Get fallback session from old clients (no SameSite support)
); // Get fallback session from old clients (no SameSite support)
// Get fallback session from clients who block 3rd-party cookies
if ($response) {
@@ -991,7 +993,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
if (
$user->isEmpty() // Check a document has been found in the DB
|| !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret, $authDuration)
|| ! Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret, $authDuration)
) { // Validate user has valid login token
$user = new Document([]);
}
@@ -1006,13 +1008,13 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
$authJWT = $request->getHeader('x-appwrite-jwt', '');
if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication
if (! empty($authJWT) && ! $project->isEmpty()) { // JWT authentication
$jwt = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
try {
$payload = $jwt->decode($authJWT);
} catch (JWTException $error) {
throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage());
throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. '.$error->getMessage());
}
$jwtUserId = $payload['userId'] ?? '';
@@ -1034,14 +1036,13 @@ App::setResource('project', function ($dbForConsole, $request, $console) {
/** @var Appwrite\Utopia\Request $request */
/** @var Utopia\Database\Database $dbForConsole */
/** @var Utopia\Database\Document $console */
$projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', ''));
if (empty($projectId) || $projectId === 'console') {
return $console;
}
$project = Authorization::skip(fn() => $dbForConsole->getDocument('projects', $projectId));
$project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId));
return $project;
}, ['dbForConsole', 'request', 'console']);
@@ -1076,12 +1077,12 @@ App::setResource('console', function () {
'limit' => (App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds
],
'authWhitelistEmails' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [],
'authWhitelistIPs' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [],
'authWhitelistEmails' => (! empty(App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [],
'authWhitelistIPs' => (! empty(App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [],
'authProviders' => [
'githubEnabled' => true,
'githubSecret' => App::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''),
'githubAppid' => App::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '')
'githubAppid' => App::getEnv('_APP_CONSOLE_GITHUB_APP_ID', ''),
],
]);
}, []);
@@ -1094,11 +1095,10 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForConsole,
$dbAdapter = $pools
->get($project->getAttribute('database'))
->pop()
->getResource()
;
->getResource();
$database = new Database($dbAdapter, $cache);
$database->setNamespace('_' . $project->getInternalId());
$database->setNamespace('_'.$project->getInternalId());
return $database;
}, ['pools', 'dbForConsole', 'cache', 'project']);
@@ -1107,8 +1107,7 @@ App::setResource('dbForConsole', function (Group $pools, Cache $cache) {
$dbAdapter = $pools
->get('console')
->pop()
->getResource()
;
->getResource();
$database = new Database($dbAdapter, $cache);
@@ -1125,8 +1124,7 @@ App::setResource('cache', function (Group $pools) {
$adapters[] = $pools
->get($value)
->pop()
->getResource()
;
->getResource();
}
return new Cache(new Sharding($adapters));
@@ -1137,15 +1135,15 @@ App::setResource('deviceLocal', function () {
});
App::setResource('deviceFiles', function ($project) {
return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
return getDevice(APP_STORAGE_UPLOADS.'/app-'.$project->getId());
}, ['project']);
App::setResource('deviceFunctions', function ($project) {
return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId());
return getDevice(APP_STORAGE_FUNCTIONS.'/app-'.$project->getId());
}, ['project']);
App::setResource('deviceBuilds', function ($project) {
return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId());
return getDevice(APP_STORAGE_BUILDS.'/app-'.$project->getId());
}, ['project']);
function getDevice($root): Device
@@ -1167,7 +1165,7 @@ function getDevice($root): Device
$bucket = $dsn->getPath();
$region = $dsn->getParam('region');
} catch (\Exception $e) {
Console::error($e->getMessage() . 'Invalid DSN. Defaulting to Local device.');
Console::error($e->getMessage().'Invalid DSN. Defaulting to Local device.');
}
switch ($device) {
@@ -1240,7 +1238,6 @@ App::setResource('promiseAdapter', function ($register) {
}, ['register']);
App::setResource('schema', function ($utopia, $dbForProject) {
$complexity = function (int $complexity, array $args) {
$queries = Query::parseQueries($args['queries'] ?? []);
$query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null;
@@ -1250,7 +1247,7 @@ App::setResource('schema', function ($utopia, $dbForProject) {
};
$attributes = function (int $limit, int $offset) use ($dbForProject) {
$attrs = Authorization::skip(fn() => $dbForProject->find('attributes', [
$attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [
Query::limit($limit),
Query::offset($offset),
]));
@@ -1280,7 +1277,7 @@ App::setResource('schema', function ($utopia, $dbForProject) {
$params = [
'list' => function (string $databaseId, string $collectionId, array $args) {
return [ 'queries' => $args['queries']];
return ['queries' => $args['queries']];
},
'create' => function (string $databaseId, string $collectionId, array $args) {
$id = $args['id'] ?? 'unique()';
@@ -1328,18 +1325,21 @@ App::setResource('schema', function ($utopia, $dbForProject) {
App::setResource('contributors', function () {
$path = 'app/config/cloud/contributors.json';
$list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
return $list;
}, []);
App::setResource('employees', function () {
$path = 'app/config/cloud/employees.json';
$list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
return $list;
}, []);
App::setResource('heroes', function () {
$path = 'app/config/cloud/heroes.json';
$list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
return $list;
}, []);
@@ -1347,12 +1347,13 @@ App::setResource('requestTimestamp', function ($request) {
//TODO: Move this to the Request class itself
$timestampHeader = $request->getHeader('x-appwrite-timestamp');
$requestTimestamp = null;
if (!empty($timestampHeader)) {
if (! empty($timestampHeader)) {
try {
$requestTimestamp = new \DateTime($timestampHeader);
} catch (\Throwable $e) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid X-Appwrite-Timestamp header value');
}
}
return $requestTimestamp;
}, ['request']);
+19 -21
View File
@@ -5,38 +5,36 @@
*
* Inializes both Appwrite API entry point, queue workers, and CLI tasks.
* Set configuration, framework resources, app constants
*
*/
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
if (file_exists(__DIR__ . '/../vendor/autoload.php')) {
require __DIR__ . '/../vendor/autoload.php';
if (file_exists(__DIR__.'/../vendor/autoload.php')) {
require __DIR__.'/../vendor/autoload.php';
}
use Utopia\Preloader\Preloader;
include __DIR__ . '/controllers/general.php';
include __DIR__.'/controllers/general.php';
$preloader = new Preloader();
foreach (
[
realpath(__DIR__ . '/../vendor/composer'),
realpath(__DIR__ . '/../vendor/amphp'),
realpath(__DIR__ . '/../vendor/felixfbecker'),
realpath(__DIR__ . '/../vendor/twig/twig'),
realpath(__DIR__ . '/../vendor/guzzlehttp/guzzle'),
realpath(__DIR__ . '/../vendor/slickdeals'),
realpath(__DIR__ . '/../vendor/psr/log'),
realpath(__DIR__ . '/../vendor/matomo'),
realpath(__DIR__ . '/../vendor/symfony'),
realpath(__DIR__ . '/../vendor/mongodb'),
realpath(__DIR__ . '/../vendor/utopia-php/websocket'), // TODO: remove workerman autoload
realpath(__DIR__ . '/../vendor/utopia-php/cache'), // TODO: remove memcached autoload
realpath(__DIR__ . '/../vendor/utopia-php/queue/src/Queue/Adapter/Workerman.php'), // TODO: remove workerman autoload
realpath(__DIR__.'/../vendor/composer'),
realpath(__DIR__.'/../vendor/amphp'),
realpath(__DIR__.'/../vendor/felixfbecker'),
realpath(__DIR__.'/../vendor/twig/twig'),
realpath(__DIR__.'/../vendor/guzzlehttp/guzzle'),
realpath(__DIR__.'/../vendor/slickdeals'),
realpath(__DIR__.'/../vendor/psr/log'),
realpath(__DIR__.'/../vendor/matomo'),
realpath(__DIR__.'/../vendor/symfony'),
realpath(__DIR__.'/../vendor/mongodb'),
realpath(__DIR__.'/../vendor/utopia-php/websocket'), // TODO: remove workerman autoload
realpath(__DIR__.'/../vendor/utopia-php/cache'), // TODO: remove memcached autoload
realpath(__DIR__.'/../vendor/utopia-php/queue/src/Queue/Adapter/Workerman.php'), // TODO: remove workerman autoload
] as $key => $value
) {
if ($value !== false) {
@@ -45,7 +43,7 @@ foreach (
}
$preloader
->paths(realpath(__DIR__ . '/../app/config'))
->paths(realpath(__DIR__ . '/../app/controllers'))
->paths(realpath(__DIR__ . '/../src'))
->paths(realpath(__DIR__.'/../app/config'))
->paths(realpath(__DIR__.'/../app/controllers'))
->paths(realpath(__DIR__.'/../src'))
->load();
+73 -76
View File
@@ -3,6 +3,7 @@
use Appwrite\Auth\Auth;
use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Network\Validator\Origin;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Swoole\Http\Request as SwooleRequest;
use Swoole\Http\Response as SwooleResponse;
@@ -12,23 +13,22 @@ use Swoole\Timer;
use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Role;
use Utopia\Logger\Log;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Appwrite\Utopia\Request;
use Utopia\Cache\Adapter\Sharding;
use Utopia\Cache\Cache;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\WebSocket\Server;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Logger\Log;
use Utopia\WebSocket\Adapter;
use Utopia\WebSocket\Server;
require_once __DIR__ . '/init.php';
require_once __DIR__.'/init.php';
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
@@ -42,8 +42,7 @@ function getConsoleDB(): Database
$dbAdapter = $pools
->get('console')
->pop()
->getResource()
;
->getResource();
$database = new Database($dbAdapter, getCache());
@@ -66,11 +65,10 @@ function getProjectDB(Document $project): Database
$dbAdapter = $pools
->get($project->getAttribute('database'))
->pop()
->getResource()
;
->getResource();
$database = new Database($dbAdapter, getCache());
$database->setNamespace('_' . $project->getInternalId());
$database->setNamespace('_'.$project->getInternalId());
return $database;
}
@@ -80,7 +78,6 @@ function getCache(): Cache
global $register;
$pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */
$list = Config::getParam('pools-cache', []);
$adapters = [];
@@ -88,8 +85,7 @@ function getCache(): Cache
$adapters[] = $pools
->get($value)
->pop()
->getResource()
;
->getResource();
}
return new Cache(new Sharding($adapters));
@@ -126,7 +122,7 @@ $logError = function (Throwable $error, string $action) use ($register) {
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
$log = new Log();
$log->setNamespace("realtime");
$log->setNamespace('realtime');
$log->setServer(\gethostname());
$log->setVersion($version);
$log->setType(Log::TYPE_ERROR);
@@ -146,13 +142,13 @@ $logError = function (Throwable $error, string $action) use ($register) {
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);
Console::info('Realtime log pushed with status code: ' . $responseCode);
Console::info('Realtime log pushed with status code: '.$responseCode);
}
Console::error('[Error] Type: ' . get_class($error));
Console::error('[Error] Message: ' . $error->getMessage());
Console::error('[Error] File: ' . $error->getFile());
Console::error('[Error] Line: ' . $error->getLine());
Console::error('[Error] Type: '.get_class($error));
Console::error('[Error] Message: '.$error->getMessage());
Console::error('[Error] File: '.$error->getFile());
Console::error('[Error] Line: '.$error->getLine());
};
$server->error($logError);
@@ -177,7 +173,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
'$permissions' => [],
'container' => $containerId,
'timestamp' => DateTime::now(),
'value' => '{}'
'value' => '{}',
]);
$statsDocument = Authorization::skip(fn () => $database->createDocument('realtime', $document));
@@ -211,7 +207,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
Authorization::skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument));
} catch (\Throwable $th) {
call_user_func($logError, $th, "updateWorkerDocument");
call_user_func($logError, $th, 'updateWorkerDocument');
} finally {
$register->get('pools')->reclaim();
}
@@ -219,12 +215,12 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
});
$server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $realtime, $logError) {
Console::success('Worker ' . $workerId . ' started successfully');
Console::success('Worker '.$workerId.' started successfully');
$attempts = 0;
$start = time();
Timer::tick(5000, function () use ($server, $register, $realtime, $stats, $logError) {
Timer::tick(5000, function () use ($server, $register, $realtime, $stats) {
/**
* Sending current connections to project channels on the console project every 5 seconds.
*/
@@ -243,34 +239,34 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
foreach ($list as $document) {
foreach (json_decode($document->getAttribute('value')) as $projectId => $value) {
if (array_key_exists($projectId, $payload)) {
$payload[$projectId] += $value;
$payload[$projectId] += $value;
} else {
$payload[$projectId] = $value;
$payload[$projectId] = $value;
}
}
}
foreach ($stats as $projectId => $value) {
if (!array_key_exists($projectId, $payload)) {
if (! array_key_exists($projectId, $payload)) {
continue;
}
$event = [
'project' => 'console',
'roles' => ['team:' . $stats->get($projectId, 'teamId')],
'roles' => ['team:'.$stats->get($projectId, 'teamId')],
'data' => [
'events' => ['stats.connections'],
'channels' => ['project'],
'timestamp' => DateTime::formatTz(DateTime::now()),
'payload' => [
$projectId => $payload[$projectId]
]
]
$projectId => $payload[$projectId],
],
],
];
$server->send($realtime->getSubscribers($event), json_encode([
'type' => 'event',
'data' => $event['data']
'data' => $event['data'],
]));
}
@@ -289,13 +285,13 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
'events' => ['test.event'],
'channels' => ['tests'],
'timestamp' => DateTime::formatTz(DateTime::now()),
'payload' => $payload
]
'payload' => $payload,
],
];
$server->send($realtime->getSubscribers($event), json_encode([
'type' => 'event',
'data' => $event['data']
'data' => $event['data'],
]));
}
});
@@ -303,8 +299,8 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
while ($attempts < 300) {
try {
if ($attempts > 0) {
Console::error('Pub/sub connection lost (lasted ' . (time() - $start) . ' seconds, worker: ' . $workerId . ').
Attempting restart in 5 seconds (attempt #' . $attempts . ')');
Console::error('Pub/sub connection lost (lasted '.(time() - $start).' seconds, worker: '.$workerId.').
Attempting restart in 5 seconds (attempt #'.$attempts.')');
sleep(5); // 5 sec delay between connection attempts
}
$start = time();
@@ -314,9 +310,9 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
if ($redis->ping(true)) {
$attempts = 0;
Console::success('Pub/sub connection established (worker: ' . $workerId . ')');
Console::success('Pub/sub connection established (worker: '.$workerId.')');
} else {
Console::error('Pub/sub failed (worker: ' . $workerId . ')');
Console::error('Pub/sub failed (worker: '.$workerId.')');
}
$redis->subscribe(['realtime'], function (Redis $redis, string $channel, string $payload) use ($server, $workerId, $stats, $register, $realtime) {
@@ -326,10 +322,10 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
$projectId = $event['project'];
$userId = $event['userId'];
if ($realtime->hasSubscriber($projectId, 'user:' . $userId)) {
$connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId]));
if ($realtime->hasSubscriber($projectId, 'user:'.$userId)) {
$connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:'.$userId]));
$consoleDatabase = getConsoleDB();
$project = Authorization::skip(fn() => $consoleDatabase->getDocument('projects', $projectId));
$project = Authorization::skip(fn () => $consoleDatabase->getDocument('projects', $projectId));
$database = getProjectDB($project);
$user = $database->getDocument('users', $userId);
@@ -344,17 +340,17 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
$receivers = $realtime->getSubscribers($event);
if (App::isDevelopment() && !empty($receivers)) {
Console::log("[Debug][Worker {$workerId}] Receivers: " . count($receivers));
Console::log("[Debug][Worker {$workerId}] Receivers Connection IDs: " . json_encode($receivers));
Console::log("[Debug][Worker {$workerId}] Event: " . $payload);
if (App::isDevelopment() && ! empty($receivers)) {
Console::log("[Debug][Worker {$workerId}] Receivers: ".count($receivers));
Console::log("[Debug][Worker {$workerId}] Receivers Connection IDs: ".json_encode($receivers));
Console::log("[Debug][Worker {$workerId}] Event: ".$payload);
}
$server->send(
$receivers,
json_encode([
'type' => 'event',
'data' => $event['data']
'data' => $event['data'],
])
);
@@ -363,11 +359,12 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
}
});
} catch (\Throwable $th) {
call_user_func($logError, $th, "pubSubConnection");
call_user_func($logError, $th, 'pubSubConnection');
Console::error('Pub/sub error: ' . $th->getMessage());
Console::error('Pub/sub error: '.$th->getMessage());
$attempts++;
sleep(DATABASE_RECONNECT_SLEEP);
continue;
} finally {
$register->get('pools')->reclaim();
@@ -384,9 +381,9 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
Console::info("Connection open (user: {$connection})");
App::setResource('pools', fn() => $register->get('pools'));
App::setResource('request', fn() => $request);
App::setResource('response', fn() => $response);
App::setResource('pools', fn () => $register->get('pools'));
App::setResource('request', fn () => $request);
App::setResource('response', fn () => $response);
try {
/** @var \Utopia\Database\Document $project */
@@ -427,7 +424,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
$origin = $request->getOrigin();
$originValidator = new Origin(\array_merge($project->getAttribute('platforms', []), $console->getAttribute('platforms', [])));
if (!$originValidator->isValid($origin) && $project->getId() !== 'console') {
if (! $originValidator->isValid($origin) && $project->getId() !== 'console') {
throw new Exception($originValidator->getDescription(), 1008);
}
@@ -450,25 +447,25 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
'type' => 'connected',
'data' => [
'channels' => array_keys($channels),
'user' => $user
]
'user' => $user,
],
]));
$stats->set($project->getId(), [
'projectId' => $project->getId(),
'teamId' => $project->getAttribute('teamId')
'teamId' => $project->getAttribute('teamId'),
]);
$stats->incr($project->getId(), 'connections');
$stats->incr($project->getId(), 'connectionsTotal');
} catch (\Throwable $th) {
call_user_func($logError, $th, "initServer");
call_user_func($logError, $th, 'initServer');
$response = [
'type' => 'error',
'data' => [
'code' => $th->getCode(),
'message' => $th->getMessage()
]
'message' => $th->getMessage(),
],
];
$server->send([$connection], json_encode($response));
@@ -476,8 +473,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
if (App::isDevelopment()) {
Console::error('[Error] Connection Error');
Console::error('[Error] Code: ' . $response['data']['code']);
Console::error('[Error] Message: ' . $response['data']['message']);
Console::error('[Error] Code: '.$response['data']['code']);
Console::error('[Error] Message: '.$response['data']['message']);
}
} finally {
$register->get('pools')->reclaim();
@@ -492,7 +489,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
$database = getConsoleDB();
if ($projectId !== 'console') {
$project = Authorization::skip(fn() => $database->getDocument('projects', $projectId));
$project = Authorization::skip(fn () => $database->getDocument('projects', $projectId));
$database = getProjectDB($project);
}
@@ -515,16 +512,16 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
$message = json_decode($message, true);
if (is_null($message) || (!array_key_exists('type', $message) && !array_key_exists('data', $message))) {
if (is_null($message) || (! array_key_exists('type', $message) && ! array_key_exists('data', $message))) {
throw new Exception('Message format is not valid.', 1003);
}
switch ($message['type']) {
/**
/**
* This type is used to authenticate.
*/
case 'authentication':
if (!array_key_exists('session', $message['data'])) {
if (! array_key_exists('session', $message['data'])) {
throw new Exception('Payload is not valid.', 1003);
}
@@ -537,7 +534,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
if (
empty($user->getId()) // Check a document has been found in the DB
|| !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret, $authDuration) // Validate user has valid login token
|| ! Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret, $authDuration) // Validate user has valid login token
) {
// cookie not valid
throw new Exception('Session is not valid.', 1003);
@@ -553,8 +550,8 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
'data' => [
'to' => 'authentication',
'success' => true,
'user' => $user
]
'user' => $user,
],
]));
break;
@@ -567,8 +564,8 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
'type' => 'error',
'data' => [
'code' => $th->getCode(),
'message' => $th->getMessage()
]
'message' => $th->getMessage(),
],
];
$server->send([$connection], json_encode($response));
@@ -587,7 +584,7 @@ $server->onClose(function (int $connection) use ($realtime, $stats) {
}
$realtime->unsubscribe($connection);
Console::info('Connection close: ' . $connection);
Console::info('Connection close: '.$connection);
});
$server->start();
+20 -20
View File
@@ -1,6 +1,6 @@
<?php
require_once __DIR__ . '/init.php';
require_once __DIR__.'/init.php';
use Appwrite\Event\Func;
use Appwrite\Event\Usage;
@@ -12,27 +12,26 @@ use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Logger\Log;
use Utopia\Logger\Logger;
use Utopia\Pools\Group;
use Utopia\Queue\Adapter\Swoole;
use Utopia\Queue\Message;
use Utopia\Queue\Server;
use Utopia\Registry\Registry;
use Utopia\Logger\Log;
use Utopia\Logger\Logger;
use Utopia\Pools\Group;
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
global $register;
Server::setResource('register', fn() => $register);
Server::setResource('register', fn () => $register);
Server::setResource('dbForConsole', function (Cache $cache, Registry $register) {
$pools = $register->get('pools');
$database = $pools
->get('console')
->pop()
->getResource()
;
->getResource();
$adapter = new Database($database, $cache);
$adapter->setNamespace('console');
@@ -52,11 +51,11 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register,
$database = $pools
->get($project->getAttribute('database'))
->pop()
->getResource()
;
->getResource();
$adapter = new Database($database, $cache);
$adapter->setNamespace('_' . $project->getInternalId());
$adapter->setNamespace('_'.$project->getInternalId());
return $adapter;
}, ['cache', 'register', 'message', 'dbForConsole']);
@@ -69,8 +68,7 @@ Server::setResource('cache', function (Registry $register) {
$adapters[] = $pools
->get($value)
->pop()
->getResource()
;
->getResource();
}
return new Cache(new Sharding($adapters));
@@ -78,6 +76,7 @@ Server::setResource('cache', function (Registry $register) {
Server::setResource('queueForFunctions', function (Registry $register) {
$pools = $register->get('pools');
return new Func(
$pools
->get('queue')
@@ -88,6 +87,7 @@ Server::setResource('queueForFunctions', function (Registry $register) {
Server::setResource('queueForUsage', function (Registry $register) {
$pools = $register->get('pools');
return new Usage(
$pools
->get('queue')
@@ -96,7 +96,7 @@ Server::setResource('queueForUsage', function (Registry $register) {
);
}, ['register']);
Server::setResource('log', fn() => new Log());
Server::setResource('log', fn () => new Log());
Server::setResource('logger', function ($register) {
return $register->get('logger');
@@ -137,12 +137,12 @@ $server
}
if ($logger && ($error->getCode() >= 500 || $error->getCode() === 0)) {
$log->setNamespace("appwrite-worker");
$log->setNamespace('appwrite-worker');
$log->setServer(\gethostname());
$log->setVersion($version);
$log->setType(Log::TYPE_ERROR);
$log->setMessage($error->getMessage());
$log->setAction('appwrite-queue-' . App::getEnv('QUEUE'));
$log->setAction('appwrite-queue-'.App::getEnv('QUEUE'));
$log->addTag('verboseType', get_class($error));
$log->addTag('code', $error->getCode());
$log->addExtra('file', $error->getFile());
@@ -155,11 +155,11 @@ $server
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);
Console::info('Usage stats log pushed with status code: ' . $responseCode);
Console::info('Usage stats log pushed with status code: '.$responseCode);
}
Console::error('[Error] Type: ' . get_class($error));
Console::error('[Error] Message: ' . $error->getMessage());
Console::error('[Error] File: ' . $error->getFile());
Console::error('[Error] Line: ' . $error->getLine());
Console::error('[Error] Type: '.get_class($error));
Console::error('[Error] Message: '.$error->getMessage());
Console::error('[Error] File: '.$error->getFile());
Console::error('[Error] Line: '.$error->getLine());
});
+3 -3
View File
@@ -5,16 +5,16 @@ use Utopia\Audit\Audit;
use Utopia\CLI\Console;
use Utopia\Database\Document;
require_once __DIR__ . '/../init.php';
require_once __DIR__.'/../init.php';
Console::title('Audits V1 Worker');
Console::success(APP_NAME . ' audits worker v1 has started');
Console::success(APP_NAME.' audits worker v1 has started');
class AuditsV1 extends Worker
{
public function getName(): string
{
return "audits";
return 'audits';
}
public function init(): void
+19 -20
View File
@@ -6,20 +6,20 @@ use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Resque\Worker;
use Appwrite\Utopia\Response\Model\Deployment;
use Executor\Executor;
use Utopia\Database\DateTime;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Database\Helpers\ID;
use Utopia\DSN\DSN;
use Utopia\Database\Document;
use Utopia\Config\Config;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Validator\Authorization;
use Utopia\DSN\DSN;
use Utopia\Storage\Storage;
require_once __DIR__ . '/../init.php';
require_once __DIR__.'/../init.php';
Console::title('Builds V1 Worker');
Console::success(APP_NAME . ' build worker v1 has started');
Console::success(APP_NAME.' build worker v1 has started');
// TODO: Executor should return appropriate response codes.
class BuildsV1 extends Worker
@@ -28,7 +28,7 @@ class BuildsV1 extends Worker
public function getName(): string
{
return "builds";
return 'builds';
}
public function init(): void
@@ -46,7 +46,7 @@ class BuildsV1 extends Worker
switch ($type) {
case BUILD_TYPE_DEPLOYMENT:
case BUILD_TYPE_RETRY:
Console::info('Creating build for deployment: ' . $deployment->getId());
Console::info('Creating build for deployment: '.$deployment->getId());
$this->buildDeployment($project, $resource, $deployment);
break;
@@ -81,7 +81,7 @@ class BuildsV1 extends Worker
$key = $function->getAttribute('runtime');
$runtime = isset($runtimes[$key]) ? $runtimes[$key] : null;
if (\is_null($runtime)) {
throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported');
throw new Exception('Runtime "'.$function->getAttribute('runtime', '').'" is not supported');
}
$connection = App::getEnv('_APP_CONNECTIONS_STORAGE', ''); /** @TODO : move this to the registry or someplace else */
@@ -90,7 +90,7 @@ class BuildsV1 extends Worker
$dsn = new DSN($connection);
$device = $dsn->getScheme();
} catch (\Exception $e) {
Console::error($e->getMessage() . 'Invalid DSN. Defaulting to Local device.');
Console::error($e->getMessage().'Invalid DSN. Defaulting to Local device.');
}
$buildId = $deployment->getAttribute('buildId', '');
@@ -111,7 +111,7 @@ class BuildsV1 extends Worker
'stdout' => '',
'stderr' => '',
'endTime' => null,
'duration' => 0
'duration' => 0,
]));
$deployment->setAttribute('buildId', $build->getId());
$deployment->setAttribute('buildInternalId', $build->getInternalId());
@@ -150,7 +150,7 @@ class BuildsV1 extends Worker
/** Trigger Realtime */
$allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [
'functionId' => $function->getId(),
'deploymentId' => $deployment->getId()
'deploymentId' => $deployment->getId(),
]);
$target = Realtime::fromPayload(
// Pass first, most verbose event pattern
@@ -171,6 +171,7 @@ class BuildsV1 extends Worker
$vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) {
$carry[$var->getAttribute('key')] = $var->getAttribute('value');
return $carry;
}, []);
@@ -183,12 +184,12 @@ class BuildsV1 extends Worker
remove: true,
entrypoint: $deployment->getAttribute('entrypoint'),
workdir: '/usr/code',
destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}",
destination: APP_STORAGE_BUILDS."/app-{$project->getId()}",
variables: $vars,
commands: [
'sh', '-c',
'tar -zxf /tmp/code.tar.gz -C /usr/code && \
cd /usr/local/src/ && ./build.sh'
cd /usr/local/src/ && ./build.sh',
]
);
@@ -222,8 +223,7 @@ class BuildsV1 extends Worker
$schedule
->setAttribute('schedule', $function->getAttribute('schedule'))
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
->setAttribute('active', ! empty($function->getAttribute('schedule')) && ! empty($function->getAttribute('deployment')));
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
} catch (\Throwable $th) {
@@ -261,12 +261,11 @@ class BuildsV1 extends Worker
->setProject($project)
->addMetric(METRIC_BUILDS, 1) // per project
->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0))
->addMetric(METRIC_BUILDS_COMPUTE, (int)$build->getAttribute('duration', 0) * 1000)
->addMetric(METRIC_BUILDS_COMPUTE, (int) $build->getAttribute('duration', 0) * 1000)
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS), 1) // per function
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE), $build->getAttribute('size', 0))
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int)$build->getAttribute('duration', 0) * 1000)
->trigger()
;
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int) $build->getAttribute('duration', 0) * 1000)
->trigger();
}
public function shutdown(): void
+68 -76
View File
@@ -7,17 +7,17 @@ use Appwrite\Template\Template;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Domains\Domain;
use Utopia\Locale\Locale;
require_once __DIR__ . '/../init.php';
require_once __DIR__.'/../init.php';
Console::title('Certificates V1 Worker');
Console::success(APP_NAME . ' certificates worker v1 has started');
Console::success(APP_NAME.' certificates worker v1 has started');
class CertificatesV1 extends Worker
{
@@ -30,7 +30,7 @@ class CertificatesV1 extends Worker
public function getName(): string
{
return "certificates";
return 'certificates';
}
public function init(): void
@@ -68,7 +68,6 @@ class CertificatesV1 extends Worker
*
* Note: Renewals are checked and scheduled from maintenence worker
*/
$this->dbForConsole = $this->getConsoleDB();
$skipCheck = $this->args['skipRenewCheck'] ?? false; // If true, we won't double-check expiry from cert file
@@ -79,7 +78,7 @@ class CertificatesV1 extends Worker
$certificate = $this->dbForConsole->findOne('certificates', [Query::equal('domain', [$domain->get()])]);
// If we don't have certificate for domain yet, let's create new document. At the end we save it
if (!$certificate) {
if (! $certificate) {
$certificate = new Document();
$certificate->setAttribute('domain', $domain->get());
}
@@ -92,14 +91,14 @@ class CertificatesV1 extends Worker
}
// Validate domain and DNS records. Skip if job is forced
if (!$skipCheck) {
if (! $skipCheck) {
$mainDomain = $this->getMainDomain();
$isMainDomain = !isset($mainDomain) || $domain->get() === $mainDomain;
$isMainDomain = ! isset($mainDomain) || $domain->get() === $mainDomain;
$this->validateDomain($domain, $isMainDomain);
}
// If certificate exists already, double-check expiry date. Skip if job is forced
if (!$skipCheck && !$this->isRenewRequired($domain->get())) {
if (! $skipCheck && ! $this->isRenewRequired($domain->get())) {
throw new Exception('Renew isn\'t required.');
}
@@ -152,16 +151,15 @@ class CertificatesV1 extends Worker
/**
* Save certificate data into database.
*
* @param string $domain Domain name that certificate is for
* @param Document $certificate Certificate document that we need to save
*
* @param string $domain Domain name that certificate is for
* @param Document $certificate Certificate document that we need to save
* @return void
*/
private function saveCertificateDocument(string $domain, Document $certificate): void
{
// Check if update or insert required
$certificateDocument = $this->dbForConsole->findOne('certificates', [Query::equal('domain', [$domain])]);
if (!empty($certificateDocument) && !$certificateDocument->isEmpty()) {
if (! empty($certificateDocument) && ! $certificateDocument->isEmpty()) {
// Merge new data with current data
$certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy()));
@@ -182,7 +180,7 @@ class CertificatesV1 extends Worker
private function getMainDomain(): ?string
{
$envDomain = App::getEnv('_APP_DOMAIN', '');
if (!empty($envDomain) && $envDomain !== 'localhost') {
if (! empty($envDomain) && $envDomain !== 'localhost') {
return $envDomain;
} else {
$domainDocument = $this->dbForConsole->findOne('domains', [Query::orderAsc('_id')]);
@@ -199,9 +197,8 @@ class CertificatesV1 extends Worker
* - Domain needs to be public and valid (prevents NFT domains that are not supported by Let's Encrypt)
* - Domain must have proper DNS record
*
* @param Domain $domain Domain which we validate
* @param bool $isMainDomain In case of master domain, we look for different DNS configurations
*
* @param Domain $domain Domain which we validate
* @param bool $isMainDomain In case of master domain, we look for different DNS configurations
* @return void
*/
private function validateDomain(Domain $domain, bool $isMainDomain): void
@@ -210,23 +207,23 @@ class CertificatesV1 extends Worker
throw new Exception('Missing certificate domain.');
}
if (!$domain->isKnown() || $domain->isTest()) {
if (! $domain->isKnown() || $domain->isTest()) {
throw new Exception('Unknown public suffix for domain.');
}
if (!$isMainDomain) {
if (! $isMainDomain) {
// TODO: Would be awesome to also support A/AAAA records here. Maybe dry run?
// Validate if domain target is properly configured
$target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', ''));
if (!$target->isKnown() || $target->isTest()) {
throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.');
if (! $target->isKnown() || $target->isTest()) {
throw new Exception('Unreachable CNAME target ('.$target->get().'), please use a domain with a public suffix.');
}
// Verify domain with DNS records
$validator = new CNAME($target->get());
if (!$validator->isValid($domain->get())) {
if (! $validator->isValid($domain->get())) {
throw new Exception('Failed to verify domain DNS records.');
}
} else {
@@ -238,13 +235,12 @@ class CertificatesV1 extends Worker
/**
* Reads expiry date of certificate from file and decides if renewal is required or not.
*
* @param string $domain Domain for which we check certificate file
*
* @param string $domain Domain for which we check certificate file
* @return bool True, if certificate needs to be renewed
*/
private function isRenewRequired(string $domain): bool
{
$certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem';
$certPath = APP_STORAGE_CERTIFICATES.'/'.$domain.'/cert.pem';
if (\file_exists($certPath)) {
$validTo = null;
@@ -268,9 +264,8 @@ class CertificatesV1 extends Worker
/**
* LetsEncrypt communication to issue certificate (using certbot CLI)
*
* @param string $folder Folder into which certificates should be generated
* @param string $domain Domain to generate certificate for
*
* @param string $folder Folder into which certificates should be generated
* @param string $domain Domain to generate certificate for
* @return array Named array with keys 'stdout' and 'stderr', both string
*/
private function issueCertificate(string $folder, string $domain, string $email): array
@@ -280,83 +275,82 @@ class CertificatesV1 extends Worker
$staging = (App::isProduction()) ? '' : ' --dry-run';
$exit = Console::execute("certbot certonly --webroot --noninteractive --agree-tos{$staging}"
. " --email " . $email
. " --cert-name " . $folder
. " -w " . APP_STORAGE_CERTIFICATES
. " -d {$domain}", '', $stdout, $stderr);
.' --email '.$email
.' --cert-name '.$folder
.' -w '.APP_STORAGE_CERTIFICATES
." -d {$domain}", '', $stdout, $stderr);
// Unexpected error, usually 5XX, API limits, ...
if ($exit !== 0) {
throw new Exception('Failed to issue a certificate with message: ' . $stderr);
throw new Exception('Failed to issue a certificate with message: '.$stderr);
}
return [
'stdout' => $stdout,
'stderr' => $stderr
'stderr' => $stderr,
];
}
/**
* Read new renew date from certificate file generated by Let's Encrypt
*
* @param string $domain Domain which certificate was generated for
*
* @param string $domain Domain which certificate was generated for
* @return string
*/
private function getRenewDate(string $domain): string
{
$certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem';
$certPath = APP_STORAGE_CERTIFICATES.'/'.$domain.'/cert.pem';
$certData = openssl_x509_parse(file_get_contents($certPath));
$validTo = $certData['validTo_time_t'] ?? null;
$dt = (new \DateTime())->setTimestamp($validTo);
return DateTime::addSeconds($dt, -60 * 60 * 24 * 30); // -30 days
}
/**
* Method to take files from Let's Encrypt, and put it into Traefik.
*
* @param string $domain Domain which certificate was generated for
* @param string $folder Folder in which certificates were generated
* @param array $letsEncryptData Let's Encrypt logs to use for additional info when throwing error
*
* @param string $domain Domain which certificate was generated for
* @param string $folder Folder in which certificates were generated
* @param array $letsEncryptData Let's Encrypt logs to use for additional info when throwing error
* @return void
*/
private function applyCertificateFiles(string $folder, string $domain, array $letsEncryptData): void
{
// Prepare folder in storage for domain
$path = APP_STORAGE_CERTIFICATES . '/' . $domain;
if (!\is_readable($path)) {
if (!\mkdir($path, 0755, true)) {
$path = APP_STORAGE_CERTIFICATES.'/'.$domain;
if (! \is_readable($path)) {
if (! \mkdir($path, 0755, true)) {
throw new Exception('Failed to create path for certificate.');
}
}
// Move generated files
if (!@\rename('/etc/letsencrypt/live/' . $folder . '/cert.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem')) {
throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']);
if (! @\rename('/etc/letsencrypt/live/'.$folder.'/cert.pem', APP_STORAGE_CERTIFICATES.'/'.$domain.'/cert.pem')) {
throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: '.$letsEncryptData['stderr'].' ; '.$letsEncryptData['stdout']);
}
if (!@\rename('/etc/letsencrypt/live/' . $folder . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/chain.pem')) {
throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']);
if (! @\rename('/etc/letsencrypt/live/'.$folder.'/chain.pem', APP_STORAGE_CERTIFICATES.'/'.$domain.'/chain.pem')) {
throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: '.$letsEncryptData['stderr'].' ; '.$letsEncryptData['stdout']);
}
if (!@\rename('/etc/letsencrypt/live/' . $folder . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/fullchain.pem')) {
throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']);
if (! @\rename('/etc/letsencrypt/live/'.$folder.'/fullchain.pem', APP_STORAGE_CERTIFICATES.'/'.$domain.'/fullchain.pem')) {
throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: '.$letsEncryptData['stderr'].' ; '.$letsEncryptData['stdout']);
}
if (!@\rename('/etc/letsencrypt/live/' . $folder . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/privkey.pem')) {
throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']);
if (! @\rename('/etc/letsencrypt/live/'.$folder.'/privkey.pem', APP_STORAGE_CERTIFICATES.'/'.$domain.'/privkey.pem')) {
throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: '.$letsEncryptData['stderr'].' ; '.$letsEncryptData['stdout']);
}
$config = \implode(PHP_EOL, [
"tls:",
" certificates:",
'tls:',
' certificates:',
" - certFile: /storage/certificates/{$domain}/fullchain.pem",
" keyFile: /storage/certificates/{$domain}/privkey.pem"
" keyFile: /storage/certificates/{$domain}/privkey.pem",
]);
// Save configuration into Traefik using our new cert files
if (!\file_put_contents(APP_STORAGE_CONFIG . '/' . $domain . '.yml', $config)) {
if (! \file_put_contents(APP_STORAGE_CONFIG.'/'.$domain.'.yml', $config)) {
throw new Exception('Failed to save Traefik configuration.');
}
}
@@ -364,39 +358,38 @@ class CertificatesV1 extends Worker
/**
* Method to make sure information about error is delivered to admnistrator.
*
* @param string $domain Domain that caused the error
* @param string $errorMessage Verbose error message
* @param int $attempt How many times it failed already
*
* @param string $domain Domain that caused the error
* @param string $errorMessage Verbose error message
* @param int $attempt How many times it failed already
* @return void
*/
private function notifyError(string $domain, string $errorMessage, int $attempt): void
{
// Log error into console
Console::warning('Cannot renew domain (' . $domain . ') on attempt no. ' . $attempt . ' certificate: ' . $errorMessage);
Console::warning('Cannot renew domain ('.$domain.') on attempt no. '.$attempt.' certificate: '.$errorMessage);
// Send mail to administratore mail
$locale = new Locale(App::getEnv('_APP_LOCALE', 'en'));
if (!$locale->getText('emails.sender') || !$locale->getText("emails.certificate.hello") || !$locale->getText("emails.certificate.subject") || !$locale->getText("emails.certificate.body") || !$locale->getText("emails.certificate.footer") || !$locale->getText("emails.certificate.thanks") || !$locale->getText("emails.certificate.signature")) {
if (! $locale->getText('emails.sender') || ! $locale->getText('emails.certificate.hello') || ! $locale->getText('emails.certificate.subject') || ! $locale->getText('emails.certificate.body') || ! $locale->getText('emails.certificate.footer') || ! $locale->getText('emails.certificate.thanks') || ! $locale->getText('emails.certificate.signature')) {
$locale->setDefault('en');
}
$body = Template::fromFile(__DIR__ . '/../config/locale/templates/email-base.tpl');
$body = Template::fromFile(__DIR__.'/../config/locale/templates/email-base.tpl');
$subject = \sprintf($locale->getText("emails.certificate.subject"), $domain);
$body->setParam('{{domain}}', $domain);
$body->setParam('{{error}}', $errorMessage);
$body->setParam('{{attempt}}', $attempt);
$subject = \sprintf($locale->getText('emails.certificate.subject'), $domain);
$body->setParam('{{domain}}', $domain);
$body->setParam('{{error}}', $errorMessage);
$body->setParam('{{attempt}}', $attempt);
$body
->setParam('{{subject}}', $subject)
->setParam('{{hello}}', $locale->getText("emails.certificate.hello"))
->setParam('{{body}}', $locale->getText("emails.certificate.body"))
->setParam('{{redirect}}', 'https://' . $domain)
->setParam('{{footer}}', $locale->getText("emails.certificate.footer"))
->setParam('{{thanks}}', $locale->getText("emails.certificate.thanks"))
->setParam('{{signature}}', $locale->getText("emails.certificate.signature"))
->setParam('{{hello}}', $locale->getText('emails.certificate.hello'))
->setParam('{{body}}', $locale->getText('emails.certificate.body'))
->setParam('{{redirect}}', 'https://'.$domain)
->setParam('{{footer}}', $locale->getText('emails.certificate.footer'))
->setParam('{{thanks}}', $locale->getText('emails.certificate.thanks'))
->setParam('{{signature}}', $locale->getText('emails.certificate.signature'))
->setParam('{{project}}', 'Console')
->setParam('{{direction}}', $locale->getText('settings.direction'))
->setParam('{{bg-body}}', '#f7f7f7')
@@ -418,9 +411,8 @@ class CertificatesV1 extends Worker
* - when renew creates new document? It might?
* - overall makes it more reliable
*
* @param string $certificateId ID of a new or updated certificate document
* @param string $domain Domain that is affected by new certificate
*
* @param string $certificateId ID of a new or updated certificate document
* @param string $domain Domain that is affected by new certificate
* @return void
*/
private function updateDomainDocuments(string $certificateId, string $domain): void
+53 -51
View File
@@ -9,10 +9,10 @@ use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception as DatabaseException;
require_once __DIR__ . '/../init.php';
require_once __DIR__.'/../init.php';
Console::title('Database V1 Worker');
Console::success(APP_NAME . ' database worker v1 has started' . "\n");
Console::success(APP_NAME.' database worker v1 has started'."\n");
class DatabaseV1 extends Worker
{
@@ -51,7 +51,7 @@ class DatabaseV1 extends Worker
break;
default:
Console::error('No database operation for type: ' . $type);
Console::error('No database operation for type: '.$type);
break;
}
}
@@ -61,10 +61,10 @@ class DatabaseV1 extends Worker
}
/**
* @param Document $database
* @param Document $collection
* @param Document $attribute
* @param Document $project
* @param Document $database
* @param Document $collection
* @param Document $attribute
* @param Document $project
*/
protected function createAttribute(Document $database, Document $collection, Document $attribute, Document $project): void
{
@@ -75,7 +75,7 @@ class DatabaseV1 extends Worker
$events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].update', [
'databaseId' => $database->getId(),
'collectionId' => $collection->getId(),
'attributeId' => $attribute->getId()
'attributeId' => $attribute->getId(),
]);
/**
* Fetch attribute from the database, since with Resque float values are loosing informations.
@@ -99,15 +99,15 @@ class DatabaseV1 extends Worker
try {
switch ($type) {
case Database::VAR_RELATIONSHIP:
$relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']);
$relatedCollection = $dbForProject->getDocument('database_'.$database->getInternalId(), $options['relatedCollection']);
if ($relatedCollection->isEmpty()) {
throw new DatabaseException('Collection not found');
}
if (
!$dbForProject->createRelationship(
collection: 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
relatedCollection: 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(),
! $dbForProject->createRelationship(
collection: 'database_'.$database->getInternalId().'_collection_'.$collection->getInternalId(),
relatedCollection: 'database_'.$database->getInternalId().'_collection_'.$relatedCollection->getInternalId(),
type: $options['relationType'],
twoWay: $options['twoWay'],
id: $key,
@@ -119,12 +119,12 @@ class DatabaseV1 extends Worker
}
if ($options['twoWay']) {
$relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']);
$relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId().'_'.$relatedCollection->getInternalId().'_'.$options['twoWayKey']);
$dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'available'));
}
break;
default:
if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) {
if (! $dbForProject->createAttribute('database_'.$database->getInternalId().'_collection_'.$collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) {
throw new Exception('Failed to create Attribute');
}
}
@@ -170,23 +170,24 @@ class DatabaseV1 extends Worker
options: [
'projectId' => $projectId,
'databaseId' => $database->getId(),
'collectionId' => $collection->getId()
'collectionId' => $collection->getId(),
]
);
}
if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) {
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId());
$dbForProject->deleteCachedDocument('database_'.$database->getInternalId(), $relatedCollection->getId());
}
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId);
$dbForProject->deleteCachedDocument('database_'.$database->getInternalId(), $collectionId);
}
/**
* @param Document $database
* @param Document $collection
* @param Document $attribute
* @param Document $project
* @param Document $database
* @param Document $collection
* @param Document $attribute
* @param Document $project
*
* @throws Throwable
*/
protected function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project): void
@@ -198,7 +199,7 @@ class DatabaseV1 extends Worker
$events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete', [
'databaseId' => $database->getId(),
'collectionId' => $collection->getId(),
'attributeId' => $attribute->getId()
'attributeId' => $attribute->getId(),
]);
$collectionId = $collection->getId();
$key = $attribute->getAttribute('key', '');
@@ -219,25 +220,25 @@ class DatabaseV1 extends Worker
if ($status !== 'failed') {
if ($type === Database::VAR_RELATIONSHIP) {
if ($options['twoWay']) {
$relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']);
$relatedCollection = $dbForProject->getDocument('database_'.$database->getInternalId(), $options['relatedCollection']);
if ($relatedCollection->isEmpty()) {
throw new DatabaseException('Collection not found');
}
$relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']);
$relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId().'_'.$relatedCollection->getInternalId().'_'.$options['twoWayKey']);
}
if (!$dbForProject->deleteRelationship('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) {
if (! $dbForProject->deleteRelationship('database_'.$database->getInternalId().'_collection_'.$collection->getInternalId(), $key)) {
$dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'stuck'));
throw new DatabaseException('Failed to delete Relationship');
}
} elseif (!$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) {
} elseif (! $dbForProject->deleteAttribute('database_'.$database->getInternalId().'_collection_'.$collection->getInternalId(), $key)) {
throw new DatabaseException('Failed to delete Attribute');
}
}
$dbForProject->deleteDocument('attributes', $attribute->getId());
if (!$relatedAttribute->isEmpty()) {
if (! $relatedAttribute->isEmpty()) {
$dbForProject->deleteDocument('attributes', $relatedAttribute->getId());
}
} catch (\Exception $e) {
@@ -245,7 +246,7 @@ class DatabaseV1 extends Worker
if ($e instanceof DatabaseException) {
$attribute->setAttribute('error', $e->getMessage());
if (!$relatedAttribute->isEmpty()) {
if (! $relatedAttribute->isEmpty()) {
$relatedAttribute->setAttribute('error', $e->getMessage());
}
}
@@ -254,7 +255,7 @@ class DatabaseV1 extends Worker
$attribute->getId(),
$attribute->setAttribute('status', 'stuck')
);
if (!$relatedAttribute->isEmpty()) {
if (! $relatedAttribute->isEmpty()) {
$dbForProject->updateDocument(
'attributes',
$relatedAttribute->getId(),
@@ -278,7 +279,7 @@ class DatabaseV1 extends Worker
options: [
'projectId' => $projectId,
'databaseId' => $database->getId(),
'collectionId' => $collection->getId()
'collectionId' => $collection->getId(),
]
);
}
@@ -334,20 +335,21 @@ class DatabaseV1 extends Worker
}
}
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId);
$dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
$dbForProject->deleteCachedDocument('database_'.$database->getInternalId(), $collectionId);
$dbForProject->deleteCachedCollection('database_'.$database->getInternalId().'_collection_'.$collection->getInternalId());
if (!$relatedCollection->isEmpty() && !$relatedAttribute->isEmpty()) {
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId());
$dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId());
if (! $relatedCollection->isEmpty() && ! $relatedAttribute->isEmpty()) {
$dbForProject->deleteCachedDocument('database_'.$database->getInternalId(), $relatedCollection->getId());
$dbForProject->deleteCachedCollection('database_'.$database->getInternalId().'_collection_'.$relatedCollection->getInternalId());
}
}
/**
* @param Document $database
* @param Document $collection
* @param Document $index
* @param Document $project
* @param Document $database
* @param Document $collection
* @param Document $index
* @param Document $project
*
* @throws \Exception
*/
protected function createIndex(Document $database, Document $collection, Document $index, Document $project): void
@@ -359,7 +361,7 @@ class DatabaseV1 extends Worker
$events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].update', [
'databaseId' => $database->getId(),
'collectionId' => $collection->getId(),
'indexId' => $index->getId()
'indexId' => $index->getId(),
]);
$collectionId = $collection->getId();
$key = $index->getAttribute('key', '');
@@ -370,7 +372,7 @@ class DatabaseV1 extends Worker
$project = $dbForConsole->getDocument('projects', $projectId);
try {
if (!$dbForProject->createIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $attributes, $lengths, $orders)) {
if (! $dbForProject->createIndex('database_'.$database->getInternalId().'_collection_'.$collection->getInternalId(), $key, $type, $attributes, $lengths, $orders)) {
throw new DatabaseException('Failed to create Index');
}
$dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available'));
@@ -402,19 +404,19 @@ class DatabaseV1 extends Worker
options: [
'projectId' => $projectId,
'databaseId' => $database->getId(),
'collectionId' => $collection->getId()
'collectionId' => $collection->getId(),
]
);
}
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId);
$dbForProject->deleteCachedDocument('database_'.$database->getInternalId(), $collectionId);
}
/**
* @param Document $database
* @param Document $collection
* @param Document $index
* @param Document $project
* @param Document $database
* @param Document $collection
* @param Document $index
* @param Document $project
*/
protected function deleteIndex(Document $database, Document $collection, Document $index, Document $project): void
{
@@ -425,14 +427,14 @@ class DatabaseV1 extends Worker
$events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].delete', [
'databaseId' => $database->getId(),
'collectionId' => $collection->getId(),
'indexId' => $index->getId()
'indexId' => $index->getId(),
]);
$key = $index->getAttribute('key');
$status = $index->getAttribute('status', '');
$project = $dbForConsole->getDocument('projects', $projectId);
try {
if ($status !== 'failed' && !$dbForProject->deleteIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) {
if ($status !== 'failed' && ! $dbForProject->deleteIndex('database_'.$database->getInternalId().'_collection_'.$collection->getInternalId(), $key)) {
throw new DatabaseException('Failed to delete index');
}
$dbForProject->deleteDocument('indexes', $index->getId());
@@ -464,11 +466,11 @@ class DatabaseV1 extends Worker
options: [
'projectId' => $projectId,
'databaseId' => $database->getId(),
'collectionId' => $collection->getId()
'collectionId' => $collection->getId(),
]
);
}
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collection->getId());
$dbForProject->deleteCachedDocument('database_'.$database->getInternalId(), $collection->getId());
}
}
+144 -131
View File
@@ -1,29 +1,29 @@
<?php
use Appwrite\Auth\Auth;
use Utopia\App;
use Utopia\Cache\Adapter\Filesystem;
use Utopia\Cache\Cache;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Appwrite\Resque\Worker;
use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\CLI\Console;
use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\Cache\Adapter\Filesystem;
use Utopia\Cache\Cache;
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Query;
require_once __DIR__ . '/../init.php';
require_once __DIR__.'/../init.php';
Console::title('Deletes V1 Worker');
Console::success(APP_NAME . ' deletes worker v1 has started' . "\n");
Console::success(APP_NAME.' deletes worker v1 has started'."\n");
class DeletesV1 extends Worker
{
public function getName(): string
{
return "deletes";
return 'deletes';
}
public function init(): void
@@ -71,7 +71,7 @@ class DeletesV1 extends Worker
$this->deleteCollection($document, $project);
break;
}
Console::error('No lazy delete operation available for document of type: ' . $document->getCollection());
Console::error('No lazy delete operation available for document of type: '.$document->getCollection());
break;
}
break;
@@ -82,14 +82,14 @@ class DeletesV1 extends Worker
case DELETE_TYPE_AUDIT:
$datetime = $this->args['datetime'] ?? null;
if (!empty($datetime)) {
if (! empty($datetime)) {
$this->deleteAuditLogs($datetime);
}
$document = new Document($this->args['document'] ?? []);
if (!$document->isEmpty()) {
$this->deleteAuditLogsByResource('document/' . $document->getId(), $project);
if (! $document->isEmpty()) {
$this->deleteAuditLogsByResource('document/'.$document->getId(), $project);
}
break;
@@ -125,7 +125,7 @@ class DeletesV1 extends Worker
$this->deleteSchedules($this->args['datetime']);
break;
default:
Console::error('No delete operation for type: ' . $type);
Console::error('No delete operation for type: '.$type);
break;
}
}
@@ -153,7 +153,8 @@ class DeletesV1 extends Worker
if ($project->isEmpty()) {
$this->getConsoleDB()->deleteDocument('schedules', $document->getId());
Console::success('Deleted schedule for deleted project ' . $document->getAttribute('projectId'));
Console::success('Deleted schedule for deleted project '.$document->getAttribute('projectId'));
return;
}
@@ -161,15 +162,16 @@ class DeletesV1 extends Worker
if ($function->isEmpty()) {
$this->getConsoleDB()->deleteDocument('schedules', $document->getId());
Console::success('Deleted schedule for function ' . $document->getAttribute('resourceId'));
Console::success('Deleted schedule for function '.$document->getAttribute('resourceId'));
}
}
);
}
/**
* @param Document $project
* @param string $resource
* @param Document $project
* @param string $resource
*
* @throws Exception
*/
protected function deleteCacheByResource(Document $project, string $resource): void
@@ -180,19 +182,19 @@ class DeletesV1 extends Worker
if ($document) {
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId)
new Filesystem(APP_STORAGE_CACHE.DIRECTORY_SEPARATOR.'app-'.$projectId)
);
$this->deleteById(
$document,
$dbForProject,
function ($document) use ($cache, $projectId) {
$path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId();
$path = APP_STORAGE_CACHE.DIRECTORY_SEPARATOR.'app-'.$projectId.DIRECTORY_SEPARATOR.$document->getId();
if ($cache->purge($document->getId())) {
Console::success('Deleting cache file: ' . $path);
Console::success('Deleting cache file: '.$path);
} else {
Console::error('Failed to delete cache file: ' . $path);
Console::error('Failed to delete cache file: '.$path);
}
}
);
@@ -200,7 +202,8 @@ class DeletesV1 extends Worker
}
/**
* @param string $datetime
* @param string $datetime
*
* @throws Exception
*/
protected function deleteCacheByDate(string $datetime): void
@@ -209,7 +212,7 @@ class DeletesV1 extends Worker
$projectId = $project->getId();
$dbForProject = $this->getProjectDB($project);
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId)
new Filesystem(APP_STORAGE_CACHE.DIRECTORY_SEPARATOR.'app-'.$projectId)
);
$query = [
@@ -221,22 +224,21 @@ class DeletesV1 extends Worker
$query,
$dbForProject,
function (Document $document) use ($cache, $projectId) {
$path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId();
$path = APP_STORAGE_CACHE.DIRECTORY_SEPARATOR.'app-'.$projectId.DIRECTORY_SEPARATOR.$document->getId();
if ($cache->purge($document->getId())) {
Console::success('Deleting cache file: ' . $path);
Console::success('Deleting cache file: '.$path);
} else {
Console::error('Failed to delete cache file: ' . $path);
Console::error('Failed to delete cache file: '.$path);
}
}
);
});
}
/**
* @param Document $document database document
* @param Document $project
* @param Document $document database document
* @param Document $project
*/
protected function deleteDatabase(Document $document, Document $project): void
{
@@ -245,18 +247,18 @@ class DeletesV1 extends Worker
$dbForProject = $this->getProjectDB($project);
$this->deleteByGroup('database_' . $document->getInternalId(), [], $dbForProject, function ($document) use ($project) {
$this->deleteByGroup('database_'.$document->getInternalId(), [], $dbForProject, function ($document) use ($project) {
$this->deleteCollection($document, $project);
});
$dbForProject->deleteCollection('database_' . $document->getInternalId());
$dbForProject->deleteCollection('database_'.$document->getInternalId());
$this->deleteAuditLogsByResource('database/' . $databaseId, $project);
$this->deleteAuditLogsByResource('database/'.$databaseId, $project);
}
/**
* @param Document $document teams document
* @param Document $project
* @param Document $document teams document
* @param Document $project
*/
protected function deleteCollection(Document $document, Document $project): void
{
@@ -273,32 +275,33 @@ class DeletesV1 extends Worker
);
foreach ($relationships as $relationship) {
if (!$relationship['twoWay']) {
if (! $relationship['twoWay']) {
continue;
}
$relatedCollection = $dbForProject->getDocument('database_' . $databaseInternalId, $relationship['relatedCollection']);
$dbForProject->deleteDocument('attributes', $databaseInternalId . '_' . $relatedCollection->getInternalId() . '_' . $relationship['twoWayKey']);
$dbForProject->deleteCachedDocument('database_' . $databaseInternalId, $relatedCollection->getId());
$dbForProject->deleteCachedCollection('database_' . $databaseInternalId . '_collection_' . $relatedCollection->getInternalId());
$relatedCollection = $dbForProject->getDocument('database_'.$databaseInternalId, $relationship['relatedCollection']);
$dbForProject->deleteDocument('attributes', $databaseInternalId.'_'.$relatedCollection->getInternalId().'_'.$relationship['twoWayKey']);
$dbForProject->deleteCachedDocument('database_'.$databaseInternalId, $relatedCollection->getId());
$dbForProject->deleteCachedCollection('database_'.$databaseInternalId.'_collection_'.$relatedCollection->getInternalId());
}
$dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $document->getInternalId());
$dbForProject->deleteCollection('database_'.$databaseInternalId.'_collection_'.$document->getInternalId());
$this->deleteByGroup('attributes', [
Query::equal('databaseInternalId', [$databaseInternalId]),
Query::equal('collectionInternalId', [$collectionInternalId])
Query::equal('collectionInternalId', [$collectionInternalId]),
], $dbForProject);
$this->deleteByGroup('indexes', [
Query::equal('databaseInternalId', [$databaseInternalId]),
Query::equal('collectionInternalId', [$collectionInternalId])
Query::equal('collectionInternalId', [$collectionInternalId]),
], $dbForProject);
$this->deleteAuditLogsByResource('database/' . $databaseId . '/collection/' . $collectionId, $project);
$this->deleteAuditLogsByResource('database/'.$databaseId.'/collection/'.$collectionId, $project);
}
/**
* @param string $hourlyUsageRetentionDatetime
* @param string $hourlyUsageRetentionDatetime
*
* @throws Exception
*/
protected function deleteUsageStats(string $hourlyUsageRetentionDatetime)
@@ -314,8 +317,8 @@ class DeletesV1 extends Worker
}
/**
* @param Document $document teams document
* @param Document $project
* @param Document $document teams document
* @param Document $project
*/
protected function deleteMemberships(Document $document, Document $project): void
{
@@ -326,7 +329,7 @@ class DeletesV1 extends Worker
$this->deleteByGroup(
'memberships',
[
Query::equal('teamInternalId', [$teamInternalId])
Query::equal('teamInternalId', [$teamInternalId]),
],
$dbForProject,
function (Document $membership) use ($dbForProject) {
@@ -337,8 +340,9 @@ class DeletesV1 extends Worker
}
/**
* @param \Utopia\Database\Document $document
* @param \Utopia\Database\Document $document
* @return void
*
* @throws \Exception
*/
protected function deleteProjectsByTeam(Document $document): void
@@ -346,7 +350,7 @@ class DeletesV1 extends Worker
$dbForConsole = $this->getConsoleDB();
$projects = $dbForConsole->find('projects', [
Query::equal('teamInternalId', [$document->getInternalId()])
Query::equal('teamInternalId', [$document->getInternalId()]),
]);
foreach ($projects as $project) {
@@ -356,7 +360,8 @@ class DeletesV1 extends Worker
}
/**
* @param Document $document project document
* @param Document $document project document
*
* @throws Exception
*/
protected function deleteProject(Document $document): void
@@ -368,7 +373,7 @@ class DeletesV1 extends Worker
$dbForConsole = $this->getConsoleDB();
$domains = $dbForConsole->find('domains', [
Query::equal('projectInternalId', [$projectInternalId])
Query::equal('projectInternalId', [$projectInternalId]),
]);
foreach ($domains as $domain) {
@@ -392,22 +397,22 @@ class DeletesV1 extends Worker
// Delete Platforms
$this->deleteByGroup('platforms', [
Query::equal('projectInternalId', [$projectInternalId])
Query::equal('projectInternalId', [$projectInternalId]),
], $dbForConsole);
// Delete Domains
$this->deleteByGroup('domains', [
Query::equal('projectInternalId', [$projectInternalId])
Query::equal('projectInternalId', [$projectInternalId]),
], $dbForConsole);
// Delete Keys
$this->deleteByGroup('keys', [
Query::equal('projectInternalId', [$projectInternalId])
Query::equal('projectInternalId', [$projectInternalId]),
], $dbForConsole);
// Delete Webhooks
$this->deleteByGroup('webhooks', [
Query::equal('projectInternalId', [$projectInternalId])
Query::equal('projectInternalId', [$projectInternalId]),
], $dbForConsole);
// Delete metadata tables
@@ -431,8 +436,8 @@ class DeletesV1 extends Worker
}
/**
* @param Document $document user document
* @param Document $project
* @param Document $document user document
* @param Document $project
*/
protected function deleteUser(Document $document, Document $project): void
{
@@ -443,19 +448,19 @@ class DeletesV1 extends Worker
// Delete all sessions of this user from the sessions table and update the sessions field of the user record
$this->deleteByGroup('sessions', [
Query::equal('userInternalId', [$userInternalId])
Query::equal('userInternalId', [$userInternalId]),
], $dbForProject);
$dbForProject->deleteCachedDocument('users', $userId);
// Delete Memberships and decrement team membership counts
$this->deleteByGroup('memberships', [
Query::equal('userInternalId', [$userInternalId])
Query::equal('userInternalId', [$userInternalId]),
], $dbForProject, function (Document $document) use ($dbForProject) {
if ($document->getAttribute('confirm')) { // Count only confirmed members
$teamId = $document->getAttribute('teamId');
$team = $dbForProject->getDocument('teams', $teamId);
if (!$team->isEmpty()) {
if (! $team->isEmpty()) {
$team = $dbForProject->updateDocument(
'teams',
$teamId,
@@ -468,17 +473,18 @@ class DeletesV1 extends Worker
// Delete tokens
$this->deleteByGroup('tokens', [
Query::equal('userInternalId', [$userInternalId])
Query::equal('userInternalId', [$userInternalId]),
], $dbForProject);
// Delete identities
$this->deleteByGroup('identities', [
Query::equal('userInternalId', [$userInternalId])
Query::equal('userInternalId', [$userInternalId]),
], $dbForProject);
}
/**
* @param string $datetime
* @param string $datetime
*
* @throws Exception
*/
protected function deleteExecutionLogs(string $datetime): void
@@ -487,7 +493,7 @@ class DeletesV1 extends Worker
$dbForProject = $this->getProjectDB($project);
// Delete Executions
$this->deleteByGroup('executions', [
Query::lessThan('$createdAt', $datetime)
Query::lessThan('$createdAt', $datetime),
], $dbForProject);
});
}
@@ -505,13 +511,14 @@ class DeletesV1 extends Worker
// Delete Sessions
$this->deleteByGroup('sessions', [
Query::lessThan('$createdAt', $expired)
Query::lessThan('$createdAt', $expired),
], $dbForProject);
});
}
/**
* @param string $datetime
* @param string $datetime
*
* @throws Exception
*/
protected function deleteRealtimeUsage(string $datetime): void
@@ -520,13 +527,14 @@ class DeletesV1 extends Worker
$dbForProject = $this->getProjectDB($project);
// Delete Dead Realtime Logs
$this->deleteByGroup('realtime', [
Query::lessThan('timestamp', $datetime)
Query::lessThan('timestamp', $datetime),
], $dbForProject);
});
}
/**
* @param string $datetime
* @param string $datetime
*
* @throws Exception
*/
protected function deleteAbuseLogs(string $datetime): void
@@ -538,17 +546,18 @@ class DeletesV1 extends Worker
$this->deleteForProjectIds(function (Document $project) use ($datetime) {
$projectId = $project->getId();
$dbForProject = $this->getProjectDB($project);
$timeLimit = new TimeLimit("", 0, 1, $dbForProject);
$timeLimit = new TimeLimit('', 0, 1, $dbForProject);
$abuse = new Abuse($timeLimit);
$status = $abuse->cleanup($datetime);
if (!$status) {
throw new Exception('Failed to delete Abuse logs for project ' . $projectId);
if (! $status) {
throw new Exception('Failed to delete Abuse logs for project '.$projectId);
}
});
}
/**
* @param string $datetime
* @param string $datetime
*
* @throws Exception
*/
protected function deleteAuditLogs(string $datetime): void
@@ -562,28 +571,28 @@ class DeletesV1 extends Worker
$dbForProject = $this->getProjectDB($project);
$audit = new Audit($dbForProject);
$status = $audit->cleanup($datetime);
if (!$status) {
throw new Exception('Failed to delete Audit logs for project' . $projectId);
if (! $status) {
throw new Exception('Failed to delete Audit logs for project'.$projectId);
}
});
}
/**
* @param string $resource
* @param Document $project
* @param string $resource
* @param Document $project
*/
protected function deleteAuditLogsByResource(string $resource, Document $project): void
{
$dbForProject = $this->getProjectDB($project);
$this->deleteByGroup(Audit::COLLECTION, [
Query::equal('resource', [$resource])
Query::equal('resource', [$resource]),
], $dbForProject);
}
/**
* @param Document $document function document
* @param Document $project
* @param Document $document function document
* @param Document $project
*/
protected function deleteFunction(Document $document, Document $project): void
{
@@ -595,41 +604,41 @@ class DeletesV1 extends Worker
/**
* Delete Variables
*/
Console::info("Deleting variables for function " . $functionId);
Console::info('Deleting variables for function '.$functionId);
$this->deleteByGroup('variables', [
Query::equal('functionInternalId', [$functionInternalId])
Query::equal('functionInternalId', [$functionInternalId]),
], $dbForProject);
/**
* Delete Deployments
*/
Console::info("Deleting deployments for function " . $functionId);
Console::info('Deleting deployments for function '.$functionId);
$storageFunctions = $this->getFunctionsDevice($projectId);
$deploymentIds = [];
$this->deleteByGroup('deployments', [
Query::equal('resourceId', [$functionId])
Query::equal('resourceId', [$functionId]),
], $dbForProject, function (Document $document) use ($storageFunctions, &$deploymentIds) {
$deploymentIds[] = $document->getId();
if ($storageFunctions->delete($document->getAttribute('path', ''), true)) {
Console::success('Deleted deployment files: ' . $document->getAttribute('path', ''));
Console::success('Deleted deployment files: '.$document->getAttribute('path', ''));
} else {
Console::error('Failed to delete deployment files: ' . $document->getAttribute('path', ''));
Console::error('Failed to delete deployment files: '.$document->getAttribute('path', ''));
}
});
/**
* Delete builds
*/
Console::info("Deleting builds for function " . $functionId);
Console::info('Deleting builds for function '.$functionId);
$storageBuilds = $this->getBuildsDevice($projectId);
foreach ($deploymentIds as $deploymentId) {
$this->deleteByGroup('builds', [
Query::equal('deploymentId', [$deploymentId])
], $dbForProject, function (Document $document) use ($storageBuilds, $deploymentId) {
Query::equal('deploymentId', [$deploymentId]),
], $dbForProject, function (Document $document) use ($storageBuilds) {
if ($storageBuilds->delete($document->getAttribute('outputPath', ''), true)) {
Console::success('Deleted build files: ' . $document->getAttribute('outputPath', ''));
Console::success('Deleted build files: '.$document->getAttribute('outputPath', ''));
} else {
Console::error('Failed to delete build files: ' . $document->getAttribute('outputPath', ''));
Console::error('Failed to delete build files: '.$document->getAttribute('outputPath', ''));
}
});
}
@@ -637,17 +646,17 @@ class DeletesV1 extends Worker
/**
* Delete Executions
*/
Console::info("Deleting executions for function " . $functionId);
Console::info('Deleting executions for function '.$functionId);
$this->deleteByGroup('executions', [
Query::equal('functionId', [$functionId])
Query::equal('functionId', [$functionId]),
], $dbForProject);
// TODO: Request executor to delete runtime
}
/**
* @param Document $document deployment document
* @param Document $project
* @param Document $document deployment document
* @param Document $project
*/
protected function deleteDeployment(Document $document, Document $project): void
{
@@ -659,45 +668,44 @@ class DeletesV1 extends Worker
/**
* Delete deployment files
*/
Console::info("Deleting deployment files for deployment " . $deploymentId);
Console::info('Deleting deployment files for deployment '.$deploymentId);
$storageFunctions = $this->getFunctionsDevice($projectId);
if ($storageFunctions->delete($document->getAttribute('path', ''), true)) {
Console::success('Deleted deployment files: ' . $document->getAttribute('path', ''));
Console::success('Deleted deployment files: '.$document->getAttribute('path', ''));
} else {
Console::error('Failed to delete deployment files: ' . $document->getAttribute('path', ''));
Console::error('Failed to delete deployment files: '.$document->getAttribute('path', ''));
}
/**
* Delete builds
*/
Console::info("Deleting builds for deployment " . $deploymentId);
Console::info('Deleting builds for deployment '.$deploymentId);
$storageBuilds = $this->getBuildsDevice($projectId);
$this->deleteByGroup('builds', [
Query::equal('deploymentId', [$deploymentId])
Query::equal('deploymentId', [$deploymentId]),
], $dbForProject, function (Document $document) use ($storageBuilds) {
if ($storageBuilds->delete($document->getAttribute('outputPath', ''), true)) {
Console::success('Deleted build files: ' . $document->getAttribute('outputPath', ''));
Console::success('Deleted build files: '.$document->getAttribute('outputPath', ''));
} else {
Console::error('Failed to delete build files: ' . $document->getAttribute('outputPath', ''));
Console::error('Failed to delete build files: '.$document->getAttribute('outputPath', ''));
}
});
// TODO: Request executor to delete runtime
}
/**
* @param Document $document to be deleted
* @param Database $database to delete it from
* @param callable|null $callback to perform after document is deleted
*
* @param Document $document to be deleted
* @param Database $database to delete it from
* @param callable|null $callback to perform after document is deleted
* @return bool
*
* @throws \Utopia\Database\Exception\Authorization
*/
protected function deleteById(Document $document, Database $database, callable $callback = null): bool
{
if ($database->deleteDocument($document->getCollection(), $document->getId())) {
Console::success('Deleted document "' . $document->getId() . '" successfully');
Console::success('Deleted document "'.$document->getId().'" successfully');
if (is_callable($callback)) {
$callback($document);
@@ -705,13 +713,15 @@ class DeletesV1 extends Worker
return true;
} else {
Console::error('Failed to delete document: ' . $document->getId());
Console::error('Failed to delete document: '.$document->getId());
return false;
}
}
/**
* @param callable $callback
* @param callable $callback
*
* @throws Exception
*/
protected function deleteForProjectIds(callable $callback): void
@@ -733,7 +743,7 @@ class DeletesV1 extends Worker
/** @var string[] $projectIds */
$sum = count($projects);
Console::info('Executing delete function for chunk #' . $chunk . '. Found ' . $sum . ' projects');
Console::info('Executing delete function for chunk #'.$chunk.'. Found '.$sum.' projects');
foreach ($projects as $project) {
$callback($project);
$count++;
@@ -741,14 +751,15 @@ class DeletesV1 extends Worker
}
$executionEnd = \microtime(true);
Console::info("Found {$count} projects " . ($executionEnd - $executionStart) . " seconds");
Console::info("Found {$count} projects ".($executionEnd - $executionStart).' seconds');
}
/**
* @param string $collection collectionID
* @param array $queries
* @param Database $database
* @param callable|null $callback
* @param string $collection collectionID
* @param array $queries
* @param Database $database
* @param callable|null $callback
*
* @throws Exception
*/
protected function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void
@@ -769,7 +780,7 @@ class DeletesV1 extends Worker
$sum = count($results);
Console::info('Deleting chunk #' . $chunk . '. Found ' . $sum . ' documents in collection ' . $database->getNamespace() . '_' . $collection);
Console::info('Deleting chunk #'.$chunk.'. Found '.$sum.' documents in collection '.$database->getNamespace().'_'.$collection);
foreach ($results as $document) {
$this->deleteById($document, $database, $callback);
@@ -782,14 +793,14 @@ class DeletesV1 extends Worker
$executionEnd = \microtime(true);
Console::info("Deleted {$count} document by group in " . ($executionEnd - $executionStart) . " seconds");
Console::info("Deleted {$count} document by group in ".($executionEnd - $executionStart).' seconds');
}
/**
* @param string $collection collectionID
* @param Query[] $queries
* @param Database $database
* @param callable $callback
* @param string $collection collectionID
* @param Query[] $queries
* @param Database $database
* @param callable $callback
*/
protected function listByGroup(string $collection, array $queries, Database $database, callable $callback = null): void
{
@@ -829,11 +840,12 @@ class DeletesV1 extends Worker
$executionEnd = \microtime(true);
Console::info("Listed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds");
Console::info("Listed {$count} document by group in ".($executionEnd - $executionStart).' seconds');
}
/**
* @param Document $document certificates document
* @param Document $document certificates document
*
* @throws \Utopia\Database\Exception\Authorization
*/
protected function deleteCertificates(Document $document): void
@@ -843,10 +855,10 @@ class DeletesV1 extends Worker
// If domain has certificate generated
if (isset($document['certificateId'])) {
$domainUsingCertificate = $consoleDB->findOne('domains', [
Query::equal('certificateId', [$document['certificateId']])
Query::equal('certificateId', [$document['certificateId']]),
]);
if (!$domainUsingCertificate) {
if (! $domainUsingCertificate) {
$mainDomain = App::getEnv('_APP_DOMAIN_TARGET', '');
if ($mainDomain === $document->getAttribute('domain')) {
$domainUsingCertificate = $mainDomain;
@@ -856,13 +868,14 @@ class DeletesV1 extends Worker
// If certificate is still used by some domain, mark we can't delete.
// Current domain should not be found, because we only have copy. Original domain is already deleted from database.
if ($domainUsingCertificate) {
Console::warning("Skipping certificate deletion, because a domain is still using it.");
Console::warning('Skipping certificate deletion, because a domain is still using it.');
return;
}
}
$domain = $document->getAttribute('domain');
$directory = APP_STORAGE_CERTIFICATES . '/' . $domain;
$directory = APP_STORAGE_CERTIFICATES.'/'.$domain;
$checkTraversal = realpath($directory) === $directory;
if ($domain && $checkTraversal && is_dir($directory)) {
@@ -872,7 +885,7 @@ class DeletesV1 extends Worker
}
// Delete files, so Traefik is aware of change
array_map('unlink', glob($directory . '/*.*'));
array_map('unlink', glob($directory.'/*.*'));
rmdir($directory);
Console::info("Deleted certificate files for {$domain}");
} else {
@@ -884,7 +897,7 @@ class DeletesV1 extends Worker
{
$projectId = $project->getId();
$dbForProject = $this->getProjectDB($project);
$dbForProject->deleteCollection('bucket_' . $document->getInternalId());
$dbForProject->deleteCollection('bucket_'.$document->getInternalId());
$device = $this->getFilesDevice($projectId);
+20 -20
View File
@@ -1,11 +1,10 @@
<?php
require_once __DIR__ . '/../worker.php';
require_once __DIR__.'/../worker.php';
use Appwrite\Event\Usage;
use Utopia\Queue\Message;
use Appwrite\Event\Event;
use Appwrite\Event\Func;
use Appwrite\Event\Usage;
use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Utopia\Response\Model\Execution;
use Executor\Executor;
@@ -16,11 +15,12 @@ use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Logger\Log;
use Utopia\Queue\Message;
use Utopia\Queue\Server;
use Utopia\Database\Helpers\Role;
Authorization::disable();
Authorization::setDefaultStatus(false);
@@ -74,8 +74,8 @@ Server::setResource('execute', function () {
/** Check if runtime is supported */
$runtimes = Config::getParam('runtimes', []);
if (!\array_key_exists($function->getAttribute('runtime'), $runtimes)) {
throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported');
if (! \array_key_exists($function->getAttribute('runtime'), $runtimes)) {
throw new Exception('Runtime "'.$function->getAttribute('runtime', '').'" is not supported');
}
$runtime = $runtimes[$function->getAttribute('runtime')];
@@ -109,7 +109,6 @@ Server::setResource('execute', function () {
/**
* Usage
*/
$queueForUsage
->addMetric(METRIC_EXECUTIONS, 1) // per project
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1); // per function
@@ -120,6 +119,7 @@ Server::setResource('execute', function () {
$vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) {
$carry[$var->getAttribute('key')] = $var->getAttribute('value');
return $carry;
}, []);
@@ -164,7 +164,7 @@ Server::setResource('execute', function () {
} catch (\Throwable $th) {
$interval = (new \DateTime())->diff(new \DateTime($execution->getCreatedAt()));
$execution
->setAttribute('duration', (float)$interval->format('%s.%f'))
->setAttribute('duration', (float) $interval->format('%s.%f'))
->setAttribute('status', 'failed')
->setAttribute('statusCode', $th->getCode())
->setAttribute('stderr', $th->getMessage());
@@ -195,7 +195,7 @@ Server::setResource('execute', function () {
/** Trigger realtime event */
$allEvents = Event::generateEvents('functions.[functionId].executions.[executionId].update', [
'functionId' => $function->getId(),
'executionId' => $execution->getId()
'executionId' => $execution->getId(),
]);
$target = Realtime::fromPayload(
// Pass first, most verbose event pattern
@@ -220,10 +220,9 @@ Server::setResource('execute', function () {
/** Trigger usage queue */
$queueForUsage
->setProject($project)
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000))// per project
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000))
->trigger()
;
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int) ($execution->getAttribute('duration') * 1000))// per project
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int) ($execution->getAttribute('duration') * 1000))
->trigger();
};
});
@@ -253,7 +252,7 @@ $server->job()
return;
}
if (!empty($events)) {
if (! empty($events)) {
$limit = 30;
$sum = 30;
$offset = 0;
@@ -262,19 +261,19 @@ $server->job()
while ($sum >= $limit) {
$functions = $dbForProject->find('functions', [
Query::limit($limit),
Query::offset($offset)
Query::offset($offset),
]);
$sum = \count($functions);
$offset = $offset + $limit;
Console::log('Fetched ' . $sum . ' functions...');
Console::log('Fetched '.$sum.' functions...');
foreach ($functions as $function) {
if (!array_intersect($events, $function->getAttribute('events', []))) {
if (! array_intersect($events, $function->getAttribute('events', []))) {
continue;
}
Console::success('Iterating function: ' . $function->getAttribute('name'));
Console::success('Iterating function: '.$function->getAttribute('name'));
try {
$execute(
log: $log,
@@ -291,12 +290,13 @@ $server->job()
executionId: null,
jwt: null
);
Console::success('Triggered function: ' . $events[0]);
Console::success('Triggered function: '.$events[0]);
} catch (\Throwable $th) {
Console::error("Failed to execute " . $function->getId() . " with error: " . $th->getMessage());
Console::error('Failed to execute '.$function->getId().' with error: '.$th->getMessage());
}
}
}
return;
}
+9 -9
View File
@@ -1,20 +1,20 @@
<?php
use Appwrite\Resque\Worker;
use PHPMailer\PHPMailer\PHPMailer;
use Utopia\App;
use Utopia\CLI\Console;
use PHPMailer\PHPMailer\PHPMailer;
require_once __DIR__ . '/../init.php';
require_once __DIR__.'/../init.php';
Console::title('Mails V1 Worker');
Console::success(APP_NAME . ' mails worker v1 has started' . "\n");
Console::success(APP_NAME.' mails worker v1 has started'."\n");
class MailsV1 extends Worker
{
public function getName(): string
{
return "mails";
return 'mails';
}
public function init(): void
@@ -29,10 +29,10 @@ class MailsV1 extends Worker
if (empty($smtp) && empty(App::getEnv('_APP_SMTP_HOST'))) {
Console::info('Skipped mail processing. No SMTP configuration has been set.');
return;
}
$recipient = $this->args['recipient'];
$subject = $this->args['subject'];
$name = $this->args['name'];
@@ -49,7 +49,7 @@ class MailsV1 extends Worker
$mail->clearBCCs();
$mail->clearCCs();
$mail->setFrom(App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), (empty($from) ? \urldecode(App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')) : $from));
$mail->setFrom(App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), (empty($from) ? \urldecode(App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server')) : $from));
$mail->addAddress($recipient, $name);
if (isset($smtp['replyTo'])) {
$mail->addReplyTo($smtp['replyTo']);
@@ -61,7 +61,7 @@ class MailsV1 extends Worker
try {
$mail->send();
} catch (\Exception $error) {
throw new Exception('Error sending mail: ' . $error->getMessage(), 500);
throw new Exception('Error sending mail: '.$error->getMessage(), 500);
}
}
@@ -77,14 +77,14 @@ class MailsV1 extends Worker
$mail->XMailer = 'Appwrite Mailer';
$mail->Host = $smtp['host'];
$mail->Port = $smtp['port'];
$mail->SMTPAuth = (!empty($username) && !empty($password));
$mail->SMTPAuth = (! empty($username) && ! empty($password));
$mail->Username = $username;
$mail->Password = $password;
$mail->SMTPSecure = $smtp['secure'] === 'tls';
$mail->SMTPAutoTLS = false;
$mail->CharSet = 'UTF-8';
$from = \urldecode($smtp['senderName'] ?? App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'));
$from = \urldecode($smtp['senderName'] ?? App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server'));
$email = $smtp['senderEmail'] ?? App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$mail->setFrom($email, $from);
+20 -17
View File
@@ -2,41 +2,43 @@
use Appwrite\Resque\Worker;
use Utopia\CLI\Console;
use Utopia\Messaging\Adapters\Email as EmailAdapter;
use Utopia\Messaging\Adapters\Email\Mailgun;
use Utopia\Messaging\Adapters\Email\SendGrid;
use Utopia\Messaging\Adapters\Push\APNS;
use Utopia\Messaging\Adapters\Push as PushAdapter;
use Utopia\Messaging\Adapters\Push\FCM;
use Utopia\Messaging\Adapters\SMS as SMSAdapter;
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\Push as PushAdapter;
use Utopia\Messaging\Adapters\Push\APNS;
use Utopia\Messaging\Adapters\Push\FCM;
use Utopia\Messaging\Adapters\Email as EmailAdapter;
use Utopia\Messaging\Adapters\Email\Mailgun;
use Utopia\Messaging\Adapters\Email\SendGrid;
require_once __DIR__ . '/../init.php';
require_once __DIR__.'/../init.php';
Console::title('Messaging V1 Worker');
Console::success(APP_NAME . ' messaging worker v1 has started' . "\n");
Console::success(APP_NAME.' messaging worker v1 has started'."\n");
class MessagingV1 extends Worker
{
protected ?SMSAdapter $sms = null;
protected ?PushAdapter $push = null;
protected ?EmailAdapter $email = null;
protected ?PushAdapter $push = null;
protected ?EmailAdapter $email = null;
protected ?string $from = null;
public function getName(): string
{
return "mails";
return 'mails';
}
public function sms($record): ?SMSAdapter
{
$credentials = $record->getAttribute('credentials');
return match ($record->getAttribute('provider')) {
'twilio' => new Twilio($credentials['accountSid'], $credentials['authToken']),
'text-magic' => new TextMagic($credentials['username'], $credentials['apiKey']),
@@ -50,6 +52,7 @@ class MessagingV1 extends Worker
public function push($record): ?PushAdapter
{
$credentials = $record->getAttribute('credentials');
return match ($record->getAttribute('provider')) {
'apns' => new APNS(
$credentials['authKey'],
@@ -66,6 +69,7 @@ class MessagingV1 extends Worker
public function email($record): ?EmailAdapter
{
$credentials = $record->getAttribute('credentials');
return match ($record->getAttribute('provider')) {
'mailgun' => new Mailgun($credentials['apiKey'], $credentials['domain']),
'sendgrid' => new SendGrid($credentials['apiKey']),
@@ -92,9 +96,9 @@ class MessagingV1 extends Worker
default => null
};
// Query for the provider
// switch on provider name
// call function passing needed credentials returns required provider.
// Query for the provider
// switch on provider name
// call function passing needed credentials returns required provider.
$messageId = $this->args['messageId'];
$messageRecord =
@@ -109,7 +113,6 @@ class MessagingV1 extends Worker
default => null
};
$provider->send($message);
}
@@ -141,7 +144,7 @@ class MessagingV1 extends Worker
return [
'from' => $from,
'to' => $to,
'body' => $body
'body' => $body,
];
}
@@ -156,7 +159,7 @@ class MessagingV1 extends Worker
'to' => $to,
'title' => $title,
'body' => $body,
'data' => $data
'data' => $data,
];
}
}
+2 -2
View File
@@ -18,10 +18,10 @@ use Utopia\Migration\Sources\NHost;
use Utopia\Migration\Sources\Supabase;
use Utopia\Migration\Transfer;
require_once __DIR__ . '/../init.php';
require_once __DIR__.'/../init.php';
Console::title('Migrations V1 Worker');
Console::success(APP_NAME . ' Migrations worker v1 has started');
Console::success(APP_NAME.' Migrations worker v1 has started');
class MigrationsV1 extends Worker
{
+45 -47
View File
@@ -1,17 +1,17 @@
<?php
require_once __DIR__ . '/../worker.php';
require_once __DIR__.'/../worker.php';
use Swoole\Timer;
use Utopia\App;
use Utopia\Cache\Cache;
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Validator\Authorization;
use Utopia\Queue\Message;
use Utopia\CLI\Console;
use Utopia\Queue\Server;
use Utopia\Registry\Registry;
@@ -20,8 +20,8 @@ Authorization::setDefaultStatus(false);
$stats = [];
$periods['1h'] = 'Y-m-d H:00';
$periods['1d'] = 'Y-m-d 00:00';
$periods['1h'] = 'Y-m-d H:00';
$periods['1d'] = 'Y-m-d 00:00';
//$periods['1m'] = 'Y-m-1 00:00';
$periods['inf'] = '0000-00-00 00:00';
@@ -32,7 +32,7 @@ const INFINITY_PERIOD = '_inf_';
* When we remove a parent document we need to deduct his children aggregation from the project scope.
*/
Server::setResource('reduce', function (Cache $cache, Registry $register, $pools) {
return function ($database, $projectInternalId, Document $document, array &$metrics) use ($pools, $cache, $register): void {
return function ($database, $projectInternalId, Document $document, array &$metrics) use ($pools, $cache): void {
try {
$dbForProject = new Database(
$pools
@@ -42,12 +42,12 @@ Server::setResource('reduce', function (Cache $cache, Registry $register, $pools
$cache
);
$dbForProject->setNamespace('_' . $projectInternalId);
$dbForProject->setNamespace('_'.$projectInternalId);
switch (true) {
case $document->getCollection() === 'users': // users
$sessions = count($document->getAttribute(METRIC_SESSIONS, 0));
if (!empty($sessions)) {
if (! empty($sessions)) {
$metrics[] = [
'key' => METRIC_SESSIONS,
'value' => ($sessions * -1),
@@ -55,28 +55,28 @@ Server::setResource('reduce', function (Cache $cache, Registry $register, $pools
}
break;
case $document->getCollection() === 'databases': // databases
$collections = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS)));
$documents = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS)));
if (!empty($collections['value'])) {
$collections = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD.str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS)));
$documents = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD.str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS)));
if (! empty($collections['value'])) {
$metrics[] = [
'key' => METRIC_COLLECTIONS,
'value' => ($collections['value'] * -1),
];
}
if (!empty($documents['value'])) {
if (! empty($documents['value'])) {
$metrics[] = [
'key' => METRIC_DOCUMENTS,
'value' => ($documents['value'] * -1),
];
}
break;
case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections
case str_starts_with($document->getCollection(), 'database_') && ! str_contains($document->getCollection(), 'collection'): //collections
$parts = explode('_', $document->getCollection());
$databaseInternalId = $parts[1] ?? 0;
$documents = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $document->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS)));
$documents = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD.str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $document->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS)));
if (!empty($documents['value'])) {
if (! empty($documents['value'])) {
$metrics[] = [
'key' => METRIC_DOCUMENTS,
'value' => ($documents['value'] * -1),
@@ -89,17 +89,17 @@ Server::setResource('reduce', function (Cache $cache, Registry $register, $pools
break;
case $document->getCollection() === 'buckets':
$files = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES)));
$storage = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE)));
$files = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD.str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES)));
$storage = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD.str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE)));
if (!empty($files['value'])) {
if (! empty($files['value'])) {
$metrics[] = [
'key' => METRIC_FILES,
'value' => ($files['value'] * -1),
];
}
if (!empty($storage['value'])) {
if (! empty($storage['value'])) {
$metrics[] = [
'key' => METRIC_FILES_STORAGE,
'value' => ($storage['value'] * -1),
@@ -108,57 +108,57 @@ Server::setResource('reduce', function (Cache $cache, Registry $register, $pools
break;
case $document->getCollection() === 'functions':
$deployments = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS)));
$deploymentsStorage = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE)));
$builds = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS)));
$buildsStorage = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE)));
$buildsCompute = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE)));
$executions = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS)));
$executionsCompute = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE)));
$deployments = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD.str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS)));
$deploymentsStorage = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD.str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE)));
$builds = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD.str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS)));
$buildsStorage = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD.str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE)));
$buildsCompute = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD.str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE)));
$executions = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD.str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS)));
$executionsCompute = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD.str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE)));
if (!empty($deployments['value'])) {
if (! empty($deployments['value'])) {
$metrics[] = [
'key' => METRIC_DEPLOYMENTS,
'value' => ($deployments['value'] * -1),
];
}
if (!empty($deploymentsStorage['value'])) {
if (! empty($deploymentsStorage['value'])) {
$metrics[] = [
'key' => METRIC_DEPLOYMENTS_STORAGE,
'value' => ($deploymentsStorage['value'] * -1),
];
}
if (!empty($builds['value'])) {
if (! empty($builds['value'])) {
$metrics[] = [
'key' => METRIC_BUILDS,
'value' => ($builds['value'] * -1),
];
}
if (!empty($buildsStorage['value'])) {
if (! empty($buildsStorage['value'])) {
$metrics[] = [
'key' => METRIC_BUILDS_STORAGE,
'value' => ($buildsStorage['value'] * -1),
];
}
if (!empty($buildsCompute['value'])) {
if (! empty($buildsCompute['value'])) {
$metrics[] = [
'key' => METRIC_BUILDS_COMPUTE,
'value' => ($buildsCompute['value'] * -1),
];
}
if (!empty($executions['value'])) {
if (! empty($executions['value'])) {
$metrics[] = [
'key' => METRIC_EXECUTIONS,
'value' => ($executions['value'] * -1),
];
}
if (!empty($executionsCompute['value'])) {
if (! empty($executionsCompute['value'])) {
$metrics[] = [
'key' => METRIC_EXECUTIONS_COMPUTE,
'value' => ($executionsCompute['value'] * -1),
@@ -169,20 +169,18 @@ Server::setResource('reduce', function (Cache $cache, Registry $register, $pools
break;
}
} catch (\Exception $e) {
console::error("[reducer] " . " {DateTime::now()} " . " {$projectInternalId} " . " {$e->getMessage()}");
console::error('[reducer] '.' {DateTime::now()} '." {$projectInternalId} "." {$e->getMessage()}");
} finally {
$pools->reclaim();
}
};
}, ['cache', 'register', 'pools']);
$server->job()
->inject('message')
->inject('reduce')
->action(function (Message $message, callable $reduce) use (&$stats) {
$payload = $message->getPayload() ?? [];
$project = new Document($payload['project'] ?? []);
$projectId = $project->getInternalId();
@@ -191,18 +189,19 @@ $server->job()
continue;
}
$reduce(
database: $project->getAttribute('database'),
projectInternalId: $project->getInternalId(),
document: new Document($document),
metrics: $payload['metrics'],
);
$reduce(
database: $project->getAttribute('database'),
projectInternalId: $project->getInternalId(),
document: new Document($document),
metrics: $payload['metrics'],
);
}
$stats[$projectId]['database'] = $project->getAttribute('database');
foreach ($payload['metrics'] ?? [] as $metric) {
if (!isset($stats[$projectId]['keys'][$metric['key']])) {
if (! isset($stats[$projectId]['keys'][$metric['key']])) {
$stats[$projectId]['keys'][$metric['key']] = $metric['value'];
continue;
}
$stats[$projectId]['keys'][$metric['key']] += $metric['value'];
@@ -215,8 +214,7 @@ $server
->inject('cache')
->inject('pools')
->action(function ($register, $cache, $pools) use ($periods, &$stats) {
Timer::tick(30000, function () use ($register, $cache, $pools, $periods, &$stats) {
Timer::tick(30000, function () use ($cache, $pools, $periods, &$stats) {
$offset = count($stats);
$projects = array_slice($stats, 0, $offset, true);
array_splice($stats, 0, $offset);
@@ -231,7 +229,7 @@ $server
$cache
);
$dbForProject->setNamespace('_' . $projectInternalId);
$dbForProject->setNamespace('_'.$projectInternalId);
foreach ($project['keys'] ?? [] as $key => $value) {
if ($value == 0) {
@@ -270,7 +268,7 @@ $server
}
}
}
if (!empty($project['keys'])) {
if (! empty($project['keys'])) {
$dbForProject->createDocument('statsLogger', new Document([
'time' => DateTime::now(),
'metrics' => $project['keys'],
@@ -278,7 +276,7 @@ $server
}
} catch (\Exception $e) {
$now = DateTime::now();
console::error("[Error] " . " Time: {$now} " . " projectInternalId: {$projectInternalId}" . " File: {$e->getFile()}" . " Line: {$e->getLine()} " . " message: {$e->getMessage()}");
console::error('[Error] '." Time: {$now} "." projectInternalId: {$projectInternalId}"." File: {$e->getFile()}"." Line: {$e->getLine()} "." message: {$e->getMessage()}");
} finally {
$pools->reclaim();
}
+15 -15
View File
@@ -5,10 +5,10 @@ use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Database\Document;
require_once __DIR__ . '/../init.php';
require_once __DIR__.'/../init.php';
Console::title('Webhooks V1 Worker');
Console::success(APP_NAME . ' webhooks worker v1 has started');
Console::success(APP_NAME.' webhooks worker v1 has started');
class WebhooksV1 extends Worker
{
@@ -16,7 +16,7 @@ class WebhooksV1 extends Worker
public function getName(): string
{
return "webhooks";
return 'webhooks';
}
public function init(): void
@@ -36,7 +36,7 @@ class WebhooksV1 extends Worker
}
}
if (!empty($this->errors)) {
if (! empty($this->errors)) {
throw new Exception(\implode(" / \n\n", $this->errors));
}
}
@@ -45,7 +45,7 @@ class WebhooksV1 extends Worker
{
$url = \rawurldecode($webhook->getAttribute('url'));
$signatureKey = $webhook->getAttribute('signatureKey');
$signature = base64_encode(hash_hmac('sha1', $url . $payload, $signatureKey, true));
$signature = base64_encode(hash_hmac('sha1', $url.$payload, $signatureKey, true));
$httpUser = $webhook->getAttribute('httpUser');
$httpPass = $webhook->getAttribute('httpPass');
$ch = \curl_init($webhook->getAttribute('url'));
@@ -64,28 +64,28 @@ class WebhooksV1 extends Worker
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,
'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)) {
if (! $webhook->getAttribute('security', true)) {
\curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
\curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
}
if (!empty($httpUser) && !empty($httpPass)) {
if (! empty($httpUser) && ! empty($httpPass)) {
\curl_setopt($ch, CURLOPT_USERPWD, "$httpUser:$httpPass");
\curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
}
if (false === \curl_exec($ch)) {
$this->errors[] = \curl_error($ch) . ' in events ' . implode(', ', $events) . ' for webhook ' . $webhook->getAttribute('name');
$this->errors[] = \curl_error($ch).' in events '.implode(', ', $events).' for webhook '.$webhook->getAttribute('name');
}
\curl_close($ch);
+69 -48
View File
@@ -9,8 +9,8 @@ use Appwrite\Auth\Hash\Phpass;
use Appwrite\Auth\Hash\Scrypt;
use Appwrite\Auth\Hash\Scryptmodified;
use Appwrite\Auth\Hash\Sha;
use Utopia\Database\Document;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Roles;
@@ -25,49 +25,69 @@ class Auth
'phpass',
'scrypt',
'scryptMod',
'plaintext'
'plaintext',
];
public const DEFAULT_ALGO = 'argon2';
public const DEFAULT_ALGO_OPTIONS = ['type' => 'argon2', 'memoryCost' => 2048, 'timeCost' => 4, 'threads' => 3];
/**
* User Roles.
*/
public const USER_ROLE_ANY = 'any';
public const USER_ROLE_GUESTS = 'guests';
public const USER_ROLE_USERS = 'users';
public const USER_ROLE_ADMIN = 'admin';
public const USER_ROLE_DEVELOPER = 'developer';
public const USER_ROLE_OWNER = 'owner';
public const USER_ROLE_APPS = 'apps';
public const USER_ROLE_SYSTEM = 'system';
/**
* Token Types.
*/
public const TOKEN_TYPE_LOGIN = 1; // Deprecated
public const TOKEN_TYPE_VERIFICATION = 2;
public const TOKEN_TYPE_RECOVERY = 3;
public const TOKEN_TYPE_INVITE = 4;
public const TOKEN_TYPE_MAGIC_URL = 5;
public const TOKEN_TYPE_PHONE = 6;
/**
* Session Providers.
*/
public const SESSION_PROVIDER_EMAIL = 'email';
public const SESSION_PROVIDER_ANONYMOUS = 'anonymous';
public const SESSION_PROVIDER_MAGIC_URL = 'magic-url';
public const SESSION_PROVIDER_PHONE = 'phone';
/**
* Token Expiration times.
*/
public const TOKEN_EXPIRATION_LOGIN_LONG = 31536000; /* 1 year */
public const TOKEN_EXPIRATION_LOGIN_SHORT = 3600; /* 1 hour */
public const TOKEN_EXPIRATION_RECOVERY = 3600; /* 1 hour */
public const TOKEN_EXPIRATION_CONFIRM = 3600 * 24 * 7; /* 7 days */
public const TOKEN_EXPIRATION_PHONE = 60 * 15; /* 15 minutes */
/**
@@ -93,7 +113,6 @@ class Auth
* Set Cookie Name.
*
* @param $string
*
* @return string
*/
public static function setCookieName($string)
@@ -104,9 +123,8 @@ class Auth
/**
* Encode Session.
*
* @param string $id
* @param string $secret
*
* @param string $id
* @param string $secret
* @return string
*/
public static function encodeSession($id, $secret)
@@ -120,8 +138,7 @@ class Auth
/**
* Decode Session.
*
* @param string $session
*
* @param string $session
* @return array
*
* @throws \Exception
@@ -131,7 +148,7 @@ class Auth
$session = \json_decode(\base64_decode($session), true);
$default = ['id' => null, 'secret' => ''];
if (!\is_array($session)) {
if (! \is_array($session)) {
return $default;
}
@@ -144,7 +161,6 @@ class Auth
* One-way encryption
*
* @param $string
*
* @return string
*/
public static function hash(string $string)
@@ -157,10 +173,9 @@ class Auth
*
* One way string hashing for user passwords
*
* @param string $string
* @param string $algo hashing algorithm to use
* @param array $options algo-specific options
*
* @param string $string
* @param string $algo hashing algorithm to use
* @param array $options algo-specific options
* @return bool|string|null
*/
public static function passwordHash(string $string, string $algo, array $options = [])
@@ -171,45 +186,51 @@ class Auth
$options = Auth::DEFAULT_ALGO_OPTIONS;
}
if (!\in_array($algo, Auth::SUPPORTED_ALGOS)) {
throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.');
if (! \in_array($algo, Auth::SUPPORTED_ALGOS)) {
throw new \Exception('Hashing algorithm \''.$algo.'\' is not supported.');
}
switch ($algo) {
case 'argon2':
$hasher = new Argon2($options);
return $hasher->hash($string);
case 'bcrypt':
$hasher = new Bcrypt($options);
return $hasher->hash($string);
case 'md5':
$hasher = new Md5($options);
return $hasher->hash($string);
case 'sha':
$hasher = new Sha($options);
return $hasher->hash($string);
case 'phpass':
$hasher = new Phpass($options);
return $hasher->hash($string);
case 'scrypt':
$hasher = new Scrypt($options);
return $hasher->hash($string);
case 'scryptMod':
$hasher = new Scryptmodified($options);
return $hasher->hash($string);
default:
throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.');
throw new \Exception('Hashing algorithm \''.$algo.'\' is not supported.');
}
}
/**
* Password verify.
*
* @param string $plain
* @param string $hash
* @param string $algo hashing algorithm used to hash
* @param array $options algo-specific options
*
* @param string $plain
* @param string $hash
* @param string $algo hashing algorithm used to hash
* @param array $options algo-specific options
* @return bool
*/
public static function passwordVerify(string $plain, string $hash, string $algo, array $options = [])
@@ -220,34 +241,41 @@ class Auth
$options = Auth::DEFAULT_ALGO_OPTIONS;
}
if (!\in_array($algo, Auth::SUPPORTED_ALGOS)) {
throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.');
if (! \in_array($algo, Auth::SUPPORTED_ALGOS)) {
throw new \Exception('Hashing algorithm \''.$algo.'\' is not supported.');
}
switch ($algo) {
case 'argon2':
$hasher = new Argon2($options);
return $hasher->verify($plain, $hash);
case 'bcrypt':
$hasher = new Bcrypt($options);
return $hasher->verify($plain, $hash);
case 'md5':
$hasher = new Md5($options);
return $hasher->verify($plain, $hash);
case 'sha':
$hasher = new Sha($options);
return $hasher->verify($plain, $hash);
case 'phpass':
$hasher = new Phpass($options);
return $hasher->verify($plain, $hash);
case 'scrypt':
$hasher = new Scrypt($options);
return $hasher->verify($plain, $hash);
case 'scryptMod':
$hasher = new Scryptmodified($options);
return $hasher->verify($plain, $hash);
default:
throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.');
throw new \Exception('Hashing algorithm \''.$algo.'\' is not supported.');
}
}
@@ -256,8 +284,7 @@ class Auth
*
* Generate random password string
*
* @param int $length
*
* @param int $length
* @return string
*/
public static function passwordGenerator(int $length = 20): string
@@ -270,8 +297,7 @@ class Auth
*
* Generate random password string
*
* @param int $length
*
* @param int $length
* @return string
*/
public static function tokenGenerator(int $length = 128): string
@@ -284,8 +310,7 @@ class Auth
*
* Generate random code string
*
* @param int $length
*
* @param int $length
* @return string
*/
public static function codeGenerator(int $length = 6): string
@@ -302,10 +327,9 @@ class Auth
/**
* Verify token and check that its not expired.
*
* @param array $tokens
* @param int $type
* @param string $secret
*
* @param array $tokens
* @param int $type
* @param string $secret
* @return bool|string
*/
public static function tokenVerify(array $tokens, int $type, string $secret)
@@ -320,7 +344,7 @@ class Auth
$token->getAttribute('secret') === self::hash($secret) &&
DateTime::formatTz($token->getAttribute('expire')) >= DateTime::formatTz(DateTime::now())
) {
return (string)$token->getId();
return (string) $token->getId();
}
}
@@ -349,10 +373,9 @@ class Auth
/**
* Verify session and check that its not expired.
*
* @param array $sessions
* @param string $secret
* @param string $expires
*
* @param array $sessions
* @param string $secret
* @param string $expires
* @return bool|string
*/
public static function sessionVerify(array $sessions, string $secret, int $expires)
@@ -375,8 +398,7 @@ class Auth
/**
* Is Privileged User?
*
* @param array $roles
*
* @param array $roles
* @return bool
*/
public static function isPrivilegedUser(array $roles): bool
@@ -395,8 +417,7 @@ class Auth
/**
* Is App User?
*
* @param array $roles
*
* @param array $roles
* @return bool
*/
public static function isAppUser(array $roles): bool
@@ -411,14 +432,14 @@ class Auth
/**
* Returns all roles for a user.
*
* @param Document $user
* @param Document $user
* @return array
*/
public static function getRoles(Document $user): array
{
$roles = [];
if (!self::isPrivilegedUser(Authorization::getRoles()) && !self::isAppUser(Authorization::getRoles())) {
if (! self::isPrivilegedUser(Authorization::getRoles()) && ! self::isAppUser(Authorization::getRoles())) {
if ($user->getId()) {
$roles[] = Role::user($user->getId())->toString();
$roles[] = Role::users()->toString();
@@ -439,7 +460,7 @@ class Auth
}
foreach ($user->getAttribute('memberships', []) as $node) {
if (!isset($node['confirm']) || !$node['confirm']) {
if (! isset($node['confirm']) || ! $node['confirm']) {
continue;
}
@@ -456,7 +477,7 @@ class Auth
}
foreach ($user->getAttribute('labels', []) as $label) {
$roles[] = 'label:' . $label;
$roles[] = 'label:'.$label;
}
return $roles;
+12 -13
View File
@@ -5,13 +5,13 @@ namespace Appwrite\Auth;
abstract class Hash
{
/**
* @var array $options Hashing-algo specific options
*/
* @var array Hashing-algo specific options
*/
protected array $options = [];
/**
* @param array $options Hashing-algo specific options
*/
* @param array $options Hashing-algo specific options
*/
public function __construct(array $options = [])
{
$this->setOptions($options);
@@ -20,11 +20,12 @@ abstract class Hash
/**
* Set hashing algo options
*
* @param array $options Hashing-algo specific options
*/
* @param array $options Hashing-algo specific options
*/
public function setOptions(array $options): self
{
$this->options = \array_merge([], $this->getDefaultOptions(), $options);
return $this;
}
@@ -32,24 +33,22 @@ abstract class Hash
* Get hashing algo options
*
* @return array $options Hashing-algo specific options
*/
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @param string $password Input password to hash
*
* @param string $password Input password to hash
* @return string hash
*/
abstract public function hash(string $password): string;
/**
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
*
* @return boolean true if password matches hash
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
* @return bool true if password matches hash
*/
abstract public function verify(string $password, string $hash): bool;
+4 -6
View File
@@ -15,8 +15,7 @@ use Appwrite\Auth\Hash;
class Argon2 extends Hash
{
/**
* @param string $password Input password to hash
*
* @param string $password Input password to hash
* @return string hash
*/
public function hash(string $password): string
@@ -25,10 +24,9 @@ class Argon2 extends Hash
}
/**
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
*
* @return boolean true if password matches hash
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
* @return bool true if password matches hash
*/
public function verify(string $password, string $hash): bool
{
+5 -7
View File
@@ -14,8 +14,7 @@ use Appwrite\Auth\Hash;
class Bcrypt extends Hash
{
/**
* @param string $password Input password to hash
*
* @param string $password Input password to hash
* @return string hash
*/
public function hash(string $password): string
@@ -24,10 +23,9 @@ class Bcrypt extends Hash
}
/**
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
*
* @return boolean true if password matches hash
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
* @return bool true if password matches hash
*/
public function verify(string $password, string $hash): bool
{
@@ -41,6 +39,6 @@ class Bcrypt extends Hash
*/
public function getDefaultOptions(): array
{
return [ 'cost' => 8 ];
return ['cost' => 8];
}
}
+4 -6
View File
@@ -12,8 +12,7 @@ use Appwrite\Auth\Hash;
class Md5 extends Hash
{
/**
* @param string $password Input password to hash
*
* @param string $password Input password to hash
* @return string hash
*/
public function hash(string $password): string
@@ -22,10 +21,9 @@ class Md5 extends Hash
}
/**
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
*
* @return boolean true if password matches hash
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
* @return bool true if password matches hash
*/
public function verify(string $password, string $hash): bool
{
+31 -30
View File
@@ -39,6 +39,7 @@ class Phpass extends Hash
* Alphabet used in itoa64 conversions.
*
* @var string
*
* @since 0.1.0
*/
protected string $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
@@ -59,8 +60,7 @@ class Phpass extends Hash
}
/**
* @param string $password Input password to hash
*
* @param string $password Input password to hash
* @return string hash
*/
public function hash(string $password): string
@@ -68,7 +68,7 @@ class Phpass extends Hash
$options = $this->getDefaultOptions();
$random = '';
if (CRYPT_BLOWFISH === 1 && !$options['portable_hashes']) {
if (CRYPT_BLOWFISH === 1 && ! $options['portable_hashes']) {
$random = $this->getRandomBytes(16, $options);
$hash = crypt($password, $this->gensaltBlowfish($random, $options));
if (strlen($hash) === 60) {
@@ -92,10 +92,9 @@ class Phpass extends Hash
}
/**
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
*
* @return boolean true if password matches hash
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
* @return bool true if password matches hash
*/
public function verify(string $password, string $hash): bool
{
@@ -114,15 +113,16 @@ class Phpass extends Hash
}
/**
* @param int $count
* @param int $count
* @return string $output
*
* @return String $output
* @since 0.1.0
*
* @throws Exception Thows an Exception if the $count parameter is not a positive integer.
*/
protected function getRandomBytes(int $count, array $options): string
{
if (!is_int($count) || $count < 1) {
if (! is_int($count) || $count < 1) {
throw new \Exception('Argument count must be a positive integer');
}
$output = '';
@@ -135,7 +135,7 @@ class Phpass extends Hash
$output = '';
for ($i = 0; $i < $count; $i += 16) {
$options['iteration_count_log2'] = md5(microtime() . $options['iteration_count_log2']);
$options['iteration_count_log2'] = md5(microtime().$options['iteration_count_log2']);
$output .= md5($options['iteration_count_log2'], true);
}
@@ -146,47 +146,48 @@ class Phpass extends Hash
}
/**
* @param String $input
* @param int $count
* @param string $input
* @param int $count
* @return string $output
*
* @return String $output
* @since 0.1.0
*
* @throws Exception Thows an Exception if the $count parameter is not a positive integer.
*/
protected function encode64($input, $count)
{
if (!is_int($count) || $count < 1) {
if (! is_int($count) || $count < 1) {
throw new \Exception('Argument count must be a positive integer');
}
$output = '';
$i = 0;
do {
$value = ord($input[$i++]);
$output .= $this->itoa64[$value & 0x3f];
$output .= $this->itoa64[$value & 0x3F];
if ($i < $count) {
$value |= ord($input[$i]) << 8;
}
$output .= $this->itoa64[($value >> 6) & 0x3f];
$output .= $this->itoa64[($value >> 6) & 0x3F];
if ($i++ >= $count) {
break;
}
if ($i < $count) {
$value |= ord($input[$i]) << 16;
}
$output .= $this->itoa64[($value >> 12) & 0x3f];
$output .= $this->itoa64[($value >> 12) & 0x3F];
if ($i++ >= $count) {
break;
}
$output .= $this->itoa64[($value >> 18) & 0x3f];
$output .= $this->itoa64[($value >> 18) & 0x3F];
} while ($i < $count);
return $output;
}
/**
* @param String $input
* @param string $input
* @return string $output
*
* @return String $output
* @since 0.1.0
*/
private function gensaltPrivate($input, $options)
@@ -199,10 +200,10 @@ class Phpass extends Hash
}
/**
* @param String $password
* @param String $setting
* @param string $password
* @param string $setting
* @return string $output
*
* @return String $output
* @since 0.1.0
*/
private function cryptPrivate($password, $setting)
@@ -233,9 +234,9 @@ class Phpass extends Hash
* consequently in lower iteration counts and hashes that are
* quicker to crack (by non-PHP code).
*/
$hash = md5($salt . $password, true);
$hash = md5($salt.$password, true);
do {
$hash = md5($hash . $password, true);
$hash = md5($hash.$password, true);
} while (--$count);
$output = substr($setting, 0, 12);
$output .= $this->encode64($hash, 16);
@@ -244,9 +245,9 @@ class Phpass extends Hash
}
/**
* @param String $input
* @param string $input
* @return string $output
*
* @return String $output
* @since 0.1.0
*/
private function gensaltBlowfish($input, $options)
@@ -278,11 +279,11 @@ class Phpass extends Hash
$c2 = ord($input[$i++]);
$c1 |= $c2 >> 4;
$output .= $itoa64[$c1];
$c1 = ($c2 & 0x0f) << 2;
$c1 = ($c2 & 0x0F) << 2;
$c2 = ord($input[$i++]);
$c1 |= $c2 >> 6;
$output .= $itoa64[$c1];
$output .= $itoa64[$c2 & 0x3f];
$output .= $itoa64[$c2 & 0x3F];
} while (1);
return $output;
+5 -7
View File
@@ -17,8 +17,7 @@ use Appwrite\Auth\Hash;
class Scrypt extends Hash
{
/**
* @param string $password Input password to hash
*
* @param string $password Input password to hash
* @return string hash
*/
public function hash(string $password): string
@@ -29,10 +28,9 @@ class Scrypt extends Hash
}
/**
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
*
* @return boolean true if password matches hash
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
* @return bool true if password matches hash
*/
public function verify(string $password, string $hash): bool
{
@@ -46,6 +44,6 @@ class Scrypt extends Hash
*/
public function getDefaultOptions(): array
{
return [ 'costCpu' => 8, 'costMemory' => 14, 'costParallel' => 1, 'length' => 64 ];
return ['costCpu' => 8, 'costMemory' => 14, 'costParallel' => 1, 'length' => 64];
}
}
+6 -8
View File
@@ -16,8 +16,7 @@ use Appwrite\Auth\Hash;
class Scryptmodified extends Hash
{
/**
* @param string $password Input password to hash
*
* @param string $password Input password to hash
* @return string hash
*/
public function hash(string $password): string
@@ -33,10 +32,9 @@ class Scryptmodified extends Hash
}
/**
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
*
* @return boolean true if password matches hash
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
* @return bool true if password matches hash
*/
public function verify(string $password, string $hash): bool
{
@@ -50,7 +48,7 @@ class Scryptmodified extends Hash
*/
public function getDefaultOptions(): array
{
return [ ];
return [];
}
private function generateDerivedKey(string $password)
@@ -60,7 +58,7 @@ class Scryptmodified extends Hash
$saltBytes = \base64_decode($options['salt']);
$saltSeparatorBytes = \base64_decode($options['saltSeparator']);
$derivedKey = \scrypt(\utf8_encode($password), $saltBytes . $saltSeparatorBytes, 16384, 8, 1, 64);
$derivedKey = \scrypt(\utf8_encode($password), $saltBytes.$saltSeparatorBytes, 16384, 8, 1, 64);
$derivedKey = \hex2bin($derivedKey);
return $derivedKey;
+5 -7
View File
@@ -16,8 +16,7 @@ use Appwrite\Auth\Hash;
class Sha extends Hash
{
/**
* @param string $password Input password to hash
*
* @param string $password Input password to hash
* @return string hash
*/
public function hash(string $password): string
@@ -28,10 +27,9 @@ class Sha extends Hash
}
/**
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
*
* @return boolean true if password matches hash
* @param string $password Input password to validate
* @param string $hash Hash to verify password against
* @return bool true if password matches hash
*/
public function verify(string $password, string $hash): bool
{
@@ -45,6 +43,6 @@ class Sha extends Hash
*/
public function getDefaultOptions(): array
{
return [ 'version' => 'sha3-512' ];
return ['version' => 'sha3-512'];
}
}
+22 -34
View File
@@ -34,11 +34,11 @@ abstract class OAuth2
/**
* OAuth2 constructor.
*
* @param string $appId
* @param string $appSecret
* @param string $callback
* @param array $state
* @param array $scopes
* @param string $appId
* @param string $appSecret
* @param string $callback
* @param array $state
* @param array $scopes
*/
public function __construct(string $appId, string $appSecret, string $callback, array $state = [], array $scopes = [])
{
@@ -62,29 +62,25 @@ abstract class OAuth2
abstract public function getLoginURL(): string;
/**
* @param string $code
*
* @param string $code
* @return array
*/
abstract protected function getTokens(string $code): array;
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
abstract public function refreshTokens(string $refreshToken): array;
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
abstract public function getUserID(string $accessToken): string;
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
abstract public function getUserEmail(string $accessToken): string;
@@ -92,28 +88,25 @@ abstract class OAuth2
/**
* Check if the OAuth email is verified
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
abstract public function isEmailVerified(string $accessToken): bool;
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
abstract public function getUserName(string $accessToken): string;
/**
* @param $scope
*
* @return $this
*/
protected function addScope(string $scope): OAuth2
{
// Add a scope to the scopes array if it isn't already present
if (!\in_array($scope, $this->scopes)) {
if (! \in_array($scope, $this->scopes)) {
$this->scopes[] = $scope;
}
@@ -129,8 +122,7 @@ abstract class OAuth2
}
/**
* @param string $code
*
* @param string $code
* @return string
*/
public function getAccessToken(string $code): string
@@ -141,8 +133,7 @@ abstract class OAuth2
}
/**
* @param string $code
*
* @param string $code
* @return string
*/
public function getRefreshToken(string $code): string
@@ -153,8 +144,7 @@ abstract class OAuth2
}
/**
* @param string $code
*
* @param string $code
* @return string
*/
public function getAccessTokenExpiry(string $code): int
@@ -169,7 +159,6 @@ abstract class OAuth2
// json_decoding
/**
* @param $state
*
* @return array
*/
public function parseState(string $state)
@@ -178,11 +167,10 @@ abstract class OAuth2
}
/**
* @param string $method
* @param string $url
* @param array $headers
* @param string $payload
*
* @param string $method
* @param string $url
* @param array $headers
* @param string $payload
* @return string
*/
protected function request(string $method, string $url = '', array $headers = [], string $payload = ''): string
@@ -194,11 +182,11 @@ abstract class OAuth2
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
\curl_setopt($ch, CURLOPT_USERAGENT, 'Appwrite OAuth2');
if (!empty($payload)) {
if (! empty($payload)) {
\curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
}
$headers[] = 'Content-length: ' . \strlen($payload);
$headers[] = 'Content-length: '.\strlen($payload);
\curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
// Send the request & save response to $response
@@ -212,6 +200,6 @@ abstract class OAuth2
throw new Exception($response, $code);
}
return (string)$response;
return (string) $response;
}
}
+16 -24
View File
@@ -25,7 +25,7 @@ class Amazon extends OAuth2
* @var array
*/
protected array $scopes = [
"profile"
'profile',
];
/**
@@ -37,8 +37,7 @@ class Amazon extends OAuth2
}
/**
* @param string $state
*
* @param string $state
* @return array
*/
public function parseState(string $state)
@@ -46,24 +45,22 @@ class Amazon extends OAuth2
return \json_decode(\html_entity_decode($state), true);
}
/**
* @return string
*/
public function getLoginURL(): string
{
return 'https://www.amazon.com/ap/oa?' . \http_build_query([
return 'https://www.amazon.com/ap/oa?'.\http_build_query([
'response_type' => 'code',
'client_id' => $this->appID,
'scope' => \implode(' ', $this->getScopes()),
'state' => \json_encode($this->state),
'redirect_uri' => $this->callback
'redirect_uri' => $this->callback,
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -79,7 +76,7 @@ class Amazon extends OAuth2
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'redirect_uri' => $this->callback,
'grant_type' => 'authorization_code'
'grant_type' => 'authorization_code',
])
), true);
}
@@ -88,8 +85,7 @@ class Amazon extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
@@ -103,7 +99,7 @@ class Amazon extends OAuth2
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken
'refresh_token' => $refreshToken,
])
), true);
@@ -115,8 +111,7 @@ class Amazon extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -127,8 +122,7 @@ class Amazon extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -143,20 +137,18 @@ class Amazon extends OAuth2
*
* If present, the email is verified. This was verfied through a manual Amazon sign up process
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
{
$email = $this->getUserEmail($accessToken);
return !empty($email);
return ! empty($email);
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -167,16 +159,16 @@ class Amazon extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$user = $this->request('GET', 'https://api.amazon.com/user/profile?access_token=' . \urlencode($accessToken));
$user = $this->request('GET', 'https://api.amazon.com/user/profile?access_token='.\urlencode($accessToken));
$this->user = \json_decode($user, true);
}
return $this->user;
}
}
+20 -27
View File
@@ -24,8 +24,8 @@ class Apple extends OAuth2
* @var array
*/
protected array $scopes = [
"name",
"email"
'name',
'email',
];
/**
@@ -46,19 +46,18 @@ class Apple extends OAuth2
*/
public function getLoginURL(): string
{
return 'https://appleid.apple.com/auth/authorize?' . \http_build_query([
return 'https://appleid.apple.com/auth/authorize?'.\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'state' => \json_encode($this->state),
'response_type' => 'code',
'response_mode' => 'form_post',
'scope' => \implode(' ', $this->getScopes())
'scope' => \implode(' ', $this->getScopes()),
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -86,8 +85,7 @@ class Apple extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
@@ -116,8 +114,7 @@ class Apple extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -126,8 +123,7 @@ class Apple extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -140,8 +136,7 @@ class Apple extends OAuth2
*
* @link https://developer.apple.com/forums/thread/121411
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -154,15 +149,14 @@ class Apple extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
{
if (
isset($this->claims['email']) &&
!empty($this->claims['email']) &&
! empty($this->claims['email']) &&
isset($this->claims['email_verified']) &&
$this->claims['email_verified'] === 'true'
) {
@@ -183,7 +177,7 @@ class Apple extends OAuth2
$keyfile = (isset($secret['p8'])) ? $secret['p8'] : ''; // Your p8 Key file
$keyID = (isset($secret['keyID'])) ? $secret['keyID'] : ''; // Your Key ID
$teamID = (isset($secret['teamID'])) ? $secret['teamID'] : ''; // Your Team ID (see Developer Portal)
$bundleID = $this->appID; // Your Bundle ID
$bundleID = $this->appID; // Your Bundle ID
$headers = [
'alg' => 'ES256',
@@ -200,22 +194,21 @@ class Apple extends OAuth2
$pkey = \openssl_pkey_get_private($keyfile);
$payload = $this->encode(\json_encode($headers)) . '.' . $this->encode(\json_encode($claims));
$payload = $this->encode(\json_encode($headers)).'.'.$this->encode(\json_encode($claims));
$signature = '';
$success = \openssl_sign($payload, $signature, $pkey, OPENSSL_ALGO_SHA256);
if (!$success) {
if (! $success) {
return '';
}
return $payload . '.' . $this->encode($this->fromDER($signature, 64));
return $payload.'.'.$this->encode($this->fromDER($signature, 64));
}
/**
* @param string $data
*
* @param string $data
* @return string
*/
protected function encode($data): string
@@ -224,7 +217,7 @@ class Apple extends OAuth2
}
/**
* @param string $data
* @param string $data
*/
protected function retrievePositiveInteger(string $data): string
{
@@ -236,8 +229,8 @@ class Apple extends OAuth2
}
/**
* @param string $der
* @param int $partLength
* @param string $der
* @param int $partLength
*/
protected function fromDER(string $der, int $partLength): string
{
@@ -270,6 +263,6 @@ class Apple extends OAuth2
$S = $this->retrievePositiveInteger(\mb_substr($hex, 4, $Sl * 2, '8bit'));
$S = \str_pad($S, $partLength, '0', STR_PAD_LEFT);
return \pack('H*', $R . $S);
return \pack('H*', $R.$S);
}
}
+17 -24
View File
@@ -16,7 +16,7 @@ class Auth0 extends OAuth2
'openid',
'profile',
'email',
'offline_access'
'offline_access',
];
/**
@@ -42,18 +42,17 @@ class Auth0 extends OAuth2
*/
public function getLoginURL(): string
{
return 'https://' . $this->getAuth0Domain() . '/authorize?' . \http_build_query([
return 'https://'.$this->getAuth0Domain().'/authorize?'.\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'state' => \json_encode($this->state),
'scope' => \implode(' ', $this->getScopes()),
'response_type' => 'code'
'response_type' => 'code',
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -62,7 +61,7 @@ class Auth0 extends OAuth2
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
'https://' . $this->getAuth0Domain() . '/oauth/token',
'https://'.$this->getAuth0Domain().'/oauth/token',
$headers,
\http_build_query([
'code' => $code,
@@ -70,7 +69,7 @@ class Auth0 extends OAuth2
'client_secret' => $this->getClientSecret(),
'redirect_uri' => $this->callback,
'scope' => \implode(' ', $this->getScopes()),
'grant_type' => 'authorization_code'
'grant_type' => 'authorization_code',
])
), true);
}
@@ -78,10 +77,8 @@ class Auth0 extends OAuth2
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
@@ -89,13 +86,13 @@ class Auth0 extends OAuth2
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
'https://' . $this->getAuth0Domain() . '/oauth/token',
'https://'.$this->getAuth0Domain().'/oauth/token',
$headers,
\http_build_query([
'refresh_token' => $refreshToken,
'client_id' => $this->appID,
'client_secret' => $this->getClientSecret(),
'grant_type' => 'refresh_token'
'grant_type' => 'refresh_token',
])
), true);
@@ -107,8 +104,7 @@ class Auth0 extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -119,8 +115,7 @@ class Auth0 extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -135,8 +130,7 @@ class Auth0 extends OAuth2
*
* @link https://auth0.com/docs/api/authentication?javascript#user-profile
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -151,8 +145,7 @@ class Auth0 extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -163,15 +156,14 @@ class Auth0 extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$headers = ['Authorization: Bearer ' . \urlencode($accessToken)];
$user = $this->request('GET', 'https://' . $this->getAuth0Domain() . '/userinfo', $headers);
$headers = ['Authorization: Bearer '.\urlencode($accessToken)];
$user = $this->request('GET', 'https://'.$this->getAuth0Domain().'/userinfo', $headers);
$this->user = \json_decode($user, true);
}
@@ -214,6 +206,7 @@ class Auth0 extends OAuth2
} catch (\Throwable $th) {
throw new \Exception('Invalid secret');
}
return $secret;
}
}
+21 -26
View File
@@ -16,7 +16,7 @@ class Authentik extends OAuth2
'openid',
'profile',
'email',
'offline_access'
'offline_access',
];
/**
@@ -42,18 +42,17 @@ class Authentik extends OAuth2
*/
public function getLoginURL(): string
{
return 'https://' . $this->getAuthentikDomain() . '/application/o/authorize?' . \http_build_query([
return 'https://'.$this->getAuthentikDomain().'/application/o/authorize?'.\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'state' => \json_encode($this->state),
'scope' => \implode(' ', $this->getScopes()),
'response_type' => 'code'
'response_type' => 'code',
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -62,7 +61,7 @@ class Authentik extends OAuth2
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
'https://' . $this->getAuthentikDomain() . '/application/o/token/',
'https://'.$this->getAuthentikDomain().'/application/o/token/',
$headers,
\http_build_query([
'code' => $code,
@@ -70,17 +69,16 @@ class Authentik extends OAuth2
'client_secret' => $this->getClientSecret(),
'redirect_uri' => $this->callback,
'scope' => \implode(' ', $this->getScopes()),
'grant_type' => 'authorization_code'
'grant_type' => 'authorization_code',
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
@@ -88,13 +86,13 @@ class Authentik extends OAuth2
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
'https://' . $this->getAuthentikDomain() . '/application/o/token/',
'https://'.$this->getAuthentikDomain().'/application/o/token/',
$headers,
\http_build_query([
'refresh_token' => $refreshToken,
'client_id' => $this->appID,
'client_secret' => $this->getClientSecret(),
'grant_type' => 'refresh_token'
'grant_type' => 'refresh_token',
])
), true);
@@ -106,8 +104,7 @@ class Authentik extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -122,8 +119,7 @@ class Authentik extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -140,8 +136,7 @@ class Authentik extends OAuth2
/**
* Check if the User email is verified
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -156,8 +151,7 @@ class Authentik extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -171,16 +165,15 @@ class Authentik extends OAuth2
return '';
}
/**
* @param string $accessToken
*
/**
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$headers = ['Authorization: Bearer ' . \urlencode($accessToken)];
$user = $this->request('GET', 'https://' . $this->getAuthentikDomain() . '/application/o/userinfo/', $headers);
$headers = ['Authorization: Bearer '.\urlencode($accessToken)];
$user = $this->request('GET', 'https://'.$this->getAuthentikDomain().'/application/o/userinfo/', $headers);
$this->user = \json_decode($user, true);
}
@@ -199,7 +192,7 @@ class Authentik extends OAuth2
return $secret['clientSecret'] ?? '';
}
/**
/**
* Extracts the authentik Domain from the JSON stored in appSecret
*
* @return string
@@ -207,6 +200,7 @@ class Authentik extends OAuth2
protected function getAuthentikDomain(): string
{
$secret = $this->getAppSecret();
return $secret['authentikDomain'] ?? '';
}
@@ -222,6 +216,7 @@ class Authentik extends OAuth2
} catch (\Throwable $th) {
throw new \Exception('Invalid secret');
}
return $secret;
}
}
+11 -18
View File
@@ -36,18 +36,17 @@ class Autodesk extends OAuth2
*/
public function getLoginURL(): string
{
return 'https://developer.api.autodesk.com/authentication/v1/authorize?' . \http_build_query([
return 'https://developer.api.autodesk.com/authentication/v1/authorize?'.\http_build_query([
'client_id' => $this->appID,
'scope' => \implode(' ', $this->getScopes()),
'state' => \json_encode($this->state),
'redirect_uri' => $this->callback,
'response_type' => 'code'
'response_type' => 'code',
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -63,7 +62,7 @@ class Autodesk extends OAuth2
'redirect_uri' => $this->callback,
'client_secret' => $this->appSecret,
'code' => $code,
'grant_type' => 'authorization_code'
'grant_type' => 'authorization_code',
])
);
@@ -74,8 +73,7 @@ class Autodesk extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
@@ -105,8 +103,7 @@ class Autodesk extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -117,8 +114,7 @@ class Autodesk extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -133,8 +129,7 @@ class Autodesk extends OAuth2
*
* @link https://docs.github.com/en/rest/users/emails#list-email-addresses-for-the-authenticated-user
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -149,8 +144,7 @@ class Autodesk extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -161,14 +155,13 @@ class Autodesk extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$headers = ['Authorization: Bearer ' . \urlencode($accessToken)];
$headers = ['Authorization: Bearer '.\urlencode($accessToken)];
$user = $this->request('GET', 'https://developer.api.autodesk.com/userprofile/v1/users/@me', $headers);
$this->user = \json_decode($user, true);
}
+13 -19
View File
@@ -37,7 +37,7 @@ class Bitbucket extends OAuth2
*/
public function getLoginURL(): string
{
return 'https://bitbucket.org/site/oauth2/authorize?' . \http_build_query([
return 'https://bitbucket.org/site/oauth2/authorize?'.\http_build_query([
'response_type' => 'code',
'client_id' => $this->appID,
'scope' => \implode(' ', $this->getScopes()),
@@ -46,8 +46,7 @@ class Bitbucket extends OAuth2
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -63,7 +62,7 @@ class Bitbucket extends OAuth2
'code' => $code,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'grant_type' => 'authorization_code'
'grant_type' => 'authorization_code',
])
), true);
}
@@ -72,8 +71,7 @@ class Bitbucket extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
@@ -87,7 +85,7 @@ class Bitbucket extends OAuth2
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken
'refresh_token' => $refreshToken,
])
), true);
@@ -99,8 +97,7 @@ class Bitbucket extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -111,8 +108,7 @@ class Bitbucket extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -125,8 +121,7 @@ class Bitbucket extends OAuth2
/**
* Check if the OAuth email is verified
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -141,8 +136,7 @@ class Bitbucket extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -153,17 +147,16 @@ class Bitbucket extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$user = $this->request('GET', 'https://api.bitbucket.org/2.0/user?access_token=' . \urlencode($accessToken));
$user = $this->request('GET', 'https://api.bitbucket.org/2.0/user?access_token='.\urlencode($accessToken));
$this->user = \json_decode($user, true);
$emails = $this->request('GET', 'https://api.bitbucket.org/2.0/user/emails?access_token=' . \urlencode($accessToken));
$emails = $this->request('GET', 'https://api.bitbucket.org/2.0/user/emails?access_token='.\urlencode($accessToken));
$emails = \json_decode($emails, true);
if (isset($emails['values'])) {
foreach ($emails['values'] as $email) {
@@ -175,6 +168,7 @@ class Bitbucket extends OAuth2
}
}
}
return $this->user;
}
}
+25 -32
View File
@@ -47,17 +47,16 @@ class Bitly extends OAuth2
*/
public function getLoginURL(): string
{
return $this->endpoint . 'authorize?' .
return $this->endpoint.'authorize?'.
\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'state' => \json_encode($this->state)
'state' => \json_encode($this->state),
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -65,14 +64,14 @@ class Bitly extends OAuth2
if (empty($this->tokens)) {
$response = $this->request(
'POST',
$this->resourceEndpoint . 'oauth/access_token',
["Content-Type: application/x-www-form-urlencoded"],
$this->resourceEndpoint.'oauth/access_token',
['Content-Type: application/x-www-form-urlencoded'],
\http_build_query([
"client_id" => $this->appID,
"client_secret" => $this->appSecret,
"code" => $code,
"redirect_uri" => $this->callback,
"state" => \json_encode($this->state)
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'code' => $code,
'redirect_uri' => $this->callback,
'state' => \json_encode($this->state),
])
);
@@ -85,21 +84,20 @@ class Bitly extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
{
$response = $this->request(
'POST',
$this->resourceEndpoint . 'oauth/access_token',
["Content-Type: application/x-www-form-urlencoded"],
$this->resourceEndpoint.'oauth/access_token',
['Content-Type: application/x-www-form-urlencoded'],
\http_build_query([
"client_id" => $this->appID,
"client_secret" => $this->appSecret,
"refresh_token" => $refreshToken,
'grant_type' => 'refresh_token'
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token',
])
);
@@ -115,8 +113,7 @@ class Bitly extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -127,8 +124,7 @@ class Bitly extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -151,8 +147,7 @@ class Bitly extends OAuth2
*
* @link https://dev.bitly.com/api-reference#getUser
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -161,8 +156,7 @@ class Bitly extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -173,19 +167,18 @@ class Bitly extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken)
{
$headers = [
'Authorization: Bearer ' . \urlencode($accessToken),
"Accept: application/json"
'Authorization: Bearer '.\urlencode($accessToken),
'Accept: application/json',
];
if (empty($this->user)) {
$this->user = \json_decode($this->request('GET', $this->resourceEndpoint . "v4/user", $headers), true);
$this->user = \json_decode($this->request('GET', $this->resourceEndpoint.'v4/user', $headers), true);
}
return $this->user;
+23 -30
View File
@@ -49,7 +49,7 @@ class Box extends OAuth2
*/
public function getLoginURL(): string
{
$url = $this->endpoint . 'authorize?' .
$url = $this->endpoint.'authorize?'.
\http_build_query([
'response_type' => 'code',
'client_id' => $this->appID,
@@ -62,8 +62,7 @@ class Box extends OAuth2
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -72,15 +71,15 @@ class Box extends OAuth2
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . 'token',
$this->endpoint.'token',
$headers,
\http_build_query([
"client_id" => $this->appID,
"client_secret" => $this->appSecret,
"code" => $code,
"grant_type" => "authorization_code",
"scope" => \implode(',', $this->getScopes()),
"redirect_uri" => $this->callback
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'code' => $code,
'grant_type' => 'authorization_code',
'scope' => \implode(',', $this->getScopes()),
'redirect_uri' => $this->callback,
])
), true);
}
@@ -89,8 +88,7 @@ class Box extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
@@ -98,13 +96,13 @@ class Box extends OAuth2
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . 'token',
$this->endpoint.'token',
$headers,
\http_build_query([
"client_id" => $this->appID,
"client_secret" => $this->appSecret,
"refresh_token" => $refreshToken,
"grant_type" => "refresh_token",
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token',
])
), true);
@@ -116,8 +114,7 @@ class Box extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -128,8 +125,7 @@ class Box extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -144,20 +140,18 @@ class Box extends OAuth2
*
* If present, the email is verified. This was verfied through a manual Box sign up process
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
{
$email = $this->getUserEmail($accessToken);
return !empty($email);
return ! empty($email);
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -168,19 +162,18 @@ class Box extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
$header = [
'Authorization: Bearer ' . \urlencode($accessToken),
'Authorization: Bearer '.\urlencode($accessToken),
];
if (empty($this->user)) {
$user = $this->request(
'GET',
$this->resourceEndpoint . 'me',
$this->resourceEndpoint.'me',
$header
);
$this->user = \json_decode($user, true);
+20 -30
View File
@@ -24,7 +24,7 @@ class Dailymotion extends OAuth2
*/
protected array $scopes = [
'userinfo',
'email'
'email',
];
/**
@@ -34,7 +34,7 @@ class Dailymotion extends OAuth2
'email',
'id',
'fullname',
'verified'
'verified',
];
/**
@@ -68,21 +68,20 @@ class Dailymotion extends OAuth2
*/
public function getLoginURL(): string
{
$url = $this->authEndpoint . '?' .
$url = $this->authEndpoint.'?'.
\http_build_query([
'response_type' => 'code',
'client_id' => $this->appID,
'state' => \json_encode($this->state),
'redirect_uri' => $this->callback,
'scope' => \implode(' ', $this->getScopes())
'scope' => \implode(' ', $this->getScopes()),
]);
return $url;
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -90,33 +89,31 @@ class Dailymotion extends OAuth2
if (empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . '/oauth/token',
["Content-Type: application/x-www-form-urlencoded"],
$this->endpoint.'/oauth/token',
['Content-Type: application/x-www-form-urlencoded'],
\http_build_query([
'grant_type' => 'authorization_code',
"client_id" => $this->appID,
"client_secret" => $this->appSecret,
"redirect_uri" => $this->callback,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'redirect_uri' => $this->callback,
'code' => $code,
'scope' => \implode(' ', $this->getScopes()),
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
{
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . '/oauth/token',
$this->endpoint.'/oauth/token',
['Content-Type: application/x-www-form-urlencoded'],
\http_build_query([
'grant_type' => 'refresh_token',
@@ -130,13 +127,11 @@ class Dailymotion extends OAuth2
$this->tokens['refresh_token'] = $refreshToken;
}
return $this->tokens;
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -149,8 +144,7 @@ class Dailymotion extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -166,8 +160,7 @@ class Dailymotion extends OAuth2
*
* @link https://developers.dailymotion.com/api/#user-fields
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -178,23 +171,20 @@ class Dailymotion extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
{
$user = $this->getUser($accessToken);
$username = $user['fullname'] ?? '';
return $username;
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
@@ -202,8 +192,8 @@ class Dailymotion extends OAuth2
if (empty($this->user)) {
$user = $this->request(
'GET',
$this->endpoint . '/user/me?fields=' . \implode(',', $this->getFields()),
['Authorization: Bearer ' . \urlencode($accessToken)],
$this->endpoint.'/user/me?fields='.\implode(',', $this->getFields()),
['Authorization: Bearer '.\urlencode($accessToken)],
);
$this->user = \json_decode($user, true);
}
+16 -23
View File
@@ -28,8 +28,8 @@ class Discord extends OAuth2
* @var array
*/
protected array $scopes = [
'identify',
'email'
'identify',
'email',
];
/**
@@ -45,21 +45,20 @@ class Discord extends OAuth2
*/
public function getLoginURL(): string
{
$url = $this->endpoint . '/oauth2/authorize?' .
$url = $this->endpoint.'/oauth2/authorize?'.
\http_build_query([
'response_type' => 'code',
'client_id' => $this->appID,
'state' => \json_encode($this->state),
'scope' => \implode(' ', $this->getScopes()),
'redirect_uri' => $this->callback
'redirect_uri' => $this->callback,
]);
return $url;
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -67,7 +66,7 @@ class Discord extends OAuth2
if (empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . '/oauth2/token',
$this->endpoint.'/oauth2/token',
['Content-Type: application/x-www-form-urlencoded'],
\http_build_query([
'grant_type' => 'authorization_code',
@@ -75,7 +74,7 @@ class Discord extends OAuth2
'redirect_uri' => $this->callback,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'scope' => \implode(' ', $this->getScopes())
'scope' => \implode(' ', $this->getScopes()),
])
), true);
}
@@ -84,15 +83,14 @@ class Discord extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
{
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . '/oauth2/token',
$this->endpoint.'/oauth2/token',
['Content-Type: application/x-www-form-urlencoded'],
\http_build_query([
'grant_type' => 'refresh_token',
@@ -110,8 +108,7 @@ class Discord extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -122,8 +119,7 @@ class Discord extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -138,8 +134,7 @@ class Discord extends OAuth2
*
* @link https://discord.com/developers/docs/resources/user
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -154,8 +149,7 @@ class Discord extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -166,8 +160,7 @@ class Discord extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
@@ -175,8 +168,8 @@ class Discord extends OAuth2
if (empty($this->user)) {
$user = $this->request(
'GET',
$this->endpoint . '/users/@me',
['Authorization: Bearer ' . \urlencode($accessToken)]
$this->endpoint.'/users/@me',
['Authorization: Bearer '.\urlencode($accessToken)]
);
$this->user = \json_decode($user, true);
}
+15 -21
View File
@@ -45,21 +45,20 @@ class Disqus extends OAuth2
*/
public function getLoginURL(): string
{
$url = $this->endpoint . 'oauth/2.0/authorize/?' .
$url = $this->endpoint.'oauth/2.0/authorize/?'.
\http_build_query([
'response_type' => 'code',
'client_id' => $this->appID,
'state' => \json_encode($this->state),
'redirect_uri' => $this->callback,
'scope' => \implode(',', $this->getScopes())
'scope' => \implode(',', $this->getScopes()),
]);
return $url;
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -67,7 +66,7 @@ class Disqus extends OAuth2
if (empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . 'oauth/2.0/access_token/',
$this->endpoint.'oauth/2.0/access_token/',
['Content-Type: application/x-www-form-urlencoded'],
\http_build_query([
'grant_type' => 'authorization_code',
@@ -79,19 +78,19 @@ class Disqus extends OAuth2
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
{
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . 'oauth/2.0/access_token/?',
$this->endpoint.'oauth/2.0/access_token/?',
['Content-Type: application/x-www-form-urlencoded'],
\http_build_query([
'grant_type' => 'refresh_token',
@@ -104,12 +103,12 @@ class Disqus extends OAuth2
if (empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return $this->tokens;
}
/**
* @param string $token
*
* @param string $token
* @return string
*/
public function getUserID(string $accessToken): string
@@ -122,8 +121,7 @@ class Disqus extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -136,13 +134,11 @@ class Disqus extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
{
// Look out for the change in their enpoint.
// It's in Beta so they may provide a parameter in the future.
// https://disqus.com/api/docs/users/details/
@@ -151,8 +147,7 @@ class Disqus extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -165,8 +160,7 @@ class Disqus extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
@@ -174,10 +168,10 @@ class Disqus extends OAuth2
if (empty($this->user)) {
$user = $this->request(
'GET',
$this->endpoint . '3.0/users/details.json?' . \http_build_query([
$this->endpoint.'3.0/users/details.json?'.\http_build_query([
'access_token' => $accessToken,
'api_key' => $this->appID,
'api_secret' => $this->appSecret
'api_secret' => $this->appSecret,
]),
);
$this->user = \json_decode($user, true)['response'];
+12 -19
View File
@@ -38,17 +38,16 @@ class Dropbox extends OAuth2
*/
public function getLoginURL(): string
{
return 'https://www.dropbox.com/oauth2/authorize?' . \http_build_query([
return 'https://www.dropbox.com/oauth2/authorize?'.\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'state' => \json_encode($this->state),
'response_type' => 'code'
'response_type' => 'code',
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -64,7 +63,7 @@ class Dropbox extends OAuth2
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'redirect_uri' => $this->callback,
'grant_type' => 'authorization_code'
'grant_type' => 'authorization_code',
])
), true);
}
@@ -73,8 +72,7 @@ class Dropbox extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
@@ -88,7 +86,7 @@ class Dropbox extends OAuth2
'refresh_token' => $refreshToken,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'grant_type' => 'refresh_token'
'grant_type' => 'refresh_token',
])
), true);
@@ -100,8 +98,7 @@ class Dropbox extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -112,8 +109,7 @@ class Dropbox extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -128,8 +124,7 @@ class Dropbox extends OAuth2
*
* @link https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -144,8 +139,7 @@ class Dropbox extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -156,14 +150,13 @@ class Dropbox extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$headers = ['Authorization: Bearer ' . \urlencode($accessToken)];
$headers = ['Authorization: Bearer '.\urlencode($accessToken)];
$user = $this->request('POST', 'https://api.dropboxapi.com/2/users/get_current_account', $headers);
$this->user = \json_decode($user, true);
}
+13 -20
View File
@@ -30,8 +30,8 @@ class Etsy extends OAuth2
* @var array
*/
protected array $scopes = [
"email_r",
"profile_r",
'email_r',
'profile_r',
];
/**
@@ -64,7 +64,7 @@ class Etsy extends OAuth2
*/
public function getLoginURL(): string
{
return 'https://www.etsy.com/oauth/connect/oauth/authorize?' . \http_build_query([
return 'https://www.etsy.com/oauth/connect/oauth/authorize?'.\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'response_type' => 'code',
@@ -76,8 +76,7 @@ class Etsy extends OAuth2
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -87,7 +86,7 @@ class Etsy extends OAuth2
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . '/oauth/token',
$this->endpoint.'/oauth/token',
$headers,
\http_build_query([
'grant_type' => 'authorization_code',
@@ -103,8 +102,7 @@ class Etsy extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
@@ -113,7 +111,7 @@ class Etsy extends OAuth2
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . '/oauth/token',
$this->endpoint.'/oauth/token',
$headers,
\http_build_query([
'grant_type' => 'refresh_token',
@@ -131,7 +129,6 @@ class Etsy extends OAuth2
/**
* @param $accessToken
*
* @return string
*/
public function getUserID(string $accessToken): string
@@ -143,7 +140,6 @@ class Etsy extends OAuth2
/**
* @param $accessToken
*
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -156,20 +152,18 @@ class Etsy extends OAuth2
*
* OAuth is only allowed if account has been verified through Etsy, itself.
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
{
$email = $this->getUserEmail($accessToken);
return !empty($email);
return ! empty($email);
}
/**
* @param $accessToken
*
* @return string
*/
public function getUserName(string $accessToken): string
@@ -178,21 +172,20 @@ class Etsy extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
if (!empty($this->user)) {
if (! empty($this->user)) {
return $this->user;
}
$headers = ['Authorization: Bearer ' . $accessToken];
$headers = ['Authorization: Bearer '.$accessToken];
$this->user = \json_decode($this->request(
'GET',
'https://api.etsy.com/v3/application/users/' . $this->getUserID($accessToken),
'https://api.etsy.com/v3/application/users/'.$this->getUserID($accessToken),
), true);
return $this->user;
+4 -2
View File
@@ -7,7 +7,9 @@ use Appwrite\Extend\Exception as AppwriteException;
class Exception extends AppwriteException
{
protected string $response = '';
protected string $error = '';
protected string $errorDescription = '';
public function __construct(string $response = '', int $code = 0, \Throwable $previous = null)
@@ -19,11 +21,11 @@ class Exception extends AppwriteException
if (\is_array($decoded['error'])) {
$this->error = $decoded['error']['status'];
$this->errorDescription = $decoded['error']['message'];
$this->message = $this->error . ': ' . $this->errorDescription;
$this->message = $this->error.': '.$this->errorDescription;
} else {
$this->error = $decoded['error'];
$this->errorDescription = $decoded['error_description'];
$this->message = $this->error . ': ' . $this->errorDescription;
$this->message = $this->error.': '.$this->errorDescription;
}
}
$type = match ($code) {
+16 -23
View File
@@ -25,7 +25,7 @@ class Facebook extends OAuth2
* @var array
*/
protected array $scopes = [
'email'
'email',
];
/**
@@ -41,17 +41,16 @@ class Facebook extends OAuth2
*/
public function getLoginURL(): string
{
return 'https://www.facebook.com/' . $this->version . '/dialog/oauth?' . \http_build_query([
return 'https://www.facebook.com/'.$this->version.'/dialog/oauth?'.\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'scope' => \implode(' ', $this->getScopes()),
'state' => \json_encode($this->state)
'state' => \json_encode($this->state),
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -59,11 +58,11 @@ class Facebook extends OAuth2
if (empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'GET',
'https://graph.facebook.com/' . $this->version . '/oauth/access_token?' . \http_build_query([
'https://graph.facebook.com/'.$this->version.'/oauth/access_token?'.\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'client_secret' => $this->appSecret,
'code' => $code
'code' => $code,
])
), true);
}
@@ -72,20 +71,19 @@ class Facebook extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
{
$this->tokens = \json_decode($this->request(
'GET',
'https://graph.facebook.com/' . $this->version . '/oauth/access_token?' . \http_build_query([
'https://graph.facebook.com/'.$this->version.'/oauth/access_token?'.\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'client_secret' => $this->appSecret,
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token'
'grant_type' => 'refresh_token',
])
), true);
@@ -97,8 +95,7 @@ class Facebook extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -109,8 +106,7 @@ class Facebook extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -125,20 +121,18 @@ class Facebook extends OAuth2
*
* If present, the email is verified. This was verfied through a manual Facebook sign up process
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
{
$email = $this->getUserEmail($accessToken);
return !empty($email);
return ! empty($email);
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -149,14 +143,13 @@ class Facebook extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$user = $this->request('GET', 'https://graph.facebook.com/' . $this->version . '/me?fields=email,name&access_token=' . \urlencode($accessToken));
$user = $this->request('GET', 'https://graph.facebook.com/'.$this->version.'/me?fields=email,name&access_token='.\urlencode($accessToken));
$this->user = \json_decode($user, true);
}
+33 -42
View File
@@ -24,7 +24,7 @@ class Firebase extends OAuth2
'https://www.googleapis.com/auth/datastore',
'https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/identitytoolkit',
'https://www.googleapis.com/auth/userinfo.profile'
'https://www.googleapis.com/auth/userinfo.profile',
];
/**
@@ -40,7 +40,7 @@ class Firebase extends OAuth2
*/
public function getLoginURL(): string
{
return 'https://accounts.google.com/o/oauth2/v2/auth?' . \http_build_query([
return 'https://accounts.google.com/o/oauth2/v2/auth?'.\http_build_query([
'access_type' => 'offline',
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
@@ -52,8 +52,7 @@ class Firebase extends OAuth2
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -68,19 +67,18 @@ class Firebase extends OAuth2
'redirect_uri' => $this->callback,
'client_secret' => $this->appSecret,
'code' => $code,
'grant_type' => 'authorization_code'
'grant_type' => 'authorization_code',
])
);
$this->tokens = \json_decode($response, true);
$this->tokens = \json_decode($response, true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
@@ -93,7 +91,7 @@ class Firebase extends OAuth2
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken
'refresh_token' => $refreshToken,
])
);
@@ -108,10 +106,8 @@ class Firebase extends OAuth2
return $this->tokens;
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -122,8 +118,7 @@ class Firebase extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -133,14 +128,12 @@ class Firebase extends OAuth2
return $user['email'] ?? '';
}
/**
* Check if the OAuth email is verified
*
* @link https://docs.github.com/en/rest/users/emails#list-email-addresses-for-the-authenticated-user
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -155,8 +148,7 @@ class Firebase extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -167,8 +159,7 @@ class Firebase extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken)
@@ -176,7 +167,7 @@ class Firebase extends OAuth2
if (empty($this->user)) {
$response = $this->request(
'GET',
'https://www.googleapis.com/oauth2/v1/userinfo?access_token=' . \urlencode($accessToken),
'https://www.googleapis.com/oauth2/v1/userinfo?access_token='.\urlencode($accessToken),
[],
);
@@ -188,7 +179,7 @@ class Firebase extends OAuth2
public function getProjects(string $accessToken): array
{
$projects = $this->request('GET', 'https://firebase.googleapis.com/v1beta1/projects', ['Authorization: Bearer ' . \urlencode($accessToken)]);
$projects = $this->request('GET', 'https://firebase.googleapis.com/v1beta1/projects', ['Authorization: Bearer '.\urlencode($accessToken)]);
$projects = \json_decode($projects, true);
@@ -201,9 +192,9 @@ class Firebase extends OAuth2
public function assignIAMRoles(string $accessToken, string $email, string $projectId)
{
// Get IAM Roles
$iamRoles = $this->request('POST', 'https://cloudresourcemanager.googleapis.com/v1/projects/' . $projectId . ':getIamPolicy', [
'Authorization: Bearer ' . \urlencode($accessToken),
'Content-Type: application/json'
$iamRoles = $this->request('POST', 'https://cloudresourcemanager.googleapis.com/v1/projects/'.$projectId.':getIamPolicy', [
'Authorization: Bearer '.\urlencode($accessToken),
'Content-Type: application/json',
]);
$iamRoles = \json_decode($iamRoles, true);
@@ -211,23 +202,23 @@ class Firebase extends OAuth2
$iamRoles['bindings'][] = [
'role' => 'roles/identitytoolkit.admin',
'members' => [
'serviceAccount:' . $email
]
'serviceAccount:'.$email,
],
];
$iamRoles['bindings'][] = [
'role' => 'roles/firebase.admin',
'members' => [
'serviceAccount:' . $email
]
'serviceAccount:'.$email,
],
];
// Set IAM Roles
$this->request('POST', 'https://cloudresourcemanager.googleapis.com/v1/projects/' . $projectId . ':setIamPolicy', [
'Authorization: Bearer ' . \urlencode($accessToken),
'Content-Type: application/json'
$this->request('POST', 'https://cloudresourcemanager.googleapis.com/v1/projects/'.$projectId.':setIamPolicy', [
'Authorization: Bearer '.\urlencode($accessToken),
'Content-Type: application/json',
], json_encode([
'policy' => $iamRoles
'policy' => $iamRoles,
]));
}
@@ -236,16 +227,16 @@ class Firebase extends OAuth2
// Create Service Account
$response = $this->request(
'POST',
'https://iam.googleapis.com/v1/projects/' . $projectId . '/serviceAccounts',
'https://iam.googleapis.com/v1/projects/'.$projectId.'/serviceAccounts',
[
'Authorization: Bearer ' . \urlencode($accessToken),
'Content-Type: application/json'
'Authorization: Bearer '.\urlencode($accessToken),
'Content-Type: application/json',
],
json_encode([
'accountId' => 'appwrite-migrations',
'serviceAccount' => [
'displayName' => 'Appwrite Migrations'
]
'displayName' => 'Appwrite Migrations',
],
])
);
@@ -256,10 +247,10 @@ class Firebase extends OAuth2
// Create Service Account Key
$responseKey = $this->request(
'POST',
'https://iam.googleapis.com/v1/projects/' . $projectId . '/serviceAccounts/' . $response['email'] . '/keys',
'https://iam.googleapis.com/v1/projects/'.$projectId.'/serviceAccounts/'.$response['email'].'/keys',
[
'Authorization: Bearer ' . \urlencode($accessToken),
'Content-Type: application/json'
'Authorization: Bearer '.\urlencode($accessToken),
'Content-Type: application/json',
]
);
+16 -24
View File
@@ -36,17 +36,16 @@ class Github extends OAuth2
*/
public function getLoginURL(): string
{
return 'https://github.com/login/oauth/authorize?' . \http_build_query([
return 'https://github.com/login/oauth/authorize?'.\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'scope' => \implode(' ', $this->getScopes()),
'state' => \json_encode($this->state)
'state' => \json_encode($this->state),
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -60,7 +59,7 @@ class Github extends OAuth2
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'client_secret' => $this->appSecret,
'code' => $code
'code' => $code,
])
);
@@ -73,8 +72,7 @@ class Github extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
@@ -87,7 +85,7 @@ class Github extends OAuth2
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken
'refresh_token' => $refreshToken,
])
);
@@ -103,8 +101,7 @@ class Github extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -115,8 +112,7 @@ class Github extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -131,8 +127,7 @@ class Github extends OAuth2
*
* @link https://docs.github.com/en/rest/users/emails#list-email-addresses-for-the-authenticated-user
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -147,8 +142,7 @@ class Github extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -159,8 +153,7 @@ class Github extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserSlug(string $accessToken): string
@@ -171,16 +164,15 @@ class Github extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken)
{
if (empty($this->user)) {
$this->user = \json_decode($this->request('GET', 'https://api.github.com/user', ['Authorization: token ' . \urlencode($accessToken)]), true);
$this->user = \json_decode($this->request('GET', 'https://api.github.com/user', ['Authorization: token '.\urlencode($accessToken)]), true);
$emails = $this->request('GET', 'https://api.github.com/user/emails', ['Authorization: token ' . \urlencode($accessToken)]);
$emails = $this->request('GET', 'https://api.github.com/user/emails', ['Authorization: token '.\urlencode($accessToken)]);
$emails = \json_decode($emails, true);
@@ -197,10 +189,10 @@ class Github extends OAuth2
}
}
if (!empty($primaryEmail)) {
if (! empty($primaryEmail)) {
$this->user['email'] = $primaryEmail['email'];
$this->user['verified'] = $primaryEmail['verified'];
} elseif (!empty($verifiedEmail)) {
} elseif (! empty($verifiedEmail)) {
$this->user['email'] = $verifiedEmail['email'];
$this->user['verified'] = $verifiedEmail['verified'];
}
+17 -23
View File
@@ -23,7 +23,7 @@ class Gitlab extends OAuth2
* @var array
*/
protected array $scopes = [
'read_user'
'read_user',
];
/**
@@ -39,18 +39,17 @@ class Gitlab extends OAuth2
*/
public function getLoginURL(): string
{
return $this->getEndpoint() . '/oauth/authorize?' . \http_build_query([
return $this->getEndpoint().'/oauth/authorize?'.\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'scope' => \implode(' ', $this->getScopes()),
'state' => \json_encode($this->state),
'response_type' => 'code'
'response_type' => 'code',
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -58,12 +57,12 @@ class Gitlab extends OAuth2
if (empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
$this->getEndpoint() . '/oauth/token?' . \http_build_query([
$this->getEndpoint().'/oauth/token?'.\http_build_query([
'code' => $code,
'client_id' => $this->appID,
'client_secret' => $this->getAppSecret()['clientSecret'],
'redirect_uri' => $this->callback,
'grant_type' => 'authorization_code'
'grant_type' => 'authorization_code',
])
), true);
}
@@ -72,19 +71,18 @@ class Gitlab extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
{
$this->tokens = \json_decode($this->request(
'POST',
$this->getEndpoint() . '/oauth/token?' . \http_build_query([
$this->getEndpoint().'/oauth/token?'.\http_build_query([
'refresh_token' => $refreshToken,
'client_id' => $this->appID,
'client_secret' => $this->getAppSecret()['clientSecret'],
'grant_type' => 'refresh_token'
'grant_type' => 'refresh_token',
])
), true);
@@ -96,8 +94,7 @@ class Gitlab extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -112,8 +109,7 @@ class Gitlab extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -128,8 +124,7 @@ class Gitlab extends OAuth2
*
* @link https://docs.gitlab.com/ee/api/users.html#list-current-user-for-normal-users
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -144,8 +139,7 @@ class Gitlab extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -156,14 +150,13 @@ class Gitlab extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$user = $this->request('GET', $this->getEndpoint() . '/api/v4/user?access_token=' . \urlencode($accessToken));
$user = $this->request('GET', $this->getEndpoint().'/api/v4/user?access_token='.\urlencode($accessToken));
$this->user = \json_decode($user, true);
}
@@ -182,10 +175,10 @@ class Gitlab extends OAuth2
} catch (\Throwable $th) {
throw new \Exception('Invalid secret');
}
return $secret;
}
/**
* Extracts the Tenant Id from the JSON stored in appSecret. Defaults to 'common' as a fallback
*
@@ -196,6 +189,7 @@ class Gitlab extends OAuth2
$defaultEndpoint = 'https://gitlab.com';
$secret = $this->getAppSecret();
$endpoint = $secret['endpoint'] ?? $defaultEndpoint;
return empty($endpoint) ? $defaultEndpoint : $endpoint;
}
}
+15 -22
View File
@@ -22,7 +22,7 @@ class Google extends OAuth2
protected array $scopes = [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'openid'
'openid',
];
/**
@@ -48,18 +48,17 @@ class Google extends OAuth2
*/
public function getLoginURL(): string
{
return 'https://accounts.google.com/o/oauth2/v2/auth?' . \http_build_query([
return 'https://accounts.google.com/o/oauth2/v2/auth?'.\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'scope' => \implode(' ', $this->getScopes()),
'state' => \json_encode($this->state),
'response_type' => 'code'
'response_type' => 'code',
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -67,13 +66,13 @@ class Google extends OAuth2
if (empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
'https://oauth2.googleapis.com/token?' . \http_build_query([
'https://oauth2.googleapis.com/token?'.\http_build_query([
'code' => $code,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'redirect_uri' => $this->callback,
'scope' => null,
'grant_type' => 'authorization_code'
'grant_type' => 'authorization_code',
])
), true);
}
@@ -82,19 +81,18 @@ class Google extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
{
$this->tokens = \json_decode($this->request(
'POST',
'https://oauth2.googleapis.com/token?' . \http_build_query([
'https://oauth2.googleapis.com/token?'.\http_build_query([
'refresh_token' => $refreshToken,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'grant_type' => 'refresh_token'
'grant_type' => 'refresh_token',
])
), true);
@@ -106,8 +104,7 @@ class Google extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -118,8 +115,7 @@ class Google extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -134,8 +130,7 @@ class Google extends OAuth2
*
* @link https://www.oauth.com/oauth2-servers/signing-in-with-google/verifying-the-user-info/
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -150,8 +145,7 @@ class Google extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -162,14 +156,13 @@ class Google extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$user = $this->request('GET', 'https://www.googleapis.com/oauth2/v3/userinfo?access_token=' . \urlencode($accessToken));
$user = $this->request('GET', 'https://www.googleapis.com/oauth2/v3/userinfo?access_token='.\urlencode($accessToken));
$this->user = \json_decode($user, true);
}
+14 -19
View File
@@ -50,7 +50,7 @@ class Linkedin extends OAuth2
*/
public function getLoginURL(): string
{
return 'https://www.linkedin.com/oauth/v2/authorization?' . \http_build_query([
return 'https://www.linkedin.com/oauth/v2/authorization?'.\http_build_query([
'response_type' => 'code',
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
@@ -60,8 +60,7 @@ class Linkedin extends OAuth2
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -80,12 +79,12 @@ class Linkedin extends OAuth2
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
@@ -106,12 +105,12 @@ class Linkedin extends OAuth2
if (empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return $this->tokens;
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -122,13 +121,12 @@ class Linkedin extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
{
$email = \json_decode($this->request('GET', 'https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))', ['Authorization: Bearer ' . \urlencode($accessToken)]), true);
$email = \json_decode($this->request('GET', 'https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))', ['Authorization: Bearer '.\urlencode($accessToken)]), true);
return $email['elements'][0]['handle~']['emailAddress'] ?? '';
}
@@ -138,20 +136,18 @@ class Linkedin extends OAuth2
*
* If present, the email is verified. This was verfied through a manual Linkedin sign up process
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
{
$email = $this->getUserEmail($accessToken);
return !empty($email);
return ! empty($email);
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -164,21 +160,20 @@ class Linkedin extends OAuth2
}
if (isset($user['localizedLastName'])) {
$name = (empty($name)) ? $user['localizedLastName'] : $name . ' ' . $user['localizedLastName'];
$name = (empty($name)) ? $user['localizedLastName'] : $name.' '.$user['localizedLastName'];
}
return $name;
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken)
{
if (empty($this->user)) {
$this->user = \json_decode($this->request('GET', 'https://api.linkedin.com/v2/me', ['Authorization: Bearer ' . \urlencode($accessToken)]), true);
$this->user = \json_decode($this->request('GET', 'https://api.linkedin.com/v2/me', ['Authorization: Bearer '.\urlencode($accessToken)]), true);
}
return $this->user;
+17 -23
View File
@@ -25,7 +25,7 @@ class Microsoft extends OAuth2
*/
protected array $scopes = [
'offline_access',
'user.read'
'user.read',
];
/**
@@ -41,19 +41,18 @@ class Microsoft extends OAuth2
*/
public function getLoginURL(): string
{
return 'https://login.microsoftonline.com/' . $this->getTenantID() . '/oauth2/v2.0/authorize?' . \http_build_query([
return 'https://login.microsoftonline.com/'.$this->getTenantID().'/oauth2/v2.0/authorize?'.\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'state' => \json_encode($this->state),
'scope' => \implode(' ', $this->getScopes()),
'response_type' => 'code',
'response_mode' => 'query'
'response_mode' => 'query',
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -62,7 +61,7 @@ class Microsoft extends OAuth2
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
'https://login.microsoftonline.com/' . $this->getTenantID() . '/oauth2/v2.0/token',
'https://login.microsoftonline.com/'.$this->getTenantID().'/oauth2/v2.0/token',
$headers,
\http_build_query([
'code' => $code,
@@ -70,7 +69,7 @@ class Microsoft extends OAuth2
'client_secret' => $this->getClientSecret(),
'redirect_uri' => $this->callback,
'scope' => \implode(' ', $this->getScopes()),
'grant_type' => 'authorization_code'
'grant_type' => 'authorization_code',
])
), true);
}
@@ -79,8 +78,7 @@ class Microsoft extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
@@ -88,13 +86,13 @@ class Microsoft extends OAuth2
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
'https://login.microsoftonline.com/' . $this->getTenantID() . '/oauth2/v2.0/token',
'https://login.microsoftonline.com/'.$this->getTenantID().'/oauth2/v2.0/token',
$headers,
\http_build_query([
'refresh_token' => $refreshToken,
'client_id' => $this->appID,
'client_secret' => $this->getClientSecret(),
'grant_type' => 'refresh_token'
'grant_type' => 'refresh_token',
])
), true);
@@ -106,8 +104,7 @@ class Microsoft extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -118,8 +115,7 @@ class Microsoft extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -134,20 +130,18 @@ class Microsoft extends OAuth2
*
* If present, the email is verified. This was verfied through a manual Microsoft sign up process
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
{
$email = $this->getUserEmail($accessToken);
return !empty($email);
return ! empty($email);
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -158,14 +152,13 @@ class Microsoft extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$headers = ['Authorization: Bearer ' . \urlencode($accessToken)];
$headers = ['Authorization: Bearer '.\urlencode($accessToken)];
$user = $this->request('GET', 'https://graph.microsoft.com/v1.0/me', $headers);
$this->user = \json_decode($user, true);
}
@@ -185,6 +178,7 @@ class Microsoft extends OAuth2
} catch (\Throwable $th) {
throw new \Exception('Invalid secret');
}
return $secret;
}
+15 -23
View File
@@ -3,7 +3,6 @@
namespace Appwrite\Auth\OAuth2;
use Appwrite\Auth\OAuth2;
use Utopia\Exception;
class Mock extends OAuth2
{
@@ -16,7 +15,7 @@ class Mock extends OAuth2
* @var array
*/
protected array $scopes = [
'email'
'email',
];
/**
@@ -42,17 +41,16 @@ class Mock extends OAuth2
*/
public function getLoginURL(): string
{
return 'http://localhost/' . $this->version . '/mock/tests/general/oauth2?' . \http_build_query([
return 'http://localhost/'.$this->version.'/mock/tests/general/oauth2?'.\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'scope' => \implode(' ', $this->getScopes()),
'state' => \json_encode($this->state)
'state' => \json_encode($this->state),
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -60,12 +58,12 @@ class Mock extends OAuth2
if (empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'GET',
'http://localhost/' . $this->version . '/mock/tests/general/oauth2/token?' .
'http://localhost/'.$this->version.'/mock/tests/general/oauth2/token?'.
\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'client_secret' => $this->appSecret,
'code' => $code
'code' => $code,
])
), true);
}
@@ -74,20 +72,19 @@ class Mock extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
{
$this->tokens = \json_decode($this->request(
'GET',
'http://localhost/' . $this->version . '/mock/tests/general/oauth2/token?' .
'http://localhost/'.$this->version.'/mock/tests/general/oauth2/token?'.
\http_build_query([
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token'
'grant_type' => 'refresh_token',
])
), true);
@@ -99,8 +96,7 @@ class Mock extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -111,8 +107,7 @@ class Mock extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -125,8 +120,7 @@ class Mock extends OAuth2
/**
* Check if the OAuth email is verified
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -135,8 +129,7 @@ class Mock extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -147,14 +140,13 @@ class Mock extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$user = $this->request('GET', 'http://localhost/' . $this->version . '/mock/tests/general/oauth2/user?token=' . \urlencode($accessToken));
$user = $this->request('GET', 'http://localhost/'.$this->version.'/mock/tests/general/oauth2/user?token='.\urlencode($accessToken));
$this->user = \json_decode($user, true);
}
+18 -25
View File
@@ -44,32 +44,31 @@ class Notion extends OAuth2
*/
public function getLoginURL(): string
{
return $this->endpoint . '/oauth/authorize?' . \http_build_query([
return $this->endpoint.'/oauth/authorize?'.\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'response_type' => 'code',
'state' => \json_encode($this->state),
'owner' => 'user'
'owner' => 'user',
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
{
if (empty($this->tokens)) {
$headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)];
$headers = ['Authorization: Basic '.\base64_encode($this->appID.':'.$this->appSecret)];
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . '/oauth/token',
$this->endpoint.'/oauth/token',
$headers,
\http_build_query([
'grant_type' => 'authorization_code',
'redirect_uri' => $this->callback,
'code' => $code
'code' => $code,
])
), true);
}
@@ -78,16 +77,15 @@ class Notion extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
{
$headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)];
$headers = ['Authorization: Basic '.\base64_encode($this->appID.':'.$this->appSecret)];
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . '/oauth/token',
$this->endpoint.'/oauth/token',
$headers,
\http_build_query([
'grant_type' => 'refresh_token',
@@ -103,8 +101,7 @@ class Notion extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -115,8 +112,7 @@ class Notion extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -131,20 +127,18 @@ class Notion extends OAuth2
*
* If present, the email is verified. This was verfied through a manual Notion sign up process
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
{
$email = $this->getUserEmail($accessToken);
return !empty($email);
return ! empty($email);
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -155,19 +149,18 @@ class Notion extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
$headers = [
'Notion-Version: ' . $this->version,
'Authorization: Bearer ' . \urlencode($accessToken)
'Notion-Version: '.$this->version,
'Authorization: Bearer '.\urlencode($accessToken),
];
if (empty($this->user)) {
$this->user = \json_decode($this->request('GET', $this->endpoint . '/users/me', $headers), true);
$this->user = \json_decode($this->request('GET', $this->endpoint.'/users/me', $headers), true);
}
return $this->user;
+40 -42
View File
@@ -43,7 +43,7 @@ class Oidc extends OAuth2
*/
public function getLoginURL(): string
{
return $this->getAuthorizationEndpoint() . '?' . \http_build_query([
return $this->getAuthorizationEndpoint().'?'.\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'state' => \json_encode($this->state),
@@ -53,8 +53,7 @@ class Oidc extends OAuth2
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -71,17 +70,16 @@ class Oidc extends OAuth2
'client_secret' => $this->getClientSecret(),
'redirect_uri' => $this->callback,
'scope' => \implode(' ', $this->getScopes()),
'grant_type' => 'authorization_code'
'grant_type' => 'authorization_code',
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
@@ -95,7 +93,7 @@ class Oidc extends OAuth2
'refresh_token' => $refreshToken,
'client_id' => $this->appID,
'client_secret' => $this->getClientSecret(),
'grant_type' => 'refresh_token'
'grant_type' => 'refresh_token',
])
), true);
@@ -107,8 +105,7 @@ class Oidc extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -123,8 +120,7 @@ class Oidc extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -141,8 +137,7 @@ class Oidc extends OAuth2
/**
* Check if the User email is verified
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -153,8 +148,7 @@ class Oidc extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -168,15 +162,14 @@ class Oidc extends OAuth2
return '';
}
/**
* @param string $accessToken
*
/**
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$headers = ['Authorization: Bearer ' . \urlencode($accessToken)];
$headers = ['Authorization: Bearer '.\urlencode($accessToken)];
$user = $this->request('GET', $this->getUserinfoEndpoint(), $headers);
$this->user = \json_decode($user, true);
}
@@ -196,7 +189,7 @@ class Oidc extends OAuth2
return $secret['clientSecret'] ?? '';
}
/**
/**
* Extracts the well known endpoint from the JSON stored in appSecret.
*
* @return string
@@ -204,13 +197,14 @@ class Oidc extends OAuth2
protected function getWellKnownEndpoint(): string
{
$secret = $this->getAppSecret();
return $secret['wellKnownEndpoint'] ?? '';
}
/**
* Extracts the authorization endpoint from the JSON stored in appSecret.
*
* If one is not provided, it will be retrieved from the well-known configuration.
* Extracts the authorization endpoint from the JSON stored in appSecret.
*
* If one is not provided, it will be retrieved from the well-known configuration.
*
* @return string
*/
@@ -219,56 +213,59 @@ class Oidc extends OAuth2
$secret = $this->getAppSecret();
$endpoint = $secret['authorizationEndpoint'] ?? '';
if (!empty($endpoint)) {
if (! empty($endpoint)) {
return $endpoint;
}
$wellKnownConfiguration = $this->getWellKnownConfiguration();
return $wellKnownConfiguration['authorization_endpoint'] ?? '';
}
/**
* Extracts the token endpoint from the JSON stored in appSecret.
*
* If one is not provided, it will be retrieved from the well-known configuration.
*
* @return string
*/
* Extracts the token endpoint from the JSON stored in appSecret.
*
* If one is not provided, it will be retrieved from the well-known configuration.
*
* @return string
*/
protected function getTokenEndpoint(): string
{
$secret = $this->getAppSecret();
$endpoint = $secret['tokenEndpoint'] ?? '';
if (!empty($endpoint)) {
if (! empty($endpoint)) {
return $endpoint;
}
$wellKnownConfiguration = $this->getWellKnownConfiguration();
return $wellKnownConfiguration['token_endpoint'] ?? '';
}
/**
* Extracts the userinfo endpoint from the JSON stored in appSecret.
*
* If one is not provided, it will be retrieved from the well-known configuration.
*
* @return string
*/
* Extracts the userinfo endpoint from the JSON stored in appSecret.
*
* If one is not provided, it will be retrieved from the well-known configuration.
*
* @return string
*/
protected function getUserinfoEndpoint(): string
{
$secret = $this->getAppSecret();
$endpoint = $secret['userinfoEndpoint'] ?? '';
if (!empty($endpoint)) {
if (! empty($endpoint)) {
return $endpoint;
}
$wellKnownConfiguration = $this->getWellKnownConfiguration();
return $wellKnownConfiguration['userinfo_endpoint'] ?? '';
}
/**
* Get the well-known configuration using the well known endpoint
*/
/**
* Get the well-known configuration using the well known endpoint
*/
protected function getWellKnownConfiguration(): array
{
if (empty($this->wellKnownConfiguration)) {
@@ -291,6 +288,7 @@ class Oidc extends OAuth2
} catch (\Throwable $th) {
throw new \Exception('Invalid secret');
}
return $secret;
}
}
+17 -24
View File
@@ -16,7 +16,7 @@ class Okta extends OAuth2
'openid',
'profile',
'email',
'offline_access'
'offline_access',
];
/**
@@ -42,18 +42,17 @@ class Okta extends OAuth2
*/
public function getLoginURL(): string
{
return 'https://' . $this->getOktaDomain() . '/oauth2/' . $this->getAuthorizationServerId() . '/v1/authorize?' . \http_build_query([
return 'https://'.$this->getOktaDomain().'/oauth2/'.$this->getAuthorizationServerId().'/v1/authorize?'.\http_build_query([
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'state' => \json_encode($this->state),
'scope' => \implode(' ', $this->getScopes()),
'response_type' => 'code'
'response_type' => 'code',
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -62,7 +61,7 @@ class Okta extends OAuth2
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
'https://' . $this->getOktaDomain() . '/oauth2/' . $this->getAuthorizationServerId() . '/v1/token',
'https://'.$this->getOktaDomain().'/oauth2/'.$this->getAuthorizationServerId().'/v1/token',
$headers,
\http_build_query([
'code' => $code,
@@ -70,7 +69,7 @@ class Okta extends OAuth2
'client_secret' => $this->getClientSecret(),
'redirect_uri' => $this->callback,
'scope' => \implode(' ', $this->getScopes()),
'grant_type' => 'authorization_code'
'grant_type' => 'authorization_code',
])
), true);
}
@@ -78,10 +77,8 @@ class Okta extends OAuth2
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
@@ -89,13 +86,13 @@ class Okta extends OAuth2
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$this->tokens = \json_decode($this->request(
'POST',
'https://' . $this->getOktaDomain() . '/oauth2/' . $this->getAuthorizationServerId() . '/v1/token',
'https://'.$this->getOktaDomain().'/oauth2/'.$this->getAuthorizationServerId().'/v1/token',
$headers,
\http_build_query([
'refresh_token' => $refreshToken,
'client_id' => $this->appID,
'client_secret' => $this->getClientSecret(),
'grant_type' => 'refresh_token'
'grant_type' => 'refresh_token',
])
), true);
@@ -107,8 +104,7 @@ class Okta extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -119,8 +115,7 @@ class Okta extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -135,8 +130,7 @@ class Okta extends OAuth2
*
* @link https://developer.okta.com/docs/reference/api/oidc/#userinfo
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -151,8 +145,7 @@ class Okta extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -163,15 +156,14 @@ class Okta extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$headers = ['Authorization: Bearer ' . \urlencode($accessToken)];
$user = $this->request('GET', 'https://' . $this->getOktaDomain() . '/oauth2/' . $this->getAuthorizationServerId() . '/v1/userinfo', $headers);
$headers = ['Authorization: Bearer '.\urlencode($accessToken)];
$user = $this->request('GET', 'https://'.$this->getOktaDomain().'/oauth2/'.$this->getAuthorizationServerId().'/v1/userinfo', $headers);
$this->user = \json_decode($user, true);
}
@@ -226,6 +218,7 @@ class Okta extends OAuth2
} catch (\Throwable $th) {
throw new \Exception('Invalid secret');
}
return $secret;
}
}
+17 -24
View File
@@ -46,7 +46,7 @@ class Paypal extends OAuth2
protected array $scopes = [
'openid',
'profile',
'email'
'email',
];
/**
@@ -62,14 +62,14 @@ class Paypal extends OAuth2
*/
public function getLoginURL(): string
{
$url = $this->endpoint[$this->environment] . 'connect/?' .
$url = $this->endpoint[$this->environment].'connect/?'.
\http_build_query([
'flowEntry' => 'static',
'response_type' => 'code',
'client_id' => $this->appID,
'scope' => \implode(' ', $this->getScopes()),
// paypal is not accepting localhost string into return uri
'redirect_uri' => \str_replace("localhost", "127.0.0.1", $this->callback),
'redirect_uri' => \str_replace('localhost', '127.0.0.1', $this->callback),
'state' => \json_encode($this->state),
]);
@@ -77,8 +77,7 @@ class Paypal extends OAuth2
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -86,8 +85,8 @@ class Paypal extends OAuth2
if (empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
$this->resourceEndpoint[$this->environment] . 'oauth2/token',
['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)],
$this->resourceEndpoint[$this->environment].'oauth2/token',
['Authorization: Basic '.\base64_encode($this->appID.':'.$this->appSecret)],
\http_build_query([
'code' => $code,
'grant_type' => 'authorization_code',
@@ -99,16 +98,15 @@ class Paypal extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
{
$this->tokens = \json_decode($this->request(
'POST',
$this->resourceEndpoint[$this->environment] . 'oauth2/token',
['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)],
$this->resourceEndpoint[$this->environment].'oauth2/token',
['Authorization: Basic '.\base64_encode($this->appID.':'.$this->appSecret)],
\http_build_query([
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token',
@@ -123,8 +121,7 @@ class Paypal extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -135,8 +132,7 @@ class Paypal extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -148,7 +144,7 @@ class Paypal extends OAuth2
return $email['primary'] === true;
});
if (!empty($email)) {
if (! empty($email)) {
return $email[0]['value'];
}
}
@@ -161,8 +157,7 @@ class Paypal extends OAuth2
*
* @link https://developer.paypal.com/docs/api/identity/v1/#userinfo_get
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -177,8 +172,7 @@ class Paypal extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -189,20 +183,19 @@ class Paypal extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
$header = [
'Content-Type: application/json',
'Authorization: Bearer ' . \urlencode($accessToken),
'Authorization: Bearer '.\urlencode($accessToken),
];
if (empty($this->user)) {
$user = $this->request(
'GET',
$this->resourceEndpoint[$this->environment] . 'identity/oauth2/userinfo?schema=paypalv1.1',
$this->resourceEndpoint[$this->environment].'identity/oauth2/userinfo?schema=paypalv1.1',
$header
);
$this->user = \json_decode($user, true);
@@ -2,8 +2,6 @@
namespace Appwrite\Auth\OAuth2;
use Appwrite\Auth\OAuth2\Paypal;
class PaypalSandbox extends Paypal
{
protected string $environment = 'sandbox';
+17 -24
View File
@@ -51,19 +51,18 @@ class Podio extends OAuth2
*/
public function getLoginURL(): string
{
$url = $this->endpoint . '/authorize?' .
$url = $this->endpoint.'/authorize?'.
\http_build_query([
'client_id' => $this->appID,
'state' => \json_encode($this->state),
'redirect_uri' => $this->callback
'redirect_uri' => $this->callback,
]);
return $url;
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -71,14 +70,14 @@ class Podio extends OAuth2
if (empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
$this->apiEndpoint . '/oauth/token',
$this->apiEndpoint.'/oauth/token',
['Content-Type: application/x-www-form-urlencoded'],
\http_build_query([
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => $this->callback,
'client_id' => $this->appID,
'client_secret' => $this->appSecret
'client_secret' => $this->appSecret,
])
), true);
}
@@ -87,15 +86,14 @@ class Podio extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
{
$this->tokens = \json_decode($this->request(
'POST',
$this->apiEndpoint . '/oauth/token',
$this->apiEndpoint.'/oauth/token',
['Content-Type: application/x-www-form-urlencoded'],
\http_build_query([
'grant_type' => 'refresh_token',
@@ -113,8 +111,7 @@ class Podio extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -125,8 +122,7 @@ class Podio extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -139,8 +135,7 @@ class Podio extends OAuth2
/**
* Check if the OAuth email is verified
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -148,7 +143,7 @@ class Podio extends OAuth2
$user = $this->getUser($accessToken);
$mails = $user['mails'];
$mainMailIndex = \array_search($user['mail'], \array_map(fn($m) => $m['mail'], $mails));
$mainMailIndex = \array_search($user['mail'], \array_map(fn ($m) => $m['mail'], $mails));
$mainMain = $mails[$mainMailIndex];
if ($mainMain['verified'] ?? false) {
@@ -159,8 +154,7 @@ class Podio extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -171,8 +165,7 @@ class Podio extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
@@ -180,14 +173,14 @@ class Podio extends OAuth2
if (empty($this->user)) {
$user = \json_decode($this->request(
'GET',
$this->apiEndpoint . '/user',
['Authorization: Bearer ' . \urlencode($accessToken)]
$this->apiEndpoint.'/user',
['Authorization: Bearer '.\urlencode($accessToken)]
), true);
$profile = \json_decode($this->request(
'GET',
$this->apiEndpoint . '/user/profile',
['Authorization: Bearer ' . \urlencode($accessToken)]
$this->apiEndpoint.'/user/profile',
['Authorization: Bearer '.\urlencode($accessToken)]
), true);
$this->user = $user;
+17 -25
View File
@@ -25,7 +25,7 @@ class Salesforce extends OAuth2
* @var array
*/
protected array $scopes = [
"openid"
'openid',
];
/**
@@ -37,8 +37,7 @@ class Salesforce extends OAuth2
}
/**
* @param string $state
*
* @param string $state
* @return array
*/
public function parseState(string $state)
@@ -46,31 +45,29 @@ class Salesforce extends OAuth2
return \json_decode(\html_entity_decode($state), true);
}
/**
* @return string
*/
public function getLoginURL(): string
{
return 'https://login.salesforce.com/services/oauth2/authorize?' . \http_build_query([
return 'https://login.salesforce.com/services/oauth2/authorize?'.\http_build_query([
'response_type' => 'code',
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'scope' => \implode(' ', $this->getScopes()),
'state' => \json_encode($this->state)
'state' => \json_encode($this->state),
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
{
if (empty($this->tokens)) {
$headers = [
'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret),
'Authorization: Basic '.\base64_encode($this->appID.':'.$this->appSecret),
'Content-Type: application/x-www-form-urlencoded',
];
$this->tokens = \json_decode($this->request(
@@ -80,7 +77,7 @@ class Salesforce extends OAuth2
\http_build_query([
'code' => $code,
'redirect_uri' => $this->callback,
'grant_type' => 'authorization_code'
'grant_type' => 'authorization_code',
])
), true);
}
@@ -89,14 +86,13 @@ class Salesforce extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
{
$headers = [
'Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret),
'Authorization: Basic '.\base64_encode($this->appID.':'.$this->appSecret),
'Content-Type: application/x-www-form-urlencoded',
];
$this->tokens = \json_decode($this->request(
@@ -105,7 +101,7 @@ class Salesforce extends OAuth2
$headers,
\http_build_query([
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token'
'grant_type' => 'refresh_token',
])
), true);
@@ -117,8 +113,7 @@ class Salesforce extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -129,8 +124,7 @@ class Salesforce extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -145,8 +139,7 @@ class Salesforce extends OAuth2
*
* @link https://help.salesforce.com/s/articleView?id=sf.remoteaccess_using_userinfo_endpoint.htm&type=5
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -161,8 +154,7 @@ class Salesforce extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -173,16 +165,16 @@ class Salesforce extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$user = $this->request('GET', 'https://login.salesforce.com/services/oauth2/userinfo?access_token=' . \urlencode($accessToken));
$user = $this->request('GET', 'https://login.salesforce.com/services/oauth2/userinfo?access_token='.\urlencode($accessToken));
$this->user = \json_decode($user, true);
}
return $this->user;
}
}
+16 -23
View File
@@ -23,7 +23,7 @@ class Slack extends OAuth2
'identity.avatar',
'identity.basic',
'identity.email',
'identity.team'
'identity.team',
];
/**
@@ -40,17 +40,16 @@ class Slack extends OAuth2
public function getLoginURL(): string
{
// https://api.slack.com/docs/oauth#step_1_-_sending_users_to_authorize_and_or_install
return 'https://slack.com/oauth/authorize?' . \http_build_query([
return 'https://slack.com/oauth/authorize?'.\http_build_query([
'client_id' => $this->appID,
'scope' => \implode(' ', $this->getScopes()),
'redirect_uri' => $this->callback,
'state' => \json_encode($this->state)
'state' => \json_encode($this->state),
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -59,11 +58,11 @@ class Slack extends OAuth2
// https://api.slack.com/docs/oauth#step_3_-_exchanging_a_verification_code_for_an_access_token
$this->tokens = \json_decode($this->request(
'GET',
'https://slack.com/api/oauth.access?' . \http_build_query([
'https://slack.com/api/oauth.access?'.\http_build_query([
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'code' => $code,
'redirect_uri' => $this->callback
'redirect_uri' => $this->callback,
])
), true);
}
@@ -72,19 +71,18 @@ class Slack extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
{
$this->tokens = \json_decode($this->request(
'GET',
'https://slack.com/api/oauth.access?' . \http_build_query([
'https://slack.com/api/oauth.access?'.\http_build_query([
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token'
'grant_type' => 'refresh_token',
])
), true);
@@ -96,8 +94,7 @@ class Slack extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -108,8 +105,7 @@ class Slack extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -126,20 +122,18 @@ class Slack extends OAuth2
*
* @link https://slack.com/help/articles/207262907-Change-your-email-address
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
{
$email = $this->getUserEmail($accessToken);
return !empty($email);
return ! empty($email);
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -152,8 +146,7 @@ class Slack extends OAuth2
/**
* @link https://api.slack.com/methods/users.identity
*
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken): array
@@ -161,7 +154,7 @@ class Slack extends OAuth2
if (empty($this->user)) {
$user = $this->request(
'GET',
'https://slack.com/api/users.identity?token=' . \urlencode($accessToken)
'https://slack.com/api/users.identity?token='.\urlencode($accessToken)
);
$this->user = \json_decode($user, true);
+20 -27
View File
@@ -49,33 +49,32 @@ class Spotify extends OAuth2
*/
public function getLoginURL(): string
{
return $this->endpoint . 'authorize?' .
return $this->endpoint.'authorize?'.
\http_build_query([
'response_type' => 'code',
'client_id' => $this->appID,
'scope' => \implode(' ', $this->getScopes()),
'redirect_uri' => $this->callback,
'state' => \json_encode($this->state)
'state' => \json_encode($this->state),
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
{
if (empty($this->tokens)) {
$headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)];
$headers = ['Authorization: Basic '.\base64_encode($this->appID.':'.$this->appSecret)];
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . 'api/token',
$this->endpoint.'api/token',
$headers,
\http_build_query([
"code" => $code,
"grant_type" => "authorization_code",
"redirect_uri" => $this->callback
'code' => $code,
'grant_type' => 'authorization_code',
'redirect_uri' => $this->callback,
])
), true);
}
@@ -84,20 +83,19 @@ class Spotify extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
{
$headers = ['Authorization: Basic ' . \base64_encode($this->appID . ':' . $this->appSecret)];
$headers = ['Authorization: Basic '.\base64_encode($this->appID.':'.$this->appSecret)];
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . 'api/token',
$this->endpoint.'api/token',
$headers,
\http_build_query([
"refresh_token" => $refreshToken,
"grant_type" => "refresh_token",
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token',
])
), true);
@@ -109,8 +107,7 @@ class Spotify extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -121,8 +118,7 @@ class Spotify extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -139,8 +135,7 @@ class Spotify extends OAuth2
*
* @link https://developer.spotify.com/documentation/web-api/reference/#/operations/get-current-users-profile
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
@@ -149,8 +144,7 @@ class Spotify extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -161,8 +155,7 @@ class Spotify extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken)
@@ -170,8 +163,8 @@ class Spotify extends OAuth2
if (empty($this->user)) {
$this->user = \json_decode($this->request(
'GET',
$this->resourceEndpoint . 'me',
['Authorization: Bearer ' . \urlencode($accessToken)]
$this->resourceEndpoint.'me',
['Authorization: Bearer '.\urlencode($accessToken)]
), true);
}
+15 -22
View File
@@ -3,7 +3,6 @@
namespace Appwrite\Auth\OAuth2;
use Appwrite\Auth\OAuth2;
use Utopia\Exception;
class Stripe extends OAuth2
{
@@ -50,18 +49,17 @@ class Stripe extends OAuth2
*/
public function getLoginURL(): string
{
return 'https://connect.stripe.com/oauth/authorize?' . \http_build_query([
return 'https://connect.stripe.com/oauth/authorize?'.\http_build_query([
'response_type' => 'code', // The only option at the moment is "code."
'client_id' => $this->appID,
'redirect_uri' => $this->callback,
'scope' => \implode(' ', $this->getScopes()),
'state' => \json_encode($this->state)
'state' => \json_encode($this->state),
]);
}
/**
* @param string $code
*
* @param string $code
* @return array
*/
protected function getTokens(string $code): array
@@ -73,7 +71,7 @@ class Stripe extends OAuth2
[],
\http_build_query([
'grant_type' => $this->grantType['authorize'],
'code' => $code
'code' => $code,
])
), true);
@@ -84,8 +82,7 @@ class Stripe extends OAuth2
}
/**
* @param string $refreshToken
*
* @param string $refreshToken
* @return array
*/
public function refreshTokens(string $refreshToken): array
@@ -105,12 +102,12 @@ class Stripe extends OAuth2
}
$this->stripeAccountId = $this->tokens['stripe_user_id'];
return $this->tokens;
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserID(string $accessToken): string
@@ -121,8 +118,7 @@ class Stripe extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserEmail(string $accessToken): string
@@ -141,20 +137,18 @@ class Stripe extends OAuth2
*
* If present, the email is verified. This was verfied through a manual Stripe sign up process
*
* @param string $accessToken
*
* @param string $accessToken
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
{
$email = $this->getUserEmail($accessToken);
return !empty($email);
return ! empty($email);
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return string
*/
public function getUserName(string $accessToken): string
@@ -165,18 +159,17 @@ class Stripe extends OAuth2
}
/**
* @param string $accessToken
*
* @param string $accessToken
* @return array
*/
protected function getUser(string $accessToken)
{
if (empty($this->user) && !empty($this->stripeAccountId)) {
if (empty($this->user) && ! empty($this->stripeAccountId)) {
$this->user = \json_decode(
$this->request(
'GET',
'https://api.stripe.com/v1/accounts/' . $this->stripeAccountId,
['Authorization: Bearer ' . \urlencode($accessToken)]
'https://api.stripe.com/v1/accounts/'.$this->stripeAccountId,
['Authorization: Bearer '.\urlencode($accessToken)]
),
true
);

Some files were not shown because too many files have changed in this diff Show More