mirror of
https://github.com/appwrite/appwrite
synced 2026-05-24 01:18:37 +00:00
Merge pull request #8741 from appwrite/feat-adding-coroutines
Feat adding coroutines
This commit is contained in:
commit
0c3fed9ef1
111 changed files with 5779 additions and 5124 deletions
16
.github/workflows/codeql-phpstan.yml
vendored
Normal file
16
.github/workflows/codeql-phpstan.yml
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on: [pull_request]
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
name: CodeQL
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Run CodeQL
|
||||||
|
run: |
|
||||||
|
docker run --rm -v $PWD:/app composer sh -c \
|
||||||
|
"composer install --profile --ignore-platform-reqs && composer check"
|
||||||
31
.github/workflows/tests.yml
vendored
31
.github/workflows/tests.yml
vendored
|
|
@ -16,22 +16,22 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
- name: Build Appwrite
|
- name: Build Appwrite
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: false
|
push: false
|
||||||
tags: ${{ env.IMAGE }}
|
tags: ${{ env.IMAGE }}
|
||||||
load: true
|
load: true
|
||||||
cache-from: type=gha,scope=appwrite
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max,scope=appwrite
|
cache-to: type=gha,mode=max
|
||||||
outputs: type=docker,dest=/tmp/${{ env.IMAGE }}.tar
|
outputs: type=docker,dest=/tmp/${{ env.IMAGE }}.tar
|
||||||
build-args: |
|
build-args: |
|
||||||
DEBUG=false
|
DEBUG=false
|
||||||
|
|
@ -39,13 +39,12 @@ jobs:
|
||||||
VERSION=dev
|
VERSION=dev
|
||||||
|
|
||||||
- name: Cache Docker Image
|
- name: Cache Docker Image
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
key: ${{ env.CACHE_KEY }}
|
key: ${{ env.CACHE_KEY }}
|
||||||
restore-keys: |
|
|
||||||
appwrite-dev-
|
|
||||||
path: /tmp/${{ env.IMAGE }}.tar
|
path: /tmp/${{ env.IMAGE }}.tar
|
||||||
|
|
||||||
|
|
||||||
unit_test:
|
unit_test:
|
||||||
name: Unit Test
|
name: Unit Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
@ -53,10 +52,10 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Load Cache
|
- name: Load Cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
key: ${{ env.CACHE_KEY }}
|
key: ${{ env.CACHE_KEY }}
|
||||||
path: /tmp/${{ env.IMAGE }}.tar
|
path: /tmp/${{ env.IMAGE }}.tar
|
||||||
|
|
@ -83,10 +82,10 @@ jobs:
|
||||||
needs: setup
|
needs: setup
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Load Cache
|
- name: Load Cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
key: ${{ env.CACHE_KEY }}
|
key: ${{ env.CACHE_KEY }}
|
||||||
path: /tmp/${{ env.IMAGE }}.tar
|
path: /tmp/${{ env.IMAGE }}.tar
|
||||||
|
|
@ -131,10 +130,10 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Load Cache
|
- name: Load Cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
key: ${{ env.CACHE_KEY }}
|
key: ${{ env.CACHE_KEY }}
|
||||||
path: /tmp/${{ env.IMAGE }}.tar
|
path: /tmp/${{ env.IMAGE }}.tar
|
||||||
|
|
@ -158,9 +157,9 @@ jobs:
|
||||||
needs: setup
|
needs: setup
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
- name: Load Cache
|
- name: Load Cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
key: ${{ env.CACHE_KEY }}
|
key: ${{ env.CACHE_KEY }}
|
||||||
path: /tmp/${{ env.IMAGE }}.tar
|
path: /tmp/${{ env.IMAGE }}.tar
|
||||||
|
|
|
||||||
272
app/cli.php
272
app/cli.php
|
|
@ -7,214 +7,134 @@ use Appwrite\Event\Delete;
|
||||||
use Appwrite\Event\Func;
|
use Appwrite\Event\Func;
|
||||||
use Appwrite\Platform\Appwrite;
|
use Appwrite\Platform\Appwrite;
|
||||||
use Appwrite\Runtimes\Runtimes;
|
use Appwrite\Runtimes\Runtimes;
|
||||||
use Utopia\Cache\Adapter\Sharding;
|
use Swoole\Runtime;
|
||||||
use Utopia\Cache\Cache;
|
use Utopia\CLI\Adapters\Swoole as SwooleCLI;
|
||||||
use Utopia\CLI\CLI;
|
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Database\Database;
|
|
||||||
use Utopia\Database\Document;
|
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\Database\Validator\Authorization;
|
||||||
use Utopia\DSN\DSN;
|
use Utopia\DI\Dependency;
|
||||||
use Utopia\Logger\Log;
|
use Utopia\Logger\Log;
|
||||||
use Utopia\Platform\Service;
|
use Utopia\Platform\Service;
|
||||||
use Utopia\Pools\Group;
|
|
||||||
use Utopia\Queue\Connection;
|
use Utopia\Queue\Connection;
|
||||||
use Utopia\Registry\Registry;
|
use Utopia\Registry\Registry;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
|
|
||||||
|
global $registry, $container;
|
||||||
|
|
||||||
|
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
||||||
|
|
||||||
// overwriting runtimes to be architectur agnostic for CLI
|
// overwriting runtimes to be architectur agnostic for CLI
|
||||||
Config::setParam('runtimes', (new Runtimes('v4'))->getAll(supported: false));
|
Config::setParam('runtimes', (new Runtimes('v4'))->getAll(supported: false));
|
||||||
|
|
||||||
// require controllers after overwriting runtimes
|
// require controllers after overwriting runtimes
|
||||||
require_once __DIR__ . '/controllers/general.php';
|
require_once __DIR__ . '/controllers/general.php';
|
||||||
|
|
||||||
Authorization::disable();
|
/**
|
||||||
|
* @var Registry $registry
|
||||||
|
* @var Container $container
|
||||||
|
*/
|
||||||
|
$context = new Dependency();
|
||||||
|
$register = new Dependency();
|
||||||
|
$logError = new Dependency();
|
||||||
|
$queueForDeletes = new Dependency();
|
||||||
|
$queueForFunctions = new Dependency();
|
||||||
|
$queueForCertificates = new Dependency();
|
||||||
|
|
||||||
CLI::setResource('register', fn () => $register);
|
$context
|
||||||
|
->setName('context')
|
||||||
|
->setCallback(fn () => $container);
|
||||||
|
|
||||||
CLI::setResource('cache', function ($pools) {
|
$register
|
||||||
$list = Config::getParam('pools-cache', []);
|
->setName('register')
|
||||||
$adapters = [];
|
->setCallback(function () use (&$registry): Registry {
|
||||||
|
return $registry;
|
||||||
|
});
|
||||||
|
|
||||||
foreach ($list as $value) {
|
$queueForFunctions
|
||||||
$adapters[] = $pools
|
->setName('queueForFunctions')
|
||||||
->get($value)
|
->inject('queue')
|
||||||
->pop()
|
->setCallback(function (Connection $queue) {
|
||||||
->getResource()
|
return new Func($queue);
|
||||||
;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return new Cache(new Sharding($adapters));
|
|
||||||
}, ['pools']);
|
|
||||||
|
|
||||||
CLI::setResource('pools', function (Registry $register) {
|
$queueForDeletes
|
||||||
return $register->get('pools');
|
->setName('queueForDeletes')
|
||||||
}, ['register']);
|
->inject('queue')
|
||||||
|
->setCallback(function (Connection $queue) {
|
||||||
|
return new Delete($queue);
|
||||||
|
});
|
||||||
|
|
||||||
CLI::setResource('dbForConsole', function ($pools, $cache) {
|
$queueForCertificates
|
||||||
$sleep = 3;
|
->setName('queueForCertificates')
|
||||||
$maxAttempts = 5;
|
->inject('queue')
|
||||||
$attempts = 0;
|
->setCallback(function (Connection $queue) {
|
||||||
$ready = false;
|
return new Certificate($queue);
|
||||||
|
});
|
||||||
|
|
||||||
do {
|
$logError
|
||||||
$attempts++;
|
->setName('logError')
|
||||||
try {
|
->inject('register')
|
||||||
// Prepare database connection
|
->setCallback(function (Registry $register) {
|
||||||
$dbAdapter = $pools
|
return function (Throwable $error, string $namespace, string $action) use ($register) {
|
||||||
->get('console')
|
$logger = $register->get('logger');
|
||||||
->pop()
|
|
||||||
->getResource();
|
|
||||||
|
|
||||||
$dbForConsole = new Database($dbAdapter, $cache);
|
if ($logger) {
|
||||||
|
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||||
|
|
||||||
$dbForConsole
|
$log = new Log();
|
||||||
->setNamespace('_console')
|
$log->setNamespace($namespace);
|
||||||
->setMetadata('host', \gethostname())
|
$log->setServer(\gethostname());
|
||||||
->setMetadata('project', 'console');
|
$log->setVersion($version);
|
||||||
|
$log->setType(Log::TYPE_ERROR);
|
||||||
|
$log->setMessage($error->getMessage());
|
||||||
|
|
||||||
// Ensure tables exist
|
$log->addTag('code', $error->getCode());
|
||||||
$collections = Config::getParam('collections', [])['console'];
|
$log->addTag('verboseType', get_class($error));
|
||||||
$last = \array_key_last($collections);
|
|
||||||
|
|
||||||
if (!($dbForConsole->exists($dbForConsole->getDatabase(), $last))) { /** TODO cache ready variable using registry */
|
$log->addExtra('file', $error->getFile());
|
||||||
throw new Exception('Tables not ready yet.');
|
$log->addExtra('line', $error->getLine());
|
||||||
|
$log->addExtra('trace', $error->getTraceAsString());
|
||||||
|
$log->addExtra('trace', $error->getTraceAsString());
|
||||||
|
|
||||||
|
$log->setAction($action);
|
||||||
|
|
||||||
|
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
|
||||||
|
|
||||||
|
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$responseCode = $logger->addLog($log);
|
||||||
|
Console::info('Error log pushed with status code: ' . $responseCode);
|
||||||
|
} catch (Throwable $th) {
|
||||||
|
Console::error('Error pushing log: ' . $th->getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$ready = true;
|
Console::warning("Failed: {$error->getMessage()}");
|
||||||
} catch (\Throwable $err) {
|
Console::warning($error->getTraceAsString());
|
||||||
Console::warning($err->getMessage());
|
};
|
||||||
$pools->get('console')->reclaim();
|
});
|
||||||
sleep($sleep);
|
|
||||||
}
|
|
||||||
} while ($attempts < $maxAttempts && !$ready);
|
|
||||||
|
|
||||||
if (!$ready) {
|
$container->set($context);
|
||||||
throw new Exception("Console is not ready yet. Please try again later.");
|
$container->set($logError);
|
||||||
}
|
$container->set($register);
|
||||||
|
$container->set($queueForDeletes);
|
||||||
return $dbForConsole;
|
$container->set($queueForFunctions);
|
||||||
}, ['pools', 'cache']);
|
$container->set($queueForCertificates);
|
||||||
|
|
||||||
CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) {
|
|
||||||
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
|
|
||||||
|
|
||||||
return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) {
|
|
||||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
|
||||||
return $dbForConsole;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$dsn = new DSN($project->getAttribute('database'));
|
|
||||||
} catch (\InvalidArgumentException) {
|
|
||||||
// TODO: Temporary until all projects are using shared tables
|
|
||||||
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($databases[$dsn->getHost()])) {
|
|
||||||
$database = $databases[$dsn->getHost()];
|
|
||||||
|
|
||||||
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
|
||||||
$database
|
|
||||||
->setSharedTables(true)
|
|
||||||
->setTenant($project->getInternalId())
|
|
||||||
->setNamespace($dsn->getParam('namespace'));
|
|
||||||
} else {
|
|
||||||
$database
|
|
||||||
->setSharedTables(false)
|
|
||||||
->setTenant(null)
|
|
||||||
->setNamespace('_' . $project->getInternalId());
|
|
||||||
}
|
|
||||||
|
|
||||||
return $database;
|
|
||||||
}
|
|
||||||
|
|
||||||
$dbAdapter = $pools
|
|
||||||
->get($dsn->getHost())
|
|
||||||
->pop()
|
|
||||||
->getResource();
|
|
||||||
|
|
||||||
$database = new Database($dbAdapter, $cache);
|
|
||||||
|
|
||||||
$databases[$dsn->getHost()] = $database;
|
|
||||||
|
|
||||||
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
|
||||||
$database
|
|
||||||
->setSharedTables(true)
|
|
||||||
->setTenant($project->getInternalId())
|
|
||||||
->setNamespace($dsn->getParam('namespace'));
|
|
||||||
} else {
|
|
||||||
$database
|
|
||||||
->setSharedTables(false)
|
|
||||||
->setTenant(null)
|
|
||||||
->setNamespace('_' . $project->getInternalId());
|
|
||||||
}
|
|
||||||
|
|
||||||
$database
|
|
||||||
->setMetadata('host', \gethostname())
|
|
||||||
->setMetadata('project', $project->getId());
|
|
||||||
|
|
||||||
return $database;
|
|
||||||
};
|
|
||||||
}, ['pools', 'dbForConsole', 'cache']);
|
|
||||||
|
|
||||||
CLI::setResource('queue', function (Group $pools) {
|
|
||||||
return $pools->get('queue')->pop()->getResource();
|
|
||||||
}, ['pools']);
|
|
||||||
CLI::setResource('queueForFunctions', function (Connection $queue) {
|
|
||||||
return new Func($queue);
|
|
||||||
}, ['queue']);
|
|
||||||
CLI::setResource('queueForDeletes', function (Connection $queue) {
|
|
||||||
return new Delete($queue);
|
|
||||||
}, ['queue']);
|
|
||||||
CLI::setResource('queueForCertificates', function (Connection $queue) {
|
|
||||||
return new Certificate($queue);
|
|
||||||
}, ['queue']);
|
|
||||||
CLI::setResource('logError', function (Registry $register) {
|
|
||||||
return function (Throwable $error, string $namespace, string $action) use ($register) {
|
|
||||||
$logger = $register->get('logger');
|
|
||||||
|
|
||||||
if ($logger) {
|
|
||||||
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
|
||||||
|
|
||||||
$log = new Log();
|
|
||||||
$log->setNamespace($namespace);
|
|
||||||
$log->setServer(\gethostname());
|
|
||||||
$log->setVersion($version);
|
|
||||||
$log->setType(Log::TYPE_ERROR);
|
|
||||||
$log->setMessage($error->getMessage());
|
|
||||||
|
|
||||||
$log->addTag('code', $error->getCode());
|
|
||||||
$log->addTag('verboseType', get_class($error));
|
|
||||||
|
|
||||||
$log->addExtra('file', $error->getFile());
|
|
||||||
$log->addExtra('line', $error->getLine());
|
|
||||||
$log->addExtra('trace', $error->getTraceAsString());
|
|
||||||
|
|
||||||
$log->setAction($action);
|
|
||||||
|
|
||||||
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
|
|
||||||
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$responseCode = $logger->addLog($log);
|
|
||||||
Console::info('Error log pushed with status code: ' . $responseCode);
|
|
||||||
} catch (Throwable $th) {
|
|
||||||
Console::error('Error pushing log: ' . $th->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Console::warning("Failed: {$error->getMessage()}");
|
|
||||||
Console::warning($error->getTraceAsString());
|
|
||||||
};
|
|
||||||
}, ['register']);
|
|
||||||
|
|
||||||
$platform = new Appwrite();
|
$platform = new Appwrite();
|
||||||
$platform->init(Service::TYPE_TASK);
|
$platform->init(Service::TYPE_TASK, ['adapter' => new SwooleCLI(1)]);
|
||||||
|
|
||||||
$cli = $platform->getCli();
|
$cli = $platform->getCli();
|
||||||
|
|
||||||
|
$cli
|
||||||
|
->init()
|
||||||
|
->inject('authorization')
|
||||||
|
->action(function (Authorization $authorization) {
|
||||||
|
$authorization->disable();
|
||||||
|
});
|
||||||
|
|
||||||
$cli
|
$cli
|
||||||
->error()
|
->error()
|
||||||
->inject('error')
|
->inject('error')
|
||||||
|
|
@ -222,4 +142,6 @@ $cli
|
||||||
Console::error($error->getMessage());
|
Console::error($error->getMessage());
|
||||||
});
|
});
|
||||||
|
|
||||||
$cli->run();
|
$cli
|
||||||
|
->setContainer($container)
|
||||||
|
->run();
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,7 @@ return [
|
||||||
'name' => '_APP_WORKER_PER_CORE',
|
'name' => '_APP_WORKER_PER_CORE',
|
||||||
'description' => 'Internal Worker per core for the API, Realtime and Executor containers. Can be configured to optimize performance.',
|
'description' => 'Internal Worker per core for the API, Realtime and Executor containers. Can be configured to optimize performance.',
|
||||||
'introduction' => '0.13.0',
|
'introduction' => '0.13.0',
|
||||||
'default' => 6,
|
'default' => 2,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'question' => '',
|
'question' => '',
|
||||||
'filter' => ''
|
'filter' => ''
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -5,7 +5,7 @@ use Appwrite\URL\URL as URLParse;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use chillerlan\QRCode\QRCode;
|
use chillerlan\QRCode\QRCode;
|
||||||
use chillerlan\QRCode\QROptions;
|
use chillerlan\QRCode\QROptions;
|
||||||
use Utopia\App;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\DateTime;
|
use Utopia\Database\DateTime;
|
||||||
|
|
@ -14,15 +14,17 @@ use Utopia\Database\Validator\Authorization;
|
||||||
use Utopia\Database\Validator\UID;
|
use Utopia\Database\Validator\UID;
|
||||||
use Utopia\Domains\Domain;
|
use Utopia\Domains\Domain;
|
||||||
use Utopia\Fetch\Client;
|
use Utopia\Fetch\Client;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Validator\Boolean;
|
||||||
|
use Utopia\Http\Validator\HexColor;
|
||||||
|
use Utopia\Http\Validator\Range;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
|
use Utopia\Http\Validator\URL;
|
||||||
|
use Utopia\Http\Validator\WhiteList;
|
||||||
use Utopia\Image\Image;
|
use Utopia\Image\Image;
|
||||||
|
use Utopia\Logger\Log;
|
||||||
use Utopia\Logger\Logger;
|
use Utopia\Logger\Logger;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
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) {
|
$avatarCallback = function (string $type, string $code, int $width, int $height, int $quality, Response $response) {
|
||||||
|
|
||||||
|
|
@ -61,9 +63,9 @@ $avatarCallback = function (string $type, string $code, int $width, int $height,
|
||||||
unset($image);
|
unset($image);
|
||||||
};
|
};
|
||||||
|
|
||||||
$getUserGitHub = function (string $userId, Document $project, Database $dbForProject, Database $dbForConsole, ?Logger $logger) {
|
$getUserGitHub = function (string $userId, Document $project, Database $dbForProject, Database $dbForConsole, ?Logger $logger, Authorization $auth) {
|
||||||
try {
|
try {
|
||||||
$user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId));
|
$user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId));
|
||||||
|
|
||||||
$sessions = $user->getAttribute('sessions', []);
|
$sessions = $user->getAttribute('sessions', []);
|
||||||
|
|
||||||
|
|
@ -114,7 +116,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
|
||||||
->setAttribute('providerRefreshToken', $refreshToken)
|
->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));
|
$auth->skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession));
|
||||||
|
|
||||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||||
} catch (Throwable $err) {
|
} catch (Throwable $err) {
|
||||||
|
|
@ -122,7 +124,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
|
||||||
do {
|
do {
|
||||||
$previousAccessToken = $gitHubSession->getAttribute('providerAccessToken');
|
$previousAccessToken = $gitHubSession->getAttribute('providerAccessToken');
|
||||||
|
|
||||||
$user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId));
|
$user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId));
|
||||||
$sessions = $user->getAttribute('sessions', []);
|
$sessions = $user->getAttribute('sessions', []);
|
||||||
|
|
||||||
$gitHubSession = new Document();
|
$gitHubSession = new Document();
|
||||||
|
|
@ -154,11 +156,42 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
|
||||||
'id' => $githubId
|
'id' => $githubId
|
||||||
];
|
];
|
||||||
} catch (Exception $error) {
|
} catch (Exception $error) {
|
||||||
|
if ($logger) {
|
||||||
|
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||||
|
|
||||||
|
$log = new Log();
|
||||||
|
$log->setNamespace('console');
|
||||||
|
$log->setServer(\gethostname());
|
||||||
|
$log->setVersion($version);
|
||||||
|
$log->setType(Log::TYPE_ERROR);
|
||||||
|
$log->setMessage($error->getMessage());
|
||||||
|
|
||||||
|
$log->addTag('code', $error->getCode());
|
||||||
|
$log->addTag('verboseType', get_class($error));
|
||||||
|
|
||||||
|
$log->addExtra('file', $error->getFile());
|
||||||
|
$log->addExtra('line', $error->getLine());
|
||||||
|
$log->addExtra('trace', $error->getTraceAsString());
|
||||||
|
$log->addExtra('detailedTrace', $error->getTrace());
|
||||||
|
|
||||||
|
$log->setAction('avatarsGetGitHub');
|
||||||
|
|
||||||
|
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
|
||||||
|
|
||||||
|
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
||||||
|
|
||||||
|
$responseCode = $logger->addLog($log);
|
||||||
|
Console::info('GitHub error log pushed with status code: ' . $responseCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Console::warning("Failed: {$error->getMessage()}");
|
||||||
|
Console::warning($error->getTraceAsString());
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
App::get('/v1/avatars/credit-cards/:code')
|
Http::get('/v1/avatars/credit-cards/:code')
|
||||||
->desc('Get credit card icon')
|
->desc('Get credit card icon')
|
||||||
->groups(['api', 'avatars'])
|
->groups(['api', 'avatars'])
|
||||||
->label('scope', 'avatars.read')
|
->label('scope', 'avatars.read')
|
||||||
|
|
@ -178,7 +211,7 @@ App::get('/v1/avatars/credit-cards/:code')
|
||||||
->inject('response')
|
->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')
|
Http::get('/v1/avatars/browsers/:code')
|
||||||
->desc('Get browser icon')
|
->desc('Get browser icon')
|
||||||
->groups(['api', 'avatars'])
|
->groups(['api', 'avatars'])
|
||||||
->label('scope', 'avatars.read')
|
->label('scope', 'avatars.read')
|
||||||
|
|
@ -198,7 +231,7 @@ App::get('/v1/avatars/browsers/:code')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('browsers', $code, $width, $height, $quality, $response));
|
->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('browsers', $code, $width, $height, $quality, $response));
|
||||||
|
|
||||||
App::get('/v1/avatars/flags/:code')
|
Http::get('/v1/avatars/flags/:code')
|
||||||
->desc('Get country flag')
|
->desc('Get country flag')
|
||||||
->groups(['api', 'avatars'])
|
->groups(['api', 'avatars'])
|
||||||
->label('scope', 'avatars.read')
|
->label('scope', 'avatars.read')
|
||||||
|
|
@ -218,7 +251,7 @@ App::get('/v1/avatars/flags/:code')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('flags', $code, $width, $height, $quality, $response));
|
->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('flags', $code, $width, $height, $quality, $response));
|
||||||
|
|
||||||
App::get('/v1/avatars/image')
|
Http::get('/v1/avatars/image')
|
||||||
->desc('Get image from URL')
|
->desc('Get image from URL')
|
||||||
->groups(['api', 'avatars'])
|
->groups(['api', 'avatars'])
|
||||||
->label('scope', 'avatars.read')
|
->label('scope', 'avatars.read')
|
||||||
|
|
@ -281,7 +314,7 @@ App::get('/v1/avatars/image')
|
||||||
unset($image);
|
unset($image);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/avatars/favicon')
|
Http::get('/v1/avatars/favicon')
|
||||||
->desc('Get favicon')
|
->desc('Get favicon')
|
||||||
->groups(['api', 'avatars'])
|
->groups(['api', 'avatars'])
|
||||||
->label('scope', 'avatars.read')
|
->label('scope', 'avatars.read')
|
||||||
|
|
@ -426,7 +459,7 @@ App::get('/v1/avatars/favicon')
|
||||||
unset($image);
|
unset($image);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/avatars/qr')
|
Http::get('/v1/avatars/qr')
|
||||||
->desc('Get QR code')
|
->desc('Get QR code')
|
||||||
->groups(['api', 'avatars'])
|
->groups(['api', 'avatars'])
|
||||||
->label('scope', 'avatars.read')
|
->label('scope', 'avatars.read')
|
||||||
|
|
@ -466,7 +499,7 @@ App::get('/v1/avatars/qr')
|
||||||
->send($image->output('png', 9));
|
->send($image->output('png', 9));
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/avatars/initials')
|
Http::get('/v1/avatars/initials')
|
||||||
->desc('Get user initials')
|
->desc('Get user initials')
|
||||||
->groups(['api', 'avatars'])
|
->groups(['api', 'avatars'])
|
||||||
->label('scope', 'avatars.read')
|
->label('scope', 'avatars.read')
|
||||||
|
|
@ -549,8 +582,8 @@ App::get('/v1/avatars/initials')
|
||||||
->file($image->getImageBlob());
|
->file($image->getImageBlob());
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/cards/cloud')
|
Http::get('/v1/cards/cloud')
|
||||||
->desc('Get front Of Cloud Card')
|
->desc('Get Front Of Cloud Card')
|
||||||
->groups(['api', 'avatars'])
|
->groups(['api', 'avatars'])
|
||||||
->label('scope', 'avatars.read')
|
->label('scope', 'avatars.read')
|
||||||
->label('cache', true)
|
->label('cache', true)
|
||||||
|
|
@ -571,8 +604,9 @@ App::get('/v1/cards/cloud')
|
||||||
->inject('contributors')
|
->inject('contributors')
|
||||||
->inject('employees')
|
->inject('employees')
|
||||||
->inject('logger')
|
->inject('logger')
|
||||||
->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) {
|
->inject('authorization')
|
||||||
$user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId));
|
->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization) use ($getUserGitHub) {
|
||||||
|
$user = $authorization->skip(fn () => $dbForConsole->getDocument('users', $userId));
|
||||||
|
|
||||||
if ($user->isEmpty() && empty($mock)) {
|
if ($user->isEmpty() && empty($mock)) {
|
||||||
throw new Exception(Exception::USER_NOT_FOUND);
|
throw new Exception(Exception::USER_NOT_FOUND);
|
||||||
|
|
@ -583,7 +617,7 @@ App::get('/v1/cards/cloud')
|
||||||
$email = $user->getAttribute('email', '');
|
$email = $user->getAttribute('email', '');
|
||||||
$createdAt = new \DateTime($user->getCreatedAt());
|
$createdAt = new \DateTime($user->getCreatedAt());
|
||||||
|
|
||||||
$gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger);
|
$gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $authorization);
|
||||||
$githubName = $gitHub['name'] ?? '';
|
$githubName = $gitHub['name'] ?? '';
|
||||||
$githubId = $gitHub['id'] ?? '';
|
$githubId = $gitHub['id'] ?? '';
|
||||||
|
|
||||||
|
|
@ -756,8 +790,8 @@ App::get('/v1/cards/cloud')
|
||||||
->file($baseImage->getImageBlob());
|
->file($baseImage->getImageBlob());
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/cards/cloud-back')
|
Http::get('/v1/cards/cloud-back')
|
||||||
->desc('Get back Of Cloud Card')
|
->desc('Get Back Of Cloud Card')
|
||||||
->groups(['api', 'avatars'])
|
->groups(['api', 'avatars'])
|
||||||
->label('scope', 'avatars.read')
|
->label('scope', 'avatars.read')
|
||||||
->label('cache', true)
|
->label('cache', true)
|
||||||
|
|
@ -778,8 +812,9 @@ App::get('/v1/cards/cloud-back')
|
||||||
->inject('contributors')
|
->inject('contributors')
|
||||||
->inject('employees')
|
->inject('employees')
|
||||||
->inject('logger')
|
->inject('logger')
|
||||||
->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) {
|
->inject('authorization')
|
||||||
$user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId));
|
->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization) use ($getUserGitHub) {
|
||||||
|
$user = $authorization->skip(fn () => $dbForConsole->getDocument('users', $userId));
|
||||||
|
|
||||||
if ($user->isEmpty() && empty($mock)) {
|
if ($user->isEmpty() && empty($mock)) {
|
||||||
throw new Exception(Exception::USER_NOT_FOUND);
|
throw new Exception(Exception::USER_NOT_FOUND);
|
||||||
|
|
@ -789,7 +824,7 @@ App::get('/v1/cards/cloud-back')
|
||||||
$userId = $user->getId();
|
$userId = $user->getId();
|
||||||
$email = $user->getAttribute('email', '');
|
$email = $user->getAttribute('email', '');
|
||||||
|
|
||||||
$gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger);
|
$gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $authorization);
|
||||||
$githubId = $gitHub['id'] ?? '';
|
$githubId = $gitHub['id'] ?? '';
|
||||||
|
|
||||||
$isHero = \array_key_exists($email, $heroes);
|
$isHero = \array_key_exists($email, $heroes);
|
||||||
|
|
@ -834,8 +869,8 @@ App::get('/v1/cards/cloud-back')
|
||||||
->file($baseImage->getImageBlob());
|
->file($baseImage->getImageBlob());
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/cards/cloud-og')
|
Http::get('/v1/cards/cloud-og')
|
||||||
->desc('Get OG image From Cloud Card')
|
->desc('Get OG Image From Cloud Card')
|
||||||
->groups(['api', 'avatars'])
|
->groups(['api', 'avatars'])
|
||||||
->label('scope', 'avatars.read')
|
->label('scope', 'avatars.read')
|
||||||
->label('cache', true)
|
->label('cache', true)
|
||||||
|
|
@ -856,8 +891,9 @@ App::get('/v1/cards/cloud-og')
|
||||||
->inject('contributors')
|
->inject('contributors')
|
||||||
->inject('employees')
|
->inject('employees')
|
||||||
->inject('logger')
|
->inject('logger')
|
||||||
->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) {
|
->inject('authorization')
|
||||||
$user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId));
|
->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization) use ($getUserGitHub) {
|
||||||
|
$user = $authorization->skip(fn () => $dbForConsole->getDocument('users', $userId));
|
||||||
|
|
||||||
if ($user->isEmpty() && empty($mock)) {
|
if ($user->isEmpty() && empty($mock)) {
|
||||||
throw new Exception(Exception::USER_NOT_FOUND);
|
throw new Exception(Exception::USER_NOT_FOUND);
|
||||||
|
|
@ -872,7 +908,7 @@ App::get('/v1/cards/cloud-og')
|
||||||
$email = $user->getAttribute('email', '');
|
$email = $user->getAttribute('email', '');
|
||||||
$createdAt = new \DateTime($user->getCreatedAt());
|
$createdAt = new \DateTime($user->getCreatedAt());
|
||||||
|
|
||||||
$gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger);
|
$gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $authorization);
|
||||||
$githubName = $gitHub['name'] ?? '';
|
$githubName = $gitHub['name'] ?? '';
|
||||||
$githubId = $gitHub['id'] ?? '';
|
$githubId = $gitHub['id'] ?? '';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
use Appwrite\Extend\Exception;
|
use Appwrite\Extend\Exception;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\Text;
|
|
||||||
|
|
||||||
App::init()
|
Http::init()
|
||||||
->groups(['console'])
|
->groups(['console'])
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->action(function (Document $project) {
|
->action(function (Document $project) {
|
||||||
|
|
@ -17,7 +17,7 @@ App::init()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
App::get('/v1/console/variables')
|
Http::get('/v1/console/variables')
|
||||||
->desc('Get variables')
|
->desc('Get variables')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.read')
|
->label('scope', 'projects.read')
|
||||||
|
|
@ -56,8 +56,8 @@ App::get('/v1/console/variables')
|
||||||
$response->dynamic($variables, Response::MODEL_CONSOLE_VARIABLES);
|
$response->dynamic($variables, Response::MODEL_CONSOLE_VARIABLES);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/console/assistant')
|
Http::post('/v1/console/assistant')
|
||||||
->desc('Ask query')
|
->desc('Ask Query')
|
||||||
->groups(['api', 'assistant'])
|
->groups(['api', 'assistant'])
|
||||||
->label('scope', 'assistant.read')
|
->label('scope', 'assistant.read')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use Ahc\Jwt\JWT;
|
use Ahc\Jwt\JWT;
|
||||||
use Appwrite\Auth\Auth;
|
use Appwrite\Auth\Auth;
|
||||||
|
use Appwrite\Auth\Authentication;
|
||||||
use Appwrite\Event\Build;
|
use Appwrite\Event\Build;
|
||||||
use Appwrite\Event\Delete;
|
use Appwrite\Event\Delete;
|
||||||
use Appwrite\Event\Event;
|
use Appwrite\Event\Event;
|
||||||
|
|
@ -20,11 +21,11 @@ use Appwrite\Utopia\Database\Validator\CustomId;
|
||||||
use Appwrite\Utopia\Database\Validator\Queries\Deployments;
|
use Appwrite\Utopia\Database\Validator\Queries\Deployments;
|
||||||
use Appwrite\Utopia\Database\Validator\Queries\Executions;
|
use Appwrite\Utopia\Database\Validator\Queries\Executions;
|
||||||
use Appwrite\Utopia\Database\Validator\Queries\Functions;
|
use Appwrite\Utopia\Database\Validator\Queries\Functions;
|
||||||
|
use Appwrite\Utopia\Request;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Appwrite\Utopia\Response\Model\Rule;
|
use Appwrite\Utopia\Response\Model\Rule;
|
||||||
use Executor\Executor;
|
use Executor\Executor;
|
||||||
use MaxMind\Db\Reader;
|
use MaxMind\Db\Reader;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
|
|
@ -37,24 +38,25 @@ use Utopia\Database\Helpers\Permission;
|
||||||
use Utopia\Database\Helpers\Role;
|
use Utopia\Database\Helpers\Role;
|
||||||
use Utopia\Database\Query;
|
use Utopia\Database\Query;
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\Database\Validator\Authorization;
|
||||||
|
use Utopia\Database\Validator\Authorization\Input;
|
||||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||||
use Utopia\Database\Validator\Roles;
|
use Utopia\Database\Validator\Roles;
|
||||||
use Utopia\Database\Validator\UID;
|
use Utopia\Database\Validator\UID;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Validator\AnyOf;
|
||||||
|
use Utopia\Http\Validator\ArrayList;
|
||||||
|
use Utopia\Http\Validator\Assoc;
|
||||||
|
use Utopia\Http\Validator\Boolean;
|
||||||
|
use Utopia\Http\Validator\Nullable;
|
||||||
|
use Utopia\Http\Validator\Range;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
|
use Utopia\Http\Validator\WhiteList;
|
||||||
use Utopia\Storage\Device;
|
use Utopia\Storage\Device;
|
||||||
use Utopia\Storage\Validator\File;
|
use Utopia\Storage\Validator\File;
|
||||||
use Utopia\Storage\Validator\FileExt;
|
use Utopia\Storage\Validator\FileExt;
|
||||||
use Utopia\Storage\Validator\FileSize;
|
use Utopia\Storage\Validator\FileSize;
|
||||||
use Utopia\Storage\Validator\Upload;
|
use Utopia\Storage\Validator\Upload;
|
||||||
use Utopia\Swoole\Request;
|
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\AnyOf;
|
|
||||||
use Utopia\Validator\ArrayList;
|
|
||||||
use Utopia\Validator\Assoc;
|
|
||||||
use Utopia\Validator\Boolean;
|
|
||||||
use Utopia\Validator\Nullable;
|
|
||||||
use Utopia\Validator\Range;
|
|
||||||
use Utopia\Validator\Text;
|
|
||||||
use Utopia\Validator\WhiteList;
|
|
||||||
use Utopia\VCS\Adapter\Git\GitHub;
|
use Utopia\VCS\Adapter\Git\GitHub;
|
||||||
use Utopia\VCS\Exception\RepositoryNotFound;
|
use Utopia\VCS\Exception\RepositoryNotFound;
|
||||||
|
|
||||||
|
|
@ -133,7 +135,7 @@ $redeployVcs = function (Request $request, Document $function, Document $project
|
||||||
->setTemplate($template);
|
->setTemplate($template);
|
||||||
};
|
};
|
||||||
|
|
||||||
App::post('/v1/functions')
|
Http::post('/v1/functions')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('Create function')
|
->desc('Create function')
|
||||||
->label('scope', 'functions.write')
|
->label('scope', 'functions.write')
|
||||||
|
|
@ -171,8 +173,8 @@ App::post('/v1/functions')
|
||||||
->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification(
|
->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification(
|
||||||
$plan,
|
$plan,
|
||||||
Config::getParam('runtime-specifications', []),
|
Config::getParam('runtime-specifications', []),
|
||||||
App::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT),
|
System::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT),
|
||||||
App::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT)
|
System::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT)
|
||||||
), 'Runtime specification for the function and builds.', true, ['plan'])
|
), 'Runtime specification for the function and builds.', true, ['plan'])
|
||||||
->inject('request')
|
->inject('request')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
|
|
@ -183,7 +185,8 @@ App::post('/v1/functions')
|
||||||
->inject('queueForBuilds')
|
->inject('queueForBuilds')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->inject('gitHub')
|
->inject('gitHub')
|
||||||
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
|
->inject('authorization')
|
||||||
|
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github, Authorization $authorization) use ($redeployVcs) {
|
||||||
$functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
|
$functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
|
||||||
|
|
||||||
$allowList = \array_filter(\explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES', '')));
|
$allowList = \array_filter(\explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES', '')));
|
||||||
|
|
@ -247,7 +250,7 @@ App::post('/v1/functions')
|
||||||
'specification' => $specification
|
'specification' => $specification
|
||||||
]));
|
]));
|
||||||
|
|
||||||
$schedule = Authorization::skip(
|
$schedule = $authorization->skip(
|
||||||
fn () => $dbForConsole->createDocument('schedules', new Document([
|
fn () => $dbForConsole->createDocument('schedules', new Document([
|
||||||
'region' => System::getEnv('_APP_REGION', 'default'), // Todo replace with projects region
|
'region' => System::getEnv('_APP_REGION', 'default'), // Todo replace with projects region
|
||||||
'resourceType' => 'function',
|
'resourceType' => 'function',
|
||||||
|
|
@ -329,7 +332,7 @@ App::post('/v1/functions')
|
||||||
$routeSubdomain = ID::unique();
|
$routeSubdomain = ID::unique();
|
||||||
$domain = "{$routeSubdomain}.{$functionsDomain}";
|
$domain = "{$routeSubdomain}.{$functionsDomain}";
|
||||||
|
|
||||||
$rule = Authorization::skip(
|
$rule = $authorization->skip(
|
||||||
fn () => $dbForConsole->createDocument('rules', new Document([
|
fn () => $dbForConsole->createDocument('rules', new Document([
|
||||||
'$id' => $ruleId,
|
'$id' => $ruleId,
|
||||||
'projectId' => $project->getId(),
|
'projectId' => $project->getId(),
|
||||||
|
|
@ -396,7 +399,7 @@ App::post('/v1/functions')
|
||||||
->dynamic($function, Response::MODEL_FUNCTION);
|
->dynamic($function, Response::MODEL_FUNCTION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/functions')
|
Http::get('/v1/functions')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('List functions')
|
->desc('List functions')
|
||||||
->label('scope', 'functions.read')
|
->label('scope', 'functions.read')
|
||||||
|
|
@ -450,7 +453,7 @@ App::get('/v1/functions')
|
||||||
]), Response::MODEL_FUNCTION_LIST);
|
]), Response::MODEL_FUNCTION_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/functions/runtimes')
|
Http::get('/v1/functions/runtimes')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('List runtimes')
|
->desc('List runtimes')
|
||||||
->label('scope', 'functions.read')
|
->label('scope', 'functions.read')
|
||||||
|
|
@ -483,7 +486,7 @@ App::get('/v1/functions/runtimes')
|
||||||
]), Response::MODEL_RUNTIME_LIST);
|
]), Response::MODEL_RUNTIME_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/functions/specifications')
|
Http::get('/v1/functions/specifications')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('List available function runtime specifications')
|
->desc('List available function runtime specifications')
|
||||||
->label('scope', 'functions.read')
|
->label('scope', 'functions.read')
|
||||||
|
|
@ -519,7 +522,7 @@ App::get('/v1/functions/specifications')
|
||||||
]), Response::MODEL_SPECIFICATION_LIST);
|
]), Response::MODEL_SPECIFICATION_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/functions/:functionId')
|
Http::get('/v1/functions/:functionId')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('Get function')
|
->desc('Get function')
|
||||||
->label('scope', 'functions.read')
|
->label('scope', 'functions.read')
|
||||||
|
|
@ -543,7 +546,7 @@ App::get('/v1/functions/:functionId')
|
||||||
$response->dynamic($function, Response::MODEL_FUNCTION);
|
$response->dynamic($function, Response::MODEL_FUNCTION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/functions/:functionId/usage')
|
Http::get('/v1/functions/:functionId/usage')
|
||||||
->desc('Get function usage')
|
->desc('Get function usage')
|
||||||
->groups(['api', 'functions', 'usage'])
|
->groups(['api', 'functions', 'usage'])
|
||||||
->label('scope', 'functions.read')
|
->label('scope', 'functions.read')
|
||||||
|
|
@ -557,7 +560,8 @@ App::get('/v1/functions/:functionId/usage')
|
||||||
->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true)
|
->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->action(function (string $functionId, string $range, Response $response, Database $dbForProject) {
|
->inject('authorization')
|
||||||
|
->action(function (string $functionId, string $range, Response $response, Database $dbForProject, Authorization $authorization) {
|
||||||
|
|
||||||
$function = $dbForProject->getDocument('functions', $functionId);
|
$function = $dbForProject->getDocument('functions', $functionId);
|
||||||
|
|
||||||
|
|
@ -580,7 +584,7 @@ App::get('/v1/functions/:functionId/usage')
|
||||||
str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS)
|
str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS)
|
||||||
];
|
];
|
||||||
|
|
||||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
$authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||||
foreach ($metrics as $metric) {
|
foreach ($metrics as $metric) {
|
||||||
$result = $dbForProject->findOne('stats', [
|
$result = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
|
|
@ -647,7 +651,7 @@ App::get('/v1/functions/:functionId/usage')
|
||||||
]), Response::MODEL_USAGE_FUNCTION);
|
]), Response::MODEL_USAGE_FUNCTION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/functions/usage')
|
Http::get('/v1/functions/usage')
|
||||||
->desc('Get functions usage')
|
->desc('Get functions usage')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->label('scope', 'functions.read')
|
->label('scope', 'functions.read')
|
||||||
|
|
@ -660,7 +664,8 @@ App::get('/v1/functions/usage')
|
||||||
->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true)
|
->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->action(function (string $range, Response $response, Database $dbForProject) {
|
->inject('authorization')
|
||||||
|
->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) {
|
||||||
|
|
||||||
$periods = Config::getParam('usage', []);
|
$periods = Config::getParam('usage', []);
|
||||||
$stats = $usage = [];
|
$stats = $usage = [];
|
||||||
|
|
@ -678,7 +683,7 @@ App::get('/v1/functions/usage')
|
||||||
METRIC_EXECUTIONS_MB_SECONDS,
|
METRIC_EXECUTIONS_MB_SECONDS,
|
||||||
];
|
];
|
||||||
|
|
||||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
$authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||||
foreach ($metrics as $metric) {
|
foreach ($metrics as $metric) {
|
||||||
$result = $dbForProject->findOne('stats', [
|
$result = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
|
|
@ -746,7 +751,7 @@ App::get('/v1/functions/usage')
|
||||||
]), Response::MODEL_USAGE_FUNCTIONS);
|
]), Response::MODEL_USAGE_FUNCTIONS);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::put('/v1/functions/:functionId')
|
Http::put('/v1/functions/:functionId')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('Update function')
|
->desc('Update function')
|
||||||
->label('scope', 'functions.write')
|
->label('scope', 'functions.write')
|
||||||
|
|
@ -780,8 +785,8 @@ App::put('/v1/functions/:functionId')
|
||||||
->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification(
|
->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification(
|
||||||
$plan,
|
$plan,
|
||||||
Config::getParam('runtime-specifications', []),
|
Config::getParam('runtime-specifications', []),
|
||||||
App::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT),
|
System::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT),
|
||||||
App::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT)
|
System::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT)
|
||||||
), 'Runtime specification for the function and builds.', true, ['plan'])
|
), 'Runtime specification for the function and builds.', true, ['plan'])
|
||||||
->inject('request')
|
->inject('request')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
|
|
@ -791,7 +796,8 @@ App::put('/v1/functions/:functionId')
|
||||||
->inject('queueForBuilds')
|
->inject('queueForBuilds')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->inject('gitHub')
|
->inject('gitHub')
|
||||||
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, ?string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
|
->inject('authorization')
|
||||||
|
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, ?string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github, Authorization $authorization) use ($redeployVcs) {
|
||||||
// TODO: If only branch changes, re-deploy
|
// TODO: If only branch changes, re-deploy
|
||||||
$function = $dbForProject->getDocument('functions', $functionId);
|
$function = $dbForProject->getDocument('functions', $functionId);
|
||||||
|
|
||||||
|
|
@ -894,7 +900,7 @@ App::put('/v1/functions/:functionId')
|
||||||
|
|
||||||
// Enforce Cold Start if spec limits change.
|
// Enforce Cold Start if spec limits change.
|
||||||
if ($function->getAttribute('specification') !== $specification && !empty($function->getAttribute('deployment'))) {
|
if ($function->getAttribute('specification') !== $specification && !empty($function->getAttribute('deployment'))) {
|
||||||
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
|
$executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST'));
|
||||||
try {
|
try {
|
||||||
$executor->deleteRuntime($project->getId(), $function->getAttribute('deployment'));
|
$executor->deleteRuntime($project->getId(), $function->getAttribute('deployment'));
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $th) {
|
||||||
|
|
@ -941,14 +947,14 @@ App::put('/v1/functions/:functionId')
|
||||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||||
->setAttribute('schedule', $function->getAttribute('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));
|
$authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||||
|
|
||||||
$queueForEvents->setParam('functionId', $function->getId());
|
$queueForEvents->setParam('functionId', $function->getId());
|
||||||
|
|
||||||
$response->dynamic($function, Response::MODEL_FUNCTION);
|
$response->dynamic($function, Response::MODEL_FUNCTION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/functions/:functionId/deployments/:deploymentId/download')
|
Http::get('/v1/functions/:functionId/deployments/:deploymentId/download')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('Download deployment')
|
->desc('Download deployment')
|
||||||
->label('scope', 'functions.read')
|
->label('scope', 'functions.read')
|
||||||
|
|
@ -1033,7 +1039,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId/download')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/functions/:functionId/deployments/:deploymentId')
|
Http::patch('/v1/functions/:functionId/deployments/:deploymentId')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('Update deployment')
|
->desc('Update deployment')
|
||||||
->label('scope', 'functions.write')
|
->label('scope', 'functions.write')
|
||||||
|
|
@ -1053,7 +1059,8 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Event $queueForEvents, Database $dbForConsole) {
|
->inject('authorization')
|
||||||
|
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Event $queueForEvents, Database $dbForConsole, Authorization $authorization) {
|
||||||
|
|
||||||
$function = $dbForProject->getDocument('functions', $functionId);
|
$function = $dbForProject->getDocument('functions', $functionId);
|
||||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||||
|
|
@ -1086,7 +1093,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
|
||||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||||
->setAttribute('schedule', $function->getAttribute('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));
|
$authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||||
|
|
||||||
$queueForEvents
|
$queueForEvents
|
||||||
->setParam('functionId', $function->getId())
|
->setParam('functionId', $function->getId())
|
||||||
|
|
@ -1095,7 +1102,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
|
||||||
$response->dynamic($function, Response::MODEL_FUNCTION);
|
$response->dynamic($function, Response::MODEL_FUNCTION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/functions/:functionId')
|
Http::delete('/v1/functions/:functionId')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('Delete function')
|
->desc('Delete function')
|
||||||
->label('scope', 'functions.write')
|
->label('scope', 'functions.write')
|
||||||
|
|
@ -1114,7 +1121,8 @@ App::delete('/v1/functions/:functionId')
|
||||||
->inject('queueForDeletes')
|
->inject('queueForDeletes')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $functionId, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Database $dbForConsole) {
|
->inject('authorization')
|
||||||
|
->action(function (string $functionId, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Database $dbForConsole, Authorization $authorization) {
|
||||||
|
|
||||||
$function = $dbForProject->getDocument('functions', $functionId);
|
$function = $dbForProject->getDocument('functions', $functionId);
|
||||||
|
|
||||||
|
|
@ -1131,7 +1139,7 @@ App::delete('/v1/functions/:functionId')
|
||||||
$schedule
|
$schedule
|
||||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||||
->setAttribute('active', false);
|
->setAttribute('active', false);
|
||||||
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
$authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||||
|
|
||||||
$queueForDeletes
|
$queueForDeletes
|
||||||
->setType(DELETE_TYPE_DOCUMENT)
|
->setType(DELETE_TYPE_DOCUMENT)
|
||||||
|
|
@ -1142,7 +1150,7 @@ App::delete('/v1/functions/:functionId')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/functions/:functionId/deployments')
|
Http::post('/v1/functions/:functionId/deployments')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('Create deployment')
|
->desc('Create deployment')
|
||||||
->label('scope', 'functions.write')
|
->label('scope', 'functions.write')
|
||||||
|
|
@ -1361,7 +1369,7 @@ App::post('/v1/functions/:functionId/deployments')
|
||||||
->dynamic($deployment, Response::MODEL_DEPLOYMENT);
|
->dynamic($deployment, Response::MODEL_DEPLOYMENT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/functions/:functionId/deployments')
|
Http::get('/v1/functions/:functionId/deployments')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('List deployments')
|
->desc('List deployments')
|
||||||
->label('scope', 'functions.read')
|
->label('scope', 'functions.read')
|
||||||
|
|
@ -1438,7 +1446,7 @@ App::get('/v1/functions/:functionId/deployments')
|
||||||
]), Response::MODEL_DEPLOYMENT_LIST);
|
]), Response::MODEL_DEPLOYMENT_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/functions/:functionId/deployments/:deploymentId')
|
Http::get('/v1/functions/:functionId/deployments/:deploymentId')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('Get deployment')
|
->desc('Get deployment')
|
||||||
->label('scope', 'functions.read')
|
->label('scope', 'functions.read')
|
||||||
|
|
@ -1481,7 +1489,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId')
|
||||||
$response->dynamic($deployment, Response::MODEL_DEPLOYMENT);
|
$response->dynamic($deployment, Response::MODEL_DEPLOYMENT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/functions/:functionId/deployments/:deploymentId')
|
Http::delete('/v1/functions/:functionId/deployments/:deploymentId')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('Delete deployment')
|
->desc('Delete deployment')
|
||||||
->label('scope', 'functions.write')
|
->label('scope', 'functions.write')
|
||||||
|
|
@ -1545,7 +1553,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
|
Http::post('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||||
->alias('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
|
->alias('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('Rebuild deployment')
|
->desc('Rebuild deployment')
|
||||||
|
|
@ -1568,7 +1576,8 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->inject('queueForBuilds')
|
->inject('queueForBuilds')
|
||||||
->inject('deviceForFunctions')
|
->inject('deviceForFunctions')
|
||||||
->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Device $deviceForFunctions) {
|
->inject('authorization')
|
||||||
|
->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Device $deviceForFunctions, Authorization $authorization) {
|
||||||
$function = $dbForProject->getDocument('functions', $functionId);
|
$function = $dbForProject->getDocument('functions', $functionId);
|
||||||
|
|
||||||
if ($function->isEmpty()) {
|
if ($function->isEmpty()) {
|
||||||
|
|
@ -1614,7 +1623,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
|
Http::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('Cancel deployment')
|
->desc('Cancel deployment')
|
||||||
->label('scope', 'functions.write')
|
->label('scope', 'functions.write')
|
||||||
|
|
@ -1632,7 +1641,8 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents) {
|
->inject('authorization')
|
||||||
|
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Authorization $authorization) {
|
||||||
$function = $dbForProject->getDocument('functions', $functionId);
|
$function = $dbForProject->getDocument('functions', $functionId);
|
||||||
|
|
||||||
if ($function->isEmpty()) {
|
if ($function->isEmpty()) {
|
||||||
|
|
@ -1645,7 +1655,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||||
throw new Exception(Exception::DEPLOYMENT_NOT_FOUND);
|
throw new Exception(Exception::DEPLOYMENT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
|
$build = $authorization->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
|
||||||
|
|
||||||
if ($build->isEmpty()) {
|
if ($build->isEmpty()) {
|
||||||
$buildId = ID::unique();
|
$buildId = ID::unique();
|
||||||
|
|
@ -1687,7 +1697,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||||
$dbForProject->purgeCachedDocument('deployments', $deployment->getId());
|
$dbForProject->purgeCachedDocument('deployments', $deployment->getId());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
|
$executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST'));
|
||||||
$executor->deleteRuntime($project->getId(), $deploymentId . "-build");
|
$executor->deleteRuntime($project->getId(), $deploymentId . "-build");
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $th) {
|
||||||
// Don't throw if the deployment doesn't exist
|
// Don't throw if the deployment doesn't exist
|
||||||
|
|
@ -1703,7 +1713,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||||
$response->dynamic($build, Response::MODEL_BUILD);
|
$response->dynamic($build, Response::MODEL_BUILD);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/functions/:functionId/executions')
|
Http::post('/v1/functions/:functionId/executions')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('Create execution')
|
->desc('Create execution')
|
||||||
->label('scope', 'execution.write')
|
->label('scope', 'execution.write')
|
||||||
|
|
@ -1733,7 +1743,9 @@ App::post('/v1/functions/:functionId/executions')
|
||||||
->inject('queueForUsage')
|
->inject('queueForUsage')
|
||||||
->inject('queueForFunctions')
|
->inject('queueForFunctions')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->action(function (string $functionId, string $body, mixed $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb) {
|
->inject('authorization')
|
||||||
|
->inject('authentication')
|
||||||
|
->action(function (string $functionId, string $body, mixed $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, Authorization $authorization, Authentication $authentication) {
|
||||||
$async = \strval($async) === 'true' || \strval($async) === '1';
|
$async = \strval($async) === 'true' || \strval($async) === '1';
|
||||||
|
|
||||||
if (!$async && !is_null($scheduledAt)) {
|
if (!$async && !is_null($scheduledAt)) {
|
||||||
|
|
@ -1763,10 +1775,10 @@ App::post('/v1/functions/:functionId/executions')
|
||||||
throw new Exception($validator->getDescription(), 400);
|
throw new Exception($validator->getDescription(), 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
$function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
||||||
|
|
||||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
$isAPIKey = Auth::isAppUser($authorization->getRoles());
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
|
|
||||||
if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||||
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
||||||
|
|
@ -1782,7 +1794,7 @@ App::post('/v1/functions/:functionId/executions')
|
||||||
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', '')));
|
$deployment = $authorization->skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', '')));
|
||||||
|
|
||||||
if ($deployment->getAttribute('resourceId') !== $function->getId()) {
|
if ($deployment->getAttribute('resourceId') !== $function->getId()) {
|
||||||
throw new Exception(Exception::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function');
|
throw new Exception(Exception::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function');
|
||||||
|
|
@ -1793,7 +1805,7 @@ App::post('/v1/functions/:functionId/executions')
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check if build has completed */
|
/** Check if build has completed */
|
||||||
$build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
|
$build = $authorization->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
|
||||||
if ($build->isEmpty()) {
|
if ($build->isEmpty()) {
|
||||||
throw new Exception(Exception::BUILD_NOT_FOUND);
|
throw new Exception(Exception::BUILD_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
@ -1802,10 +1814,8 @@ App::post('/v1/functions/:functionId/executions')
|
||||||
throw new Exception(Exception::BUILD_NOT_READY);
|
throw new Exception(Exception::BUILD_NOT_READY);
|
||||||
}
|
}
|
||||||
|
|
||||||
$validator = new Authorization('execute');
|
if (!$authorization->isValid(new Input('execute', $function->getAttribute('execute')))) { // Check if user has write access to execute function
|
||||||
|
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
|
||||||
if (!$validator->isValid($function->getAttribute('execute'))) { // Check if user has write access to execute function
|
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$jwt = ''; // initialize
|
$jwt = ''; // initialize
|
||||||
|
|
@ -1815,7 +1825,7 @@ App::post('/v1/functions/:functionId/executions')
|
||||||
|
|
||||||
foreach ($sessions as $session) {
|
foreach ($sessions as $session) {
|
||||||
/** @var Utopia\Database\Document $session */
|
/** @var Utopia\Database\Document $session */
|
||||||
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
|
if ($session->getAttribute('secret') == Auth::hash($authentication->getSecret())) { // If current session delete the cookies too
|
||||||
$current = $session;
|
$current = $session;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1899,8 +1909,9 @@ App::post('/v1/functions/:functionId/executions')
|
||||||
->setContext('function', $function);
|
->setContext('function', $function);
|
||||||
|
|
||||||
if ($async) {
|
if ($async) {
|
||||||
|
|
||||||
if (is_null($scheduledAt)) {
|
if (is_null($scheduledAt)) {
|
||||||
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
|
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||||
$queueForFunctions
|
$queueForFunctions
|
||||||
->setType('http')
|
->setType('http')
|
||||||
->setExecution($execution)
|
->setExecution($execution)
|
||||||
|
|
@ -1941,7 +1952,7 @@ App::post('/v1/functions/:functionId/executions')
|
||||||
->setAttribute('scheduleInternalId', $schedule->getInternalId())
|
->setAttribute('scheduleInternalId', $schedule->getInternalId())
|
||||||
->setAttribute('scheduledAt', $scheduledAt);
|
->setAttribute('scheduledAt', $scheduledAt);
|
||||||
|
|
||||||
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
|
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $response
|
return $response
|
||||||
|
|
@ -2027,8 +2038,7 @@ App::post('/v1/functions/:functionId/executions')
|
||||||
runtimeEntrypoint: $command,
|
runtimeEntrypoint: $command,
|
||||||
cpus: $spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT,
|
cpus: $spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT,
|
||||||
memory: $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT,
|
memory: $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT,
|
||||||
logging: $function->getAttribute('logging', true),
|
logging: $function->getAttribute('logging', true)
|
||||||
requestTimeout: 30
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$headersFiltered = [];
|
$headersFiltered = [];
|
||||||
|
|
@ -2069,10 +2079,10 @@ App::post('/v1/functions/:functionId/executions')
|
||||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT)))
|
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT)))
|
||||||
;
|
;
|
||||||
|
|
||||||
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
|
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||||
}
|
}
|
||||||
|
|
||||||
$roles = Authorization::getRoles();
|
$roles = $authorization->getRoles();
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||||
$isAppUser = Auth::isAppUser($roles);
|
$isAppUser = Auth::isAppUser($roles);
|
||||||
|
|
||||||
|
|
@ -2105,7 +2115,7 @@ App::post('/v1/functions/:functionId/executions')
|
||||||
->dynamic($execution, Response::MODEL_EXECUTION);
|
->dynamic($execution, Response::MODEL_EXECUTION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/functions/:functionId/executions')
|
Http::get('/v1/functions/:functionId/executions')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('List executions')
|
->desc('List executions')
|
||||||
->label('scope', 'execution.read')
|
->label('scope', 'execution.read')
|
||||||
|
|
@ -2122,11 +2132,12 @@ App::get('/v1/functions/:functionId/executions')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('mode')
|
->inject('mode')
|
||||||
->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) {
|
->inject('authorization')
|
||||||
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject, string $mode, Authorization $authorization) {
|
||||||
|
$function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
||||||
|
|
||||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
$isAPIKey = Auth::isAppUser($authorization->getRoles());
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
|
|
||||||
if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||||
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
||||||
|
|
@ -2169,7 +2180,7 @@ App::get('/v1/functions/:functionId/executions')
|
||||||
$results = $dbForProject->find('executions', $queries);
|
$results = $dbForProject->find('executions', $queries);
|
||||||
$total = $dbForProject->count('executions', $filterQueries, APP_LIMIT_COUNT);
|
$total = $dbForProject->count('executions', $filterQueries, APP_LIMIT_COUNT);
|
||||||
|
|
||||||
$roles = Authorization::getRoles();
|
$roles = $authorization->getRoles();
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||||
$isAppUser = Auth::isAppUser($roles);
|
$isAppUser = Auth::isAppUser($roles);
|
||||||
if (!$isPrivilegedUser && !$isAppUser) {
|
if (!$isPrivilegedUser && !$isAppUser) {
|
||||||
|
|
@ -2186,7 +2197,7 @@ App::get('/v1/functions/:functionId/executions')
|
||||||
]), Response::MODEL_EXECUTION_LIST);
|
]), Response::MODEL_EXECUTION_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/functions/:functionId/executions/:executionId')
|
Http::get('/v1/functions/:functionId/executions/:executionId')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('Get execution')
|
->desc('Get execution')
|
||||||
->label('scope', 'execution.read')
|
->label('scope', 'execution.read')
|
||||||
|
|
@ -2202,11 +2213,12 @@ App::get('/v1/functions/:functionId/executions/:executionId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('mode')
|
->inject('mode')
|
||||||
->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, string $mode) {
|
->inject('authorization')
|
||||||
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, string $mode, Authorization $authorization) {
|
||||||
|
$function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
||||||
|
|
||||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
$isAPIKey = Auth::isAppUser($authorization->getRoles());
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
|
|
||||||
if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||||
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
||||||
|
|
@ -2222,7 +2234,7 @@ App::get('/v1/functions/:functionId/executions/:executionId')
|
||||||
throw new Exception(Exception::EXECUTION_NOT_FOUND);
|
throw new Exception(Exception::EXECUTION_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$roles = Authorization::getRoles();
|
$roles = $authorization->getRoles();
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||||
$isAppUser = Auth::isAppUser($roles);
|
$isAppUser = Auth::isAppUser($roles);
|
||||||
if (!$isPrivilegedUser && !$isAppUser) {
|
if (!$isPrivilegedUser && !$isAppUser) {
|
||||||
|
|
@ -2233,7 +2245,7 @@ App::get('/v1/functions/:functionId/executions/:executionId')
|
||||||
$response->dynamic($execution, Response::MODEL_EXECUTION);
|
$response->dynamic($execution, Response::MODEL_EXECUTION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/functions/:functionId/executions/:executionId')
|
Http::delete('/v1/functions/:functionId/executions/:executionId')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->desc('Delete execution')
|
->desc('Delete execution')
|
||||||
->label('scope', 'execution.write')
|
->label('scope', 'execution.write')
|
||||||
|
|
@ -2252,7 +2264,8 @@ App::delete('/v1/functions/:functionId/executions/:executionId')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, Database $dbForConsole, Event $queueForEvents) {
|
->inject('authorization')
|
||||||
|
->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, Database $dbForConsole, Event $queueForEvents, Authorization $authorization) {
|
||||||
$function = $dbForProject->getDocument('functions', $functionId);
|
$function = $dbForProject->getDocument('functions', $functionId);
|
||||||
|
|
||||||
if ($function->isEmpty()) {
|
if ($function->isEmpty()) {
|
||||||
|
|
@ -2284,12 +2297,12 @@ App::delete('/v1/functions/:functionId/executions/:executionId')
|
||||||
Query::equal('active', [true]),
|
Query::equal('active', [true]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($schedule && !$schedule->isEmpty()) {
|
if (!$schedule->isEmpty()) {
|
||||||
$schedule
|
$schedule
|
||||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||||
->setAttribute('active', false);
|
->setAttribute('active', false);
|
||||||
|
|
||||||
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
$authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2303,7 +2316,7 @@ App::delete('/v1/functions/:functionId/executions/:executionId')
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
|
|
||||||
App::post('/v1/functions/:functionId/variables')
|
Http::post('/v1/functions/:functionId/variables')
|
||||||
->desc('Create variable')
|
->desc('Create variable')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->label('scope', 'functions.write')
|
->label('scope', 'functions.write')
|
||||||
|
|
@ -2322,7 +2335,8 @@ App::post('/v1/functions/:functionId/variables')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject, Database $dbForConsole) {
|
->inject('authorization')
|
||||||
|
->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization) {
|
||||||
$function = $dbForProject->getDocument('functions', $functionId);
|
$function = $dbForProject->getDocument('functions', $functionId);
|
||||||
|
|
||||||
if ($function->isEmpty()) {
|
if ($function->isEmpty()) {
|
||||||
|
|
@ -2360,14 +2374,14 @@ App::post('/v1/functions/:functionId/variables')
|
||||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||||
->setAttribute('schedule', $function->getAttribute('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));
|
$authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||||
|
|
||||||
$response
|
$response
|
||||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||||
->dynamic($variable, Response::MODEL_VARIABLE);
|
->dynamic($variable, Response::MODEL_VARIABLE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/functions/:functionId/variables')
|
Http::get('/v1/functions/:functionId/variables')
|
||||||
->desc('List variables')
|
->desc('List variables')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->label('scope', 'functions.read')
|
->label('scope', 'functions.read')
|
||||||
|
|
@ -2394,7 +2408,7 @@ App::get('/v1/functions/:functionId/variables')
|
||||||
]), Response::MODEL_VARIABLE_LIST);
|
]), Response::MODEL_VARIABLE_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/functions/:functionId/variables/:variableId')
|
Http::get('/v1/functions/:functionId/variables/:variableId')
|
||||||
->desc('Get variable')
|
->desc('Get variable')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->label('scope', 'functions.read')
|
->label('scope', 'functions.read')
|
||||||
|
|
@ -2433,7 +2447,7 @@ App::get('/v1/functions/:functionId/variables/:variableId')
|
||||||
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::put('/v1/functions/:functionId/variables/:variableId')
|
Http::put('/v1/functions/:functionId/variables/:variableId')
|
||||||
->desc('Update variable')
|
->desc('Update variable')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->label('scope', 'functions.write')
|
->label('scope', 'functions.write')
|
||||||
|
|
@ -2453,7 +2467,8 @@ App::put('/v1/functions/:functionId/variables/:variableId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject, Database $dbForConsole) {
|
->inject('authorization')
|
||||||
|
->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization) {
|
||||||
|
|
||||||
$function = $dbForProject->getDocument('functions', $functionId);
|
$function = $dbForProject->getDocument('functions', $functionId);
|
||||||
|
|
||||||
|
|
@ -2489,12 +2504,12 @@ App::put('/v1/functions/:functionId/variables/:variableId')
|
||||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||||
->setAttribute('schedule', $function->getAttribute('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));
|
$authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||||
|
|
||||||
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/functions/:functionId/variables/:variableId')
|
Http::delete('/v1/functions/:functionId/variables/:variableId')
|
||||||
->desc('Delete variable')
|
->desc('Delete variable')
|
||||||
->groups(['api', 'functions'])
|
->groups(['api', 'functions'])
|
||||||
->label('scope', 'functions.write')
|
->label('scope', 'functions.write')
|
||||||
|
|
@ -2511,7 +2526,8 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject, Database $dbForConsole) {
|
->inject('authorization')
|
||||||
|
->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization) {
|
||||||
$function = $dbForProject->getDocument('functions', $functionId);
|
$function = $dbForProject->getDocument('functions', $functionId);
|
||||||
|
|
||||||
if ($function->isEmpty()) {
|
if ($function->isEmpty()) {
|
||||||
|
|
@ -2537,12 +2553,12 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
|
||||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||||
->setAttribute('schedule', $function->getAttribute('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));
|
$authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||||
|
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/functions/templates')
|
Http::get('/v1/functions/templates')
|
||||||
->groups(['api'])
|
->groups(['api'])
|
||||||
->desc('List function templates')
|
->desc('List function templates')
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
|
|
@ -2580,7 +2596,7 @@ App::get('/v1/functions/templates')
|
||||||
]), Response::MODEL_TEMPLATE_FUNCTION_LIST);
|
]), Response::MODEL_TEMPLATE_FUNCTION_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/functions/templates/:templateId')
|
Http::get('/v1/functions/templates/:templateId')
|
||||||
->desc('Get function template')
|
->desc('Get function template')
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
->label('sdk.namespace', 'functions')
|
->label('sdk.namespace', 'functions')
|
||||||
|
|
@ -2595,9 +2611,10 @@ App::get('/v1/functions/templates/:templateId')
|
||||||
->action(function (string $templateId, Response $response) {
|
->action(function (string $templateId, Response $response) {
|
||||||
$templates = Config::getParam('function-templates', []);
|
$templates = Config::getParam('function-templates', []);
|
||||||
|
|
||||||
$template = array_shift(\array_filter($templates, function ($template) use ($templateId) {
|
$array = \array_filter($templates, function ($template) use ($templateId) {
|
||||||
return $template['id'] === $templateId;
|
return $template['id'] === $templateId;
|
||||||
}));
|
});
|
||||||
|
$template = array_shift($array);
|
||||||
|
|
||||||
if (empty($template)) {
|
if (empty($template)) {
|
||||||
throw new Exception(Exception::FUNCTION_TEMPLATE_NOT_FOUND);
|
throw new Exception(Exception::FUNCTION_TEMPLATE_NOT_FOUND);
|
||||||
|
|
|
||||||
|
|
@ -14,27 +14,28 @@ use GraphQL\Validator\Rules\DisableIntrospection;
|
||||||
use GraphQL\Validator\Rules\QueryComplexity;
|
use GraphQL\Validator\Rules\QueryComplexity;
|
||||||
use GraphQL\Validator\Rules\QueryDepth;
|
use GraphQL\Validator\Rules\QueryDepth;
|
||||||
use Swoole\Coroutine\WaitGroup;
|
use Swoole\Coroutine\WaitGroup;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\Database\Validator\Authorization;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Validator\JSON;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\JSON;
|
|
||||||
use Utopia\Validator\Text;
|
|
||||||
|
|
||||||
App::init()
|
Http::init()
|
||||||
->groups(['graphql'])
|
->groups(['graphql'])
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->action(function (Document $project) {
|
->inject('authorization')
|
||||||
|
->action(function (Document $project, Authorization $authorization) {
|
||||||
if (
|
if (
|
||||||
array_key_exists('graphql', $project->getAttribute('apis', []))
|
array_key_exists('graphql', $project->getAttribute('apis', []))
|
||||||
&& !$project->getAttribute('apis', [])['graphql']
|
&& !$project->getAttribute('apis', [])['graphql']
|
||||||
&& !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles()))
|
&& !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles()))
|
||||||
) {
|
) {
|
||||||
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
|
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/graphql')
|
Http::get('/v1/graphql')
|
||||||
->desc('GraphQL endpoint')
|
->desc('GraphQL endpoint')
|
||||||
->groups(['graphql'])
|
->groups(['graphql'])
|
||||||
->label('scope', 'graphql')
|
->label('scope', 'graphql')
|
||||||
|
|
@ -74,7 +75,7 @@ App::get('/v1/graphql')
|
||||||
->json($output);
|
->json($output);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/graphql/mutation')
|
Http::post('/v1/graphql/mutation')
|
||||||
->desc('GraphQL endpoint')
|
->desc('GraphQL endpoint')
|
||||||
->groups(['graphql'])
|
->groups(['graphql'])
|
||||||
->label('scope', 'graphql')
|
->label('scope', 'graphql')
|
||||||
|
|
@ -119,7 +120,7 @@ App::post('/v1/graphql/mutation')
|
||||||
->json($output);
|
->json($output);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/graphql')
|
Http::post('/v1/graphql')
|
||||||
->desc('GraphQL endpoint')
|
->desc('GraphQL endpoint')
|
||||||
->groups(['graphql'])
|
->groups(['graphql'])
|
||||||
->label('scope', 'graphql')
|
->label('scope', 'graphql')
|
||||||
|
|
@ -156,7 +157,6 @@ App::post('/v1/graphql')
|
||||||
if (\str_starts_with($type, 'multipart/form-data')) {
|
if (\str_starts_with($type, 'multipart/form-data')) {
|
||||||
$query = parseMultipart($query, $request);
|
$query = parseMultipart($query, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
$output = execute($schema, $promiseAdapter, $query);
|
$output = execute($schema, $promiseAdapter, $query);
|
||||||
|
|
||||||
$response
|
$response
|
||||||
|
|
@ -205,7 +205,7 @@ function execute(
|
||||||
$validations[] = new QueryComplexity($maxComplexity);
|
$validations[] = new QueryComplexity($maxComplexity);
|
||||||
$validations[] = new QueryDepth($maxDepth);
|
$validations[] = new QueryDepth($maxDepth);
|
||||||
}
|
}
|
||||||
if (App::getMode() === App::MODE_TYPE_PRODUCTION) {
|
if (Http::getMode() === Http::MODE_TYPE_PRODUCTION) {
|
||||||
$flags = DebugFlag::NONE;
|
$flags = DebugFlag::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -306,9 +306,10 @@ function processResult($result, $debugFlags): array
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
App::shutdown()
|
Http::shutdown()
|
||||||
->groups(['schema'])
|
->groups(['schema'])
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->action(function (Document $project) {
|
->inject('schemaVariable')
|
||||||
Schema::setDirty($project->getId());
|
->action(function (Document $project, Schema $schemaVariable) {
|
||||||
|
$schemaVariable->setDirty($project->getId());
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,19 @@
|
||||||
use Appwrite\ClamAV\Network;
|
use Appwrite\ClamAV\Network;
|
||||||
use Appwrite\Event\Event;
|
use Appwrite\Event\Event;
|
||||||
use Appwrite\Extend\Exception;
|
use Appwrite\Extend\Exception;
|
||||||
|
use Appwrite\Utopia\Queue\Connections;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
|
use Utopia\Database\Adapter\MariaDB;
|
||||||
|
use Utopia\Database\Adapter\MySQL;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Domains\Validator\PublicDomain;
|
use Utopia\Domains\Validator\PublicDomain;
|
||||||
use Utopia\Pools\Group;
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Validator\Domain;
|
||||||
|
use Utopia\Http\Validator\Integer;
|
||||||
|
use Utopia\Http\Validator\Multiple;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
|
use Utopia\Http\Validator\WhiteList;
|
||||||
use Utopia\Queue\Client;
|
use Utopia\Queue\Client;
|
||||||
use Utopia\Queue\Connection;
|
use Utopia\Queue\Connection;
|
||||||
use Utopia\Registry\Registry;
|
use Utopia\Registry\Registry;
|
||||||
|
|
@ -16,13 +23,8 @@ use Utopia\Storage\Device;
|
||||||
use Utopia\Storage\Device\Local;
|
use Utopia\Storage\Device\Local;
|
||||||
use Utopia\Storage\Storage;
|
use Utopia\Storage\Storage;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\Domain;
|
|
||||||
use Utopia\Validator\Integer;
|
|
||||||
use Utopia\Validator\Multiple;
|
|
||||||
use Utopia\Validator\Text;
|
|
||||||
use Utopia\Validator\WhiteList;
|
|
||||||
|
|
||||||
App::get('/v1/health')
|
Http::get('/v1/health')
|
||||||
->desc('Get HTTP')
|
->desc('Get HTTP')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -45,7 +47,7 @@ App::get('/v1/health')
|
||||||
$response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS);
|
$response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/health/version')
|
Http::get('/v1/health/version')
|
||||||
->desc('Get version')
|
->desc('Get version')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
|
|
@ -57,7 +59,7 @@ App::get('/v1/health/version')
|
||||||
$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')
|
Http::get('/v1/health/db')
|
||||||
->desc('Get DB')
|
->desc('Get DB')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -70,21 +72,34 @@ App::get('/v1/health/db')
|
||||||
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
|
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('pools')
|
->inject('pools')
|
||||||
->action(function (Response $response, Group $pools) {
|
->inject('connections')
|
||||||
|
->action(function (Response $response, array $pools, Connections $connections) {
|
||||||
|
|
||||||
$output = [];
|
$output = [];
|
||||||
|
|
||||||
$configs = [
|
$configs = [
|
||||||
'Console.DB' => Config::getParam('pools-console'),
|
'console' => Config::getParam('pools-console'),
|
||||||
'Projects.DB' => Config::getParam('pools-database'),
|
'database' => Config::getParam('pools-database'),
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($configs as $key => $config) {
|
foreach ($configs as $key => $config) {
|
||||||
foreach ($config as $database) {
|
foreach ($config as $database) {
|
||||||
try {
|
$checkStart = \microtime(true);
|
||||||
$adapter = $pools->get($database)->pop()->getResource();
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
$pool = $pools['pools-'.$key.'-'.$database]['pool'];
|
||||||
|
$dsn = $pools['pools-'.$key.'-'.$database]['dsn'];
|
||||||
|
|
||||||
|
$connection = $pool->get();
|
||||||
|
$connections->add($connection, $pool);
|
||||||
|
$adapter = match ($dsn->getScheme()) {
|
||||||
|
'mariadb' => new MariaDB($connection),
|
||||||
|
'mysql' => new MySQL($connection),
|
||||||
|
default => null
|
||||||
|
};
|
||||||
|
$adapter->setDatabase($dsn->getPath());
|
||||||
|
|
||||||
$checkStart = \microtime(true);
|
|
||||||
|
|
||||||
if ($adapter->ping()) {
|
if ($adapter->ping()) {
|
||||||
$output[] = new Document([
|
$output[] = new Document([
|
||||||
|
|
@ -111,7 +126,7 @@ App::get('/v1/health/db')
|
||||||
]), Response::MODEL_HEALTH_STATUS_LIST);
|
]), Response::MODEL_HEALTH_STATUS_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/health/cache')
|
Http::get('/v1/health/cache')
|
||||||
->desc('Get cache')
|
->desc('Get cache')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -124,7 +139,8 @@ App::get('/v1/health/cache')
|
||||||
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
|
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('pools')
|
->inject('pools')
|
||||||
->action(function (Response $response, Group $pools) {
|
->inject('connections')
|
||||||
|
->action(function (Response $response, array $pools, Connections $connections) {
|
||||||
|
|
||||||
$output = [];
|
$output = [];
|
||||||
|
|
||||||
|
|
@ -134,10 +150,15 @@ App::get('/v1/health/cache')
|
||||||
|
|
||||||
foreach ($configs as $key => $config) {
|
foreach ($configs as $key => $config) {
|
||||||
foreach ($config as $database) {
|
foreach ($config as $database) {
|
||||||
|
$checkStart = \microtime(true);
|
||||||
try {
|
try {
|
||||||
$adapter = $pools->get($database)->pop()->getResource();
|
$pool = $pools['pools-cache-' . $database]['pool'];
|
||||||
|
$dsn = $pools['pools-cache-' . $database]['dsn'];
|
||||||
|
$connection = $pool->get();
|
||||||
|
$connections->add($connection, $pool);
|
||||||
|
|
||||||
|
$adapter = new Connection\Redis($dsn->getHost(), $dsn->getPort());
|
||||||
|
|
||||||
$checkStart = \microtime(true);
|
|
||||||
|
|
||||||
if ($adapter->ping()) {
|
if ($adapter->ping()) {
|
||||||
$output[] = new Document([
|
$output[] = new Document([
|
||||||
|
|
@ -158,6 +179,8 @@ App::get('/v1/health/cache')
|
||||||
'status' => 'fail',
|
'status' => 'fail',
|
||||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||||
]);
|
]);
|
||||||
|
} finally {
|
||||||
|
$connections->reclaim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -168,7 +191,7 @@ App::get('/v1/health/cache')
|
||||||
]), Response::MODEL_HEALTH_STATUS_LIST);
|
]), Response::MODEL_HEALTH_STATUS_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/health/queue')
|
Http::get('/v1/health/queue')
|
||||||
->desc('Get queue')
|
->desc('Get queue')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -181,7 +204,8 @@ App::get('/v1/health/queue')
|
||||||
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
|
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('pools')
|
->inject('pools')
|
||||||
->action(function (Response $response, Group $pools) {
|
->inject('connections')
|
||||||
|
->action(function (Response $response, array $pools, Connections $connections) {
|
||||||
|
|
||||||
$output = [];
|
$output = [];
|
||||||
|
|
||||||
|
|
@ -190,12 +214,16 @@ App::get('/v1/health/queue')
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($configs as $key => $config) {
|
foreach ($configs as $key => $config) {
|
||||||
|
$checkStart = \microtime(true);
|
||||||
|
|
||||||
foreach ($config as $database) {
|
foreach ($config as $database) {
|
||||||
try {
|
try {
|
||||||
$adapter = $pools->get($database)->pop()->getResource();
|
$pool = $pools['pools-queue-' . $database]['pool'];
|
||||||
|
$dsn = $pools['pools-queue-' . $database]['dsn'];
|
||||||
$checkStart = \microtime(true);
|
$connection = $pool->get();
|
||||||
|
$connections->add($connection, $pool);
|
||||||
|
|
||||||
|
$adapter = new Connection\Redis($dsn->getHost(), $dsn->getPort());
|
||||||
if ($adapter->ping()) {
|
if ($adapter->ping()) {
|
||||||
$output[] = new Document([
|
$output[] = new Document([
|
||||||
'name' => $key . " ($database)",
|
'name' => $key . " ($database)",
|
||||||
|
|
@ -215,6 +243,8 @@ App::get('/v1/health/queue')
|
||||||
'status' => 'fail',
|
'status' => 'fail',
|
||||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||||
]);
|
]);
|
||||||
|
} finally {
|
||||||
|
$connections->reclaim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -225,7 +255,7 @@ App::get('/v1/health/queue')
|
||||||
]), Response::MODEL_HEALTH_STATUS_LIST);
|
]), Response::MODEL_HEALTH_STATUS_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/health/pubsub')
|
Http::get('/v1/health/pubsub')
|
||||||
->desc('Get pubsub')
|
->desc('Get pubsub')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -238,7 +268,8 @@ App::get('/v1/health/pubsub')
|
||||||
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
|
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('pools')
|
->inject('pools')
|
||||||
->action(function (Response $response, Group $pools) {
|
->inject('connections')
|
||||||
|
->action(function (Response $response, array $pools, Connections $connections) {
|
||||||
|
|
||||||
$output = [];
|
$output = [];
|
||||||
|
|
||||||
|
|
@ -249,7 +280,12 @@ App::get('/v1/health/pubsub')
|
||||||
foreach ($configs as $key => $config) {
|
foreach ($configs as $key => $config) {
|
||||||
foreach ($config as $database) {
|
foreach ($config as $database) {
|
||||||
try {
|
try {
|
||||||
$adapter = $pools->get($database)->pop()->getResource();
|
$pool = $pools['pools-pubsub-' . $database]['pool'];
|
||||||
|
|
||||||
|
$connection = $pool->get();
|
||||||
|
$connections->add($connection, $pool);
|
||||||
|
|
||||||
|
$adapter = new Connection\Redis($connection);
|
||||||
|
|
||||||
$checkStart = \microtime(true);
|
$checkStart = \microtime(true);
|
||||||
|
|
||||||
|
|
@ -272,6 +308,8 @@ App::get('/v1/health/pubsub')
|
||||||
'status' => 'fail',
|
'status' => 'fail',
|
||||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||||
]);
|
]);
|
||||||
|
} finally {
|
||||||
|
$connections->reclaim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -282,7 +320,7 @@ App::get('/v1/health/pubsub')
|
||||||
]), Response::MODEL_HEALTH_STATUS_LIST);
|
]), Response::MODEL_HEALTH_STATUS_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/health/time')
|
Http::get('/v1/health/time')
|
||||||
->desc('Get time')
|
->desc('Get time')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -339,7 +377,7 @@ App::get('/v1/health/time')
|
||||||
$response->dynamic(new Document($output), Response::MODEL_HEALTH_TIME);
|
$response->dynamic(new Document($output), Response::MODEL_HEALTH_TIME);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/health/queue/webhooks')
|
Http::get('/v1/health/queue/webhooks')
|
||||||
->desc('Get webhooks queue')
|
->desc('Get webhooks queue')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -366,7 +404,7 @@ App::get('/v1/health/queue/webhooks')
|
||||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||||
}, ['response']);
|
}, ['response']);
|
||||||
|
|
||||||
App::get('/v1/health/queue/logs')
|
Http::get('/v1/health/queue/logs')
|
||||||
->desc('Get logs queue')
|
->desc('Get logs queue')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -393,7 +431,7 @@ App::get('/v1/health/queue/logs')
|
||||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||||
}, ['response']);
|
}, ['response']);
|
||||||
|
|
||||||
App::get('/v1/health/certificate')
|
Http::get('/v1/health/certificate')
|
||||||
->desc('Get the SSL certificate for a domain')
|
->desc('Get the SSL certificate for a domain')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -443,7 +481,7 @@ App::get('/v1/health/certificate')
|
||||||
]), Response::MODEL_HEALTH_CERTIFICATE);
|
]), Response::MODEL_HEALTH_CERTIFICATE);
|
||||||
}, ['response']);
|
}, ['response']);
|
||||||
|
|
||||||
App::get('/v1/health/queue/certificates')
|
Http::get('/v1/health/queue/certificates')
|
||||||
->desc('Get certificates queue')
|
->desc('Get certificates queue')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -470,7 +508,7 @@ App::get('/v1/health/queue/certificates')
|
||||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||||
}, ['response']);
|
}, ['response']);
|
||||||
|
|
||||||
App::get('/v1/health/queue/builds')
|
Http::get('/v1/health/queue/builds')
|
||||||
->desc('Get builds queue')
|
->desc('Get builds queue')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -497,7 +535,7 @@ App::get('/v1/health/queue/builds')
|
||||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||||
}, ['response']);
|
}, ['response']);
|
||||||
|
|
||||||
App::get('/v1/health/queue/databases')
|
Http::get('/v1/health/queue/databases')
|
||||||
->desc('Get databases queue')
|
->desc('Get databases queue')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -525,7 +563,7 @@ App::get('/v1/health/queue/databases')
|
||||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||||
}, ['response']);
|
}, ['response']);
|
||||||
|
|
||||||
App::get('/v1/health/queue/deletes')
|
Http::get('/v1/health/queue/deletes')
|
||||||
->desc('Get deletes queue')
|
->desc('Get deletes queue')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -552,7 +590,7 @@ App::get('/v1/health/queue/deletes')
|
||||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||||
}, ['response']);
|
}, ['response']);
|
||||||
|
|
||||||
App::get('/v1/health/queue/mails')
|
Http::get('/v1/health/queue/mails')
|
||||||
->desc('Get mails queue')
|
->desc('Get mails queue')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -579,7 +617,7 @@ App::get('/v1/health/queue/mails')
|
||||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||||
}, ['response']);
|
}, ['response']);
|
||||||
|
|
||||||
App::get('/v1/health/queue/messaging')
|
Http::get('/v1/health/queue/messaging')
|
||||||
->desc('Get messaging queue')
|
->desc('Get messaging queue')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -606,7 +644,7 @@ App::get('/v1/health/queue/messaging')
|
||||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||||
}, ['response']);
|
}, ['response']);
|
||||||
|
|
||||||
App::get('/v1/health/queue/migrations')
|
Http::get('/v1/health/queue/migrations')
|
||||||
->desc('Get migrations queue')
|
->desc('Get migrations queue')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -633,7 +671,7 @@ App::get('/v1/health/queue/migrations')
|
||||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||||
}, ['response']);
|
}, ['response']);
|
||||||
|
|
||||||
App::get('/v1/health/queue/functions')
|
Http::get('/v1/health/queue/functions')
|
||||||
->desc('Get functions queue')
|
->desc('Get functions queue')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -660,7 +698,7 @@ App::get('/v1/health/queue/functions')
|
||||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||||
}, ['response']);
|
}, ['response']);
|
||||||
|
|
||||||
App::get('/v1/health/queue/usage')
|
Http::get('/v1/health/queue/usage')
|
||||||
->desc('Get usage queue')
|
->desc('Get usage queue')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -687,7 +725,7 @@ App::get('/v1/health/queue/usage')
|
||||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/health/queue/usage-dump')
|
Http::get('/v1/health/queue/usage-dump')
|
||||||
->desc('Get usage dump queue')
|
->desc('Get usage dump queue')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -714,7 +752,7 @@ App::get('/v1/health/queue/usage-dump')
|
||||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/health/storage/local')
|
Http::get('/v1/health/storage/local')
|
||||||
->desc('Get local storage')
|
->desc('Get local storage')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -757,7 +795,7 @@ App::get('/v1/health/storage/local')
|
||||||
$response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS);
|
$response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/health/storage')
|
Http::get('/v1/health/storage')
|
||||||
->desc('Get storage')
|
->desc('Get storage')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -798,7 +836,7 @@ App::get('/v1/health/storage')
|
||||||
$response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS);
|
$response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/health/anti-virus')
|
Http::get('/v1/health/anti-virus')
|
||||||
->desc('Get antivirus')
|
->desc('Get antivirus')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -837,7 +875,7 @@ App::get('/v1/health/anti-virus')
|
||||||
$response->dynamic(new Document($output), Response::MODEL_HEALTH_ANTIVIRUS);
|
$response->dynamic(new Document($output), Response::MODEL_HEALTH_ANTIVIRUS);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/health/queue/failed/:name')
|
Http::get('/v1/health/queue/failed/:name')
|
||||||
->desc('Get number of failed queue jobs')
|
->desc('Get number of failed queue jobs')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
|
@ -878,7 +916,7 @@ App::get('/v1/health/queue/failed/:name')
|
||||||
$response->dynamic(new Document([ 'size' => $failed ]), Response::MODEL_HEALTH_QUEUE);
|
$response->dynamic(new Document([ 'size' => $failed ]), Response::MODEL_HEALTH_QUEUE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/health/stats') // Currently only used internally
|
Http::get('/v1/health/stats') // Currently only used internally
|
||||||
->desc('Get system stats')
|
->desc('Get system stats')
|
||||||
->groups(['api', 'health'])
|
->groups(['api', 'health'])
|
||||||
->label('scope', 'root')
|
->label('scope', 'root')
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@
|
||||||
use Appwrite\Utopia\Request;
|
use Appwrite\Utopia\Request;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use MaxMind\Db\Reader;
|
use MaxMind\Db\Reader;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
|
use Utopia\Http\Http;
|
||||||
use Utopia\Locale\Locale;
|
use Utopia\Locale\Locale;
|
||||||
|
|
||||||
App::get('/v1/locale')
|
Http::get('/v1/locale')
|
||||||
->desc('Get user locale')
|
->desc('Get user locale')
|
||||||
->groups(['api', 'locale'])
|
->groups(['api', 'locale'])
|
||||||
->label('scope', 'locale.read')
|
->label('scope', 'locale.read')
|
||||||
|
|
@ -68,8 +68,8 @@ App::get('/v1/locale')
|
||||||
$response->dynamic(new Document($output), Response::MODEL_LOCALE);
|
$response->dynamic(new Document($output), Response::MODEL_LOCALE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/locale/codes')
|
Http::get('/v1/locale/codes')
|
||||||
->desc('List locale codes')
|
->desc('List Locale Codes')
|
||||||
->groups(['api', 'locale'])
|
->groups(['api', 'locale'])
|
||||||
->label('scope', 'locale.read')
|
->label('scope', 'locale.read')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||||
|
|
@ -90,7 +90,7 @@ App::get('/v1/locale/codes')
|
||||||
]), Response::MODEL_LOCALE_CODE_LIST);
|
]), Response::MODEL_LOCALE_CODE_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/locale/countries')
|
Http::get('/v1/locale/countries')
|
||||||
->desc('List countries')
|
->desc('List countries')
|
||||||
->groups(['api', 'locale'])
|
->groups(['api', 'locale'])
|
||||||
->label('scope', 'locale.read')
|
->label('scope', 'locale.read')
|
||||||
|
|
@ -123,7 +123,7 @@ App::get('/v1/locale/countries')
|
||||||
$response->dynamic(new Document(['countries' => $output, 'total' => \count($output)]), Response::MODEL_COUNTRY_LIST);
|
$response->dynamic(new Document(['countries' => $output, 'total' => \count($output)]), Response::MODEL_COUNTRY_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/locale/countries/eu')
|
Http::get('/v1/locale/countries/eu')
|
||||||
->desc('List EU countries')
|
->desc('List EU countries')
|
||||||
->groups(['api', 'locale'])
|
->groups(['api', 'locale'])
|
||||||
->label('scope', 'locale.read')
|
->label('scope', 'locale.read')
|
||||||
|
|
@ -158,7 +158,7 @@ App::get('/v1/locale/countries/eu')
|
||||||
$response->dynamic(new Document(['countries' => $output, 'total' => \count($output)]), Response::MODEL_COUNTRY_LIST);
|
$response->dynamic(new Document(['countries' => $output, 'total' => \count($output)]), Response::MODEL_COUNTRY_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/locale/countries/phones')
|
Http::get('/v1/locale/countries/phones')
|
||||||
->desc('List countries phone codes')
|
->desc('List countries phone codes')
|
||||||
->groups(['api', 'locale'])
|
->groups(['api', 'locale'])
|
||||||
->label('scope', 'locale.read')
|
->label('scope', 'locale.read')
|
||||||
|
|
@ -192,7 +192,7 @@ App::get('/v1/locale/countries/phones')
|
||||||
$response->dynamic(new Document(['phones' => $output, 'total' => \count($output)]), Response::MODEL_PHONE_LIST);
|
$response->dynamic(new Document(['phones' => $output, 'total' => \count($output)]), Response::MODEL_PHONE_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/locale/continents')
|
Http::get('/v1/locale/continents')
|
||||||
->desc('List continents')
|
->desc('List continents')
|
||||||
->groups(['api', 'locale'])
|
->groups(['api', 'locale'])
|
||||||
->label('scope', 'locale.read')
|
->label('scope', 'locale.read')
|
||||||
|
|
@ -224,7 +224,7 @@ App::get('/v1/locale/continents')
|
||||||
$response->dynamic(new Document(['continents' => $output, 'total' => \count($output)]), Response::MODEL_CONTINENT_LIST);
|
$response->dynamic(new Document(['continents' => $output, 'total' => \count($output)]), Response::MODEL_CONTINENT_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/locale/currencies')
|
Http::get('/v1/locale/currencies')
|
||||||
->desc('List currencies')
|
->desc('List currencies')
|
||||||
->groups(['api', 'locale'])
|
->groups(['api', 'locale'])
|
||||||
->label('scope', 'locale.read')
|
->label('scope', 'locale.read')
|
||||||
|
|
@ -247,7 +247,7 @@ App::get('/v1/locale/currencies')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
App::get('/v1/locale/languages')
|
Http::get('/v1/locale/languages')
|
||||||
->desc('List languages')
|
->desc('List languages')
|
||||||
->groups(['api', 'locale'])
|
->groups(['api', 'locale'])
|
||||||
->label('scope', 'locale.read')
|
->label('scope', 'locale.read')
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ use Appwrite\Utopia\Database\Validator\Queries\Targets;
|
||||||
use Appwrite\Utopia\Database\Validator\Queries\Topics;
|
use Appwrite\Utopia\Database\Validator\Queries\Topics;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use MaxMind\Db\Reader;
|
use MaxMind\Db\Reader;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Audit\Audit;
|
use Utopia\Audit\Audit;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\DateTime;
|
use Utopia\Database\DateTime;
|
||||||
|
|
@ -30,25 +29,27 @@ use Utopia\Database\Exception\Query as QueryException;
|
||||||
use Utopia\Database\Helpers\ID;
|
use Utopia\Database\Helpers\ID;
|
||||||
use Utopia\Database\Query;
|
use Utopia\Database\Query;
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\Database\Validator\Authorization;
|
||||||
|
use Utopia\Database\Validator\Authorization\Input;
|
||||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||||
use Utopia\Database\Validator\Queries;
|
use Utopia\Database\Validator\Queries;
|
||||||
use Utopia\Database\Validator\Query\Limit;
|
use Utopia\Database\Validator\Query\Limit;
|
||||||
use Utopia\Database\Validator\Query\Offset;
|
use Utopia\Database\Validator\Query\Offset;
|
||||||
use Utopia\Database\Validator\Roles;
|
use Utopia\Database\Validator\Roles;
|
||||||
use Utopia\Database\Validator\UID;
|
use Utopia\Database\Validator\UID;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Validator\ArrayList;
|
||||||
|
use Utopia\Http\Validator\Boolean;
|
||||||
|
use Utopia\Http\Validator\Integer;
|
||||||
|
use Utopia\Http\Validator\JSON;
|
||||||
|
use Utopia\Http\Validator\Range;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
|
use Utopia\Http\Validator\WhiteList;
|
||||||
use Utopia\Locale\Locale;
|
use Utopia\Locale\Locale;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\ArrayList;
|
|
||||||
use Utopia\Validator\Boolean;
|
|
||||||
use Utopia\Validator\Integer;
|
|
||||||
use Utopia\Validator\JSON;
|
|
||||||
use Utopia\Validator\Range;
|
|
||||||
use Utopia\Validator\Text;
|
|
||||||
use Utopia\Validator\WhiteList;
|
|
||||||
|
|
||||||
use function Swoole\Coroutine\batch;
|
use function Swoole\Coroutine\batch;
|
||||||
|
|
||||||
App::post('/v1/messaging/providers/mailgun')
|
Http::post('/v1/messaging/providers/mailgun')
|
||||||
->desc('Create Mailgun provider')
|
->desc('Create Mailgun provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.create')
|
->label('audits.event', 'provider.create')
|
||||||
|
|
@ -135,7 +136,7 @@ App::post('/v1/messaging/providers/mailgun')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/messaging/providers/sendgrid')
|
Http::post('/v1/messaging/providers/sendgrid')
|
||||||
->desc('Create Sendgrid provider')
|
->desc('Create Sendgrid provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.create')
|
->label('audits.event', 'provider.create')
|
||||||
|
|
@ -210,7 +211,7 @@ App::post('/v1/messaging/providers/sendgrid')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/messaging/providers/smtp')
|
Http::post('/v1/messaging/providers/smtp')
|
||||||
->desc('Create SMTP provider')
|
->desc('Create SMTP provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.create')
|
->label('audits.event', 'provider.create')
|
||||||
|
|
@ -298,7 +299,7 @@ App::post('/v1/messaging/providers/smtp')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/messaging/providers/msg91')
|
Http::post('/v1/messaging/providers/msg91')
|
||||||
->desc('Create Msg91 provider')
|
->desc('Create Msg91 provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.create')
|
->label('audits.event', 'provider.create')
|
||||||
|
|
@ -374,7 +375,7 @@ App::post('/v1/messaging/providers/msg91')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/messaging/providers/telesign')
|
Http::post('/v1/messaging/providers/telesign')
|
||||||
->desc('Create Telesign provider')
|
->desc('Create Telesign provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.create')
|
->label('audits.event', 'provider.create')
|
||||||
|
|
@ -451,7 +452,7 @@ App::post('/v1/messaging/providers/telesign')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/messaging/providers/textmagic')
|
Http::post('/v1/messaging/providers/textmagic')
|
||||||
->desc('Create Textmagic provider')
|
->desc('Create Textmagic provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.create')
|
->label('audits.event', 'provider.create')
|
||||||
|
|
@ -528,7 +529,7 @@ App::post('/v1/messaging/providers/textmagic')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/messaging/providers/twilio')
|
Http::post('/v1/messaging/providers/twilio')
|
||||||
->desc('Create Twilio provider')
|
->desc('Create Twilio provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.create')
|
->label('audits.event', 'provider.create')
|
||||||
|
|
@ -605,7 +606,7 @@ App::post('/v1/messaging/providers/twilio')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/messaging/providers/vonage')
|
Http::post('/v1/messaging/providers/vonage')
|
||||||
->desc('Create Vonage provider')
|
->desc('Create Vonage provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.create')
|
->label('audits.event', 'provider.create')
|
||||||
|
|
@ -682,7 +683,7 @@ App::post('/v1/messaging/providers/vonage')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/messaging/providers/fcm')
|
Http::post('/v1/messaging/providers/fcm')
|
||||||
->desc('Create FCM provider')
|
->desc('Create FCM provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.create')
|
->label('audits.event', 'provider.create')
|
||||||
|
|
@ -745,7 +746,7 @@ App::post('/v1/messaging/providers/fcm')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/messaging/providers/apns')
|
Http::post('/v1/messaging/providers/apns')
|
||||||
->desc('Create APNS provider')
|
->desc('Create APNS provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.create')
|
->label('audits.event', 'provider.create')
|
||||||
|
|
@ -831,7 +832,7 @@ App::post('/v1/messaging/providers/apns')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/messaging/providers')
|
Http::get('/v1/messaging/providers')
|
||||||
->desc('List providers')
|
->desc('List providers')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('scope', 'providers.read')
|
->label('scope', 'providers.read')
|
||||||
|
|
@ -846,7 +847,8 @@ App::get('/v1/messaging/providers')
|
||||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->action(function (array $queries, string $search, Database $dbForProject, Response $response) {
|
->inject('authorization')
|
||||||
|
->action(function (array $queries, string $search, Database $dbForProject, Response $response, Authorization $authorization) {
|
||||||
try {
|
try {
|
||||||
$queries = Query::parseQueries($queries);
|
$queries = Query::parseQueries($queries);
|
||||||
} catch (QueryException $e) {
|
} catch (QueryException $e) {
|
||||||
|
|
@ -867,7 +869,7 @@ App::get('/v1/messaging/providers')
|
||||||
|
|
||||||
if ($cursor) {
|
if ($cursor) {
|
||||||
$providerId = $cursor->getValue();
|
$providerId = $cursor->getValue();
|
||||||
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('providers', $providerId));
|
$cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('providers', $providerId));
|
||||||
|
|
||||||
if ($cursorDocument->isEmpty()) {
|
if ($cursorDocument->isEmpty()) {
|
||||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Provider '{$providerId}' for the 'cursor' value not found.");
|
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Provider '{$providerId}' for the 'cursor' value not found.");
|
||||||
|
|
@ -882,7 +884,7 @@ App::get('/v1/messaging/providers')
|
||||||
]), Response::MODEL_PROVIDER_LIST);
|
]), Response::MODEL_PROVIDER_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/messaging/providers/:providerId/logs')
|
Http::get('/v1/messaging/providers/:providerId/logs')
|
||||||
->desc('List provider logs')
|
->desc('List provider logs')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('scope', 'providers.read')
|
->label('scope', 'providers.read')
|
||||||
|
|
@ -970,7 +972,7 @@ App::get('/v1/messaging/providers/:providerId/logs')
|
||||||
]), Response::MODEL_LOG_LIST);
|
]), Response::MODEL_LOG_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/messaging/providers/:providerId')
|
Http::get('/v1/messaging/providers/:providerId')
|
||||||
->desc('Get provider')
|
->desc('Get provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('scope', 'providers.read')
|
->label('scope', 'providers.read')
|
||||||
|
|
@ -994,7 +996,7 @@ App::get('/v1/messaging/providers/:providerId')
|
||||||
$response->dynamic($provider, Response::MODEL_PROVIDER);
|
$response->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/messaging/providers/mailgun/:providerId')
|
Http::patch('/v1/messaging/providers/mailgun/:providerId')
|
||||||
->desc('Update Mailgun provider')
|
->desc('Update Mailgun provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.update')
|
->label('audits.event', 'provider.update')
|
||||||
|
|
@ -1100,7 +1102,7 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/messaging/providers/sendgrid/:providerId')
|
Http::patch('/v1/messaging/providers/sendgrid/:providerId')
|
||||||
->desc('Update Sendgrid provider')
|
->desc('Update Sendgrid provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.update')
|
->label('audits.event', 'provider.update')
|
||||||
|
|
@ -1191,7 +1193,7 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/messaging/providers/smtp/:providerId')
|
Http::patch('/v1/messaging/providers/smtp/:providerId')
|
||||||
->desc('Update SMTP provider')
|
->desc('Update SMTP provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.update')
|
->label('audits.event', 'provider.update')
|
||||||
|
|
@ -1313,7 +1315,7 @@ App::patch('/v1/messaging/providers/smtp/:providerId')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/messaging/providers/msg91/:providerId')
|
Http::patch('/v1/messaging/providers/msg91/:providerId')
|
||||||
->desc('Update Msg91 provider')
|
->desc('Update Msg91 provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.update')
|
->label('audits.event', 'provider.update')
|
||||||
|
|
@ -1393,7 +1395,7 @@ App::patch('/v1/messaging/providers/msg91/:providerId')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/messaging/providers/telesign/:providerId')
|
Http::patch('/v1/messaging/providers/telesign/:providerId')
|
||||||
->desc('Update Telesign provider')
|
->desc('Update Telesign provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.update')
|
->label('audits.event', 'provider.update')
|
||||||
|
|
@ -1475,7 +1477,7 @@ App::patch('/v1/messaging/providers/telesign/:providerId')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/messaging/providers/textmagic/:providerId')
|
Http::patch('/v1/messaging/providers/textmagic/:providerId')
|
||||||
->desc('Update Textmagic provider')
|
->desc('Update Textmagic provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.update')
|
->label('audits.event', 'provider.update')
|
||||||
|
|
@ -1557,7 +1559,7 @@ App::patch('/v1/messaging/providers/textmagic/:providerId')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/messaging/providers/twilio/:providerId')
|
Http::patch('/v1/messaging/providers/twilio/:providerId')
|
||||||
->desc('Update Twilio provider')
|
->desc('Update Twilio provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.update')
|
->label('audits.event', 'provider.update')
|
||||||
|
|
@ -1639,7 +1641,7 @@ App::patch('/v1/messaging/providers/twilio/:providerId')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/messaging/providers/vonage/:providerId')
|
Http::patch('/v1/messaging/providers/vonage/:providerId')
|
||||||
->desc('Update Vonage provider')
|
->desc('Update Vonage provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.update')
|
->label('audits.event', 'provider.update')
|
||||||
|
|
@ -1721,7 +1723,7 @@ App::patch('/v1/messaging/providers/vonage/:providerId')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/messaging/providers/fcm/:providerId')
|
Http::patch('/v1/messaging/providers/fcm/:providerId')
|
||||||
->desc('Update FCM provider')
|
->desc('Update FCM provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.update')
|
->label('audits.event', 'provider.update')
|
||||||
|
|
@ -1790,7 +1792,7 @@ App::patch('/v1/messaging/providers/fcm/:providerId')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
App::patch('/v1/messaging/providers/apns/:providerId')
|
Http::patch('/v1/messaging/providers/apns/:providerId')
|
||||||
->desc('Update APNS provider')
|
->desc('Update APNS provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.update')
|
->label('audits.event', 'provider.update')
|
||||||
|
|
@ -1885,7 +1887,7 @@ App::patch('/v1/messaging/providers/apns/:providerId')
|
||||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/messaging/providers/:providerId')
|
Http::delete('/v1/messaging/providers/:providerId')
|
||||||
->desc('Delete provider')
|
->desc('Delete provider')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'provider.delete')
|
->label('audits.event', 'provider.delete')
|
||||||
|
|
@ -1920,7 +1922,7 @@ App::delete('/v1/messaging/providers/:providerId')
|
||||||
->noContent();
|
->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/messaging/topics')
|
Http::post('/v1/messaging/topics')
|
||||||
->desc('Create topic')
|
->desc('Create topic')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'topic.create')
|
->label('audits.event', 'topic.create')
|
||||||
|
|
@ -1963,7 +1965,7 @@ App::post('/v1/messaging/topics')
|
||||||
->dynamic($topic, Response::MODEL_TOPIC);
|
->dynamic($topic, Response::MODEL_TOPIC);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/messaging/topics')
|
Http::get('/v1/messaging/topics')
|
||||||
->desc('List topics')
|
->desc('List topics')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('scope', 'topics.read')
|
->label('scope', 'topics.read')
|
||||||
|
|
@ -1978,7 +1980,8 @@ App::get('/v1/messaging/topics')
|
||||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->action(function (array $queries, string $search, Database $dbForProject, Response $response) {
|
->inject('authorization')
|
||||||
|
->action(function (array $queries, string $search, Database $dbForProject, Response $response, Authorization $authorization) {
|
||||||
try {
|
try {
|
||||||
$queries = Query::parseQueries($queries);
|
$queries = Query::parseQueries($queries);
|
||||||
} catch (QueryException $e) {
|
} catch (QueryException $e) {
|
||||||
|
|
@ -1999,7 +2002,7 @@ App::get('/v1/messaging/topics')
|
||||||
|
|
||||||
if ($cursor) {
|
if ($cursor) {
|
||||||
$topicId = $cursor->getValue();
|
$topicId = $cursor->getValue();
|
||||||
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
|
$cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId));
|
||||||
|
|
||||||
if ($cursorDocument->isEmpty()) {
|
if ($cursorDocument->isEmpty()) {
|
||||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Topic '{$topicId}' for the 'cursor' value not found.");
|
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Topic '{$topicId}' for the 'cursor' value not found.");
|
||||||
|
|
@ -2014,7 +2017,7 @@ App::get('/v1/messaging/topics')
|
||||||
]), Response::MODEL_TOPIC_LIST);
|
]), Response::MODEL_TOPIC_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/messaging/topics/:topicId/logs')
|
Http::get('/v1/messaging/topics/:topicId/logs')
|
||||||
->desc('List topic logs')
|
->desc('List topic logs')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('scope', 'topics.read')
|
->label('scope', 'topics.read')
|
||||||
|
|
@ -2031,7 +2034,8 @@ App::get('/v1/messaging/topics/:topicId/logs')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->action(function (string $topicId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
|
->inject('authorization')
|
||||||
|
->action(function (string $topicId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) {
|
||||||
$topic = $dbForProject->getDocument('topics', $topicId);
|
$topic = $dbForProject->getDocument('topics', $topicId);
|
||||||
|
|
||||||
if ($topic->isEmpty()) {
|
if ($topic->isEmpty()) {
|
||||||
|
|
@ -2103,7 +2107,7 @@ App::get('/v1/messaging/topics/:topicId/logs')
|
||||||
]), Response::MODEL_LOG_LIST);
|
]), Response::MODEL_LOG_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/messaging/topics/:topicId')
|
Http::get('/v1/messaging/topics/:topicId')
|
||||||
->desc('Get topic')
|
->desc('Get topic')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('scope', 'topics.read')
|
->label('scope', 'topics.read')
|
||||||
|
|
@ -2128,7 +2132,7 @@ App::get('/v1/messaging/topics/:topicId')
|
||||||
->dynamic($topic, Response::MODEL_TOPIC);
|
->dynamic($topic, Response::MODEL_TOPIC);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/messaging/topics/:topicId')
|
Http::patch('/v1/messaging/topics/:topicId')
|
||||||
->desc('Update topic')
|
->desc('Update topic')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'topic.update')
|
->label('audits.event', 'topic.update')
|
||||||
|
|
@ -2172,7 +2176,7 @@ App::patch('/v1/messaging/topics/:topicId')
|
||||||
->dynamic($topic, Response::MODEL_TOPIC);
|
->dynamic($topic, Response::MODEL_TOPIC);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/messaging/topics/:topicId')
|
Http::delete('/v1/messaging/topics/:topicId')
|
||||||
->desc('Delete topic')
|
->desc('Delete topic')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'topic.delete')
|
->label('audits.event', 'topic.delete')
|
||||||
|
|
@ -2212,7 +2216,7 @@ App::delete('/v1/messaging/topics/:topicId')
|
||||||
->noContent();
|
->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/messaging/topics/:topicId/subscribers')
|
Http::post('/v1/messaging/topics/:topicId/subscribers')
|
||||||
->desc('Create subscriber')
|
->desc('Create subscriber')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'subscriber.create')
|
->label('audits.event', 'subscriber.create')
|
||||||
|
|
@ -2232,28 +2236,27 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->action(function (string $subscriberId, string $topicId, string $targetId, Event $queueForEvents, Database $dbForProject, Response $response) {
|
->inject('authorization')
|
||||||
|
->action(function (string $subscriberId, string $topicId, string $targetId, Event $queueForEvents, Database $dbForProject, Response $response, Authorization $authorization) {
|
||||||
$subscriberId = $subscriberId == 'unique()' ? ID::unique() : $subscriberId;
|
$subscriberId = $subscriberId == 'unique()' ? ID::unique() : $subscriberId;
|
||||||
|
|
||||||
$topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
|
$topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId));
|
||||||
|
|
||||||
if ($topic->isEmpty()) {
|
if ($topic->isEmpty()) {
|
||||||
throw new Exception(Exception::TOPIC_NOT_FOUND);
|
throw new Exception(Exception::TOPIC_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$validator = new Authorization('subscribe');
|
if (!$authorization->isValid(new Input('subscribe', $topic->getAttribute('subscribe')))) {
|
||||||
|
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
|
||||||
if (!$validator->isValid($topic->getAttribute('subscribe'))) {
|
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
|
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId));
|
||||||
|
|
||||||
if ($target->isEmpty()) {
|
if ($target->isEmpty()) {
|
||||||
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
|
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
|
$user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
|
||||||
|
|
||||||
$subscriber = new Document([
|
$subscriber = new Document([
|
||||||
'$id' => $subscriberId,
|
'$id' => $subscriberId,
|
||||||
|
|
@ -2286,7 +2289,7 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
|
||||||
default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE),
|
default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE),
|
||||||
};
|
};
|
||||||
|
|
||||||
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute(
|
$authorization->skip(fn () => $dbForProject->increaseDocumentAttribute(
|
||||||
'topics',
|
'topics',
|
||||||
$topicId,
|
$topicId,
|
||||||
$totalAttribute,
|
$totalAttribute,
|
||||||
|
|
@ -2308,7 +2311,7 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
|
||||||
->dynamic($subscriber, Response::MODEL_SUBSCRIBER);
|
->dynamic($subscriber, Response::MODEL_SUBSCRIBER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/messaging/topics/:topicId/subscribers')
|
Http::get('/v1/messaging/topics/:topicId/subscribers')
|
||||||
->desc('List subscribers')
|
->desc('List subscribers')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('scope', 'subscribers.read')
|
->label('scope', 'subscribers.read')
|
||||||
|
|
@ -2324,7 +2327,8 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
|
||||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->action(function (string $topicId, array $queries, string $search, Database $dbForProject, Response $response) {
|
->inject('authorization')
|
||||||
|
->action(function (string $topicId, array $queries, string $search, Database $dbForProject, Response $response, Authorization $authorization) {
|
||||||
try {
|
try {
|
||||||
$queries = Query::parseQueries($queries);
|
$queries = Query::parseQueries($queries);
|
||||||
} catch (QueryException $e) {
|
} catch (QueryException $e) {
|
||||||
|
|
@ -2335,7 +2339,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
|
||||||
$queries[] = Query::search('search', $search);
|
$queries[] = Query::search('search', $search);
|
||||||
}
|
}
|
||||||
|
|
||||||
$topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
|
$topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId));
|
||||||
|
|
||||||
if ($topic->isEmpty()) {
|
if ($topic->isEmpty()) {
|
||||||
throw new Exception(Exception::TOPIC_NOT_FOUND);
|
throw new Exception(Exception::TOPIC_NOT_FOUND);
|
||||||
|
|
@ -2353,7 +2357,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
|
||||||
|
|
||||||
if ($cursor) {
|
if ($cursor) {
|
||||||
$subscriberId = $cursor->getValue();
|
$subscriberId = $cursor->getValue();
|
||||||
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId));
|
$cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId));
|
||||||
|
|
||||||
if ($cursorDocument->isEmpty()) {
|
if ($cursorDocument->isEmpty()) {
|
||||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Subscriber '{$subscriberId}' for the 'cursor' value not found.");
|
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Subscriber '{$subscriberId}' for the 'cursor' value not found.");
|
||||||
|
|
@ -2364,10 +2368,10 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
|
||||||
|
|
||||||
$subscribers = $dbForProject->find('subscribers', $queries);
|
$subscribers = $dbForProject->find('subscribers', $queries);
|
||||||
|
|
||||||
$subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject) {
|
$subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject, $authorization) {
|
||||||
return function () use ($subscriber, $dbForProject) {
|
return function () use ($subscriber, $dbForProject, $authorization) {
|
||||||
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
|
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
|
||||||
$user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
|
$user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
|
||||||
|
|
||||||
return $subscriber
|
return $subscriber
|
||||||
->setAttribute('target', $target)
|
->setAttribute('target', $target)
|
||||||
|
|
@ -2382,7 +2386,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
|
||||||
]), Response::MODEL_SUBSCRIBER_LIST);
|
]), Response::MODEL_SUBSCRIBER_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/messaging/subscribers/:subscriberId/logs')
|
Http::get('/v1/messaging/subscribers/:subscriberId/logs')
|
||||||
->desc('List subscriber logs')
|
->desc('List subscriber logs')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('scope', 'subscribers.read')
|
->label('scope', 'subscribers.read')
|
||||||
|
|
@ -2399,7 +2403,8 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->action(function (string $subscriberId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
|
->inject('authorization')
|
||||||
|
->action(function (string $subscriberId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) {
|
||||||
$subscriber = $dbForProject->getDocument('subscribers', $subscriberId);
|
$subscriber = $dbForProject->getDocument('subscribers', $subscriberId);
|
||||||
|
|
||||||
if ($subscriber->isEmpty()) {
|
if ($subscriber->isEmpty()) {
|
||||||
|
|
@ -2471,7 +2476,7 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
|
||||||
]), Response::MODEL_LOG_LIST);
|
]), Response::MODEL_LOG_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
Http::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||||
->desc('Get subscriber')
|
->desc('Get subscriber')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('scope', 'subscribers.read')
|
->label('scope', 'subscribers.read')
|
||||||
|
|
@ -2486,8 +2491,9 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||||
->param('subscriberId', '', new UID(), 'Subscriber ID.')
|
->param('subscriberId', '', new UID(), 'Subscriber ID.')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->action(function (string $topicId, string $subscriberId, Database $dbForProject, Response $response) {
|
->inject('authorization')
|
||||||
$topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
|
->action(function (string $topicId, string $subscriberId, Database $dbForProject, Response $response, Authorization $authorization) {
|
||||||
|
$topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId));
|
||||||
|
|
||||||
if ($topic->isEmpty()) {
|
if ($topic->isEmpty()) {
|
||||||
throw new Exception(Exception::TOPIC_NOT_FOUND);
|
throw new Exception(Exception::TOPIC_NOT_FOUND);
|
||||||
|
|
@ -2499,8 +2505,8 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||||
throw new Exception(Exception::SUBSCRIBER_NOT_FOUND);
|
throw new Exception(Exception::SUBSCRIBER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
|
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
|
||||||
$user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
|
$user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
|
||||||
|
|
||||||
$subscriber
|
$subscriber
|
||||||
->setAttribute('target', $target)
|
->setAttribute('target', $target)
|
||||||
|
|
@ -2510,7 +2516,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||||
->dynamic($subscriber, Response::MODEL_SUBSCRIBER);
|
->dynamic($subscriber, Response::MODEL_SUBSCRIBER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
Http::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||||
->desc('Delete subscriber')
|
->desc('Delete subscriber')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'subscriber.delete')
|
->label('audits.event', 'subscriber.delete')
|
||||||
|
|
@ -2529,8 +2535,9 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->action(function (string $topicId, string $subscriberId, Event $queueForEvents, Database $dbForProject, Response $response) {
|
->inject('authorization')
|
||||||
$topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
|
->action(function (string $topicId, string $subscriberId, Event $queueForEvents, Database $dbForProject, Response $response, Authorization $authorization) {
|
||||||
|
$topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId));
|
||||||
|
|
||||||
if ($topic->isEmpty()) {
|
if ($topic->isEmpty()) {
|
||||||
throw new Exception(Exception::TOPIC_NOT_FOUND);
|
throw new Exception(Exception::TOPIC_NOT_FOUND);
|
||||||
|
|
@ -2553,7 +2560,7 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||||
default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE),
|
default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE),
|
||||||
};
|
};
|
||||||
|
|
||||||
Authorization::skip(fn () => $dbForProject->decreaseDocumentAttribute(
|
$authorization->skip(fn () => $dbForProject->decreaseDocumentAttribute(
|
||||||
'topics',
|
'topics',
|
||||||
$topicId,
|
$topicId,
|
||||||
$totalAttribute,
|
$totalAttribute,
|
||||||
|
|
@ -2569,7 +2576,7 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||||
->noContent();
|
->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/messaging/messages/email')
|
Http::post('/v1/messaging/messages/email')
|
||||||
->desc('Create email')
|
->desc('Create email')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'message.create')
|
->label('audits.event', 'message.create')
|
||||||
|
|
@ -2721,7 +2728,7 @@ App::post('/v1/messaging/messages/email')
|
||||||
->dynamic($message, Response::MODEL_MESSAGE);
|
->dynamic($message, Response::MODEL_MESSAGE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/messaging/messages/sms')
|
Http::post('/v1/messaging/messages/sms')
|
||||||
->desc('Create SMS')
|
->desc('Create SMS')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'message.create')
|
->label('audits.event', 'message.create')
|
||||||
|
|
@ -2837,7 +2844,7 @@ App::post('/v1/messaging/messages/sms')
|
||||||
->dynamic($message, Response::MODEL_MESSAGE);
|
->dynamic($message, Response::MODEL_MESSAGE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/messaging/messages/push')
|
Http::post('/v1/messaging/messages/push')
|
||||||
->desc('Create push notification')
|
->desc('Create push notification')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'message.create')
|
->label('audits.event', 'message.create')
|
||||||
|
|
@ -3013,7 +3020,7 @@ App::post('/v1/messaging/messages/push')
|
||||||
->dynamic($message, Response::MODEL_MESSAGE);
|
->dynamic($message, Response::MODEL_MESSAGE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/messaging/messages')
|
Http::get('/v1/messaging/messages')
|
||||||
->desc('List messages')
|
->desc('List messages')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('scope', 'messages.read')
|
->label('scope', 'messages.read')
|
||||||
|
|
@ -3028,7 +3035,8 @@ App::get('/v1/messaging/messages')
|
||||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->action(function (array $queries, string $search, Database $dbForProject, Response $response) {
|
->inject('authorization')
|
||||||
|
->action(function (array $queries, string $search, Database $dbForProject, Response $response, Authorization $authorization) {
|
||||||
try {
|
try {
|
||||||
$queries = Query::parseQueries($queries);
|
$queries = Query::parseQueries($queries);
|
||||||
} catch (QueryException $e) {
|
} catch (QueryException $e) {
|
||||||
|
|
@ -3049,7 +3057,7 @@ App::get('/v1/messaging/messages')
|
||||||
|
|
||||||
if ($cursor) {
|
if ($cursor) {
|
||||||
$messageId = $cursor->getValue();
|
$messageId = $cursor->getValue();
|
||||||
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('messages', $messageId));
|
$cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('messages', $messageId));
|
||||||
|
|
||||||
if ($cursorDocument->isEmpty()) {
|
if ($cursorDocument->isEmpty()) {
|
||||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Message '{$messageId}' for the 'cursor' value not found.");
|
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Message '{$messageId}' for the 'cursor' value not found.");
|
||||||
|
|
@ -3064,7 +3072,7 @@ App::get('/v1/messaging/messages')
|
||||||
]), Response::MODEL_MESSAGE_LIST);
|
]), Response::MODEL_MESSAGE_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/messaging/messages/:messageId/logs')
|
Http::get('/v1/messaging/messages/:messageId/logs')
|
||||||
->desc('List message logs')
|
->desc('List message logs')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('scope', 'messages.read')
|
->label('scope', 'messages.read')
|
||||||
|
|
@ -3081,7 +3089,8 @@ App::get('/v1/messaging/messages/:messageId/logs')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->action(function (string $messageId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
|
->inject('authorization')
|
||||||
|
->action(function (string $messageId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) {
|
||||||
$message = $dbForProject->getDocument('messages', $messageId);
|
$message = $dbForProject->getDocument('messages', $messageId);
|
||||||
|
|
||||||
if ($message->isEmpty()) {
|
if ($message->isEmpty()) {
|
||||||
|
|
@ -3153,7 +3162,7 @@ App::get('/v1/messaging/messages/:messageId/logs')
|
||||||
]), Response::MODEL_LOG_LIST);
|
]), Response::MODEL_LOG_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/messaging/messages/:messageId/targets')
|
Http::get('/v1/messaging/messages/:messageId/targets')
|
||||||
->desc('List message targets')
|
->desc('List message targets')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('scope', 'messages.read')
|
->label('scope', 'messages.read')
|
||||||
|
|
@ -3218,7 +3227,7 @@ App::get('/v1/messaging/messages/:messageId/targets')
|
||||||
]), Response::MODEL_TARGET_LIST);
|
]), Response::MODEL_TARGET_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/messaging/messages/:messageId')
|
Http::get('/v1/messaging/messages/:messageId')
|
||||||
->desc('Get message')
|
->desc('Get message')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('scope', 'messages.read')
|
->label('scope', 'messages.read')
|
||||||
|
|
@ -3242,7 +3251,7 @@ App::get('/v1/messaging/messages/:messageId')
|
||||||
$response->dynamic($message, Response::MODEL_MESSAGE);
|
$response->dynamic($message, Response::MODEL_MESSAGE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/messaging/messages/email/:messageId')
|
Http::patch('/v1/messaging/messages/email/:messageId')
|
||||||
->desc('Update email')
|
->desc('Update email')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'message.update')
|
->label('audits.event', 'message.update')
|
||||||
|
|
@ -3442,7 +3451,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
|
||||||
->dynamic($message, Response::MODEL_MESSAGE);
|
->dynamic($message, Response::MODEL_MESSAGE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/messaging/messages/sms/:messageId')
|
Http::patch('/v1/messaging/messages/sms/:messageId')
|
||||||
->desc('Update SMS')
|
->desc('Update SMS')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'message.update')
|
->label('audits.event', 'message.update')
|
||||||
|
|
@ -3597,7 +3606,7 @@ App::patch('/v1/messaging/messages/sms/:messageId')
|
||||||
->dynamic($message, Response::MODEL_MESSAGE);
|
->dynamic($message, Response::MODEL_MESSAGE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/messaging/messages/push/:messageId')
|
Http::patch('/v1/messaging/messages/push/:messageId')
|
||||||
->desc('Update push notification')
|
->desc('Update push notification')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'message.update')
|
->label('audits.event', 'message.update')
|
||||||
|
|
@ -3835,7 +3844,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
||||||
->dynamic($message, Response::MODEL_MESSAGE);
|
->dynamic($message, Response::MODEL_MESSAGE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/messaging/messages/:messageId')
|
Http::delete('/v1/messaging/messages/:messageId')
|
||||||
->desc('Delete message')
|
->desc('Delete message')
|
||||||
->groups(['api', 'messaging'])
|
->groups(['api', 'messaging'])
|
||||||
->label('audits.event', 'message.delete')
|
->label('audits.event', 'message.delete')
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ use Appwrite\Role;
|
||||||
use Appwrite\Utopia\Database\Validator\Queries\Migrations;
|
use Appwrite\Utopia\Database\Validator\Queries\Migrations;
|
||||||
use Appwrite\Utopia\Request;
|
use Appwrite\Utopia\Request;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\DateTime;
|
use Utopia\Database\DateTime;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
|
|
@ -17,21 +16,22 @@ use Utopia\Database\Exception\Query as QueryException;
|
||||||
use Utopia\Database\Helpers\ID;
|
use Utopia\Database\Helpers\ID;
|
||||||
use Utopia\Database\Query;
|
use Utopia\Database\Query;
|
||||||
use Utopia\Database\Validator\UID;
|
use Utopia\Database\Validator\UID;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Validator\ArrayList;
|
||||||
|
use Utopia\Http\Validator\Host;
|
||||||
|
use Utopia\Http\Validator\Integer;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
|
use Utopia\Http\Validator\URL;
|
||||||
|
use Utopia\Http\Validator\WhiteList;
|
||||||
use Utopia\Migration\Sources\Appwrite;
|
use Utopia\Migration\Sources\Appwrite;
|
||||||
use Utopia\Migration\Sources\Firebase;
|
use Utopia\Migration\Sources\Firebase;
|
||||||
use Utopia\Migration\Sources\NHost;
|
use Utopia\Migration\Sources\NHost;
|
||||||
use Utopia\Migration\Sources\Supabase;
|
use Utopia\Migration\Sources\Supabase;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\ArrayList;
|
|
||||||
use Utopia\Validator\Host;
|
|
||||||
use Utopia\Validator\Integer;
|
|
||||||
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')
|
Http::post('/v1/migrations/appwrite')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->desc('Migrate Appwrite data')
|
->desc('Migrate Appwrite data')
|
||||||
->label('scope', 'migrations.write')
|
->label('scope', 'migrations.write')
|
||||||
|
|
@ -86,7 +86,7 @@ App::post('/v1/migrations/appwrite')
|
||||||
->dynamic($migration, Response::MODEL_MIGRATION);
|
->dynamic($migration, Response::MODEL_MIGRATION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/migrations/firebase/oauth')
|
Http::post('/v1/migrations/firebase/oauth')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->desc('Migrate Firebase data (OAuth)')
|
->desc('Migrate Firebase data (OAuth)')
|
||||||
->label('scope', 'migrations.write')
|
->label('scope', 'migrations.write')
|
||||||
|
|
@ -120,7 +120,7 @@ App::post('/v1/migrations/firebase/oauth')
|
||||||
Query::equal('provider', ['firebase']),
|
Query::equal('provider', ['firebase']),
|
||||||
Query::equal('userInternalId', [$user->getInternalId()]),
|
Query::equal('userInternalId', [$user->getInternalId()]),
|
||||||
]);
|
]);
|
||||||
if ($identity === false || $identity->isEmpty()) {
|
if ($identity->isEmpty()) {
|
||||||
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
|
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,7 +189,7 @@ App::post('/v1/migrations/firebase/oauth')
|
||||||
->dynamic($migration, Response::MODEL_MIGRATION);
|
->dynamic($migration, Response::MODEL_MIGRATION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/migrations/firebase')
|
Http::post('/v1/migrations/firebase')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->desc('Migrate Firebase data (Service Account)')
|
->desc('Migrate Firebase data (Service Account)')
|
||||||
->label('scope', 'migrations.write')
|
->label('scope', 'migrations.write')
|
||||||
|
|
@ -250,7 +250,7 @@ App::post('/v1/migrations/firebase')
|
||||||
->dynamic($migration, Response::MODEL_MIGRATION);
|
->dynamic($migration, Response::MODEL_MIGRATION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/migrations/supabase')
|
Http::post('/v1/migrations/supabase')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->desc('Migrate Supabase data')
|
->desc('Migrate Supabase data')
|
||||||
->label('scope', 'migrations.write')
|
->label('scope', 'migrations.write')
|
||||||
|
|
@ -311,7 +311,7 @@ App::post('/v1/migrations/supabase')
|
||||||
->dynamic($migration, Response::MODEL_MIGRATION);
|
->dynamic($migration, Response::MODEL_MIGRATION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/migrations/nhost')
|
Http::post('/v1/migrations/nhost')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->desc('Migrate NHost data')
|
->desc('Migrate NHost data')
|
||||||
->label('scope', 'migrations.write')
|
->label('scope', 'migrations.write')
|
||||||
|
|
@ -374,7 +374,7 @@ App::post('/v1/migrations/nhost')
|
||||||
->dynamic($migration, Response::MODEL_MIGRATION);
|
->dynamic($migration, Response::MODEL_MIGRATION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/migrations')
|
Http::get('/v1/migrations')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->desc('List migrations')
|
->desc('List migrations')
|
||||||
->label('scope', 'migrations.read')
|
->label('scope', 'migrations.read')
|
||||||
|
|
@ -427,7 +427,7 @@ App::get('/v1/migrations')
|
||||||
]), Response::MODEL_MIGRATION_LIST);
|
]), Response::MODEL_MIGRATION_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/migrations/:migrationId')
|
Http::get('/v1/migrations/:migrationId')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->desc('Get migration')
|
->desc('Get migration')
|
||||||
->label('scope', 'migrations.read')
|
->label('scope', 'migrations.read')
|
||||||
|
|
@ -451,7 +451,7 @@ App::get('/v1/migrations/:migrationId')
|
||||||
$response->dynamic($migration, Response::MODEL_MIGRATION);
|
$response->dynamic($migration, Response::MODEL_MIGRATION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/migrations/appwrite/report')
|
Http::get('/v1/migrations/appwrite/report')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->desc('Generate a report on Appwrite data')
|
->desc('Generate a report on Appwrite data')
|
||||||
->label('scope', 'migrations.write')
|
->label('scope', 'migrations.write')
|
||||||
|
|
@ -493,7 +493,7 @@ App::get('/v1/migrations/appwrite/report')
|
||||||
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
|
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/migrations/firebase/report')
|
Http::get('/v1/migrations/firebase/report')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->desc('Generate a report on Firebase data')
|
->desc('Generate a report on Firebase data')
|
||||||
->label('scope', 'migrations.write')
|
->label('scope', 'migrations.write')
|
||||||
|
|
@ -540,7 +540,7 @@ App::get('/v1/migrations/firebase/report')
|
||||||
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
|
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/migrations/firebase/report/oauth')
|
Http::get('/v1/migrations/firebase/report/oauth')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->desc('Generate a report on Firebase data using OAuth')
|
->desc('Generate a report on Firebase data using OAuth')
|
||||||
->label('scope', 'migrations.write')
|
->label('scope', 'migrations.write')
|
||||||
|
|
@ -569,7 +569,7 @@ App::get('/v1/migrations/firebase/report/oauth')
|
||||||
Query::equal('userInternalId', [$user->getInternalId()]),
|
Query::equal('userInternalId', [$user->getInternalId()]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($identity === false || $identity->isEmpty()) {
|
if ($identity->isEmpty()) {
|
||||||
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
|
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -631,7 +631,7 @@ App::get('/v1/migrations/firebase/report/oauth')
|
||||||
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
|
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/migrations/firebase/connect')
|
Http::get('/v1/migrations/firebase/connect')
|
||||||
->desc('Authorize with Firebase')
|
->desc('Authorize with Firebase')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->label('scope', 'migrations.write')
|
->label('scope', 'migrations.write')
|
||||||
|
|
@ -673,7 +673,7 @@ App::get('/v1/migrations/firebase/connect')
|
||||||
->redirect($url);
|
->redirect($url);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/migrations/firebase/redirect')
|
Http::get('/v1/migrations/firebase/redirect')
|
||||||
->desc('Capture and receive data on Firebase authorization')
|
->desc('Capture and receive data on Firebase authorization')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
|
|
@ -744,7 +744,7 @@ App::get('/v1/migrations/firebase/redirect')
|
||||||
Query::equal('providerEmail', [$email]),
|
Query::equal('providerEmail', [$email]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($identity !== false && !$identity->isEmpty()) {
|
if (!$identity->isEmpty()) {
|
||||||
if ($identity->getAttribute('userInternalId', '') !== $user->getInternalId()) {
|
if ($identity->getAttribute('userInternalId', '') !== $user->getInternalId()) {
|
||||||
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
|
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
@ -785,8 +785,8 @@ App::get('/v1/migrations/firebase/redirect')
|
||||||
->redirect($redirect);
|
->redirect($redirect);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/migrations/firebase/projects')
|
Http::get('/v1/migrations/firebase/projects')
|
||||||
->desc('List Firebase projects')
|
->desc('List Firebase Projects')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->label('scope', 'migrations.read')
|
->label('scope', 'migrations.read')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
|
@ -813,7 +813,7 @@ App::get('/v1/migrations/firebase/projects')
|
||||||
Query::equal('userInternalId', [$user->getInternalId()]),
|
Query::equal('userInternalId', [$user->getInternalId()]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($identity === false || $identity->isEmpty()) {
|
if ($identity->isEmpty()) {
|
||||||
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
|
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -874,8 +874,8 @@ App::get('/v1/migrations/firebase/projects')
|
||||||
]), Response::MODEL_MIGRATION_FIREBASE_PROJECT_LIST);
|
]), Response::MODEL_MIGRATION_FIREBASE_PROJECT_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/migrations/firebase/deauthorize')
|
Http::get('/v1/migrations/firebase/deauthorize')
|
||||||
->desc('Revoke Appwrite\'s authorization to access Firebase projects')
|
->desc('Revoke Appwrite\'s authorization to access Firebase Projects')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->label('scope', 'migrations.write')
|
->label('scope', 'migrations.write')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
|
@ -893,7 +893,7 @@ App::get('/v1/migrations/firebase/deauthorize')
|
||||||
Query::equal('userInternalId', [$user->getInternalId()]),
|
Query::equal('userInternalId', [$user->getInternalId()]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($identity === false || $identity->isEmpty()) {
|
if ($identity->isEmpty()) {
|
||||||
throw new Exception(Exception::GENERAL_ACCESS_FORBIDDEN, 'Not authenticated with Firebase'); //TODO: Replace with USER_IDENTITY_NOT_FOUND
|
throw new Exception(Exception::GENERAL_ACCESS_FORBIDDEN, 'Not authenticated with Firebase'); //TODO: Replace with USER_IDENTITY_NOT_FOUND
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -902,7 +902,7 @@ App::get('/v1/migrations/firebase/deauthorize')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/migrations/supabase/report')
|
Http::get('/v1/migrations/supabase/report')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->desc('Generate a report on Supabase Data')
|
->desc('Generate a report on Supabase Data')
|
||||||
->label('scope', 'migrations.write')
|
->label('scope', 'migrations.write')
|
||||||
|
|
@ -945,7 +945,7 @@ App::get('/v1/migrations/supabase/report')
|
||||||
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
|
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/migrations/nhost/report')
|
Http::get('/v1/migrations/nhost/report')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->desc('Generate a report on NHost Data')
|
->desc('Generate a report on NHost Data')
|
||||||
->label('scope', 'migrations.write')
|
->label('scope', 'migrations.write')
|
||||||
|
|
@ -988,7 +988,7 @@ App::get('/v1/migrations/nhost/report')
|
||||||
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
|
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/migrations/:migrationId')
|
Http::patch('/v1/migrations/:migrationId')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->desc('Retry migration')
|
->desc('Retry migration')
|
||||||
->label('scope', 'migrations.write')
|
->label('scope', 'migrations.write')
|
||||||
|
|
@ -1033,7 +1033,7 @@ App::patch('/v1/migrations/:migrationId')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/migrations/:migrationId')
|
Http::delete('/v1/migrations/:migrationId')
|
||||||
->groups(['api', 'migrations'])
|
->groups(['api', 'migrations'])
|
||||||
->desc('Delete migration')
|
->desc('Delete migration')
|
||||||
->label('scope', 'migrations.write')
|
->label('scope', 'migrations.write')
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
use Appwrite\Extend\Exception;
|
use Appwrite\Extend\Exception;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||||
|
|
@ -13,10 +12,11 @@ use Utopia\Database\Query;
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\Database\Validator\Authorization;
|
||||||
use Utopia\Database\Validator\Datetime as DateTimeValidator;
|
use Utopia\Database\Validator\Datetime as DateTimeValidator;
|
||||||
use Utopia\Database\Validator\UID;
|
use Utopia\Database\Validator\UID;
|
||||||
use Utopia\Validator\Text;
|
use Utopia\Http\Http;
|
||||||
use Utopia\Validator\WhiteList;
|
use Utopia\Http\Validator\Text;
|
||||||
|
use Utopia\Http\Validator\WhiteList;
|
||||||
|
|
||||||
App::get('/v1/project/usage')
|
Http::get('/v1/project/usage')
|
||||||
->desc('Get project usage stats')
|
->desc('Get project usage stats')
|
||||||
->groups(['api', 'usage'])
|
->groups(['api', 'usage'])
|
||||||
->label('scope', 'projects.read')
|
->label('scope', 'projects.read')
|
||||||
|
|
@ -31,7 +31,8 @@ App::get('/v1/project/usage')
|
||||||
->param('period', '1d', new WhiteList(['1h', '1d']), 'Period used', true)
|
->param('period', '1d', new WhiteList(['1h', '1d']), 'Period used', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->action(function (string $startDate, string $endDate, string $period, Response $response, Database $dbForProject) {
|
->inject('authorization')
|
||||||
|
->action(function (string $startDate, string $endDate, string $period, Response $response, Database $dbForProject, Authorization $authorization) {
|
||||||
$stats = $total = $usage = [];
|
$stats = $total = $usage = [];
|
||||||
$format = 'Y-m-d 00:00:00';
|
$format = 'Y-m-d 00:00:00';
|
||||||
$firstDay = (new DateTime($startDate))->format($format);
|
$firstDay = (new DateTime($startDate))->format($format);
|
||||||
|
|
@ -78,7 +79,7 @@ App::get('/v1/project/usage')
|
||||||
'1d' => 'Y-m-d\T00:00:00.000P',
|
'1d' => 'Y-m-d\T00:00:00.000P',
|
||||||
};
|
};
|
||||||
|
|
||||||
Authorization::skip(function () use ($dbForProject, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) {
|
$authorization->skip(function () use ($dbForProject, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) {
|
||||||
foreach ($metrics['total'] as $metric) {
|
foreach ($metrics['total'] as $metric) {
|
||||||
$result = $dbForProject->findOne('stats', [
|
$result = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
|
|
@ -296,8 +297,6 @@ App::get('/v1/project/usage')
|
||||||
'buildsStorageTotal' => $total[METRIC_BUILDS_STORAGE],
|
'buildsStorageTotal' => $total[METRIC_BUILDS_STORAGE],
|
||||||
'deploymentsStorageTotal' => $total[METRIC_DEPLOYMENTS_STORAGE],
|
'deploymentsStorageTotal' => $total[METRIC_DEPLOYMENTS_STORAGE],
|
||||||
'executionsBreakdown' => $executionsBreakdown,
|
'executionsBreakdown' => $executionsBreakdown,
|
||||||
'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown,
|
|
||||||
'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown,
|
|
||||||
'bucketsBreakdown' => $bucketsBreakdown,
|
'bucketsBreakdown' => $bucketsBreakdown,
|
||||||
'databasesStorageBreakdown' => $databasesStorageBreakdown,
|
'databasesStorageBreakdown' => $databasesStorageBreakdown,
|
||||||
'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown,
|
'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown,
|
||||||
|
|
@ -308,8 +307,8 @@ App::get('/v1/project/usage')
|
||||||
|
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
App::post('/v1/project/variables')
|
Http::post('/v1/project/variables')
|
||||||
->desc('Create variable')
|
->desc('Create Variable')
|
||||||
->groups(['api'])
|
->groups(['api'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
->label('audits.event', 'variable.create')
|
->label('audits.event', 'variable.create')
|
||||||
|
|
@ -363,8 +362,8 @@ App::post('/v1/project/variables')
|
||||||
->dynamic($variable, Response::MODEL_VARIABLE);
|
->dynamic($variable, Response::MODEL_VARIABLE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/project/variables')
|
Http::get('/v1/project/variables')
|
||||||
->desc('List variables')
|
->desc('List Variables')
|
||||||
->groups(['api'])
|
->groups(['api'])
|
||||||
->label('scope', 'projects.read')
|
->label('scope', 'projects.read')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
|
@ -388,8 +387,8 @@ App::get('/v1/project/variables')
|
||||||
]), Response::MODEL_VARIABLE_LIST);
|
]), Response::MODEL_VARIABLE_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/project/variables/:variableId')
|
Http::get('/v1/project/variables/:variableId')
|
||||||
->desc('Get variable')
|
->desc('Get Variable')
|
||||||
->groups(['api'])
|
->groups(['api'])
|
||||||
->label('scope', 'projects.read')
|
->label('scope', 'projects.read')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
|
@ -412,8 +411,8 @@ App::get('/v1/project/variables/:variableId')
|
||||||
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::put('/v1/project/variables/:variableId')
|
Http::put('/v1/project/variables/:variableId')
|
||||||
->desc('Update variable')
|
->desc('Update Variable')
|
||||||
->groups(['api'])
|
->groups(['api'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
|
@ -458,8 +457,8 @@ App::put('/v1/project/variables/:variableId')
|
||||||
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/project/variables/:variableId')
|
Http::delete('/v1/project/variables/:variableId')
|
||||||
->desc('Delete variable')
|
->desc('Delete Variable')
|
||||||
->groups(['api'])
|
->groups(['api'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,16 @@ use Appwrite\Network\Validator\Origin;
|
||||||
use Appwrite\Template\Template;
|
use Appwrite\Template\Template;
|
||||||
use Appwrite\Utopia\Database\Validator\ProjectId;
|
use Appwrite\Utopia\Database\Validator\ProjectId;
|
||||||
use Appwrite\Utopia\Database\Validator\Queries\Projects;
|
use Appwrite\Utopia\Database\Validator\Queries\Projects;
|
||||||
|
use Appwrite\Utopia\Queue\Connections;
|
||||||
use Appwrite\Utopia\Request;
|
use Appwrite\Utopia\Request;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use PHPMailer\PHPMailer\PHPMailer;
|
use PHPMailer\PHPMailer\PHPMailer;
|
||||||
use Utopia\Abuse\Adapters\Database\TimeLimit;
|
use Utopia\Abuse\Adapters\Database\TimeLimit;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Audit\Audit;
|
use Utopia\Audit\Audit;
|
||||||
use Utopia\Cache\Cache;
|
use Utopia\Cache\Cache;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
|
use Utopia\Database\Adapter\MariaDB;
|
||||||
|
use Utopia\Database\Adapter\MySQL;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\DateTime;
|
use Utopia\Database\DateTime;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
|
|
@ -30,24 +32,25 @@ use Utopia\Database\Helpers\ID;
|
||||||
use Utopia\Database\Helpers\Permission;
|
use Utopia\Database\Helpers\Permission;
|
||||||
use Utopia\Database\Helpers\Role;
|
use Utopia\Database\Helpers\Role;
|
||||||
use Utopia\Database\Query;
|
use Utopia\Database\Query;
|
||||||
|
use Utopia\Database\Validator\Authorization;
|
||||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||||
use Utopia\Database\Validator\UID;
|
use Utopia\Database\Validator\UID;
|
||||||
use Utopia\Domains\Validator\PublicDomain;
|
use Utopia\Domains\Validator\PublicDomain;
|
||||||
use Utopia\DSN\DSN;
|
use Utopia\DSN\DSN;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Validator\ArrayList;
|
||||||
|
use Utopia\Http\Validator\Boolean;
|
||||||
|
use Utopia\Http\Validator\Hostname;
|
||||||
|
use Utopia\Http\Validator\Integer;
|
||||||
|
use Utopia\Http\Validator\Multiple;
|
||||||
|
use Utopia\Http\Validator\Range;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
|
use Utopia\Http\Validator\URL;
|
||||||
|
use Utopia\Http\Validator\WhiteList;
|
||||||
use Utopia\Locale\Locale;
|
use Utopia\Locale\Locale;
|
||||||
use Utopia\Pools\Group;
|
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\ArrayList;
|
|
||||||
use Utopia\Validator\Boolean;
|
|
||||||
use Utopia\Validator\Hostname;
|
|
||||||
use Utopia\Validator\Integer;
|
|
||||||
use Utopia\Validator\Multiple;
|
|
||||||
use Utopia\Validator\Range;
|
|
||||||
use Utopia\Validator\Text;
|
|
||||||
use Utopia\Validator\URL;
|
|
||||||
use Utopia\Validator\WhiteList;
|
|
||||||
|
|
||||||
App::init()
|
Http::init()
|
||||||
->groups(['projects'])
|
->groups(['projects'])
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->action(function (Document $project) {
|
->action(function (Document $project) {
|
||||||
|
|
@ -56,7 +59,7 @@ App::init()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/projects')
|
Http::post('/v1/projects')
|
||||||
->desc('Create project')
|
->desc('Create project')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('audits.event', 'projects.create')
|
->label('audits.event', 'projects.create')
|
||||||
|
|
@ -86,8 +89,9 @@ App::post('/v1/projects')
|
||||||
->inject('cache')
|
->inject('cache')
|
||||||
->inject('pools')
|
->inject('pools')
|
||||||
->inject('hooks')
|
->inject('hooks')
|
||||||
->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, Request $request, Response $response, Database $dbForConsole, Cache $cache, Group $pools, Hooks $hooks) {
|
->inject('authorization')
|
||||||
|
->inject('connections')
|
||||||
|
->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, Request $request, Response $response, Database $dbForConsole, Cache $cache, array $pools, Hooks $hooks, Authorization $authorization, Connections $connections) {
|
||||||
$team = $dbForConsole->getDocument('teams', $teamId);
|
$team = $dbForConsole->getDocument('teams', $teamId);
|
||||||
|
|
||||||
if ($team->isEmpty()) {
|
if ($team->isEmpty()) {
|
||||||
|
|
@ -189,8 +193,21 @@ App::post('/v1/projects')
|
||||||
$dsn = new DSN('mysql://' . $dsn);
|
$dsn = new DSN('mysql://' . $dsn);
|
||||||
}
|
}
|
||||||
|
|
||||||
$adapter = $pools->get($dsn->getHost())->pop()->getResource();
|
$pool = $pools['pools-database-' . $dsn->getHost()]['pool'];
|
||||||
|
$connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn'];
|
||||||
|
$connection = $pool->get();
|
||||||
|
$connections->add($connection, $pool);
|
||||||
|
|
||||||
|
$adapter = match ($connectionDsn->getScheme()) {
|
||||||
|
'mariadb' => new MariaDB($connection),
|
||||||
|
'mysql' => new MySQL($connection),
|
||||||
|
default => null
|
||||||
|
};
|
||||||
|
|
||||||
|
$adapter->setDatabase($connectionDsn->getPath());
|
||||||
|
|
||||||
$dbForProject = new Database($adapter, $cache);
|
$dbForProject = new Database($adapter, $cache);
|
||||||
|
$dbForProject->setAuthorization($authorization);
|
||||||
|
|
||||||
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
||||||
$dbForProject
|
$dbForProject
|
||||||
|
|
@ -208,10 +225,8 @@ App::post('/v1/projects')
|
||||||
|
|
||||||
$audit = new Audit($dbForProject);
|
$audit = new Audit($dbForProject);
|
||||||
$audit->setup();
|
$audit->setup();
|
||||||
|
|
||||||
$abuse = new TimeLimit('', 0, 1, $dbForProject);
|
$abuse = new TimeLimit('', 0, 1, $dbForProject);
|
||||||
$abuse->setup();
|
$abuse->setup();
|
||||||
|
|
||||||
/** @var array $collections */
|
/** @var array $collections */
|
||||||
$collections = Config::getParam('collections', [])['projects'] ?? [];
|
$collections = Config::getParam('collections', [])['projects'] ?? [];
|
||||||
|
|
||||||
|
|
@ -234,17 +249,17 @@ App::post('/v1/projects')
|
||||||
// Collection already exists
|
// Collection already exists
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$connections->reclaim();
|
||||||
// Hook allowing instant project mirroring during migration
|
// Hook allowing instant project mirroring during migration
|
||||||
// Outside of migration, hook is not registered and has no effect
|
// Outside of migration, hook is not registered and has no effect
|
||||||
$hooks->trigger('afterProjectCreation', [ $project, $pools, $cache ]);
|
$hooks->trigger('afterProjectCreation', [$project, $pools, $cache]);
|
||||||
|
|
||||||
$response
|
$response
|
||||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||||
->dynamic($project, Response::MODEL_PROJECT);
|
->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/projects')
|
Http::get('/v1/projects')
|
||||||
->desc('List projects')
|
->desc('List projects')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.read')
|
->label('scope', 'projects.read')
|
||||||
|
|
@ -259,7 +274,6 @@ App::get('/v1/projects')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (array $queries, string $search, Response $response, Database $dbForConsole) {
|
->action(function (array $queries, string $search, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$queries = Query::parseQueries($queries);
|
$queries = Query::parseQueries($queries);
|
||||||
} catch (QueryException $e) {
|
} catch (QueryException $e) {
|
||||||
|
|
@ -297,7 +311,7 @@ App::get('/v1/projects')
|
||||||
]), Response::MODEL_PROJECT_LIST);
|
]), Response::MODEL_PROJECT_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/projects/:projectId')
|
Http::get('/v1/projects/:projectId')
|
||||||
->desc('Get project')
|
->desc('Get project')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.read')
|
->label('scope', 'projects.read')
|
||||||
|
|
@ -311,7 +325,6 @@ App::get('/v1/projects/:projectId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -321,7 +334,7 @@ App::get('/v1/projects/:projectId')
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId')
|
Http::patch('/v1/projects/:projectId')
|
||||||
->desc('Update project')
|
->desc('Update project')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -345,31 +358,34 @@ App::patch('/v1/projects/:projectId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->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) {
|
->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);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project
|
$project = $dbForConsole->updateDocument(
|
||||||
->setAttribute('name', $name)
|
'projects',
|
||||||
->setAttribute('description', $description)
|
$project->getId(),
|
||||||
->setAttribute('logo', $logo)
|
$project
|
||||||
->setAttribute('url', $url)
|
->setAttribute('name', $name)
|
||||||
->setAttribute('legalName', $legalName)
|
->setAttribute('description', $description)
|
||||||
->setAttribute('legalCountry', $legalCountry)
|
->setAttribute('logo', $logo)
|
||||||
->setAttribute('legalState', $legalState)
|
->setAttribute('url', $url)
|
||||||
->setAttribute('legalCity', $legalCity)
|
->setAttribute('legalName', $legalName)
|
||||||
->setAttribute('legalAddress', $legalAddress)
|
->setAttribute('legalCountry', $legalCountry)
|
||||||
->setAttribute('legalTaxId', $legalTaxId)
|
->setAttribute('legalState', $legalState)
|
||||||
->setAttribute('search', implode(' ', [$projectId, $name])));
|
->setAttribute('legalCity', $legalCity)
|
||||||
|
->setAttribute('legalAddress', $legalAddress)
|
||||||
|
->setAttribute('legalTaxId', $legalTaxId)
|
||||||
|
->setAttribute('search', implode(' ', [$projectId, $name]))
|
||||||
|
);
|
||||||
|
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/team')
|
Http::patch('/v1/projects/:projectId/team')
|
||||||
->desc('Update project team')
|
->desc('Update Project Team')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
|
@ -383,7 +399,6 @@ App::patch('/v1/projects/:projectId/team')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $teamId, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $teamId, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
$team = $dbForConsole->getDocument('teams', $teamId);
|
$team = $dbForConsole->getDocument('teams', $teamId);
|
||||||
|
|
||||||
|
|
@ -436,7 +451,7 @@ App::patch('/v1/projects/:projectId/team')
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/service')
|
Http::patch('/v1/projects/:projectId/service')
|
||||||
->desc('Update service status')
|
->desc('Update service status')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -452,7 +467,6 @@ App::patch('/v1/projects/:projectId/service')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $service, bool $status, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $service, bool $status, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -467,7 +481,7 @@ App::patch('/v1/projects/:projectId/service')
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/service/all')
|
Http::patch('/v1/projects/:projectId/service/all')
|
||||||
->desc('Update all service status')
|
->desc('Update all service status')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -482,7 +496,6 @@ App::patch('/v1/projects/:projectId/service/all')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, bool $status, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, bool $status, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -501,7 +514,7 @@ App::patch('/v1/projects/:projectId/service/all')
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/api')
|
Http::patch('/v1/projects/:projectId/api')
|
||||||
->desc('Update API status')
|
->desc('Update API status')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -517,7 +530,6 @@ App::patch('/v1/projects/:projectId/api')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $api, bool $status, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $api, bool $status, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -532,7 +544,7 @@ App::patch('/v1/projects/:projectId/api')
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/api/all')
|
Http::patch('/v1/projects/:projectId/api/all')
|
||||||
->desc('Update all API status')
|
->desc('Update all API status')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -547,7 +559,6 @@ App::patch('/v1/projects/:projectId/api/all')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, bool $status, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, bool $status, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -566,7 +577,7 @@ App::patch('/v1/projects/:projectId/api/all')
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/oauth2')
|
Http::patch('/v1/projects/:projectId/oauth2')
|
||||||
->desc('Update project OAuth2')
|
->desc('Update project OAuth2')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -584,7 +595,6 @@ App::patch('/v1/projects/:projectId/oauth2')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $provider, ?string $appId, ?string $secret, ?bool $enabled, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $provider, ?string $appId, ?string $secret, ?bool $enabled, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -610,7 +620,7 @@ App::patch('/v1/projects/:projectId/oauth2')
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/auth/session-alerts')
|
Http::patch('/v1/projects/:projectId/auth/session-alerts')
|
||||||
->desc('Update project sessions emails')
|
->desc('Update project sessions emails')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -641,7 +651,7 @@ App::patch('/v1/projects/:projectId/auth/session-alerts')
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/auth/limit')
|
Http::patch('/v1/projects/:projectId/auth/limit')
|
||||||
->desc('Update project users limit')
|
->desc('Update project users limit')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -656,7 +666,6 @@ App::patch('/v1/projects/:projectId/auth/limit')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -666,13 +675,17 @@ App::patch('/v1/projects/:projectId/auth/limit')
|
||||||
$auths = $project->getAttribute('auths', []);
|
$auths = $project->getAttribute('auths', []);
|
||||||
$auths['limit'] = $limit;
|
$auths['limit'] = $limit;
|
||||||
|
|
||||||
$dbForConsole->updateDocument('projects', $project->getId(), $project
|
$dbForConsole->updateDocument(
|
||||||
->setAttribute('auths', $auths));
|
'projects',
|
||||||
|
$project->getId(),
|
||||||
|
$project
|
||||||
|
->setAttribute('auths', $auths)
|
||||||
|
);
|
||||||
|
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/auth/duration')
|
Http::patch('/v1/projects/:projectId/auth/duration')
|
||||||
->desc('Update project authentication duration')
|
->desc('Update project authentication duration')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -687,7 +700,6 @@ App::patch('/v1/projects/:projectId/auth/duration')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, int $duration, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, int $duration, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -697,13 +709,17 @@ App::patch('/v1/projects/:projectId/auth/duration')
|
||||||
$auths = $project->getAttribute('auths', []);
|
$auths = $project->getAttribute('auths', []);
|
||||||
$auths['duration'] = $duration;
|
$auths['duration'] = $duration;
|
||||||
|
|
||||||
$dbForConsole->updateDocument('projects', $project->getId(), $project
|
$dbForConsole->updateDocument(
|
||||||
->setAttribute('auths', $auths));
|
'projects',
|
||||||
|
$project->getId(),
|
||||||
|
$project
|
||||||
|
->setAttribute('auths', $auths)
|
||||||
|
);
|
||||||
|
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/auth/:method')
|
Http::patch('/v1/projects/:projectId/auth/:method')
|
||||||
->desc('Update project auth method status. Use this endpoint to enable or disable a given auth method for this project.')
|
->desc('Update project auth method status. Use this endpoint to enable or disable a given auth method for this project.')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -719,10 +735,9 @@ App::patch('/v1/projects/:projectId/auth/:method')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $method, bool $status, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $method, bool $status, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
$auth = Config::getParam('auth')[$method] ?? [];
|
$authConfig = Config::getParam('auth')[$method] ?? [];
|
||||||
$authKey = $auth['key'] ?? '';
|
$authKey = $authConfig['key'] ?? '';
|
||||||
$status = ($status === '1' || $status === 'true' || $status === 1 || $status === true);
|
$status = ($status === '1' || $status === 'true' || $status === 1 || $status === true);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -737,7 +752,7 @@ App::patch('/v1/projects/:projectId/auth/:method')
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/auth/password-history')
|
Http::patch('/v1/projects/:projectId/auth/password-history')
|
||||||
->desc('Update authentication password history. Use this endpoint to set the number of password history to save and 0 to disable password history.')
|
->desc('Update authentication password history. Use this endpoint to set the number of password history to save and 0 to disable password history.')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -752,7 +767,6 @@ App::patch('/v1/projects/:projectId/auth/password-history')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -762,13 +776,17 @@ App::patch('/v1/projects/:projectId/auth/password-history')
|
||||||
$auths = $project->getAttribute('auths', []);
|
$auths = $project->getAttribute('auths', []);
|
||||||
$auths['passwordHistory'] = $limit;
|
$auths['passwordHistory'] = $limit;
|
||||||
|
|
||||||
$dbForConsole->updateDocument('projects', $project->getId(), $project
|
$dbForConsole->updateDocument(
|
||||||
->setAttribute('auths', $auths));
|
'projects',
|
||||||
|
$project->getId(),
|
||||||
|
$project
|
||||||
|
->setAttribute('auths', $auths)
|
||||||
|
);
|
||||||
|
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/auth/password-dictionary')
|
Http::patch('/v1/projects/:projectId/auth/password-dictionary')
|
||||||
->desc('Update authentication password dictionary status. Use this endpoint to enable or disable the dicitonary check for user password')
|
->desc('Update authentication password dictionary status. Use this endpoint to enable or disable the dicitonary check for user password')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -783,7 +801,6 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, bool $enabled, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, bool $enabled, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -793,13 +810,17 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
|
||||||
$auths = $project->getAttribute('auths', []);
|
$auths = $project->getAttribute('auths', []);
|
||||||
$auths['passwordDictionary'] = $enabled;
|
$auths['passwordDictionary'] = $enabled;
|
||||||
|
|
||||||
$dbForConsole->updateDocument('projects', $project->getId(), $project
|
$dbForConsole->updateDocument(
|
||||||
->setAttribute('auths', $auths));
|
'projects',
|
||||||
|
$project->getId(),
|
||||||
|
$project
|
||||||
|
->setAttribute('auths', $auths)
|
||||||
|
);
|
||||||
|
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/auth/personal-data')
|
Http::patch('/v1/projects/:projectId/auth/personal-data')
|
||||||
->desc('Enable or disable checking user passwords for similarity with their personal data.')
|
->desc('Enable or disable checking user passwords for similarity with their personal data.')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -814,7 +835,6 @@ App::patch('/v1/projects/:projectId/auth/personal-data')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, bool $enabled, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, bool $enabled, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -824,13 +844,17 @@ App::patch('/v1/projects/:projectId/auth/personal-data')
|
||||||
$auths = $project->getAttribute('auths', []);
|
$auths = $project->getAttribute('auths', []);
|
||||||
$auths['personalDataCheck'] = $enabled;
|
$auths['personalDataCheck'] = $enabled;
|
||||||
|
|
||||||
$dbForConsole->updateDocument('projects', $project->getId(), $project
|
$dbForConsole->updateDocument(
|
||||||
->setAttribute('auths', $auths));
|
'projects',
|
||||||
|
$project->getId(),
|
||||||
|
$project
|
||||||
|
->setAttribute('auths', $auths)
|
||||||
|
);
|
||||||
|
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/auth/max-sessions')
|
Http::patch('/v1/projects/:projectId/auth/max-sessions')
|
||||||
->desc('Update project user sessions limit')
|
->desc('Update project user sessions limit')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -845,7 +869,6 @@ App::patch('/v1/projects/:projectId/auth/max-sessions')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -855,13 +878,17 @@ App::patch('/v1/projects/:projectId/auth/max-sessions')
|
||||||
$auths = $project->getAttribute('auths', []);
|
$auths = $project->getAttribute('auths', []);
|
||||||
$auths['maxSessions'] = $limit;
|
$auths['maxSessions'] = $limit;
|
||||||
|
|
||||||
$dbForConsole->updateDocument('projects', $project->getId(), $project
|
$dbForConsole->updateDocument(
|
||||||
->setAttribute('auths', $auths));
|
'projects',
|
||||||
|
$project->getId(),
|
||||||
|
$project
|
||||||
|
->setAttribute('auths', $auths)
|
||||||
|
);
|
||||||
|
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/auth/mock-numbers')
|
Http::patch('/v1/projects/:projectId/auth/mock-numbers')
|
||||||
->desc('Update the mock numbers for the project')
|
->desc('Update the mock numbers for the project')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -900,7 +927,7 @@ App::patch('/v1/projects/:projectId/auth/mock-numbers')
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/projects/:projectId')
|
Http::delete('/v1/projects/:projectId')
|
||||||
->desc('Delete project')
|
->desc('Delete project')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('audits.event', 'projects.delete')
|
->label('audits.event', 'projects.delete')
|
||||||
|
|
@ -936,7 +963,7 @@ App::delete('/v1/projects/:projectId')
|
||||||
|
|
||||||
// Webhooks
|
// Webhooks
|
||||||
|
|
||||||
App::post('/v1/projects/:projectId/webhooks')
|
Http::post('/v1/projects/:projectId/webhooks')
|
||||||
->desc('Create webhook')
|
->desc('Create webhook')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -957,14 +984,13 @@ App::post('/v1/projects/:projectId/webhooks')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $name, bool $enabled, array $events, string $url, bool $security, string $httpUser, string $httpPass, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $name, bool $enabled, array $events, string $url, bool $security, string $httpUser, string $httpPass, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$security = (bool) filter_var($security, FILTER_VALIDATE_BOOLEAN);
|
$security = (bool)filter_var($security, FILTER_VALIDATE_BOOLEAN);
|
||||||
|
|
||||||
$webhook = new Document([
|
$webhook = new Document([
|
||||||
'$id' => ID::unique(),
|
'$id' => ID::unique(),
|
||||||
|
|
@ -994,7 +1020,7 @@ App::post('/v1/projects/:projectId/webhooks')
|
||||||
->dynamic($webhook, Response::MODEL_WEBHOOK);
|
->dynamic($webhook, Response::MODEL_WEBHOOK);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/projects/:projectId/webhooks')
|
Http::get('/v1/projects/:projectId/webhooks')
|
||||||
->desc('List webhooks')
|
->desc('List webhooks')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.read')
|
->label('scope', 'projects.read')
|
||||||
|
|
@ -1008,7 +1034,6 @@ App::get('/v1/projects/:projectId/webhooks')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -1026,7 +1051,7 @@ App::get('/v1/projects/:projectId/webhooks')
|
||||||
]), Response::MODEL_WEBHOOK_LIST);
|
]), Response::MODEL_WEBHOOK_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/projects/:projectId/webhooks/:webhookId')
|
Http::get('/v1/projects/:projectId/webhooks/:webhookId')
|
||||||
->desc('Get webhook')
|
->desc('Get webhook')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.read')
|
->label('scope', 'projects.read')
|
||||||
|
|
@ -1041,7 +1066,6 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -1053,14 +1077,14 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
|
||||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($webhook === false || $webhook->isEmpty()) {
|
if ($webhook->isEmpty()) {
|
||||||
throw new Exception(Exception::WEBHOOK_NOT_FOUND);
|
throw new Exception(Exception::WEBHOOK_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
|
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
Http::put('/v1/projects/:projectId/webhooks/:webhookId')
|
||||||
->desc('Update webhook')
|
->desc('Update webhook')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -1082,7 +1106,6 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $webhookId, string $name, bool $enabled, array $events, string $url, bool $security, string $httpUser, string $httpPass, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $webhookId, string $name, bool $enabled, array $events, string $url, bool $security, string $httpUser, string $httpPass, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -1096,7 +1119,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
||||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($webhook === false || $webhook->isEmpty()) {
|
if ($webhook->isEmpty()) {
|
||||||
throw new Exception(Exception::WEBHOOK_NOT_FOUND);
|
throw new Exception(Exception::WEBHOOK_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1119,7 +1142,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
||||||
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
|
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
|
Http::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
|
||||||
->desc('Update webhook signature key')
|
->desc('Update webhook signature key')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -1134,7 +1157,6 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -1146,7 +1168,7 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
|
||||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($webhook === false || $webhook->isEmpty()) {
|
if ($webhook->isEmpty()) {
|
||||||
throw new Exception(Exception::WEBHOOK_NOT_FOUND);
|
throw new Exception(Exception::WEBHOOK_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1158,7 +1180,7 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
|
||||||
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
|
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/projects/:projectId/webhooks/:webhookId')
|
Http::delete('/v1/projects/:projectId/webhooks/:webhookId')
|
||||||
->desc('Delete webhook')
|
->desc('Delete webhook')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -1172,7 +1194,6 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -1184,7 +1205,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
|
||||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($webhook === false || $webhook->isEmpty()) {
|
if ($webhook->isEmpty()) {
|
||||||
throw new Exception(Exception::WEBHOOK_NOT_FOUND);
|
throw new Exception(Exception::WEBHOOK_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1197,7 +1218,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
|
||||||
|
|
||||||
// Keys
|
// Keys
|
||||||
|
|
||||||
App::post('/v1/projects/:projectId/keys')
|
Http::post('/v1/projects/:projectId/keys')
|
||||||
->desc('Create key')
|
->desc('Create key')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'keys.write')
|
->label('scope', 'keys.write')
|
||||||
|
|
@ -1214,7 +1235,6 @@ App::post('/v1/projects/:projectId/keys')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -1247,7 +1267,7 @@ App::post('/v1/projects/:projectId/keys')
|
||||||
->dynamic($key, Response::MODEL_KEY);
|
->dynamic($key, Response::MODEL_KEY);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/projects/:projectId/keys')
|
Http::get('/v1/projects/:projectId/keys')
|
||||||
->desc('List keys')
|
->desc('List keys')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'keys.read')
|
->label('scope', 'keys.read')
|
||||||
|
|
@ -1261,7 +1281,6 @@ App::get('/v1/projects/:projectId/keys')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -1279,7 +1298,7 @@ App::get('/v1/projects/:projectId/keys')
|
||||||
]), Response::MODEL_KEY_LIST);
|
]), Response::MODEL_KEY_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/projects/:projectId/keys/:keyId')
|
Http::get('/v1/projects/:projectId/keys/:keyId')
|
||||||
->desc('Get key')
|
->desc('Get key')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'keys.read')
|
->label('scope', 'keys.read')
|
||||||
|
|
@ -1294,7 +1313,6 @@ App::get('/v1/projects/:projectId/keys/:keyId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -1306,14 +1324,14 @@ App::get('/v1/projects/:projectId/keys/:keyId')
|
||||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($key === false || $key->isEmpty()) {
|
if ($key->isEmpty()) {
|
||||||
throw new Exception(Exception::KEY_NOT_FOUND);
|
throw new Exception(Exception::KEY_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$response->dynamic($key, Response::MODEL_KEY);
|
$response->dynamic($key, Response::MODEL_KEY);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::put('/v1/projects/:projectId/keys/:keyId')
|
Http::put('/v1/projects/:projectId/keys/:keyId')
|
||||||
->desc('Update key')
|
->desc('Update key')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'keys.write')
|
->label('scope', 'keys.write')
|
||||||
|
|
@ -1331,7 +1349,6 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -1343,7 +1360,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
||||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($key === false || $key->isEmpty()) {
|
if ($key->isEmpty()) {
|
||||||
throw new Exception(Exception::KEY_NOT_FOUND);
|
throw new Exception(Exception::KEY_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1359,7 +1376,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
||||||
$response->dynamic($key, Response::MODEL_KEY);
|
$response->dynamic($key, Response::MODEL_KEY);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/projects/:projectId/keys/:keyId')
|
Http::delete('/v1/projects/:projectId/keys/:keyId')
|
||||||
->desc('Delete key')
|
->desc('Delete key')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'keys.write')
|
->label('scope', 'keys.write')
|
||||||
|
|
@ -1373,7 +1390,6 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -1385,7 +1401,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
|
||||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($key === false || $key->isEmpty()) {
|
if ($key->isEmpty()) {
|
||||||
throw new Exception(Exception::KEY_NOT_FOUND);
|
throw new Exception(Exception::KEY_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1398,7 +1414,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
|
||||||
|
|
||||||
// JWT Keys
|
// JWT Keys
|
||||||
|
|
||||||
App::post('/v1/projects/:projectId/jwts')
|
Http::post('/v1/projects/:projectId/jwts')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->desc('Create JWT')
|
->desc('Create JWT')
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -1433,7 +1449,7 @@ App::post('/v1/projects/:projectId/jwts')
|
||||||
|
|
||||||
// Platforms
|
// Platforms
|
||||||
|
|
||||||
App::post('/v1/projects/:projectId/platforms')
|
Http::post('/v1/projects/:projectId/platforms')
|
||||||
->desc('Create platform')
|
->desc('Create platform')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('audits.event', 'platforms.create')
|
->label('audits.event', 'platforms.create')
|
||||||
|
|
@ -1484,7 +1500,7 @@ App::post('/v1/projects/:projectId/platforms')
|
||||||
->dynamic($platform, Response::MODEL_PLATFORM);
|
->dynamic($platform, Response::MODEL_PLATFORM);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/projects/:projectId/platforms')
|
Http::get('/v1/projects/:projectId/platforms')
|
||||||
->desc('List platforms')
|
->desc('List platforms')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'platforms.read')
|
->label('scope', 'platforms.read')
|
||||||
|
|
@ -1498,7 +1514,6 @@ App::get('/v1/projects/:projectId/platforms')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -1516,7 +1531,7 @@ App::get('/v1/projects/:projectId/platforms')
|
||||||
]), Response::MODEL_PLATFORM_LIST);
|
]), Response::MODEL_PLATFORM_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/projects/:projectId/platforms/:platformId')
|
Http::get('/v1/projects/:projectId/platforms/:platformId')
|
||||||
->desc('Get platform')
|
->desc('Get platform')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'platforms.read')
|
->label('scope', 'platforms.read')
|
||||||
|
|
@ -1531,7 +1546,6 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -1543,14 +1557,14 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
|
||||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($platform === false || $platform->isEmpty()) {
|
if ($platform->isEmpty()) {
|
||||||
throw new Exception(Exception::PLATFORM_NOT_FOUND);
|
throw new Exception(Exception::PLATFORM_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$response->dynamic($platform, Response::MODEL_PLATFORM);
|
$response->dynamic($platform, Response::MODEL_PLATFORM);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::put('/v1/projects/:projectId/platforms/:platformId')
|
Http::put('/v1/projects/:projectId/platforms/:platformId')
|
||||||
->desc('Update platform')
|
->desc('Update platform')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'platforms.write')
|
->label('scope', 'platforms.write')
|
||||||
|
|
@ -1580,7 +1594,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
|
||||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($platform === false || $platform->isEmpty()) {
|
if ($platform->isEmpty()) {
|
||||||
throw new Exception(Exception::PLATFORM_NOT_FOUND);
|
throw new Exception(Exception::PLATFORM_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1597,7 +1611,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
|
||||||
$response->dynamic($platform, Response::MODEL_PLATFORM);
|
$response->dynamic($platform, Response::MODEL_PLATFORM);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/projects/:projectId/platforms/:platformId')
|
Http::delete('/v1/projects/:projectId/platforms/:platformId')
|
||||||
->desc('Delete platform')
|
->desc('Delete platform')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('audits.event', 'platforms.delete')
|
->label('audits.event', 'platforms.delete')
|
||||||
|
|
@ -1612,7 +1626,6 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -1624,7 +1637,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
|
||||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($platform === false || $platform->isEmpty()) {
|
if ($platform->isEmpty()) {
|
||||||
throw new Exception(Exception::PLATFORM_NOT_FOUND);
|
throw new Exception(Exception::PLATFORM_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1637,7 +1650,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
|
||||||
|
|
||||||
|
|
||||||
// CUSTOM SMTP and Templates
|
// CUSTOM SMTP and Templates
|
||||||
App::patch('/v1/projects/:projectId/smtp')
|
Http::patch('/v1/projects/:projectId/smtp')
|
||||||
->desc('Update SMTP')
|
->desc('Update SMTP')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -1660,7 +1673,6 @@ App::patch('/v1/projects/:projectId/smtp')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, bool $enabled, string $senderName, string $senderEmail, string $replyTo, string $host, int $port, string $username, string $password, string $secure, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, bool $enabled, string $senderName, string $senderEmail, string $replyTo, string $host, int $port, string $username, string $password, string $secure, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -1727,7 +1739,7 @@ App::patch('/v1/projects/:projectId/smtp')
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/projects/:projectId/smtp/tests')
|
Http::post('/v1/projects/:projectId/smtp/tests')
|
||||||
->desc('Create SMTP test')
|
->desc('Create SMTP test')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -1786,7 +1798,7 @@ App::post('/v1/projects/:projectId/smtp/tests')
|
||||||
return $response->noContent();
|
return $response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/projects/:projectId/templates/sms/:type/:locale')
|
Http::get('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||||
->desc('Get custom SMS template')
|
->desc('Get custom SMS template')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -1802,7 +1814,6 @@ App::get('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
|
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
@ -1812,7 +1823,7 @@ App::get('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||||
}
|
}
|
||||||
|
|
||||||
$templates = $project->getAttribute('templates', []);
|
$templates = $project->getAttribute('templates', []);
|
||||||
$template = $templates['sms.' . $type . '-' . $locale] ?? null;
|
$template = $templates['sms.' . $type . '-' . $locale] ?? null;
|
||||||
|
|
||||||
if (is_null($template)) {
|
if (is_null($template)) {
|
||||||
$template = [
|
$template = [
|
||||||
|
|
@ -1827,7 +1838,7 @@ App::get('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
App::get('/v1/projects/:projectId/templates/email/:type/:locale')
|
Http::get('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||||
->desc('Get custom email template')
|
->desc('Get custom email template')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -1843,7 +1854,6 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -1851,7 +1861,7 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||||
}
|
}
|
||||||
|
|
||||||
$templates = $project->getAttribute('templates', []);
|
$templates = $project->getAttribute('templates', []);
|
||||||
$template = $templates['email.' . $type . '-' . $locale] ?? null;
|
$template = $templates['email.' . $type . '-' . $locale] ?? null;
|
||||||
|
|
||||||
$localeObj = new Locale($locale);
|
$localeObj = new Locale($locale);
|
||||||
if (is_null($template)) {
|
if (is_null($template)) {
|
||||||
|
|
@ -1859,7 +1869,7 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||||
$message
|
$message
|
||||||
->setParam('{{hello}}', $localeObj->getText("emails.{$type}.hello"))
|
->setParam('{{hello}}', $localeObj->getText("emails.{$type}.hello"))
|
||||||
->setParam('{{footer}}', $localeObj->getText("emails.{$type}.footer"))
|
->setParam('{{footer}}', $localeObj->getText("emails.{$type}.footer"))
|
||||||
->setParam('{{body}}', $localeObj->getText('emails.' . $type . '.body'), escapeHtml: false)
|
->setParam('{{body}}', $localeObj->getText('emails.' . $type . '.body'), escape: false)
|
||||||
->setParam('{{thanks}}', $localeObj->getText("emails.{$type}.thanks"))
|
->setParam('{{thanks}}', $localeObj->getText("emails.{$type}.thanks"))
|
||||||
->setParam('{{signature}}', $localeObj->getText("emails.{$type}.signature"))
|
->setParam('{{signature}}', $localeObj->getText("emails.{$type}.signature"))
|
||||||
->setParam('{{direction}}', $localeObj->getText('settings.direction'));
|
->setParam('{{direction}}', $localeObj->getText('settings.direction'));
|
||||||
|
|
@ -1879,7 +1889,7 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||||
$response->dynamic(new Document($template), Response::MODEL_EMAIL_TEMPLATE);
|
$response->dynamic(new Document($template), Response::MODEL_EMAIL_TEMPLATE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
|
Http::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||||
->desc('Update custom SMS template')
|
->desc('Update custom SMS template')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -1896,7 +1906,6 @@ App::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $type, string $locale, string $message, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $type, string $locale, string $message, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
|
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
@ -1919,7 +1928,7 @@ App::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||||
]), Response::MODEL_SMS_TEMPLATE);
|
]), Response::MODEL_SMS_TEMPLATE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
|
Http::patch('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||||
->desc('Update custom email templates')
|
->desc('Update custom email templates')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -1940,7 +1949,6 @@ App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $type, string $locale, string $subject, string $message, string $senderName, string $senderEmail, string $replyTo, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $type, string $locale, string $subject, string $message, string $senderName, string $senderEmail, string $replyTo, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -1969,7 +1977,7 @@ App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||||
]), Response::MODEL_EMAIL_TEMPLATE);
|
]), Response::MODEL_EMAIL_TEMPLATE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
|
Http::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||||
->desc('Reset custom SMS template')
|
->desc('Reset custom SMS template')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -1985,7 +1993,6 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
|
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
@ -1995,7 +2002,7 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||||
}
|
}
|
||||||
|
|
||||||
$templates = $project->getAttribute('templates', []);
|
$templates = $project->getAttribute('templates', []);
|
||||||
$template = $templates['sms.' . $type . '-' . $locale] ?? null;
|
$template = $templates['sms.' . $type . '-' . $locale] ?? null;
|
||||||
|
|
||||||
if (is_null($template)) {
|
if (is_null($template)) {
|
||||||
throw new Exception(Exception::PROJECT_TEMPLATE_DEFAULT_DELETION);
|
throw new Exception(Exception::PROJECT_TEMPLATE_DEFAULT_DELETION);
|
||||||
|
|
@ -2012,7 +2019,7 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||||
]), Response::MODEL_SMS_TEMPLATE);
|
]), Response::MODEL_SMS_TEMPLATE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/projects/:projectId/templates/email/:type/:locale')
|
Http::delete('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||||
->desc('Reset custom email template')
|
->desc('Reset custom email template')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.write')
|
->label('scope', 'projects.write')
|
||||||
|
|
@ -2028,7 +2035,6 @@ App::delete('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) {
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
|
|
@ -2036,7 +2042,7 @@ App::delete('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||||
}
|
}
|
||||||
|
|
||||||
$templates = $project->getAttribute('templates', []);
|
$templates = $project->getAttribute('templates', []);
|
||||||
$template = $templates['email.' . $type . '-' . $locale] ?? null;
|
$template = $templates['email.' . $type . '-' . $locale] ?? null;
|
||||||
|
|
||||||
if (is_null($template)) {
|
if (is_null($template)) {
|
||||||
throw new Exception(Exception::PROJECT_TEMPLATE_DEFAULT_DELETION);
|
throw new Exception(Exception::PROJECT_TEMPLATE_DEFAULT_DELETION);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ use Appwrite\Extend\Exception;
|
||||||
use Appwrite\Network\Validator\CNAME;
|
use Appwrite\Network\Validator\CNAME;
|
||||||
use Appwrite\Utopia\Database\Validator\Queries\Rules;
|
use Appwrite\Utopia\Database\Validator\Queries\Rules;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Exception\Query as QueryException;
|
use Utopia\Database\Exception\Query as QueryException;
|
||||||
|
|
@ -15,13 +14,14 @@ use Utopia\Database\Helpers\ID;
|
||||||
use Utopia\Database\Query;
|
use Utopia\Database\Query;
|
||||||
use Utopia\Database\Validator\UID;
|
use Utopia\Database\Validator\UID;
|
||||||
use Utopia\Domains\Domain;
|
use Utopia\Domains\Domain;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Validator\Domain as ValidatorDomain;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
|
use Utopia\Http\Validator\WhiteList;
|
||||||
use Utopia\Logger\Log;
|
use Utopia\Logger\Log;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\Domain as ValidatorDomain;
|
|
||||||
use Utopia\Validator\Text;
|
|
||||||
use Utopia\Validator\WhiteList;
|
|
||||||
|
|
||||||
App::post('/v1/proxy/rules')
|
Http::post('/v1/proxy/rules')
|
||||||
->groups(['api', 'proxy'])
|
->groups(['api', 'proxy'])
|
||||||
->desc('Create rule')
|
->desc('Create rule')
|
||||||
->label('scope', 'rules.write')
|
->label('scope', 'rules.write')
|
||||||
|
|
@ -63,7 +63,7 @@ App::post('/v1/proxy/rules')
|
||||||
Query::equal('domain', [$domain]),
|
Query::equal('domain', [$domain]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($document && !$document->isEmpty()) {
|
if (!$document->isEmpty()) {
|
||||||
if ($document->getAttribute('projectId') === $project->getId()) {
|
if ($document->getAttribute('projectId') === $project->getId()) {
|
||||||
$resourceType = $document->getAttribute('resourceType');
|
$resourceType = $document->getAttribute('resourceType');
|
||||||
$resourceId = $document->getAttribute('resourceId');
|
$resourceId = $document->getAttribute('resourceId');
|
||||||
|
|
@ -147,7 +147,7 @@ App::post('/v1/proxy/rules')
|
||||||
->dynamic($rule, Response::MODEL_PROXY_RULE);
|
->dynamic($rule, Response::MODEL_PROXY_RULE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/proxy/rules')
|
Http::get('/v1/proxy/rules')
|
||||||
->groups(['api', 'proxy'])
|
->groups(['api', 'proxy'])
|
||||||
->desc('List rules')
|
->desc('List rules')
|
||||||
->label('scope', 'rules.read')
|
->label('scope', 'rules.read')
|
||||||
|
|
@ -210,7 +210,7 @@ App::get('/v1/proxy/rules')
|
||||||
]), Response::MODEL_PROXY_RULE_LIST);
|
]), Response::MODEL_PROXY_RULE_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/proxy/rules/:ruleId')
|
Http::get('/v1/proxy/rules/:ruleId')
|
||||||
->groups(['api', 'proxy'])
|
->groups(['api', 'proxy'])
|
||||||
->desc('Get rule')
|
->desc('Get rule')
|
||||||
->label('scope', 'rules.read')
|
->label('scope', 'rules.read')
|
||||||
|
|
@ -239,7 +239,7 @@ App::get('/v1/proxy/rules/:ruleId')
|
||||||
$response->dynamic($rule, Response::MODEL_PROXY_RULE);
|
$response->dynamic($rule, Response::MODEL_PROXY_RULE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/proxy/rules/:ruleId')
|
Http::delete('/v1/proxy/rules/:ruleId')
|
||||||
->groups(['api', 'proxy'])
|
->groups(['api', 'proxy'])
|
||||||
->desc('Delete rule')
|
->desc('Delete rule')
|
||||||
->label('scope', 'rules.write')
|
->label('scope', 'rules.write')
|
||||||
|
|
@ -276,8 +276,8 @@ App::delete('/v1/proxy/rules/:ruleId')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/proxy/rules/:ruleId/verification')
|
Http::patch('/v1/proxy/rules/:ruleId/verification')
|
||||||
->desc('Update rule verification status')
|
->desc('Update Rule Verification Status')
|
||||||
->groups(['api', 'proxy'])
|
->groups(['api', 'proxy'])
|
||||||
->label('scope', 'rules.write')
|
->label('scope', 'rules.write')
|
||||||
->label('event', 'rules.[ruleId].update')
|
->label('event', 'rules.[ruleId].update')
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ use Appwrite\OpenSSL\OpenSSL;
|
||||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||||
use Appwrite\Utopia\Database\Validator\Queries\Buckets;
|
use Appwrite\Utopia\Database\Validator\Queries\Buckets;
|
||||||
use Appwrite\Utopia\Database\Validator\Queries\Files;
|
use Appwrite\Utopia\Database\Validator\Queries\Files;
|
||||||
|
use Appwrite\Utopia\Request;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
|
|
@ -23,8 +23,16 @@ use Utopia\Database\Helpers\Permission;
|
||||||
use Utopia\Database\Helpers\Role;
|
use Utopia\Database\Helpers\Role;
|
||||||
use Utopia\Database\Query;
|
use Utopia\Database\Query;
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\Database\Validator\Authorization;
|
||||||
|
use Utopia\Database\Validator\Authorization\Input;
|
||||||
use Utopia\Database\Validator\Permissions;
|
use Utopia\Database\Validator\Permissions;
|
||||||
use Utopia\Database\Validator\UID;
|
use Utopia\Database\Validator\UID;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Validator\ArrayList;
|
||||||
|
use Utopia\Http\Validator\Boolean;
|
||||||
|
use Utopia\Http\Validator\HexColor;
|
||||||
|
use Utopia\Http\Validator\Range;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
|
use Utopia\Http\Validator\WhiteList;
|
||||||
use Utopia\Image\Image;
|
use Utopia\Image\Image;
|
||||||
use Utopia\Storage\Compression\Algorithms\GZIP;
|
use Utopia\Storage\Compression\Algorithms\GZIP;
|
||||||
use Utopia\Storage\Compression\Algorithms\Zstd;
|
use Utopia\Storage\Compression\Algorithms\Zstd;
|
||||||
|
|
@ -35,16 +43,9 @@ use Utopia\Storage\Validator\File;
|
||||||
use Utopia\Storage\Validator\FileExt;
|
use Utopia\Storage\Validator\FileExt;
|
||||||
use Utopia\Storage\Validator\FileSize;
|
use Utopia\Storage\Validator\FileSize;
|
||||||
use Utopia\Storage\Validator\Upload;
|
use Utopia\Storage\Validator\Upload;
|
||||||
use Utopia\Swoole\Request;
|
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\ArrayList;
|
|
||||||
use Utopia\Validator\Boolean;
|
|
||||||
use Utopia\Validator\HexColor;
|
|
||||||
use Utopia\Validator\Range;
|
|
||||||
use Utopia\Validator\Text;
|
|
||||||
use Utopia\Validator\WhiteList;
|
|
||||||
|
|
||||||
App::post('/v1/storage/buckets')
|
Http::post('/v1/storage/buckets')
|
||||||
->desc('Create bucket')
|
->desc('Create bucket')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
->label('scope', 'buckets.write')
|
->label('scope', 'buckets.write')
|
||||||
|
|
@ -142,7 +143,7 @@ App::post('/v1/storage/buckets')
|
||||||
->dynamic($bucket, Response::MODEL_BUCKET);
|
->dynamic($bucket, Response::MODEL_BUCKET);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/storage/buckets')
|
Http::get('/v1/storage/buckets')
|
||||||
->desc('List buckets')
|
->desc('List buckets')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
->label('scope', 'buckets.read')
|
->label('scope', 'buckets.read')
|
||||||
|
|
@ -196,7 +197,7 @@ App::get('/v1/storage/buckets')
|
||||||
]), Response::MODEL_BUCKET_LIST);
|
]), Response::MODEL_BUCKET_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/storage/buckets/:bucketId')
|
Http::get('/v1/storage/buckets/:bucketId')
|
||||||
->desc('Get bucket')
|
->desc('Get bucket')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
->label('scope', 'buckets.read')
|
->label('scope', 'buckets.read')
|
||||||
|
|
@ -221,7 +222,7 @@ App::get('/v1/storage/buckets/:bucketId')
|
||||||
$response->dynamic($bucket, Response::MODEL_BUCKET);
|
$response->dynamic($bucket, Response::MODEL_BUCKET);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::put('/v1/storage/buckets/:bucketId')
|
Http::put('/v1/storage/buckets/:bucketId')
|
||||||
->desc('Update bucket')
|
->desc('Update bucket')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
->label('scope', 'buckets.write')
|
->label('scope', 'buckets.write')
|
||||||
|
|
@ -288,7 +289,7 @@ App::put('/v1/storage/buckets/:bucketId')
|
||||||
$response->dynamic($bucket, Response::MODEL_BUCKET);
|
$response->dynamic($bucket, Response::MODEL_BUCKET);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/storage/buckets/:bucketId')
|
Http::delete('/v1/storage/buckets/:bucketId')
|
||||||
->desc('Delete bucket')
|
->desc('Delete bucket')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
->label('scope', 'buckets.write')
|
->label('scope', 'buckets.write')
|
||||||
|
|
@ -329,7 +330,7 @@ App::delete('/v1/storage/buckets/:bucketId')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/storage/buckets/:bucketId/files')
|
Http::post('/v1/storage/buckets/:bucketId/files')
|
||||||
->alias('/v1/storage/files', ['bucketId' => 'default'])
|
->alias('/v1/storage/files', ['bucketId' => 'default'])
|
||||||
->desc('Create file')
|
->desc('Create file')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
|
|
@ -361,19 +362,19 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||||
->inject('mode')
|
->inject('mode')
|
||||||
->inject('deviceForFiles')
|
->inject('deviceForFiles')
|
||||||
->inject('deviceForLocal')
|
->inject('deviceForLocal')
|
||||||
->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Device $deviceForFiles, Device $deviceForLocal) {
|
->inject('authorization')
|
||||||
|
->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Device $deviceForFiles, Device $deviceForLocal, Authorization $authorization) {
|
||||||
|
|
||||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||||
|
|
||||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
$isAPIKey = Auth::isAppUser($authorization->getRoles());
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
|
|
||||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$validator = new Authorization(Database::PERMISSION_CREATE);
|
if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) {
|
||||||
if (!$validator->isValid($bucket->getCreate())) {
|
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -397,7 +398,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Users can only manage their own roles, API keys and Admin users can manage any
|
// Users can only manage their own roles, API keys and Admin users can manage any
|
||||||
$roles = Authorization::getRoles();
|
$roles = $authorization->getRoles();
|
||||||
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
|
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
|
||||||
foreach (Database::PERMISSIONS as $type) {
|
foreach (Database::PERMISSIONS as $type) {
|
||||||
foreach ($permissions as $permission) {
|
foreach ($permissions as $permission) {
|
||||||
|
|
@ -410,7 +411,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||||
$permission->getIdentifier(),
|
$permission->getIdentifier(),
|
||||||
$permission->getDimension()
|
$permission->getDimension()
|
||||||
))->toString();
|
))->toString();
|
||||||
if (!Authorization::isRole($role)) {
|
if (!$authorization->isRole($role)) {
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
|
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -630,11 +631,10 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||||
* However as with chunk upload even if we are updating, we are essentially creating a file
|
* However as with chunk upload even if we are updating, we are essentially creating a file
|
||||||
* adding it's new chunk so we validate create permission instead of update
|
* adding it's new chunk so we validate create permission instead of update
|
||||||
*/
|
*/
|
||||||
$validator = new Authorization(Database::PERMISSION_CREATE);
|
if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) {
|
||||||
if (!$validator->isValid($bucket->getCreate())) {
|
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file));
|
$file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($file->isEmpty()) {
|
if ($file->isEmpty()) {
|
||||||
|
|
@ -669,11 +669,10 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||||
* However as with chunk upload even if we are updating, we are essentially creating a file
|
* However as with chunk upload even if we are updating, we are essentially creating a file
|
||||||
* adding it's new chunk so we validate create permission instead of update
|
* adding it's new chunk so we validate create permission instead of update
|
||||||
*/
|
*/
|
||||||
$validator = new Authorization(Database::PERMISSION_CREATE);
|
if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) {
|
||||||
if (!$validator->isValid($bucket->getCreate())) {
|
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file));
|
$file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -690,7 +689,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||||
->dynamic($file, Response::MODEL_FILE);
|
->dynamic($file, Response::MODEL_FILE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/storage/buckets/:bucketId/files')
|
Http::get('/v1/storage/buckets/:bucketId/files')
|
||||||
->alias('/v1/storage/files', ['bucketId' => 'default'])
|
->alias('/v1/storage/files', ['bucketId' => 'default'])
|
||||||
->desc('List files')
|
->desc('List files')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
|
|
@ -708,19 +707,19 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('mode')
|
->inject('mode')
|
||||||
->action(function (string $bucketId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) {
|
->inject('authorization')
|
||||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
->action(function (string $bucketId, array $queries, string $search, Response $response, Database $dbForProject, string $mode, Authorization $authorization) {
|
||||||
|
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||||
|
|
||||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
$isAPIKey = Auth::isAppUser($authorization->getRoles());
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
|
|
||||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||||
$validator = new Authorization(Database::PERMISSION_READ);
|
$valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
|
||||||
$valid = $validator->isValid($bucket->getRead());
|
|
||||||
if (!$fileSecurity && !$valid) {
|
if (!$fileSecurity && !$valid) {
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
@ -749,7 +748,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
||||||
if ($fileSecurity && !$valid) {
|
if ($fileSecurity && !$valid) {
|
||||||
$cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
$cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||||
} else {
|
} else {
|
||||||
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
$cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($cursorDocument->isEmpty()) {
|
if ($cursorDocument->isEmpty()) {
|
||||||
|
|
@ -765,8 +764,8 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
||||||
$files = $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries);
|
$files = $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries);
|
||||||
$total = $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT);
|
$total = $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT);
|
||||||
} else {
|
} else {
|
||||||
$files = Authorization::skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries));
|
$files = $authorization->skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries));
|
||||||
$total = Authorization::skip(fn () => $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT));
|
$total = $authorization->skip(fn () => $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT));
|
||||||
}
|
}
|
||||||
|
|
||||||
$response->dynamic(new Document([
|
$response->dynamic(new Document([
|
||||||
|
|
@ -775,7 +774,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
||||||
]), Response::MODEL_FILE_LIST);
|
]), Response::MODEL_FILE_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/storage/buckets/:bucketId/files/:fileId')
|
Http::get('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||||
->alias('/v1/storage/files/:fileId', ['bucketId' => 'default'])
|
->alias('/v1/storage/files/:fileId', ['bucketId' => 'default'])
|
||||||
->desc('Get file')
|
->desc('Get file')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
|
|
@ -792,19 +791,19 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('mode')
|
->inject('mode')
|
||||||
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, string $mode) {
|
->inject('authorization')
|
||||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, string $mode, Authorization $authorization) {
|
||||||
|
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||||
|
|
||||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
$isAPIKey = Auth::isAppUser($authorization->getRoles());
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
|
|
||||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||||
$validator = new Authorization(Database::PERMISSION_READ);
|
$valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
|
||||||
$valid = $validator->isValid($bucket->getRead());
|
|
||||||
if (!$fileSecurity && !$valid) {
|
if (!$fileSecurity && !$valid) {
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
@ -812,7 +811,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||||
if ($fileSecurity && !$valid) {
|
if ($fileSecurity && !$valid) {
|
||||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||||
} else {
|
} else {
|
||||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
$file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($file->isEmpty()) {
|
if ($file->isEmpty()) {
|
||||||
|
|
@ -822,7 +821,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||||
$response->dynamic($file, Response::MODEL_FILE);
|
$response->dynamic($file, Response::MODEL_FILE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||||
->alias('/v1/storage/files/:fileId/preview', ['bucketId' => 'default'])
|
->alias('/v1/storage/files/:fileId/preview', ['bucketId' => 'default'])
|
||||||
->desc('Get file preview')
|
->desc('Get file preview')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
|
|
@ -857,24 +856,24 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||||
->inject('mode')
|
->inject('mode')
|
||||||
->inject('deviceForFiles')
|
->inject('deviceForFiles')
|
||||||
->inject('deviceForLocal')
|
->inject('deviceForLocal')
|
||||||
->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 $deviceForFiles, Device $deviceForLocal) {
|
->inject('authorization')
|
||||||
|
->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 $deviceForFiles, Device $deviceForLocal, Authorization $authorization) {
|
||||||
|
|
||||||
if (!\extension_loaded('imagick')) {
|
if (!\extension_loaded('imagick')) {
|
||||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
|
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
|
||||||
}
|
}
|
||||||
|
|
||||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||||
|
|
||||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
$isAPIKey = Auth::isAppUser($authorization->getRoles());
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
|
|
||||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||||
$validator = new Authorization(Database::PERMISSION_READ);
|
$valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
|
||||||
$valid = $validator->isValid($bucket->getRead());
|
|
||||||
if (!$fileSecurity && !$valid) {
|
if (!$fileSecurity && !$valid) {
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
@ -882,7 +881,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||||
if ($fileSecurity && !$valid) {
|
if ($fileSecurity && !$valid) {
|
||||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||||
} else {
|
} else {
|
||||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
$file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($file->isEmpty()) {
|
if ($file->isEmpty()) {
|
||||||
|
|
@ -994,7 +993,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||||
unset($image);
|
unset($image);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
Http::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||||
->alias('/v1/storage/files/:fileId/download', ['bucketId' => 'default'])
|
->alias('/v1/storage/files/:fileId/download', ['bucketId' => 'default'])
|
||||||
->desc('Get file for download')
|
->desc('Get file for download')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
|
|
@ -1013,20 +1012,20 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('mode')
|
->inject('mode')
|
||||||
->inject('deviceForFiles')
|
->inject('deviceForFiles')
|
||||||
->action(function (string $bucketId, string $fileId, Request $request, Response $response, Database $dbForProject, string $mode, Device $deviceForFiles) {
|
->inject('authorization')
|
||||||
|
->action(function (string $bucketId, string $fileId, Request $request, Response $response, Database $dbForProject, string $mode, Device $deviceForFiles, Authorization $authorization) {
|
||||||
|
|
||||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||||
|
|
||||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
$isAPIKey = Auth::isAppUser($authorization->getRoles());
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
|
|
||||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||||
$validator = new Authorization(Database::PERMISSION_READ);
|
$valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
|
||||||
$valid = $validator->isValid($bucket->getRead());
|
|
||||||
if (!$fileSecurity && !$valid) {
|
if (!$fileSecurity && !$valid) {
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
@ -1034,7 +1033,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||||
if ($fileSecurity && !$valid) {
|
if ($fileSecurity && !$valid) {
|
||||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||||
} else {
|
} else {
|
||||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
$file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($file->isEmpty()) {
|
if ($file->isEmpty()) {
|
||||||
|
|
@ -1134,7 +1133,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
Http::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||||
->alias('/v1/storage/files/:fileId/view', ['bucketId' => 'default'])
|
->alias('/v1/storage/files/:fileId/view', ['bucketId' => 'default'])
|
||||||
->desc('Get file for view')
|
->desc('Get file for view')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
|
|
@ -1153,19 +1152,19 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('mode')
|
->inject('mode')
|
||||||
->inject('deviceForFiles')
|
->inject('deviceForFiles')
|
||||||
->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, string $mode, Device $deviceForFiles) {
|
->inject('authorization')
|
||||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, string $mode, Device $deviceForFiles, Authorization $authorization) {
|
||||||
|
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||||
|
|
||||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
$isAPIKey = Auth::isAppUser($authorization->getRoles());
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
|
|
||||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||||
$validator = new Authorization(Database::PERMISSION_READ);
|
$valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
|
||||||
$valid = $validator->isValid($bucket->getRead());
|
|
||||||
if (!$fileSecurity && !$valid) {
|
if (!$fileSecurity && !$valid) {
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
@ -1173,7 +1172,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||||
if ($fileSecurity && !$valid) {
|
if ($fileSecurity && !$valid) {
|
||||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||||
} else {
|
} else {
|
||||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
$file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($file->isEmpty()) {
|
if ($file->isEmpty()) {
|
||||||
|
|
@ -1286,7 +1285,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
Http::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||||
->desc('Get file for push notification')
|
->desc('Get file for push notification')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
|
|
@ -1302,8 +1301,9 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('mode')
|
->inject('mode')
|
||||||
->inject('deviceForFiles')
|
->inject('deviceForFiles')
|
||||||
->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Document $project, string $mode, Device $deviceForFiles) {
|
->inject('authorization')
|
||||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Document $project, string $mode, Device $deviceForFiles, Authorization $authorization) {
|
||||||
|
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||||
|
|
||||||
$decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
|
$decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
|
||||||
|
|
||||||
|
|
@ -1321,14 +1321,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
$isAPIKey = Auth::isAppUser($authorization->getRoles());
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
|
|
||||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
$file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||||
|
|
||||||
if ($file->isEmpty()) {
|
if ($file->isEmpty()) {
|
||||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||||
|
|
@ -1439,7 +1439,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
Http::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||||
->alias('/v1/storage/files/:fileId', ['bucketId' => 'default'])
|
->alias('/v1/storage/files/:fileId', ['bucketId' => 'default'])
|
||||||
->desc('Update file')
|
->desc('Update file')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
|
|
@ -1466,26 +1466,26 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||||
->inject('user')
|
->inject('user')
|
||||||
->inject('mode')
|
->inject('mode')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents) {
|
->inject('authorization')
|
||||||
|
->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents, Authorization $authorization) {
|
||||||
|
|
||||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||||
|
|
||||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
$isAPIKey = Auth::isAppUser($authorization->getRoles());
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
|
|
||||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||||
$validator = new Authorization(Database::PERMISSION_UPDATE);
|
$valid = $authorization->isValid(new Input(Database::PERMISSION_UPDATE, $bucket->getUpdate()));
|
||||||
$valid = $validator->isValid($bucket->getUpdate());
|
|
||||||
if (!$fileSecurity && !$valid) {
|
if (!$fileSecurity && !$valid) {
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read permission should not be required for update
|
// 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()) {
|
if ($file->isEmpty()) {
|
||||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||||
|
|
@ -1499,7 +1499,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Users can only manage their own roles, API keys and Admin users can manage any
|
// Users can only manage their own roles, API keys and Admin users can manage any
|
||||||
$roles = Authorization::getRoles();
|
$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 (Database::PERMISSIONS as $type) {
|
||||||
foreach ($permissions as $permission) {
|
foreach ($permissions as $permission) {
|
||||||
|
|
@ -1512,7 +1512,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||||
$permission->getIdentifier(),
|
$permission->getIdentifier(),
|
||||||
$permission->getDimension()
|
$permission->getDimension()
|
||||||
))->toString();
|
))->toString();
|
||||||
if (!Authorization::isRole($role)) {
|
if (!$authorization->isRole($role)) {
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
|
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1532,7 +1532,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||||
if ($fileSecurity && !$valid) {
|
if ($fileSecurity && !$valid) {
|
||||||
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
|
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
|
||||||
} else {
|
} else {
|
||||||
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file));
|
$file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file));
|
||||||
}
|
}
|
||||||
|
|
||||||
$queueForEvents
|
$queueForEvents
|
||||||
|
|
@ -1544,8 +1544,8 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||||
$response->dynamic($file, Response::MODEL_FILE);
|
$response->dynamic($file, Response::MODEL_FILE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
Http::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||||
->desc('Delete file')
|
->desc('Delete File')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
->label('scope', 'files.write')
|
->label('scope', 'files.write')
|
||||||
->label('event', 'buckets.[bucketId].files.[fileId].delete')
|
->label('event', 'buckets.[bucketId].files.[fileId].delete')
|
||||||
|
|
@ -1568,32 +1568,32 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||||
->inject('mode')
|
->inject('mode')
|
||||||
->inject('deviceForFiles')
|
->inject('deviceForFiles')
|
||||||
->inject('queueForDeletes')
|
->inject('queueForDeletes')
|
||||||
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes) {
|
->inject('authorization')
|
||||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes, Authorization $authorization) {
|
||||||
|
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||||
|
|
||||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
$isAPIKey = Auth::isAppUser($authorization->getRoles());
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
|
|
||||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||||
$validator = new Authorization(Database::PERMISSION_DELETE);
|
$valid = $authorization->isValid(new Input(Database::PERMISSION_DELETE, $bucket->getDelete()));
|
||||||
$valid = $validator->isValid($bucket->getDelete());
|
|
||||||
if (!$fileSecurity && !$valid) {
|
if (!$fileSecurity && !$valid) {
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read permission should not be required for delete
|
// 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()) {
|
if ($file->isEmpty()) {
|
||||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we don't delete the file before the document permission check occurs
|
// Make sure we don't delete the file before the document permission check occurs
|
||||||
if ($fileSecurity && !$valid && !$validator->isValid($file->getDelete())) {
|
if ($fileSecurity && !$valid && !$authorization->isValid(new Input(Database::PERMISSION_DELETE, $file->getDelete()))) {
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1617,7 +1617,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||||
if ($fileSecurity && !$valid) {
|
if ($fileSecurity && !$valid) {
|
||||||
$deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
$deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||||
} else {
|
} 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) {
|
||||||
|
|
@ -1637,7 +1637,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/storage/usage')
|
Http::get('/v1/storage/usage')
|
||||||
->desc('Get storage usage stats')
|
->desc('Get storage usage stats')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
->label('scope', 'files.read')
|
->label('scope', 'files.read')
|
||||||
|
|
@ -1650,7 +1650,8 @@ App::get('/v1/storage/usage')
|
||||||
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
|
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->action(function (string $range, Response $response, Database $dbForProject) {
|
->inject('authorization')
|
||||||
|
->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) {
|
||||||
|
|
||||||
$periods = Config::getParam('usage', []);
|
$periods = Config::getParam('usage', []);
|
||||||
$stats = $usage = [];
|
$stats = $usage = [];
|
||||||
|
|
@ -1662,7 +1663,7 @@ App::get('/v1/storage/usage')
|
||||||
];
|
];
|
||||||
|
|
||||||
$total = [];
|
$total = [];
|
||||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
|
$authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
|
||||||
foreach ($metrics as $metric) {
|
foreach ($metrics as $metric) {
|
||||||
$result = $dbForProject->findOne('stats', [
|
$result = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
|
|
@ -1716,7 +1717,7 @@ App::get('/v1/storage/usage')
|
||||||
]), Response::MODEL_USAGE_STORAGE);
|
]), Response::MODEL_USAGE_STORAGE);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/storage/:bucketId/usage')
|
Http::get('/v1/storage/:bucketId/usage')
|
||||||
->desc('Get bucket usage stats')
|
->desc('Get bucket usage stats')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
->label('scope', 'files.read')
|
->label('scope', 'files.read')
|
||||||
|
|
@ -1730,7 +1731,8 @@ App::get('/v1/storage/:bucketId/usage')
|
||||||
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
|
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->action(function (string $bucketId, string $range, Response $response, Database $dbForProject) {
|
->inject('authorization')
|
||||||
|
->action(function (string $bucketId, string $range, Response $response, Database $dbForProject, Authorization $authorization) {
|
||||||
|
|
||||||
$bucket = $dbForProject->getDocument('buckets', $bucketId);
|
$bucket = $dbForProject->getDocument('buckets', $bucketId);
|
||||||
|
|
||||||
|
|
@ -1746,8 +1748,7 @@ App::get('/v1/storage/:bucketId/usage')
|
||||||
str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE),
|
str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
|
||||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
|
|
||||||
foreach ($metrics as $metric) {
|
foreach ($metrics as $metric) {
|
||||||
$result = $dbForProject->findOne('stats', [
|
$result = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Appwrite\Auth\Auth;
|
use Appwrite\Auth\Auth;
|
||||||
|
use Appwrite\Auth\Authentication;
|
||||||
use Appwrite\Auth\MFA\Type\TOTP;
|
use Appwrite\Auth\MFA\Type\TOTP;
|
||||||
use Appwrite\Auth\Validator\Phone;
|
use Appwrite\Auth\Validator\Phone;
|
||||||
use Appwrite\Detector\Detector;
|
use Appwrite\Detector\Detector;
|
||||||
|
|
@ -18,7 +19,6 @@ use Appwrite\Utopia\Database\Validator\Queries\Teams;
|
||||||
use Appwrite\Utopia\Request;
|
use Appwrite\Utopia\Request;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use MaxMind\Db\Reader;
|
use MaxMind\Db\Reader;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Audit\Audit;
|
use Utopia\Audit\Audit;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
|
|
@ -37,15 +37,16 @@ use Utopia\Database\Validator\Queries;
|
||||||
use Utopia\Database\Validator\Query\Limit;
|
use Utopia\Database\Validator\Query\Limit;
|
||||||
use Utopia\Database\Validator\Query\Offset;
|
use Utopia\Database\Validator\Query\Offset;
|
||||||
use Utopia\Database\Validator\UID;
|
use Utopia\Database\Validator\UID;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Validator\ArrayList;
|
||||||
|
use Utopia\Http\Validator\Assoc;
|
||||||
|
use Utopia\Http\Validator\Host;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
|
use Utopia\Http\Validator\WhiteList;
|
||||||
use Utopia\Locale\Locale;
|
use Utopia\Locale\Locale;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\ArrayList;
|
|
||||||
use Utopia\Validator\Assoc;
|
|
||||||
use Utopia\Validator\Host;
|
|
||||||
use Utopia\Validator\Text;
|
|
||||||
use Utopia\Validator\WhiteList;
|
|
||||||
|
|
||||||
App::post('/v1/teams')
|
Http::post('/v1/teams')
|
||||||
->desc('Create team')
|
->desc('Create team')
|
||||||
->groups(['api', 'teams'])
|
->groups(['api', 'teams'])
|
||||||
->label('event', 'teams.[teamId].create')
|
->label('event', 'teams.[teamId].create')
|
||||||
|
|
@ -66,15 +67,16 @@ App::post('/v1/teams')
|
||||||
->inject('user')
|
->inject('user')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
|
->inject('authorization')
|
||||||
|
->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) {
|
||||||
|
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
$isAppUser = Auth::isAppUser($authorization->getRoles());
|
||||||
|
|
||||||
$teamId = $teamId == 'unique()' ? ID::unique() : $teamId;
|
$teamId = $teamId == 'unique()' ? ID::unique() : $teamId;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$team = Authorization::skip(fn () => $dbForProject->createDocument('teams', new Document([
|
$team = $authorization->skip(fn () => $dbForProject->createDocument('teams', new Document([
|
||||||
'$id' => $teamId,
|
'$id' => $teamId,
|
||||||
'$permissions' => [
|
'$permissions' => [
|
||||||
Permission::read(Role::team($teamId)),
|
Permission::read(Role::team($teamId)),
|
||||||
|
|
@ -133,7 +135,7 @@ App::post('/v1/teams')
|
||||||
->dynamic($team, Response::MODEL_TEAM);
|
->dynamic($team, Response::MODEL_TEAM);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/teams')
|
Http::get('/v1/teams')
|
||||||
->desc('List teams')
|
->desc('List teams')
|
||||||
->groups(['api', 'teams'])
|
->groups(['api', 'teams'])
|
||||||
->label('scope', 'teams.read')
|
->label('scope', 'teams.read')
|
||||||
|
|
@ -191,7 +193,7 @@ App::get('/v1/teams')
|
||||||
]), Response::MODEL_TEAM_LIST);
|
]), Response::MODEL_TEAM_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/teams/:teamId')
|
Http::get('/v1/teams/:teamId')
|
||||||
->desc('Get team')
|
->desc('Get team')
|
||||||
->groups(['api', 'teams'])
|
->groups(['api', 'teams'])
|
||||||
->label('scope', 'teams.read')
|
->label('scope', 'teams.read')
|
||||||
|
|
@ -218,7 +220,7 @@ App::get('/v1/teams/:teamId')
|
||||||
$response->dynamic($team, Response::MODEL_TEAM);
|
$response->dynamic($team, Response::MODEL_TEAM);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/teams/:teamId/prefs')
|
Http::get('/v1/teams/:teamId/prefs')
|
||||||
->desc('Get team preferences')
|
->desc('Get team preferences')
|
||||||
->groups(['api', 'teams'])
|
->groups(['api', 'teams'])
|
||||||
->label('scope', 'teams.read')
|
->label('scope', 'teams.read')
|
||||||
|
|
@ -246,7 +248,7 @@ App::get('/v1/teams/:teamId/prefs')
|
||||||
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::put('/v1/teams/:teamId')
|
Http::put('/v1/teams/:teamId')
|
||||||
->desc('Update name')
|
->desc('Update name')
|
||||||
->groups(['api', 'teams'])
|
->groups(['api', 'teams'])
|
||||||
->label('event', 'teams.[teamId].update')
|
->label('event', 'teams.[teamId].update')
|
||||||
|
|
@ -289,7 +291,7 @@ App::put('/v1/teams/:teamId')
|
||||||
$response->dynamic($team, Response::MODEL_TEAM);
|
$response->dynamic($team, Response::MODEL_TEAM);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::put('/v1/teams/:teamId/prefs')
|
Http::put('/v1/teams/:teamId/prefs')
|
||||||
->desc('Update preferences')
|
->desc('Update preferences')
|
||||||
->groups(['api', 'teams'])
|
->groups(['api', 'teams'])
|
||||||
->label('event', 'teams.[teamId].update.prefs')
|
->label('event', 'teams.[teamId].update.prefs')
|
||||||
|
|
@ -325,7 +327,7 @@ App::put('/v1/teams/:teamId/prefs')
|
||||||
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/teams/:teamId')
|
Http::delete('/v1/teams/:teamId')
|
||||||
->desc('Delete team')
|
->desc('Delete team')
|
||||||
->groups(['api', 'teams'])
|
->groups(['api', 'teams'])
|
||||||
->label('event', 'teams.[teamId].delete')
|
->label('event', 'teams.[teamId].delete')
|
||||||
|
|
@ -374,7 +376,7 @@ App::delete('/v1/teams/:teamId')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/teams/:teamId/memberships')
|
Http::post('/v1/teams/:teamId/memberships')
|
||||||
->desc('Create team membership')
|
->desc('Create team membership')
|
||||||
->groups(['api', 'teams', 'auth'])
|
->groups(['api', 'teams', 'auth'])
|
||||||
->label('event', 'teams.[teamId].memberships.[membershipId].create')
|
->label('event', 'teams.[teamId].memberships.[membershipId].create')
|
||||||
|
|
@ -416,9 +418,10 @@ App::post('/v1/teams/:teamId/memberships')
|
||||||
->inject('queueForMails')
|
->inject('queueForMails')
|
||||||
->inject('queueForMessaging')
|
->inject('queueForMessaging')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->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 $queueForMails, Messaging $queueForMessaging, Event $queueForEvents) {
|
->inject('authorization')
|
||||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
->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 $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, Authorization $authorization) {
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isAPIKey = Auth::isAppUser($authorization->getRoles());
|
||||||
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
|
|
||||||
$url = htmlentities($url);
|
$url = htmlentities($url);
|
||||||
if (empty($url)) {
|
if (empty($url)) {
|
||||||
|
|
@ -430,8 +433,8 @@ App::post('/v1/teams/:teamId/memberships')
|
||||||
if (empty($userId) && empty($email) && empty($phone)) {
|
if (empty($userId) && empty($email) && empty($phone)) {
|
||||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'At least one of userId, email, or phone is required');
|
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'At least one of userId, email, or phone is required');
|
||||||
}
|
}
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
$isAppUser = Auth::isAppUser($authorization->getRoles());
|
||||||
|
|
||||||
if (!$isPrivilegedUser && !$isAppUser && empty(System::getEnv('_APP_SMTP_HOST'))) {
|
if (!$isPrivilegedUser && !$isAppUser && empty(System::getEnv('_APP_SMTP_HOST'))) {
|
||||||
throw new Exception(Exception::GENERAL_SMTP_DISABLED);
|
throw new Exception(Exception::GENERAL_SMTP_DISABLED);
|
||||||
|
|
@ -460,17 +463,17 @@ App::post('/v1/teams/:teamId/memberships')
|
||||||
$name = empty($name) ? $invitee->getAttribute('name', '') : $name;
|
$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
|
$invitee = $dbForProject->findOne('users', [Query::equal('email', [$email])]); // Get user by email address
|
||||||
if (!empty($invitee) && !empty($phone) && $invitee->getAttribute('phone', '') !== $phone) {
|
if ($invitee->isEmpty() && !empty($phone) && $invitee->getAttribute('phone', '') !== $phone) {
|
||||||
throw new Exception(Exception::USER_ALREADY_EXISTS, 'Given email and phone doesn\'t match', 409);
|
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])]);
|
$invitee = $dbForProject->findOne('users', [Query::equal('phone', [$phone])]);
|
||||||
if (!empty($invitee) && !empty($email) && $invitee->getAttribute('email', '') !== $email) {
|
if ($invitee->isEmpty() && !empty($email) && $invitee->getAttribute('email', '') !== $email) {
|
||||||
throw new Exception(Exception::USER_ALREADY_EXISTS, 'Given phone and email doesn\'t match', 409);
|
throw new Exception(Exception::USER_ALREADY_EXISTS, 'Given phone and email doesn\'t match', 409);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($invitee)) { // Create new user if no user with same email found
|
if (!isset($invitee) || $invitee->isEmpty()) { // Create new user if no user with same email found
|
||||||
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
|
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
|
||||||
|
|
||||||
if (!$isPrivilegedUser && !$isAppUser && $limit !== 0 && $project->getId() !== 'console') { // check users limit, console invites are allways allowed.
|
if (!$isPrivilegedUser && !$isAppUser && $limit !== 0 && $project->getId() !== 'console') { // check users limit, console invites are allways allowed.
|
||||||
|
|
@ -491,7 +494,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$userId = ID::unique();
|
$userId = ID::unique();
|
||||||
$invitee = Authorization::skip(fn () => $dbForProject->createDocument('users', new Document([
|
$invitee = $authorization->skip(fn () => $dbForProject->createDocument('users', new Document([
|
||||||
'$id' => $userId,
|
'$id' => $userId,
|
||||||
'$permissions' => [
|
'$permissions' => [
|
||||||
Permission::read(Role::any()),
|
Permission::read(Role::any()),
|
||||||
|
|
@ -522,12 +525,13 @@ App::post('/v1/teams/:teamId/memberships')
|
||||||
'memberships' => null,
|
'memberships' => null,
|
||||||
'search' => implode(' ', [$userId, $email, $name]),
|
'search' => implode(' ', [$userId, $email, $name]),
|
||||||
])));
|
])));
|
||||||
|
|
||||||
} catch (Duplicate $th) {
|
} catch (Duplicate $th) {
|
||||||
throw new Exception(Exception::USER_ALREADY_EXISTS);
|
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');
|
throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to send invitations for this team');
|
||||||
|
|
@ -559,12 +563,12 @@ App::post('/v1/teams/:teamId/memberships')
|
||||||
|
|
||||||
if ($isPrivilegedUser || $isAppUser) { // Allow admin to create membership
|
if ($isPrivilegedUser || $isAppUser) { // Allow admin to create membership
|
||||||
try {
|
try {
|
||||||
$membership = Authorization::skip(fn () => $dbForProject->createDocument('memberships', $membership));
|
$membership = $authorization->skip(fn () => $dbForProject->createDocument('memberships', $membership));
|
||||||
} catch (Duplicate $th) {
|
} catch (Duplicate $th) {
|
||||||
throw new Exception(Exception::TEAM_INVITE_ALREADY_EXISTS);
|
throw new Exception(Exception::TEAM_INVITE_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
|
$authorization->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
|
||||||
|
|
||||||
$dbForProject->purgeCachedDocument('users', $invitee->getId());
|
$dbForProject->purgeCachedDocument('users', $invitee->getId());
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -586,7 +590,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||||
|
|
||||||
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-inner-base.tpl');
|
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-inner-base.tpl');
|
||||||
$message
|
$message
|
||||||
->setParam('{{body}}', $body, escapeHtml: false)
|
->setParam('{{body}}', $body, escape: false)
|
||||||
->setParam('{{hello}}', $locale->getText("emails.invitation.hello"))
|
->setParam('{{hello}}', $locale->getText("emails.invitation.hello"))
|
||||||
->setParam('{{footer}}', $locale->getText("emails.invitation.footer"))
|
->setParam('{{footer}}', $locale->getText("emails.invitation.footer"))
|
||||||
->setParam('{{thanks}}', $locale->getText("emails.invitation.thanks"))
|
->setParam('{{thanks}}', $locale->getText("emails.invitation.thanks"))
|
||||||
|
|
@ -704,7 +708,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/teams/:teamId/memberships')
|
Http::get('/v1/teams/:teamId/memberships')
|
||||||
->desc('List team memberships')
|
->desc('List team memberships')
|
||||||
->groups(['api', 'teams'])
|
->groups(['api', 'teams'])
|
||||||
->label('scope', 'teams.read')
|
->label('scope', 'teams.read')
|
||||||
|
|
@ -807,7 +811,7 @@ App::get('/v1/teams/:teamId/memberships')
|
||||||
]), Response::MODEL_MEMBERSHIP_LIST);
|
]), Response::MODEL_MEMBERSHIP_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/teams/:teamId/memberships/:membershipId')
|
Http::get('/v1/teams/:teamId/memberships/:membershipId')
|
||||||
->desc('Get team membership')
|
->desc('Get team membership')
|
||||||
->groups(['api', 'teams'])
|
->groups(['api', 'teams'])
|
||||||
->label('scope', 'teams.read')
|
->label('scope', 'teams.read')
|
||||||
|
|
@ -863,7 +867,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
|
||||||
$response->dynamic($membership, Response::MODEL_MEMBERSHIP);
|
$response->dynamic($membership, Response::MODEL_MEMBERSHIP);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
Http::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||||
->desc('Update membership')
|
->desc('Update membership')
|
||||||
->groups(['api', 'teams'])
|
->groups(['api', 'teams'])
|
||||||
->label('event', 'teams.[teamId].memberships.[membershipId].update')
|
->label('event', 'teams.[teamId].memberships.[membershipId].update')
|
||||||
|
|
@ -895,7 +899,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||||
->inject('user')
|
->inject('user')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
|
->inject('authorization')
|
||||||
|
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) {
|
||||||
|
|
||||||
$team = $dbForProject->getDocument('teams', $teamId);
|
$team = $dbForProject->getDocument('teams', $teamId);
|
||||||
if ($team->isEmpty()) {
|
if ($team->isEmpty()) {
|
||||||
|
|
@ -912,9 +917,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||||
throw new Exception(Exception::USER_NOT_FOUND);
|
throw new Exception(Exception::USER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
$isAppUser = Auth::isAppUser(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');
|
throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to modify roles');
|
||||||
|
|
@ -945,7 +950,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
Http::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||||
->desc('Update team membership status')
|
->desc('Update team membership status')
|
||||||
->groups(['api', 'teams'])
|
->groups(['api', 'teams'])
|
||||||
->label('event', 'teams.[teamId].memberships.[membershipId].update.status')
|
->label('event', 'teams.[teamId].memberships.[membershipId].update.status')
|
||||||
|
|
@ -971,7 +976,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents) {
|
->inject('authorization')
|
||||||
|
->inject('authentication')
|
||||||
|
->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents, Authorization $authorization, Authentication $authentication) {
|
||||||
$protocol = $request->getProtocol();
|
$protocol = $request->getProtocol();
|
||||||
|
|
||||||
$membership = $dbForProject->getDocument('memberships', $membershipId);
|
$membership = $dbForProject->getDocument('memberships', $membershipId);
|
||||||
|
|
@ -980,7 +987,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||||
throw new Exception(Exception::MEMBERSHIP_NOT_FOUND);
|
throw new Exception(Exception::MEMBERSHIP_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$team = Authorization::skip(fn () => $dbForProject->getDocument('teams', $teamId));
|
$team = $authorization->skip(fn () => $dbForProject->getDocument('teams', $teamId));
|
||||||
|
|
||||||
if ($team->isEmpty()) {
|
if ($team->isEmpty()) {
|
||||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||||
|
|
@ -1015,11 +1022,11 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||||
->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
|
// Log user in
|
||||||
|
|
||||||
Authorization::setRole(Role::user($user->getId())->toString());
|
$authorization->addRole(Role::user($user->getId())->toString());
|
||||||
|
|
||||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||||
$record = $geodb->get($request->getIP());
|
$record = $geodb->get($request->getIP());
|
||||||
|
|
@ -1049,13 +1056,13 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||||
|
|
||||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||||
|
|
||||||
Authorization::setRole(Role::user($userId)->toString());
|
$authorization->addRole(Role::user($userId)->toString());
|
||||||
|
|
||||||
$membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership);
|
$membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership);
|
||||||
|
|
||||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||||
|
|
||||||
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
|
$authorization->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
|
||||||
|
|
||||||
$queueForEvents
|
$queueForEvents
|
||||||
->setParam('userId', $user->getId())
|
->setParam('userId', $user->getId())
|
||||||
|
|
@ -1065,13 +1072,13 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||||
|
|
||||||
if (!Config::getParam('domainVerification')) {
|
if (!Config::getParam('domainVerification')) {
|
||||||
$response
|
$response
|
||||||
->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]))
|
->addHeader('X-Fallback-Cookies', \json_encode([$authentication->getCookieName() => Auth::encodeSession($user->getId(), $secret)]))
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
$response
|
$response
|
||||||
->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
|
->addCookie($authentication->getCookieName() . '_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($authentication->getCookieName(), Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
|
||||||
;
|
;
|
||||||
|
|
||||||
$response->dynamic(
|
$response->dynamic(
|
||||||
|
|
@ -1083,7 +1090,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
Http::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||||
->desc('Delete team membership')
|
->desc('Delete team membership')
|
||||||
->groups(['api', 'teams'])
|
->groups(['api', 'teams'])
|
||||||
->label('event', 'teams.[teamId].memberships.[membershipId].delete')
|
->label('event', 'teams.[teamId].memberships.[membershipId].delete')
|
||||||
|
|
@ -1101,7 +1108,8 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $queueForEvents) {
|
->inject('authorization')
|
||||||
|
->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) {
|
||||||
|
|
||||||
$membership = $dbForProject->getDocument('memberships', $membershipId);
|
$membership = $dbForProject->getDocument('memberships', $membershipId);
|
||||||
|
|
||||||
|
|
@ -1136,7 +1144,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||||
|
|
||||||
if ($membership->getAttribute('confirm')) { // Count only confirmed members
|
if ($membership->getAttribute('confirm')) { // Count only confirmed members
|
||||||
Authorization::skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0));
|
$authorization->skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
$queueForEvents
|
$queueForEvents
|
||||||
|
|
@ -1149,7 +1157,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/teams/:teamId/logs')
|
Http::get('/v1/teams/:teamId/logs')
|
||||||
->desc('List team logs')
|
->desc('List team logs')
|
||||||
->groups(['api', 'teams'])
|
->groups(['api', 'teams'])
|
||||||
->label('scope', 'teams.read')
|
->label('scope', 'teams.read')
|
||||||
|
|
@ -1166,7 +1174,8 @@ App::get('/v1/teams/:teamId/logs')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->action(function (string $teamId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
|
->inject('authorization')
|
||||||
|
->action(function (string $teamId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) {
|
||||||
|
|
||||||
$team = $dbForProject->getDocument('teams', $teamId);
|
$team = $dbForProject->getDocument('teams', $teamId);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ use Appwrite\Utopia\Database\Validator\Queries\Users;
|
||||||
use Appwrite\Utopia\Request;
|
use Appwrite\Utopia\Request;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use MaxMind\Db\Reader;
|
use MaxMind\Db\Reader;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Audit\Audit;
|
use Utopia\Audit\Audit;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
|
|
@ -39,15 +38,16 @@ use Utopia\Database\Validator\Queries;
|
||||||
use Utopia\Database\Validator\Query\Limit;
|
use Utopia\Database\Validator\Query\Limit;
|
||||||
use Utopia\Database\Validator\Query\Offset;
|
use Utopia\Database\Validator\Query\Offset;
|
||||||
use Utopia\Database\Validator\UID;
|
use Utopia\Database\Validator\UID;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Validator\ArrayList;
|
||||||
|
use Utopia\Http\Validator\Assoc;
|
||||||
|
use Utopia\Http\Validator\Boolean;
|
||||||
|
use Utopia\Http\Validator\Integer;
|
||||||
|
use Utopia\Http\Validator\Range;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
|
use Utopia\Http\Validator\WhiteList;
|
||||||
use Utopia\Locale\Locale;
|
use Utopia\Locale\Locale;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\ArrayList;
|
|
||||||
use Utopia\Validator\Assoc;
|
|
||||||
use Utopia\Validator\Boolean;
|
|
||||||
use Utopia\Validator\Integer;
|
|
||||||
use Utopia\Validator\Range;
|
|
||||||
use Utopia\Validator\Text;
|
|
||||||
use Utopia\Validator\WhiteList;
|
|
||||||
|
|
||||||
/** TODO: Remove function when we move to using utopia/platform */
|
/** 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 $queueForEvents, Hooks $hooks): Document
|
function createUser(string $hash, mixed $hashOptions, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks): Document
|
||||||
|
|
@ -63,7 +63,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||||
$identityWithMatchingEmail = $dbForProject->findOne('identities', [
|
$identityWithMatchingEmail = $dbForProject->findOne('identities', [
|
||||||
Query::equal('providerEmail', [$email]),
|
Query::equal('providerEmail', [$email]),
|
||||||
]);
|
]);
|
||||||
if ($identityWithMatchingEmail !== false && !$identityWithMatchingEmail->isEmpty()) {
|
if (!$identityWithMatchingEmail->isEmpty()) {
|
||||||
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
|
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -140,7 +140,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||||
$existingTarget = $dbForProject->findOne('targets', [
|
$existingTarget = $dbForProject->findOne('targets', [
|
||||||
Query::equal('identifier', [$email]),
|
Query::equal('identifier', [$email]),
|
||||||
]);
|
]);
|
||||||
if ($existingTarget) {
|
if (!$existingTarget->isEmpty()) {
|
||||||
$user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
|
$user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -164,7 +164,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||||
$existingTarget = $dbForProject->findOne('targets', [
|
$existingTarget = $dbForProject->findOne('targets', [
|
||||||
Query::equal('identifier', [$phone]),
|
Query::equal('identifier', [$phone]),
|
||||||
]);
|
]);
|
||||||
if ($existingTarget) {
|
if (!$existingTarget->isEmpty()) {
|
||||||
$user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
|
$user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -180,7 +180,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
App::post('/v1/users')
|
Http::post('/v1/users')
|
||||||
->desc('Create user')
|
->desc('Create user')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].create')
|
->label('event', 'users.[userId].create')
|
||||||
|
|
@ -211,7 +211,7 @@ App::post('/v1/users')
|
||||||
->dynamic($user, Response::MODEL_USER);
|
->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/users/bcrypt')
|
Http::post('/v1/users/bcrypt')
|
||||||
->desc('Create user with bcrypt password')
|
->desc('Create user with bcrypt password')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].create')
|
->label('event', 'users.[userId].create')
|
||||||
|
|
@ -242,7 +242,7 @@ App::post('/v1/users/bcrypt')
|
||||||
->dynamic($user, Response::MODEL_USER);
|
->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/users/md5')
|
Http::post('/v1/users/md5')
|
||||||
->desc('Create user with MD5 password')
|
->desc('Create user with MD5 password')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].create')
|
->label('event', 'users.[userId].create')
|
||||||
|
|
@ -273,7 +273,7 @@ App::post('/v1/users/md5')
|
||||||
->dynamic($user, Response::MODEL_USER);
|
->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/users/argon2')
|
Http::post('/v1/users/argon2')
|
||||||
->desc('Create user with Argon2 password')
|
->desc('Create user with Argon2 password')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].create')
|
->label('event', 'users.[userId].create')
|
||||||
|
|
@ -304,7 +304,7 @@ App::post('/v1/users/argon2')
|
||||||
->dynamic($user, Response::MODEL_USER);
|
->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/users/sha')
|
Http::post('/v1/users/sha')
|
||||||
->desc('Create user with SHA password')
|
->desc('Create user with SHA password')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].create')
|
->label('event', 'users.[userId].create')
|
||||||
|
|
@ -342,7 +342,7 @@ App::post('/v1/users/sha')
|
||||||
->dynamic($user, Response::MODEL_USER);
|
->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/users/phpass')
|
Http::post('/v1/users/phpass')
|
||||||
->desc('Create user with PHPass password')
|
->desc('Create user with PHPass password')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].create')
|
->label('event', 'users.[userId].create')
|
||||||
|
|
@ -373,7 +373,7 @@ App::post('/v1/users/phpass')
|
||||||
->dynamic($user, Response::MODEL_USER);
|
->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/users/scrypt')
|
Http::post('/v1/users/scrypt')
|
||||||
->desc('Create user with Scrypt password')
|
->desc('Create user with Scrypt password')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].create')
|
->label('event', 'users.[userId].create')
|
||||||
|
|
@ -417,7 +417,7 @@ App::post('/v1/users/scrypt')
|
||||||
->dynamic($user, Response::MODEL_USER);
|
->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/users/scrypt-modified')
|
Http::post('/v1/users/scrypt-modified')
|
||||||
->desc('Create user with Scrypt modified password')
|
->desc('Create user with Scrypt modified password')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].create')
|
->label('event', 'users.[userId].create')
|
||||||
|
|
@ -451,7 +451,7 @@ App::post('/v1/users/scrypt-modified')
|
||||||
->dynamic($user, Response::MODEL_USER);
|
->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/users/:userId/targets')
|
Http::post('/v1/users/:userId/targets')
|
||||||
->desc('Create user target')
|
->desc('Create user target')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('audits.event', 'target.create')
|
->label('audits.event', 'target.create')
|
||||||
|
|
@ -540,7 +540,7 @@ App::post('/v1/users/:userId/targets')
|
||||||
->dynamic($target, Response::MODEL_TARGET);
|
->dynamic($target, Response::MODEL_TARGET);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/users')
|
Http::get('/v1/users')
|
||||||
->desc('List users')
|
->desc('List users')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('scope', 'users.read')
|
->label('scope', 'users.read')
|
||||||
|
|
@ -594,7 +594,7 @@ App::get('/v1/users')
|
||||||
]), Response::MODEL_USER_LIST);
|
]), Response::MODEL_USER_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/users/:userId')
|
Http::get('/v1/users/:userId')
|
||||||
->desc('Get user')
|
->desc('Get user')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('scope', 'users.read')
|
->label('scope', 'users.read')
|
||||||
|
|
@ -619,7 +619,7 @@ App::get('/v1/users/:userId')
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/users/:userId/prefs')
|
Http::get('/v1/users/:userId/prefs')
|
||||||
->desc('Get user preferences')
|
->desc('Get user preferences')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('scope', 'users.read')
|
->label('scope', 'users.read')
|
||||||
|
|
@ -646,7 +646,7 @@ App::get('/v1/users/:userId/prefs')
|
||||||
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/users/:userId/targets/:targetId')
|
Http::get('/v1/users/:userId/targets/:targetId')
|
||||||
->desc('Get user target')
|
->desc('Get user target')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('scope', 'targets.read')
|
->label('scope', 'targets.read')
|
||||||
|
|
@ -678,7 +678,7 @@ App::get('/v1/users/:userId/targets/:targetId')
|
||||||
$response->dynamic($target, Response::MODEL_TARGET);
|
$response->dynamic($target, Response::MODEL_TARGET);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/users/:userId/sessions')
|
Http::get('/v1/users/:userId/sessions')
|
||||||
->desc('List user sessions')
|
->desc('List user sessions')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('scope', 'users.read')
|
->label('scope', 'users.read')
|
||||||
|
|
@ -719,7 +719,7 @@ App::get('/v1/users/:userId/sessions')
|
||||||
]), Response::MODEL_SESSION_LIST);
|
]), Response::MODEL_SESSION_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/users/:userId/memberships')
|
Http::get('/v1/users/:userId/memberships')
|
||||||
->desc('List user memberships')
|
->desc('List user memberships')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('scope', 'users.read')
|
->label('scope', 'users.read')
|
||||||
|
|
@ -758,7 +758,7 @@ App::get('/v1/users/:userId/memberships')
|
||||||
]), Response::MODEL_MEMBERSHIP_LIST);
|
]), Response::MODEL_MEMBERSHIP_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/users/:userId/logs')
|
Http::get('/v1/users/:userId/logs')
|
||||||
->desc('List user logs')
|
->desc('List user logs')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('scope', 'users.read')
|
->label('scope', 'users.read')
|
||||||
|
|
@ -775,7 +775,8 @@ App::get('/v1/users/:userId/logs')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->action(function (string $userId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
|
->inject('authorization')
|
||||||
|
->action(function (string $userId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) {
|
||||||
|
|
||||||
$user = $dbForProject->getDocument('users', $userId);
|
$user = $dbForProject->getDocument('users', $userId);
|
||||||
|
|
||||||
|
|
@ -847,7 +848,7 @@ App::get('/v1/users/:userId/logs')
|
||||||
]), Response::MODEL_LOG_LIST);
|
]), Response::MODEL_LOG_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/users/:userId/targets')
|
Http::get('/v1/users/:userId/targets')
|
||||||
->desc('List user targets')
|
->desc('List user targets')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('scope', 'targets.read')
|
->label('scope', 'targets.read')
|
||||||
|
|
@ -902,7 +903,7 @@ App::get('/v1/users/:userId/targets')
|
||||||
]), Response::MODEL_TARGET_LIST);
|
]), Response::MODEL_TARGET_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/users/identities')
|
Http::get('/v1/users/identities')
|
||||||
->desc('List identities')
|
->desc('List identities')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('scope', 'users.read')
|
->label('scope', 'users.read')
|
||||||
|
|
@ -956,7 +957,7 @@ App::get('/v1/users/identities')
|
||||||
]), Response::MODEL_IDENTITY_LIST);
|
]), Response::MODEL_IDENTITY_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/users/:userId/status')
|
Http::patch('/v1/users/:userId/status')
|
||||||
->desc('Update user status')
|
->desc('Update user status')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].update.status')
|
->label('event', 'users.[userId].update.status')
|
||||||
|
|
@ -992,7 +993,7 @@ App::patch('/v1/users/:userId/status')
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::put('/v1/users/:userId/labels')
|
Http::put('/v1/users/:userId/labels')
|
||||||
->desc('Update user labels')
|
->desc('Update user labels')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].update.labels')
|
->label('event', 'users.[userId].update.labels')
|
||||||
|
|
@ -1029,7 +1030,7 @@ App::put('/v1/users/:userId/labels')
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/users/:userId/verification/phone')
|
Http::patch('/v1/users/:userId/verification/phone')
|
||||||
->desc('Update phone verification')
|
->desc('Update phone verification')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].update.verification')
|
->label('event', 'users.[userId].update.verification')
|
||||||
|
|
@ -1064,7 +1065,7 @@ App::patch('/v1/users/:userId/verification/phone')
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/users/:userId/name')
|
Http::patch('/v1/users/:userId/name')
|
||||||
->desc('Update name')
|
->desc('Update name')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].update.name')
|
->label('event', 'users.[userId].update.name')
|
||||||
|
|
@ -1101,7 +1102,7 @@ App::patch('/v1/users/:userId/name')
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/users/:userId/password')
|
Http::patch('/v1/users/:userId/password')
|
||||||
->desc('Update password')
|
->desc('Update password')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].update.password')
|
->label('event', 'users.[userId].update.password')
|
||||||
|
|
@ -1178,7 +1179,7 @@ App::patch('/v1/users/:userId/password')
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/users/:userId/email')
|
Http::patch('/v1/users/:userId/email')
|
||||||
->desc('Update email')
|
->desc('Update email')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].update.email')
|
->label('event', 'users.[userId].update.email')
|
||||||
|
|
@ -1214,7 +1215,7 @@ App::patch('/v1/users/:userId/email')
|
||||||
Query::equal('providerEmail', [$email]),
|
Query::equal('providerEmail', [$email]),
|
||||||
Query::notEqual('userInternalId', $user->getInternalId()),
|
Query::notEqual('userInternalId', $user->getInternalId()),
|
||||||
]);
|
]);
|
||||||
if ($identityWithMatchingEmail !== false && !$identityWithMatchingEmail->isEmpty()) {
|
if (!$identityWithMatchingEmail->isEmpty()) {
|
||||||
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
|
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1222,7 +1223,7 @@ App::patch('/v1/users/:userId/email')
|
||||||
Query::equal('identifier', [$email]),
|
Query::equal('identifier', [$email]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($target instanceof Document && !$target->isEmpty()) {
|
if (!$target->isEmpty()) {
|
||||||
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1273,7 +1274,7 @@ App::patch('/v1/users/:userId/email')
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/users/:userId/phone')
|
Http::patch('/v1/users/:userId/phone')
|
||||||
->desc('Update phone')
|
->desc('Update phone')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].update.phone')
|
->label('event', 'users.[userId].update.phone')
|
||||||
|
|
@ -1312,7 +1313,7 @@ App::patch('/v1/users/:userId/phone')
|
||||||
Query::equal('identifier', [$number]),
|
Query::equal('identifier', [$number]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($target instanceof Document && !$target->isEmpty()) {
|
if (!$target->isEmpty()) {
|
||||||
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1356,7 +1357,7 @@ App::patch('/v1/users/:userId/phone')
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/users/:userId/verification')
|
Http::patch('/v1/users/:userId/verification')
|
||||||
->desc('Update email verification')
|
->desc('Update email verification')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].update.verification')
|
->label('event', 'users.[userId].update.verification')
|
||||||
|
|
@ -1391,7 +1392,7 @@ App::patch('/v1/users/:userId/verification')
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/users/:userId/prefs')
|
Http::patch('/v1/users/:userId/prefs')
|
||||||
->desc('Update user preferences')
|
->desc('Update user preferences')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].update.prefs')
|
->label('event', 'users.[userId].update.prefs')
|
||||||
|
|
@ -1424,7 +1425,7 @@ App::patch('/v1/users/:userId/prefs')
|
||||||
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/users/:userId/targets/:targetId')
|
Http::patch('/v1/users/:userId/targets/:targetId')
|
||||||
->desc('Update user target')
|
->desc('Update user target')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('audits.event', 'target.update')
|
->label('audits.event', 'target.update')
|
||||||
|
|
@ -1518,7 +1519,7 @@ App::patch('/v1/users/:userId/targets/:targetId')
|
||||||
->dynamic($target, Response::MODEL_TARGET);
|
->dynamic($target, Response::MODEL_TARGET);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/users/:userId/mfa')
|
Http::patch('/v1/users/:userId/mfa')
|
||||||
->desc('Update MFA')
|
->desc('Update MFA')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].update.mfa')
|
->label('event', 'users.[userId].update.mfa')
|
||||||
|
|
@ -1556,7 +1557,7 @@ App::patch('/v1/users/:userId/mfa')
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/users/:userId/mfa/factors')
|
Http::get('/v1/users/:userId/mfa/factors')
|
||||||
->desc('List factors')
|
->desc('List factors')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('scope', 'users.read')
|
->label('scope', 'users.read')
|
||||||
|
|
@ -1589,7 +1590,7 @@ App::get('/v1/users/:userId/mfa/factors')
|
||||||
$response->dynamic($factors, Response::MODEL_MFA_FACTORS);
|
$response->dynamic($factors, Response::MODEL_MFA_FACTORS);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/users/:userId/mfa/recovery-codes')
|
Http::get('/v1/users/:userId/mfa/recovery-codes')
|
||||||
->desc('Get MFA recovery codes')
|
->desc('Get MFA recovery codes')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('scope', 'users.read')
|
->label('scope', 'users.read')
|
||||||
|
|
@ -1624,7 +1625,7 @@ App::get('/v1/users/:userId/mfa/recovery-codes')
|
||||||
$response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES);
|
$response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/users/:userId/mfa/recovery-codes')
|
Http::patch('/v1/users/:userId/mfa/recovery-codes')
|
||||||
->desc('Create MFA recovery codes')
|
->desc('Create MFA recovery codes')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].create.mfa.recovery-codes')
|
->label('event', 'users.[userId].create.mfa.recovery-codes')
|
||||||
|
|
@ -1670,7 +1671,7 @@ App::patch('/v1/users/:userId/mfa/recovery-codes')
|
||||||
$response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES);
|
$response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::put('/v1/users/:userId/mfa/recovery-codes')
|
Http::put('/v1/users/:userId/mfa/recovery-codes')
|
||||||
->desc('Regenerate MFA recovery codes')
|
->desc('Regenerate MFA recovery codes')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].update.mfa.recovery-codes')
|
->label('event', 'users.[userId].update.mfa.recovery-codes')
|
||||||
|
|
@ -1715,7 +1716,7 @@ App::put('/v1/users/:userId/mfa/recovery-codes')
|
||||||
$response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES);
|
$response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/users/:userId/mfa/authenticators/:type')
|
Http::delete('/v1/users/:userId/mfa/authenticators/:type')
|
||||||
->desc('Delete authenticator')
|
->desc('Delete authenticator')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].delete.mfa')
|
->label('event', 'users.[userId].delete.mfa')
|
||||||
|
|
@ -1757,7 +1758,7 @@ App::delete('/v1/users/:userId/mfa/authenticators/:type')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/users/:userId/sessions')
|
Http::post('/v1/users/:userId/sessions')
|
||||||
->desc('Create session')
|
->desc('Create session')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||||
|
|
@ -1827,7 +1828,7 @@ App::post('/v1/users/:userId/sessions')
|
||||||
->dynamic($session, Response::MODEL_SESSION);
|
->dynamic($session, Response::MODEL_SESSION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/users/:userId/tokens')
|
Http::post('/v1/users/:userId/tokens')
|
||||||
->desc('Create token')
|
->desc('Create token')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].tokens.[tokenId].create')
|
->label('event', 'users.[userId].tokens.[tokenId].create')
|
||||||
|
|
@ -1884,7 +1885,7 @@ App::post('/v1/users/:userId/tokens')
|
||||||
->dynamic($token, Response::MODEL_TOKEN);
|
->dynamic($token, Response::MODEL_TOKEN);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/users/:userId/sessions/:sessionId')
|
Http::delete('/v1/users/:userId/sessions/:sessionId')
|
||||||
->desc('Delete user session')
|
->desc('Delete user session')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].sessions.[sessionId].delete')
|
->label('event', 'users.[userId].sessions.[sessionId].delete')
|
||||||
|
|
@ -1927,7 +1928,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/users/:userId/sessions')
|
Http::delete('/v1/users/:userId/sessions')
|
||||||
->desc('Delete user sessions')
|
->desc('Delete user sessions')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].sessions.delete')
|
->label('event', 'users.[userId].sessions.delete')
|
||||||
|
|
@ -1969,7 +1970,7 @@ App::delete('/v1/users/:userId/sessions')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/users/:userId')
|
Http::delete('/v1/users/:userId')
|
||||||
->desc('Delete user')
|
->desc('Delete user')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].delete')
|
->label('event', 'users.[userId].delete')
|
||||||
|
|
@ -2011,7 +2012,7 @@ App::delete('/v1/users/:userId')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/users/:userId/targets/:targetId')
|
Http::delete('/v1/users/:userId/targets/:targetId')
|
||||||
->desc('Delete user target')
|
->desc('Delete user target')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('audits.event', 'target.delete')
|
->label('audits.event', 'target.delete')
|
||||||
|
|
@ -2062,7 +2063,7 @@ App::delete('/v1/users/:userId/targets/:targetId')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/users/identities/:identityId')
|
Http::delete('/v1/users/identities/:identityId')
|
||||||
->desc('Delete identity')
|
->desc('Delete identity')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('event', 'users.[userId].identities.[identityId].delete')
|
->label('event', 'users.[userId].identities.[identityId].delete')
|
||||||
|
|
@ -2097,7 +2098,7 @@ App::delete('/v1/users/identities/:identityId')
|
||||||
return $response->noContent();
|
return $response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/users/:userId/jwts')
|
Http::post('/v1/users/:userId/jwts')
|
||||||
->desc('Create user JWT')
|
->desc('Create user JWT')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('scope', 'users.write')
|
->label('scope', 'users.write')
|
||||||
|
|
@ -2147,7 +2148,7 @@ App::post('/v1/users/:userId/jwts')
|
||||||
])]), Response::MODEL_JWT);
|
])]), Response::MODEL_JWT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/users/usage')
|
Http::get('/v1/users/usage')
|
||||||
->desc('Get users usage stats')
|
->desc('Get users usage stats')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('scope', 'users.read')
|
->label('scope', 'users.read')
|
||||||
|
|
@ -2160,8 +2161,8 @@ App::get('/v1/users/usage')
|
||||||
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
|
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('register')
|
->inject('authorization')
|
||||||
->action(function (string $range, Response $response, Database $dbForProject) {
|
->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) {
|
||||||
|
|
||||||
$periods = Config::getParam('usage', []);
|
$periods = Config::getParam('usage', []);
|
||||||
$stats = $usage = [];
|
$stats = $usage = [];
|
||||||
|
|
@ -2171,7 +2172,7 @@ App::get('/v1/users/usage')
|
||||||
METRIC_SESSIONS,
|
METRIC_SESSIONS,
|
||||||
];
|
];
|
||||||
|
|
||||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
$authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||||
foreach ($metrics as $count => $metric) {
|
foreach ($metrics as $count => $metric) {
|
||||||
$result = $dbForProject->findOne('stats', [
|
$result = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ use Appwrite\Utopia\Database\Validator\Queries\Installations;
|
||||||
use Appwrite\Utopia\Request;
|
use Appwrite\Utopia\Request;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Appwrite\Vcs\Comment;
|
use Appwrite\Vcs\Comment;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
|
|
@ -32,17 +31,17 @@ use Utopia\Detector\Adapter\Python;
|
||||||
use Utopia\Detector\Adapter\Ruby;
|
use Utopia\Detector\Adapter\Ruby;
|
||||||
use Utopia\Detector\Adapter\Swift;
|
use Utopia\Detector\Adapter\Swift;
|
||||||
use Utopia\Detector\Detector;
|
use Utopia\Detector\Detector;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Validator\Boolean;
|
||||||
|
use Utopia\Http\Validator\Host;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\Boolean;
|
|
||||||
use Utopia\Validator\Host;
|
|
||||||
use Utopia\Validator\Text;
|
|
||||||
use Utopia\VCS\Adapter\Git\GitHub;
|
use Utopia\VCS\Adapter\Git\GitHub;
|
||||||
use Utopia\VCS\Exception\RepositoryNotFound;
|
use Utopia\VCS\Exception\RepositoryNotFound;
|
||||||
|
|
||||||
use function Swoole\Coroutine\batch;
|
use function Swoole\Coroutine\batch;
|
||||||
|
|
||||||
$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForConsole, Build $queueForBuilds, callable $getProjectDB, Request $request) {
|
$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForConsole, Build $queueForBuilds, callable $getProjectDB, Request $request, Authorization $auth) {
|
||||||
$errors = [];
|
|
||||||
foreach ($repositories as $resource) {
|
foreach ($repositories as $resource) {
|
||||||
try {
|
try {
|
||||||
$resourceType = $resource->getAttribute('resourceType');
|
$resourceType = $resource->getAttribute('resourceType');
|
||||||
|
|
@ -52,11 +51,11 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||||
}
|
}
|
||||||
|
|
||||||
$projectId = $resource->getAttribute('projectId');
|
$projectId = $resource->getAttribute('projectId');
|
||||||
$project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId));
|
$project = $auth->skip(fn () => $dbForConsole->getDocument('projects', $projectId));
|
||||||
$dbForProject = $getProjectDB($project);
|
$dbForProject = $getProjectDB($project);
|
||||||
|
|
||||||
$functionId = $resource->getAttribute('resourceId');
|
$functionId = $resource->getAttribute('resourceId');
|
||||||
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
$function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
||||||
$functionInternalId = $function->getInternalId();
|
$functionInternalId = $function->getInternalId();
|
||||||
|
|
||||||
$deploymentId = ID::unique();
|
$deploymentId = ID::unique();
|
||||||
|
|
@ -102,14 +101,14 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||||
|
|
||||||
$latestCommentId = '';
|
$latestCommentId = '';
|
||||||
|
|
||||||
if (!empty($providerPullRequestId) && $function->getAttribute('providerSilentMode', false) === false) {
|
if (!empty($providerPullRequestId) && $function->getAttribute('providerSilentMode', false)) {
|
||||||
$latestComment = Authorization::skip(fn () => $dbForConsole->findOne('vcsComments', [
|
$latestComment = $auth->skip(fn () => $dbForConsole->findOne('vcsComments', [
|
||||||
Query::equal('providerRepositoryId', [$providerRepositoryId]),
|
Query::equal('providerRepositoryId', [$providerRepositoryId]),
|
||||||
Query::equal('providerPullRequestId', [$providerPullRequestId]),
|
Query::equal('providerPullRequestId', [$providerPullRequestId]),
|
||||||
Query::orderDesc('$createdAt'),
|
Query::orderDesc('$createdAt'),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
if ($latestComment !== false && !$latestComment->isEmpty()) {
|
if (!$latestComment->isEmpty()) {
|
||||||
$latestCommentId = $latestComment->getAttribute('providerCommentId', '');
|
$latestCommentId = $latestComment->getAttribute('providerCommentId', '');
|
||||||
$comment = new Comment();
|
$comment = new Comment();
|
||||||
$comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId));
|
$comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId));
|
||||||
|
|
@ -124,7 +123,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||||
if (!empty($latestCommentId)) {
|
if (!empty($latestCommentId)) {
|
||||||
$teamId = $project->getAttribute('teamId', '');
|
$teamId = $project->getAttribute('teamId', '');
|
||||||
|
|
||||||
$latestComment = Authorization::skip(fn () => $dbForConsole->createDocument('vcsComments', new Document([
|
$latestComment = $auth->skip(fn () => $dbForConsole->createDocument('vcsComments', new Document([
|
||||||
'$id' => ID::unique(),
|
'$id' => ID::unique(),
|
||||||
'$permissions' => [
|
'$permissions' => [
|
||||||
Permission::read(Role::team(ID::custom($teamId))),
|
Permission::read(Role::team(ID::custom($teamId))),
|
||||||
|
|
@ -145,7 +144,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif (!empty($providerBranch)) {
|
} elseif (!empty($providerBranch)) {
|
||||||
$latestComments = Authorization::skip(fn () => $dbForConsole->find('vcsComments', [
|
$latestComments = $auth->skip(fn () => $dbForConsole->find('vcsComments', [
|
||||||
Query::equal('providerRepositoryId', [$providerRepositoryId]),
|
Query::equal('providerRepositoryId', [$providerRepositoryId]),
|
||||||
Query::equal('providerBranch', [$providerBranch]),
|
Query::equal('providerBranch', [$providerBranch]),
|
||||||
Query::orderDesc('$createdAt'),
|
Query::orderDesc('$createdAt'),
|
||||||
|
|
@ -262,8 +261,8 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
App::get('/v1/vcs/github/authorize')
|
Http::get('/v1/vcs/github/authorize')
|
||||||
->desc('Install GitHub app')
|
->desc('Install GitHub App')
|
||||||
->groups(['api', 'vcs'])
|
->groups(['api', 'vcs'])
|
||||||
->label('scope', 'vcs.read')
|
->label('scope', 'vcs.read')
|
||||||
->label('sdk.namespace', 'vcs')
|
->label('sdk.namespace', 'vcs')
|
||||||
|
|
@ -304,8 +303,8 @@ App::get('/v1/vcs/github/authorize')
|
||||||
->redirect($url);
|
->redirect($url);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/vcs/github/callback')
|
Http::get('/v1/vcs/github/callback')
|
||||||
->desc('Capture installation and authorization from GitHub app')
|
->desc('Capture installation and authorization from GitHub App')
|
||||||
->groups(['api', 'vcs'])
|
->groups(['api', 'vcs'])
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||||
|
|
@ -370,7 +369,7 @@ App::get('/v1/vcs/github/callback')
|
||||||
$identity = $dbForConsole->findOne('identities', [
|
$identity = $dbForConsole->findOne('identities', [
|
||||||
Query::equal('providerEmail', [$email]),
|
Query::equal('providerEmail', [$email]),
|
||||||
]);
|
]);
|
||||||
if ($identity !== false && !$identity->isEmpty()) {
|
if (!$identity->isEmpty()) {
|
||||||
if ($identity->getAttribute('userInternalId', '') !== $user->getInternalId()) {
|
if ($identity->getAttribute('userInternalId', '') !== $user->getInternalId()) {
|
||||||
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
|
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
@ -417,7 +416,7 @@ App::get('/v1/vcs/github/callback')
|
||||||
Query::equal('projectInternalId', [$projectInternalId])
|
Query::equal('projectInternalId', [$projectInternalId])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($installation === false || $installation->isEmpty()) {
|
if ($installation->isEmpty()) {
|
||||||
$teamId = $project->getAttribute('teamId', '');
|
$teamId = $project->getAttribute('teamId', '');
|
||||||
|
|
||||||
$installation = new Document([
|
$installation = new Document([
|
||||||
|
|
@ -464,7 +463,7 @@ App::get('/v1/vcs/github/callback')
|
||||||
->redirect($redirectSuccess);
|
->redirect($redirectSuccess);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/contents')
|
Http::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/contents')
|
||||||
->desc('Get files and directories of a VCS repository')
|
->desc('Get files and directories of a VCS repository')
|
||||||
->groups(['api', 'vcs'])
|
->groups(['api', 'vcs'])
|
||||||
->label('scope', 'vcs.read')
|
->label('scope', 'vcs.read')
|
||||||
|
|
@ -525,7 +524,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||||
]), Response::MODEL_VCS_CONTENT_LIST);
|
]), Response::MODEL_VCS_CONTENT_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection')
|
Http::post('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection')
|
||||||
->desc('Detect runtime settings from source code')
|
->desc('Detect runtime settings from source code')
|
||||||
->groups(['api', 'vcs'])
|
->groups(['api', 'vcs'])
|
||||||
->label('scope', 'vcs.write')
|
->label('scope', 'vcs.write')
|
||||||
|
|
@ -597,8 +596,8 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:pr
|
||||||
$response->dynamic(new Document($detection), Response::MODEL_DETECTION);
|
$response->dynamic(new Document($detection), Response::MODEL_DETECTION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
|
Http::get('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||||
->desc('List repositories')
|
->desc('List Repositories')
|
||||||
->groups(['api', 'vcs'])
|
->groups(['api', 'vcs'])
|
||||||
->label('scope', 'vcs.read')
|
->label('scope', 'vcs.read')
|
||||||
->label('sdk.namespace', 'vcs')
|
->label('sdk.namespace', 'vcs')
|
||||||
|
|
@ -692,7 +691,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||||
]), Response::MODEL_PROVIDER_REPOSITORY_LIST);
|
]), Response::MODEL_PROVIDER_REPOSITORY_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/vcs/github/installations/:installationId/providerRepositories')
|
Http::post('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||||
->desc('Create repository')
|
->desc('Create repository')
|
||||||
->groups(['api', 'vcs'])
|
->groups(['api', 'vcs'])
|
||||||
->label('scope', 'vcs.write')
|
->label('scope', 'vcs.write')
|
||||||
|
|
@ -725,7 +724,7 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||||
Query::equal('provider', ['github']),
|
Query::equal('provider', ['github']),
|
||||||
Query::equal('userInternalId', [$user->getInternalId()]),
|
Query::equal('userInternalId', [$user->getInternalId()]),
|
||||||
]);
|
]);
|
||||||
if ($identity === false || $identity->isEmpty()) {
|
if ($identity->isEmpty()) {
|
||||||
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
|
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -793,7 +792,7 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||||
$response->dynamic(new Document($repository), Response::MODEL_PROVIDER_REPOSITORY);
|
$response->dynamic(new Document($repository), Response::MODEL_PROVIDER_REPOSITORY);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId')
|
Http::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId')
|
||||||
->desc('Get repository')
|
->desc('Get repository')
|
||||||
->groups(['api', 'vcs'])
|
->groups(['api', 'vcs'])
|
||||||
->label('scope', 'vcs.read')
|
->label('scope', 'vcs.read')
|
||||||
|
|
@ -842,8 +841,8 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||||
$response->dynamic(new Document($repository), Response::MODEL_PROVIDER_REPOSITORY);
|
$response->dynamic(new Document($repository), Response::MODEL_PROVIDER_REPOSITORY);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/branches')
|
Http::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/branches')
|
||||||
->desc('List repository branches')
|
->desc('List Repository Branches')
|
||||||
->groups(['api', 'vcs'])
|
->groups(['api', 'vcs'])
|
||||||
->label('scope', 'vcs.read')
|
->label('scope', 'vcs.read')
|
||||||
->label('sdk.namespace', 'vcs')
|
->label('sdk.namespace', 'vcs')
|
||||||
|
|
@ -891,8 +890,8 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||||
]), Response::MODEL_BRANCH_LIST);
|
]), Response::MODEL_BRANCH_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/vcs/github/events')
|
Http::post('/v1/vcs/github/events')
|
||||||
->desc('Create event')
|
->desc('Create Event')
|
||||||
->groups(['api', 'vcs'])
|
->groups(['api', 'vcs'])
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
->inject('gitHub')
|
->inject('gitHub')
|
||||||
|
|
@ -901,8 +900,9 @@ App::post('/v1/vcs/github/events')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->inject('getProjectDB')
|
->inject('getProjectDB')
|
||||||
->inject('queueForBuilds')
|
->inject('queueForBuilds')
|
||||||
|
->inject('authorization')
|
||||||
->action(
|
->action(
|
||||||
function (GitHub $github, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds) use ($createGitDeployments) {
|
function (GitHub $github, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds, Authorization $authorization) use ($createGitDeployments) {
|
||||||
$payload = $request->getRawPayload();
|
$payload = $request->getRawPayload();
|
||||||
$signatureRemote = $request->getHeader('x-hub-signature-256', '');
|
$signatureRemote = $request->getHeader('x-hub-signature-256', '');
|
||||||
$signatureLocal = System::getEnv('_APP_VCS_GITHUB_WEBHOOK_SECRET', '');
|
$signatureLocal = System::getEnv('_APP_VCS_GITHUB_WEBHOOK_SECRET', '');
|
||||||
|
|
@ -936,14 +936,14 @@ App::post('/v1/vcs/github/events')
|
||||||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||||
|
|
||||||
//find functionId from functions table
|
//find functionId from functions table
|
||||||
$repositories = Authorization::skip(fn () => $dbForConsole->find('repositories', [
|
$repositories = $authorization->skip(fn () => $dbForConsole->find('repositories', [
|
||||||
Query::equal('providerRepositoryId', [$providerRepositoryId]),
|
Query::equal('providerRepositoryId', [$providerRepositoryId]),
|
||||||
Query::limit(100),
|
Query::limit(100),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
// create new deployment only on push and not when branch is created
|
// create new deployment only on push and not when branch is created
|
||||||
if (!$providerBranchCreated) {
|
if (!$providerBranchCreated) {
|
||||||
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForConsole, $queueForBuilds, $getProjectDB, $request);
|
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $authorization);
|
||||||
}
|
}
|
||||||
} elseif ($event == $github::EVENT_INSTALLATION) {
|
} elseif ($event == $github::EVENT_INSTALLATION) {
|
||||||
if ($parsedPayload["action"] == "deleted") {
|
if ($parsedPayload["action"] == "deleted") {
|
||||||
|
|
@ -956,13 +956,13 @@ App::post('/v1/vcs/github/events')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
foreach ($installations as $installation) {
|
foreach ($installations as $installation) {
|
||||||
$repositories = Authorization::skip(fn () => $dbForConsole->find('repositories', [
|
$repositories = $authorization->skip(fn () => $dbForConsole->find('repositories', [
|
||||||
Query::equal('installationInternalId', [$installation->getInternalId()]),
|
Query::equal('installationInternalId', [$installation->getInternalId()]),
|
||||||
Query::limit(1000)
|
Query::limit(1000)
|
||||||
]));
|
]));
|
||||||
|
|
||||||
foreach ($repositories as $repository) {
|
foreach ($repositories as $repository) {
|
||||||
Authorization::skip(fn () => $dbForConsole->deleteDocument('repositories', $repository->getId()));
|
$authorization->skip(fn () => $dbForConsole->deleteDocument('repositories', $repository->getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
$dbForConsole->deleteDocument('installations', $installation->getId());
|
$dbForConsole->deleteDocument('installations', $installation->getId());
|
||||||
|
|
@ -994,12 +994,12 @@ App::post('/v1/vcs/github/events')
|
||||||
$providerCommitAuthor = $commitDetails["commitAuthor"] ?? '';
|
$providerCommitAuthor = $commitDetails["commitAuthor"] ?? '';
|
||||||
$providerCommitMessage = $commitDetails["commitMessage"] ?? '';
|
$providerCommitMessage = $commitDetails["commitMessage"] ?? '';
|
||||||
|
|
||||||
$repositories = Authorization::skip(fn () => $dbForConsole->find('repositories', [
|
$repositories = $authorization->skip(fn () => $dbForConsole->find('repositories', [
|
||||||
Query::equal('providerRepositoryId', [$providerRepositoryId]),
|
Query::equal('providerRepositoryId', [$providerRepositoryId]),
|
||||||
Query::orderDesc('$createdAt')
|
Query::orderDesc('$createdAt')
|
||||||
]));
|
]));
|
||||||
|
|
||||||
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForConsole, $queueForBuilds, $getProjectDB, $request);
|
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $authorization);
|
||||||
} elseif ($parsedPayload["action"] == "closed") {
|
} elseif ($parsedPayload["action"] == "closed") {
|
||||||
// Allowed external contributions cleanup
|
// Allowed external contributions cleanup
|
||||||
|
|
||||||
|
|
@ -1008,7 +1008,7 @@ App::post('/v1/vcs/github/events')
|
||||||
$external = $parsedPayload["external"] ?? true;
|
$external = $parsedPayload["external"] ?? true;
|
||||||
|
|
||||||
if ($external) {
|
if ($external) {
|
||||||
$repositories = Authorization::skip(fn () => $dbForConsole->find('repositories', [
|
$repositories = $authorization->skip(fn () => $dbForConsole->find('repositories', [
|
||||||
Query::equal('providerRepositoryId', [$providerRepositoryId]),
|
Query::equal('providerRepositoryId', [$providerRepositoryId]),
|
||||||
Query::orderDesc('$createdAt')
|
Query::orderDesc('$createdAt')
|
||||||
]));
|
]));
|
||||||
|
|
@ -1019,7 +1019,7 @@ App::post('/v1/vcs/github/events')
|
||||||
if (\in_array($providerPullRequestId, $providerPullRequestIds)) {
|
if (\in_array($providerPullRequestId, $providerPullRequestIds)) {
|
||||||
$providerPullRequestIds = \array_diff($providerPullRequestIds, [$providerPullRequestId]);
|
$providerPullRequestIds = \array_diff($providerPullRequestIds, [$providerPullRequestId]);
|
||||||
$repository = $repository->setAttribute('providerPullRequestIds', $providerPullRequestIds);
|
$repository = $repository->setAttribute('providerPullRequestIds', $providerPullRequestIds);
|
||||||
$repository = Authorization::skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository));
|
$repository = $authorization->skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1030,7 +1030,7 @@ App::post('/v1/vcs/github/events')
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
App::get('/v1/vcs/installations')
|
Http::get('/v1/vcs/installations')
|
||||||
->desc('List installations')
|
->desc('List installations')
|
||||||
->groups(['api', 'vcs'])
|
->groups(['api', 'vcs'])
|
||||||
->label('scope', 'vcs.read')
|
->label('scope', 'vcs.read')
|
||||||
|
|
@ -1090,7 +1090,7 @@ App::get('/v1/vcs/installations')
|
||||||
]), Response::MODEL_INSTALLATION_LIST);
|
]), Response::MODEL_INSTALLATION_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/vcs/installations/:installationId')
|
Http::get('/v1/vcs/installations/:installationId')
|
||||||
->desc('Get installation')
|
->desc('Get installation')
|
||||||
->groups(['api', 'vcs'])
|
->groups(['api', 'vcs'])
|
||||||
->label('scope', 'vcs.read')
|
->label('scope', 'vcs.read')
|
||||||
|
|
@ -1119,8 +1119,8 @@ App::get('/v1/vcs/installations/:installationId')
|
||||||
$response->dynamic($installation, Response::MODEL_INSTALLATION);
|
$response->dynamic($installation, Response::MODEL_INSTALLATION);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::delete('/v1/vcs/installations/:installationId')
|
Http::delete('/v1/vcs/installations/:installationId')
|
||||||
->desc('Delete installation')
|
->desc('Delete Installation')
|
||||||
->groups(['api', 'vcs'])
|
->groups(['api', 'vcs'])
|
||||||
->label('scope', 'vcs.write')
|
->label('scope', 'vcs.write')
|
||||||
->label('sdk.namespace', 'vcs')
|
->label('sdk.namespace', 'vcs')
|
||||||
|
|
@ -1152,7 +1152,7 @@ App::delete('/v1/vcs/installations/:installationId')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositoryId')
|
Http::patch('/v1/vcs/github/installations/:installationId/repositories/:repositoryId')
|
||||||
->desc('Authorize external deployment')
|
->desc('Authorize external deployment')
|
||||||
->groups(['api', 'vcs'])
|
->groups(['api', 'vcs'])
|
||||||
->label('scope', 'vcs.write')
|
->label('scope', 'vcs.write')
|
||||||
|
|
@ -1172,14 +1172,15 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->inject('getProjectDB')
|
->inject('getProjectDB')
|
||||||
->inject('queueForBuilds')
|
->inject('queueForBuilds')
|
||||||
->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Request $request, Response $response, Document $project, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds) use ($createGitDeployments) {
|
->inject('authorization')
|
||||||
|
->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Request $request, Response $response, Document $project, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds, Authorization $authorization) use ($createGitDeployments) {
|
||||||
$installation = $dbForConsole->getDocument('installations', $installationId);
|
$installation = $dbForConsole->getDocument('installations', $installationId);
|
||||||
|
|
||||||
if ($installation->isEmpty()) {
|
if ($installation->isEmpty()) {
|
||||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$repository = Authorization::skip(fn () => $dbForConsole->getDocument('repositories', $repositoryId, [
|
$repository = $authorization->skip(fn () => $dbForConsole->getDocument('repositories', $repositoryId, [
|
||||||
Query::equal('projectInternalId', [$project->getInternalId()])
|
Query::equal('projectInternalId', [$project->getInternalId()])
|
||||||
]));
|
]));
|
||||||
|
|
||||||
|
|
@ -1196,7 +1197,7 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor
|
||||||
|
|
||||||
// TODO: Delete from array when PR is closed
|
// TODO: Delete from array when PR is closed
|
||||||
|
|
||||||
$repository = Authorization::skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository));
|
$repository = $authorization->skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository));
|
||||||
|
|
||||||
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
|
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
|
||||||
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
|
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
|
||||||
|
|
@ -1220,7 +1221,7 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor
|
||||||
$providerBranch = \explode(':', $pullRequestResponse['head']['label'])[1] ?? '';
|
$providerBranch = \explode(':', $pullRequestResponse['head']['label'])[1] ?? '';
|
||||||
$providerCommitHash = $pullRequestResponse['head']['sha'] ?? '';
|
$providerCommitHash = $pullRequestResponse['head']['sha'] ?? '';
|
||||||
|
|
||||||
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerCommitHash, $providerPullRequestId, true, $dbForConsole, $queueForBuilds, $getProjectDB, $request);
|
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerCommitHash, $providerPullRequestId, true, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $authorization);
|
||||||
|
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once __DIR__ . '/../init.php';
|
|
||||||
|
|
||||||
use Ahc\Jwt\JWT;
|
use Ahc\Jwt\JWT;
|
||||||
use Appwrite\Auth\Auth;
|
use Appwrite\Auth\Auth;
|
||||||
use Appwrite\Event\Certificate;
|
use Appwrite\Event\Certificate;
|
||||||
|
|
@ -9,6 +7,7 @@ use Appwrite\Event\Event;
|
||||||
use Appwrite\Event\Usage;
|
use Appwrite\Event\Usage;
|
||||||
use Appwrite\Extend\Exception as AppwriteException;
|
use Appwrite\Extend\Exception as AppwriteException;
|
||||||
use Appwrite\Network\Validator\Origin;
|
use Appwrite\Network\Validator\Origin;
|
||||||
|
use Appwrite\Utopia\Queue\Connections;
|
||||||
use Appwrite\Utopia\Request;
|
use Appwrite\Utopia\Request;
|
||||||
use Appwrite\Utopia\Request\Filters\V16 as RequestV16;
|
use Appwrite\Utopia\Request\Filters\V16 as RequestV16;
|
||||||
use Appwrite\Utopia\Request\Filters\V17 as RequestV17;
|
use Appwrite\Utopia\Request\Filters\V17 as RequestV17;
|
||||||
|
|
@ -20,8 +19,6 @@ use Appwrite\Utopia\Response\Filters\V18 as ResponseV18;
|
||||||
use Appwrite\Utopia\View;
|
use Appwrite\Utopia\View;
|
||||||
use Executor\Executor;
|
use Executor\Executor;
|
||||||
use MaxMind\Db\Reader;
|
use MaxMind\Db\Reader;
|
||||||
use Swoole\Http\Request as SwooleRequest;
|
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
|
|
@ -31,33 +28,35 @@ use Utopia\Database\Query;
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\Database\Validator\Authorization;
|
||||||
use Utopia\Domains\Domain;
|
use Utopia\Domains\Domain;
|
||||||
use Utopia\DSN\DSN;
|
use Utopia\DSN\DSN;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Route;
|
||||||
|
use Utopia\Http\Validator\Hostname;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
use Utopia\Locale\Locale;
|
use Utopia\Locale\Locale;
|
||||||
use Utopia\Logger\Adapter\Sentry;
|
use Utopia\Logger\Adapter\Sentry;
|
||||||
use Utopia\Logger\Log;
|
use Utopia\Logger\Log;
|
||||||
use Utopia\Logger\Log\User;
|
use Utopia\Logger\Log\User;
|
||||||
use Utopia\Logger\Logger;
|
use Utopia\Logger\Logger;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\Hostname;
|
|
||||||
use Utopia\Validator\Text;
|
|
||||||
|
|
||||||
Config::setParam('domainVerification', false);
|
Config::setParam('domainVerification', false);
|
||||||
Config::setParam('cookieDomain', 'localhost');
|
Config::setParam('cookieDomain', 'localhost');
|
||||||
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
|
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
|
||||||
|
|
||||||
function router(App $utopia, Database $dbForConsole, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, Usage $queueForUsage, Reader $geodb)
|
function router(Database $dbForConsole, callable $getProjectDB, Request $request, Response $response, Route $route, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Authorization $auth)
|
||||||
{
|
{
|
||||||
$utopia->getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml');
|
$route?->label('error', __DIR__ . '/../views/general/error.phtml');
|
||||||
|
|
||||||
$host = $request->getHostname() ?? '';
|
$host = $request->getHostname() ?? '';
|
||||||
|
|
||||||
$route = Authorization::skip(
|
$rule = $auth->skip(
|
||||||
fn () => $dbForConsole->find('rules', [
|
fn () => $dbForConsole->find('rules', [
|
||||||
Query::equal('domain', [$host]),
|
Query::equal('domain', [$host]),
|
||||||
Query::limit(1)
|
Query::limit(1)
|
||||||
])
|
])
|
||||||
)[0] ?? null;
|
)[0] ?? null;
|
||||||
|
|
||||||
if ($route === null) {
|
if ($rule === null) {
|
||||||
if ($host === System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) {
|
if ($host === System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) {
|
||||||
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.');
|
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.');
|
||||||
}
|
}
|
||||||
|
|
@ -73,12 +72,12 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Act as API - no Proxy logic
|
// Act as API - no Proxy logic
|
||||||
$utopia->getRoute()?->label('error', '');
|
$route?->label('error', '');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$projectId = $route->getAttribute('projectId');
|
$projectId = $rule->getAttribute('projectId');
|
||||||
$project = Authorization::skip(
|
$project = $auth->skip(
|
||||||
fn () => $dbForConsole->getDocument('projects', $projectId)
|
fn () => $dbForConsole->getDocument('projects', $projectId)
|
||||||
);
|
);
|
||||||
if (array_key_exists('proxy', $project->getAttribute('services', []))) {
|
if (array_key_exists('proxy', $project->getAttribute('services', []))) {
|
||||||
|
|
@ -89,16 +88,16 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip Appwrite Router for ACME challenge. Nessessary for certificate generation
|
// Skip Appwrite Router for ACME challenge. Nessessary for certificate generation
|
||||||
$path = ($swooleRequest->server['request_uri'] ?? '/');
|
$path = ($request->getURI() ?? '/');
|
||||||
if (\str_starts_with($path, '/.well-known/acme-challenge')) {
|
if (\str_starts_with($path, '/.well-known/acme-challenge')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$type = $route->getAttribute('resourceType');
|
$type = $rule->getAttribute('resourceType');
|
||||||
|
|
||||||
if ($type === 'function') {
|
if ($type === 'function') {
|
||||||
$utopia->getRoute()?->label('sdk.namespace', 'functions');
|
$route->label('sdk.namespace', 'functions');
|
||||||
$utopia->getRoute()?->label('sdk.method', 'createExecution');
|
$route->label('sdk.method', 'createExecution');
|
||||||
|
|
||||||
if (System::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
|
if (System::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
|
||||||
if ($request->getProtocol() !== 'https') {
|
if ($request->getProtocol() !== 'https') {
|
||||||
|
|
@ -110,26 +109,25 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$functionId = $route->getAttribute('resourceId');
|
$functionId = $rule->getAttribute('resourceId');
|
||||||
$projectId = $route->getAttribute('projectId');
|
$projectId = $rule->getAttribute('projectId');
|
||||||
|
|
||||||
$path = ($swooleRequest->server['request_uri'] ?? '/');
|
$path = ($request->getURI() ?? '/');
|
||||||
$query = ($swooleRequest->server['query_string'] ?? '');
|
$query = ($request->getQueryString() ?? '');
|
||||||
if (!empty($query)) {
|
if (!empty($query)) {
|
||||||
$path .= '?' . $query;
|
$path .= '?' . $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$body = $request->getRawPayload() ?? '';
|
||||||
$body = $swooleRequest->getContent() ?? '';
|
$method = $request->getMethod();
|
||||||
$method = $swooleRequest->server['request_method'];
|
|
||||||
|
|
||||||
$requestHeaders = $request->getHeaders();
|
$requestHeaders = $request->getHeaders();
|
||||||
|
|
||||||
$project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId));
|
$project = $auth->skip(fn () => $dbForConsole->getDocument('projects', $projectId));
|
||||||
|
|
||||||
$dbForProject = $getProjectDB($project);
|
$dbForProject = $getProjectDB($project);
|
||||||
|
|
||||||
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
$function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
||||||
|
|
||||||
if ($function->isEmpty() || !$function->getAttribute('enabled')) {
|
if ($function->isEmpty() || !$function->getAttribute('enabled')) {
|
||||||
throw new AppwriteException(AppwriteException::FUNCTION_NOT_FOUND);
|
throw new AppwriteException(AppwriteException::FUNCTION_NOT_FOUND);
|
||||||
|
|
@ -145,7 +143,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||||
throw new AppwriteException(AppwriteException::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported');
|
throw new AppwriteException(AppwriteException::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported');
|
||||||
}
|
}
|
||||||
|
|
||||||
$deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', '')));
|
$deployment = $auth->skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', '')));
|
||||||
|
|
||||||
if ($deployment->getAttribute('resourceId') !== $function->getId()) {
|
if ($deployment->getAttribute('resourceId') !== $function->getId()) {
|
||||||
throw new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function');
|
throw new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function');
|
||||||
|
|
@ -156,7 +154,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check if build has completed */
|
/** Check if build has completed */
|
||||||
$build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
|
$build = $auth->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
|
||||||
if ($build->isEmpty()) {
|
if ($build->isEmpty()) {
|
||||||
throw new AppwriteException(AppwriteException::BUILD_NOT_FOUND);
|
throw new AppwriteException(AppwriteException::BUILD_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
@ -217,7 +215,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||||
'deploymentInternalId' => $deployment->getInternalId(),
|
'deploymentInternalId' => $deployment->getInternalId(),
|
||||||
'deploymentId' => $deployment->getId(),
|
'deploymentId' => $deployment->getId(),
|
||||||
'trigger' => 'http', // http / schedule / event
|
'trigger' => 'http', // http / schedule / event
|
||||||
'status' => 'processing', // waiting / processing / completed / failed
|
'status' => 'processing', // waiting / processing / completed / failed
|
||||||
'responseStatusCode' => 0,
|
'responseStatusCode' => 0,
|
||||||
'responseHeaders' => [],
|
'responseHeaders' => [],
|
||||||
'requestPath' => $path,
|
'requestPath' => $path,
|
||||||
|
|
@ -313,7 +311,6 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||||
cpus: $spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT,
|
cpus: $spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT,
|
||||||
memory: $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT,
|
memory: $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT,
|
||||||
logging: $function->getAttribute('logging', true),
|
logging: $function->getAttribute('logging', true),
|
||||||
requestTimeout: 30
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$headersFiltered = [];
|
$headersFiltered = [];
|
||||||
|
|
@ -331,7 +328,6 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||||
$execution->setAttribute('logs', $executionResponse['logs']);
|
$execution->setAttribute('logs', $executionResponse['logs']);
|
||||||
$execution->setAttribute('errors', $executionResponse['errors']);
|
$execution->setAttribute('errors', $executionResponse['errors']);
|
||||||
$execution->setAttribute('duration', $executionResponse['duration']);
|
$execution->setAttribute('duration', $executionResponse['duration']);
|
||||||
|
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $th) {
|
||||||
$durationEnd = \microtime(true);
|
$durationEnd = \microtime(true);
|
||||||
|
|
||||||
|
|
@ -366,7 +362,8 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||||
->trigger()
|
->trigger()
|
||||||
;
|
;
|
||||||
|
|
||||||
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
|
/** @var Document $execution */
|
||||||
|
$execution = $auth->skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||||
}
|
}
|
||||||
|
|
||||||
$execution->setAttribute('logs', '');
|
$execution->setAttribute('logs', '');
|
||||||
|
|
@ -398,18 +395,15 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} elseif ($type === 'api') {
|
} elseif ($type === 'api') {
|
||||||
$utopia->getRoute()?->label('error', '');
|
$route?->label('error', '');
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Unknown resource type ' . $type);
|
throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Unknown resource type ' . $type);
|
||||||
}
|
}
|
||||||
|
|
||||||
$utopia->getRoute()?->label('error', '');
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
App::init()
|
Http::init()
|
||||||
->groups(['api'])
|
->groups(['api'])
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('mode')
|
->inject('mode')
|
||||||
|
|
@ -420,7 +414,7 @@ App::init()
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
|
|
||||||
App::init()
|
Http::init()
|
||||||
->groups(['database', 'functions', 'storage', 'messaging'])
|
->groups(['database', 'functions', 'storage', 'messaging'])
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('request')
|
->inject('request')
|
||||||
|
|
@ -433,12 +427,11 @@ App::init()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App::init()
|
Http::init()
|
||||||
->groups(['api', 'web'])
|
->groups(['api', 'web'])
|
||||||
->inject('utopia')
|
|
||||||
->inject('swooleRequest')
|
|
||||||
->inject('request')
|
->inject('request')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
|
->inject('route')
|
||||||
->inject('console')
|
->inject('console')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
|
|
@ -450,7 +443,27 @@ App::init()
|
||||||
->inject('queueForUsage')
|
->inject('queueForUsage')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->inject('queueForCertificates')
|
->inject('queueForCertificates')
|
||||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates) {
|
->inject('authorization')
|
||||||
|
->action(function (
|
||||||
|
Request $request,
|
||||||
|
Response $response,
|
||||||
|
Route $route,
|
||||||
|
Document $console,
|
||||||
|
Document $project,
|
||||||
|
Database $dbForConsole,
|
||||||
|
$getProjectDB,
|
||||||
|
Locale $locale,
|
||||||
|
array $localeCodes,
|
||||||
|
array $clients,
|
||||||
|
/**
|
||||||
|
* @disregard P1009 Undefined type
|
||||||
|
*/
|
||||||
|
Reader $geodb,
|
||||||
|
Usage $queueForUsage,
|
||||||
|
Event $queueForEvents,
|
||||||
|
Certificate $queueForCertificates,
|
||||||
|
Authorization $authorization
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
* Appwrite Router
|
* Appwrite Router
|
||||||
*/
|
*/
|
||||||
|
|
@ -458,7 +471,7 @@ App::init()
|
||||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||||
// Only run Router when external domain
|
// Only run Router when external domain
|
||||||
if ($host !== $mainDomain) {
|
if ($host !== $mainDomain) {
|
||||||
if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb)) {
|
if (router($dbForConsole, $getProjectDB, $request, $response, $route, $queueForEvents, $queueForUsage, $geodb, $authorization)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -466,8 +479,8 @@ App::init()
|
||||||
/*
|
/*
|
||||||
* Request format
|
* Request format
|
||||||
*/
|
*/
|
||||||
$route = $utopia->getRoute();
|
//$route = $utopia->getRoute();
|
||||||
Request::setRoute($route);
|
//Request::setRoute($route);
|
||||||
|
|
||||||
if ($route === null) {
|
if ($route === null) {
|
||||||
return $response
|
return $response
|
||||||
|
|
@ -499,7 +512,7 @@ App::init()
|
||||||
} elseif (str_starts_with($request->getURI(), '/.well-known/acme-challenge')) {
|
} elseif (str_starts_with($request->getURI(), '/.well-known/acme-challenge')) {
|
||||||
Console::warning('Skipping SSL certificates generation on ACME challenge.');
|
Console::warning('Skipping SSL certificates generation on ACME challenge.');
|
||||||
} else {
|
} else {
|
||||||
Authorization::disable();
|
$authorization->disable();
|
||||||
|
|
||||||
$envDomain = System::getEnv('_APP_DOMAIN', '');
|
$envDomain = System::getEnv('_APP_DOMAIN', '');
|
||||||
$mainDomain = null;
|
$mainDomain = null;
|
||||||
|
|
@ -507,7 +520,7 @@ App::init()
|
||||||
$mainDomain = $envDomain;
|
$mainDomain = $envDomain;
|
||||||
} else {
|
} else {
|
||||||
$domainDocument = $dbForConsole->findOne('rules', [Query::orderAsc('$id')]);
|
$domainDocument = $dbForConsole->findOne('rules', [Query::orderAsc('$id')]);
|
||||||
$mainDomain = $domainDocument ? $domainDocument->getAttribute('domain') : $domain->get();
|
$mainDomain = !$domainDocument->isEmpty() ? $domainDocument->getAttribute('domain') : $domain->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($mainDomain !== $domain->get()) {
|
if ($mainDomain !== $domain->get()) {
|
||||||
|
|
@ -517,7 +530,7 @@ App::init()
|
||||||
Query::equal('domain', [$domain->get()])
|
Query::equal('domain', [$domain->get()])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!$domainDocument) {
|
if ($domainDocument->isEmpty()) {
|
||||||
$domainDocument = new Document([
|
$domainDocument = new Document([
|
||||||
'domain' => $domain->get(),
|
'domain' => $domain->get(),
|
||||||
'resourceType' => 'api',
|
'resourceType' => 'api',
|
||||||
|
|
@ -538,12 +551,12 @@ App::init()
|
||||||
}
|
}
|
||||||
$domains[$domain->get()] = true;
|
$domains[$domain->get()] = true;
|
||||||
|
|
||||||
Authorization::reset(); // ensure authorization is re-enabled
|
$authorization->reset(); // ensure authorization is re-enabled
|
||||||
}
|
}
|
||||||
Config::setParam('domains', $domains);
|
Config::setParam('domains', $domains);
|
||||||
}
|
}
|
||||||
|
|
||||||
$localeParam = (string) $request->getParam('locale', $request->getHeader('x-appwrite-locale', ''));
|
$localeParam = (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', ''));
|
||||||
if (\in_array($localeParam, $localeCodes)) {
|
if (\in_array($localeParam, $localeCodes)) {
|
||||||
$locale->setDefault($localeParam);
|
$locale->setDefault($localeParam);
|
||||||
}
|
}
|
||||||
|
|
@ -571,7 +584,7 @@ App::init()
|
||||||
Config::setParam(
|
Config::setParam(
|
||||||
'domainVerification',
|
'domainVerification',
|
||||||
($selfDomain->getRegisterable() === $endDomain->getRegisterable()) &&
|
($selfDomain->getRegisterable() === $endDomain->getRegisterable()) &&
|
||||||
$endDomain->getRegisterable() !== ''
|
$endDomain->getRegisterable() !== ''
|
||||||
);
|
);
|
||||||
|
|
||||||
$isLocalHost = $request->getHostname() === 'localhost' || $request->getHostname() === 'localhost:' . $request->getPort();
|
$isLocalHost = $request->getHostname() === 'localhost' || $request->getHostname() === 'localhost:' . $request->getPort();
|
||||||
|
|
@ -586,8 +599,8 @@ App::init()
|
||||||
? null
|
? null
|
||||||
: (
|
: (
|
||||||
$isConsoleProject && $isConsoleRootSession
|
$isConsoleProject && $isConsoleRootSession
|
||||||
? '.' . $selfDomain->getRegisterable()
|
? '.' . $selfDomain->getRegisterable()
|
||||||
: '.' . $request->getHostname()
|
: '.' . $request->getHostname()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -606,7 +619,7 @@ App::init()
|
||||||
$response->addFilter(new ResponseV18());
|
$response->addFilter(new ResponseV18());
|
||||||
}
|
}
|
||||||
if (version_compare($responseFormat, APP_VERSION_STABLE, '>')) {
|
if (version_compare($responseFormat, APP_VERSION_STABLE, '>')) {
|
||||||
$response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is ". APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks");
|
$response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is " . APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -617,7 +630,9 @@ App::init()
|
||||||
* @see https://www.owasp.org/index.php/List_of_useful_HTTP_headers
|
* @see https://www.owasp.org/index.php/List_of_useful_HTTP_headers
|
||||||
*/
|
*/
|
||||||
if (System::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
|
if (System::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
|
||||||
if ($request->getProtocol() !== 'https' && ($swooleRequest->header['host'] ?? '') !== 'localhost' && ($swooleRequest->header['host'] ?? '') !== APP_HOSTNAME_INTERNAL) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations
|
if ($request->getProtocol() !== 'https' // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations
|
||||||
|
&& ($request->getHeader('host') ?? '') !== 'localhost'
|
||||||
|
&& ($request->getHeader('host') ?? '') !== APP_HOSTNAME_INTERNAL) {
|
||||||
if ($request->getMethod() !== Request::METHOD_GET) {
|
if ($request->getMethod() !== Request::METHOD_GET) {
|
||||||
throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.');
|
throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.');
|
||||||
}
|
}
|
||||||
|
|
@ -657,9 +672,8 @@ App::init()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App::options()
|
Http::options()
|
||||||
->inject('utopia')
|
->inject('route')
|
||||||
->inject('swooleRequest')
|
|
||||||
->inject('request')
|
->inject('request')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
|
|
@ -667,7 +681,8 @@ App::options()
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->inject('queueForUsage')
|
->inject('queueForUsage')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb) {
|
->inject('authorization')
|
||||||
|
->action(function (Route $route, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Authorization $authorization) {
|
||||||
/*
|
/*
|
||||||
* Appwrite Router
|
* Appwrite Router
|
||||||
*/
|
*/
|
||||||
|
|
@ -675,7 +690,7 @@ App::options()
|
||||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||||
// Only run Router when external domain
|
// Only run Router when external domain
|
||||||
if ($host !== $mainDomain) {
|
if ($host !== $mainDomain) {
|
||||||
if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb)) {
|
if (router($dbForConsole, $getProjectDB, $request, $response, $route, $queueForEvents, $queueForUsage, $geodb, $authorization)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -692,18 +707,25 @@ App::options()
|
||||||
->noContent();
|
->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::error()
|
Http::error()
|
||||||
->inject('error')
|
->inject('error')
|
||||||
->inject('utopia')
|
->inject('user')
|
||||||
|
->inject('route')
|
||||||
->inject('request')
|
->inject('request')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('logger')
|
->inject('logger')
|
||||||
->inject('log')
|
->inject('log')
|
||||||
|
->inject('authorization')
|
||||||
|
->inject('connections')
|
||||||
->inject('queueForUsage')
|
->inject('queueForUsage')
|
||||||
->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Usage $queueForUsage) {
|
->action(function (Throwable $error, Document $user, ?Route $route, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $authorization, Connections $connections, Usage $queueForUsage) {
|
||||||
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||||
$route = $utopia->getRoute();
|
|
||||||
|
if (is_null($route)) {
|
||||||
|
$route = new Route($request->getMethod(), $request->getURI());
|
||||||
|
}
|
||||||
|
|
||||||
$class = \get_class($error);
|
$class = \get_class($error);
|
||||||
$code = $error->getCode();
|
$code = $error->getCode();
|
||||||
$message = $error->getMessage();
|
$message = $error->getMessage();
|
||||||
|
|
@ -724,9 +746,9 @@ App::error()
|
||||||
Console::error('[Error] File: ' . $file);
|
Console::error('[Error] File: ' . $file);
|
||||||
Console::error('[Error] Line: ' . $line);
|
Console::error('[Error] Line: ' . $line);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($class) {
|
switch ($class) {
|
||||||
case 'Utopia\Exception':
|
case 'Utopia\Servers\Exception':
|
||||||
|
case 'Utopia\Http\Exception':
|
||||||
$error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error);
|
$error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error);
|
||||||
switch ($code) {
|
switch ($code) {
|
||||||
case 400:
|
case 400:
|
||||||
|
|
@ -771,35 +793,36 @@ App::error()
|
||||||
} else {
|
} else {
|
||||||
$publish = $error->getCode() === 0 || $error->getCode() >= 500;
|
$publish = $error->getCode() === 0 || $error->getCode() >= 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($error->getCode() >= 400 && $error->getCode() < 500) {
|
if ($error->getCode() >= 400 && $error->getCode() < 500) {
|
||||||
// Register error logger
|
// Register error logger
|
||||||
$providerName = System::getEnv('_APP_EXPERIMENT_LOGGING_PROVIDER', '');
|
$providerName = System::getEnv('_APP_EXPERIMENT_LOGGING_PROVIDER', '');
|
||||||
$providerConfig = System::getEnv('_APP_EXPERIMENT_LOGGING_CONFIG', '');
|
$providerConfig = System::getEnv('_APP_EXPERIMENT_LOGGING_CONFIG', '');
|
||||||
|
|
||||||
try {
|
if (!(empty($providerName) || empty($providerConfig))) {
|
||||||
$loggingProvider = new DSN($providerConfig ?? '');
|
try {
|
||||||
$providerName = $loggingProvider->getScheme();
|
$loggingProvider = new DSN($providerConfig);
|
||||||
|
$providerName = $loggingProvider->getScheme();
|
||||||
|
|
||||||
if (!empty($providerName) && $providerName === 'sentry') {
|
if (!empty($providerName) && $providerName === 'sentry') {
|
||||||
$key = $loggingProvider->getPassword();
|
$key = $loggingProvider->getPassword();
|
||||||
$projectId = $loggingProvider->getUser() ?? '';
|
$projectId = $loggingProvider->getUser() ?? '';
|
||||||
$host = 'https://' . $loggingProvider->getHost();
|
$host = 'https://' . $loggingProvider->getHost();
|
||||||
|
|
||||||
$adapter = new Sentry($projectId, $key, $host);
|
$adapter = new Sentry($projectId, $key, $host);
|
||||||
$logger = new Logger($adapter);
|
$logger = new Logger($adapter);
|
||||||
$logger->setSample(0.04);
|
$logger->setSample(0.04);
|
||||||
$publish = true;
|
$publish = true;
|
||||||
} else {
|
} else {
|
||||||
throw new \Exception('Invalid experimental logging provider');
|
throw new \Exception('Invalid experimental logging provider');
|
||||||
|
}
|
||||||
|
} catch (\Throwable $th) {
|
||||||
|
Console::warning('Failed to initialize logging provider: ' . $th->getMessage());
|
||||||
}
|
}
|
||||||
} catch (\Throwable $th) {
|
|
||||||
Console::warning('Failed to initialize logging provider: ' . $th->getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($publish && $project->getId() !== 'console') {
|
if ($publish && $project->getId() !== 'console') {
|
||||||
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
|
if (!Auth::isPrivilegedUser($authorization->getRoles())) {
|
||||||
$fileSize = 0;
|
$fileSize = 0;
|
||||||
$file = $request->getFiles('file');
|
$file = $request->getFiles('file');
|
||||||
if (!empty($file)) {
|
if (!empty($file)) {
|
||||||
|
|
@ -818,14 +841,7 @@ App::error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ($logger && $publish) {
|
if ($logger && ($publish || $error->getCode() === 0)) {
|
||||||
try {
|
|
||||||
/** @var Utopia\Database\Document $user */
|
|
||||||
$user = $utopia->getResource('user');
|
|
||||||
} catch (\Throwable) {
|
|
||||||
// All good, user is optional information for logger
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($user) && !$user->isEmpty()) {
|
if (isset($user) && !$user->isEmpty()) {
|
||||||
$log->setUser(new User($user->getId()));
|
$log->setUser(new User($user->getId()));
|
||||||
}
|
}
|
||||||
|
|
@ -855,7 +871,7 @@ App::error()
|
||||||
$log->addExtra('file', $error->getFile());
|
$log->addExtra('file', $error->getFile());
|
||||||
$log->addExtra('line', $error->getLine());
|
$log->addExtra('line', $error->getLine());
|
||||||
$log->addExtra('trace', $error->getTraceAsString());
|
$log->addExtra('trace', $error->getTraceAsString());
|
||||||
$log->addExtra('roles', Authorization::getRoles());
|
$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);
|
$log->setAction($action);
|
||||||
|
|
@ -873,7 +889,7 @@ App::error()
|
||||||
|
|
||||||
/** Wrap all exceptions inside Appwrite\Extend\Exception */
|
/** Wrap all exceptions inside Appwrite\Extend\Exception */
|
||||||
if (!($error instanceof AppwriteException)) {
|
if (!($error instanceof AppwriteException)) {
|
||||||
$error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error);
|
$error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, (int)$code, $error);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($code) { // Don't show 500 errors!
|
switch ($code) { // Don't show 500 errors!
|
||||||
|
|
@ -893,14 +909,14 @@ App::error()
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$code = 500; // All other errors get the generic 500 server error status code
|
$code = 500; // All other errors get the generic 500 server error status code
|
||||||
$message = 'Server Error';
|
$message = (Http::getMode() === Http::MODE_TYPE_DEVELOPMENT) ? $message : 'Server Error';
|
||||||
}
|
}
|
||||||
|
|
||||||
//$_SERVER = []; // Reset before reporting to error log to avoid keys being compromised
|
//$_SERVER = []; // Reset before reporting to error log to avoid keys being compromised
|
||||||
|
|
||||||
$type = $error->getType();
|
$type = $error->getType();
|
||||||
|
|
||||||
$output = ((App::isDevelopment())) ? [
|
$output = ((Http::isDevelopment())) ? [
|
||||||
'message' => $message,
|
'message' => $message,
|
||||||
'code' => $code,
|
'code' => $code,
|
||||||
'file' => $file,
|
'file' => $file,
|
||||||
|
|
@ -928,7 +944,7 @@ App::error()
|
||||||
|
|
||||||
$layout
|
$layout
|
||||||
->setParam('title', $project->getAttribute('name') . ' - Error')
|
->setParam('title', $project->getAttribute('name') . ' - Error')
|
||||||
->setParam('development', App::isDevelopment())
|
->setParam('development', Http::isDevelopment())
|
||||||
->setParam('projectName', $project->getAttribute('name'))
|
->setParam('projectName', $project->getAttribute('name'))
|
||||||
->setParam('projectURL', $project->getAttribute('url'))
|
->setParam('projectURL', $project->getAttribute('url'))
|
||||||
->setParam('message', $output['message'] ?? '')
|
->setParam('message', $output['message'] ?? '')
|
||||||
|
|
@ -939,18 +955,18 @@ App::error()
|
||||||
$response->html($layout->render());
|
$response->html($layout->render());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$connections->reclaim();
|
||||||
|
|
||||||
$response->dynamic(
|
$response->dynamic(
|
||||||
new Document($output),
|
new Document($output),
|
||||||
$utopia->isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR
|
Http::isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/robots.txt')
|
Http::get('/robots.txt')
|
||||||
->desc('Robots.txt File')
|
->desc('Robots.txt File')
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
->label('docs', false)
|
->label('docs', false)
|
||||||
->inject('utopia')
|
|
||||||
->inject('swooleRequest')
|
|
||||||
->inject('request')
|
->inject('request')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
|
|
@ -958,7 +974,9 @@ App::get('/robots.txt')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->inject('queueForUsage')
|
->inject('queueForUsage')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb) {
|
->inject('route')
|
||||||
|
->inject('authorization')
|
||||||
|
->action(function (Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, ?Route $route, Authorization $authorization) {
|
||||||
$host = $request->getHostname() ?? '';
|
$host = $request->getHostname() ?? '';
|
||||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||||
|
|
||||||
|
|
@ -966,16 +984,17 @@ App::get('/robots.txt')
|
||||||
$template = new View(__DIR__ . '/../views/general/robots.phtml');
|
$template = new View(__DIR__ . '/../views/general/robots.phtml');
|
||||||
$response->text($template->render(false));
|
$response->text($template->render(false));
|
||||||
} else {
|
} else {
|
||||||
router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb);
|
if (is_null($route)) {
|
||||||
|
$route = new Route($request->getMethod(), $request->getURI());
|
||||||
|
}
|
||||||
|
router($dbForConsole, $getProjectDB, $request, $response, $route, $queueForEvents, $queueForUsage, $geodb, $authorization);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/humans.txt')
|
Http::get('/humans.txt')
|
||||||
->desc('Humans.txt File')
|
->desc('Humans.txt File')
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
->label('docs', false)
|
->label('docs', false)
|
||||||
->inject('utopia')
|
|
||||||
->inject('swooleRequest')
|
|
||||||
->inject('request')
|
->inject('request')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
|
|
@ -983,7 +1002,9 @@ App::get('/humans.txt')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->inject('queueForUsage')
|
->inject('queueForUsage')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb) {
|
->inject('route')
|
||||||
|
->inject('authorization')
|
||||||
|
->action(function (Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Route $route, Authorization $authorization) {
|
||||||
$host = $request->getHostname() ?? '';
|
$host = $request->getHostname() ?? '';
|
||||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||||
|
|
||||||
|
|
@ -991,11 +1012,11 @@ App::get('/humans.txt')
|
||||||
$template = new View(__DIR__ . '/../views/general/humans.phtml');
|
$template = new View(__DIR__ . '/../views/general/humans.phtml');
|
||||||
$response->text($template->render(false));
|
$response->text($template->render(false));
|
||||||
} else {
|
} else {
|
||||||
router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb);
|
router($dbForConsole, $getProjectDB, $request, $response, $route, $queueForEvents, $queueForUsage, $geodb, $authorization);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/.well-known/acme-challenge/*')
|
Http::get('/.well-known/acme-challenge/*')
|
||||||
->desc('SSL Verification')
|
->desc('SSL Verification')
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
->label('docs', false)
|
->label('docs', false)
|
||||||
|
|
@ -1045,16 +1066,32 @@ App::get('/.well-known/acme-challenge/*')
|
||||||
$response->text($content);
|
$response->text($content);
|
||||||
});
|
});
|
||||||
|
|
||||||
include_once __DIR__ . '/shared/api.php';
|
Http::wildcard()
|
||||||
include_once __DIR__ . '/shared/api/auth.php';
|
|
||||||
|
|
||||||
App::wildcard()
|
|
||||||
->groups(['api'])
|
->groups(['api'])
|
||||||
->label('scope', 'global')
|
->label('scope', 'global')
|
||||||
->action(function () {
|
->action(function () {
|
||||||
throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND);
|
throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND);
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach (Config::getParam('services', []) as $service) {
|
include_once 'mock.php';
|
||||||
include_once $service['controller'];
|
include_once 'shared/api.php';
|
||||||
}
|
include_once 'shared/api/auth.php';
|
||||||
|
include_once 'api/account.php';
|
||||||
|
include_once 'api/avatars.php';
|
||||||
|
include_once 'api/console.php';
|
||||||
|
include_once 'api/databases.php';
|
||||||
|
include_once 'api/functions.php';
|
||||||
|
include_once 'api/graphql.php';
|
||||||
|
include_once 'api/health.php';
|
||||||
|
include_once 'api/locale.php';
|
||||||
|
include_once 'api/messaging.php';
|
||||||
|
include_once 'api/migrations.php';
|
||||||
|
include_once 'api/project.php';
|
||||||
|
include_once 'api/projects.php';
|
||||||
|
include_once 'api/proxy.php';
|
||||||
|
include_once 'api/storage.php';
|
||||||
|
include_once 'api/teams.php';
|
||||||
|
include_once 'api/users.php';
|
||||||
|
include_once 'api/vcs.php';
|
||||||
|
include_once 'web/console.php';
|
||||||
|
include_once 'web/home.php';
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,7 @@
|
||||||
global $utopia, $request, $response;
|
global $utopia, $request, $response;
|
||||||
|
|
||||||
use Appwrite\Extend\Exception;
|
use Appwrite\Extend\Exception;
|
||||||
use Appwrite\Utopia\Request;
|
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
|
|
@ -13,13 +11,15 @@ use Utopia\Database\Helpers\ID;
|
||||||
use Utopia\Database\Helpers\Permission;
|
use Utopia\Database\Helpers\Permission;
|
||||||
use Utopia\Database\Helpers\Role;
|
use Utopia\Database\Helpers\Role;
|
||||||
use Utopia\Database\Validator\UID;
|
use Utopia\Database\Validator\UID;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Route;
|
||||||
|
use Utopia\Http\Validator\Host;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
|
use Utopia\Http\Validator\WhiteList;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\Host;
|
|
||||||
use Utopia\Validator\Text;
|
|
||||||
use Utopia\Validator\WhiteList;
|
|
||||||
use Utopia\VCS\Adapter\Git\GitHub;
|
use Utopia\VCS\Adapter\Git\GitHub;
|
||||||
|
|
||||||
App::get('/v1/mock/tests/general/oauth2')
|
Http::get('/v1/mock/tests/general/oauth2')
|
||||||
->desc('OAuth Login')
|
->desc('OAuth Login')
|
||||||
->groups(['mock'])
|
->groups(['mock'])
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
|
|
@ -35,7 +35,7 @@ App::get('/v1/mock/tests/general/oauth2')
|
||||||
$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')
|
Http::get('/v1/mock/tests/general/oauth2/token')
|
||||||
->desc('OAuth2 Token')
|
->desc('OAuth2 Token')
|
||||||
->groups(['mock'])
|
->groups(['mock'])
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
|
|
@ -81,7 +81,7 @@ App::get('/v1/mock/tests/general/oauth2/token')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/mock/tests/general/oauth2/user')
|
Http::get('/v1/mock/tests/general/oauth2/user')
|
||||||
->desc('OAuth2 User')
|
->desc('OAuth2 User')
|
||||||
->groups(['mock'])
|
->groups(['mock'])
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
|
|
@ -101,7 +101,7 @@ App::get('/v1/mock/tests/general/oauth2/user')
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/mock/tests/general/oauth2/success')
|
Http::get('/v1/mock/tests/general/oauth2/success')
|
||||||
->desc('OAuth2 Success')
|
->desc('OAuth2 Success')
|
||||||
->groups(['mock'])
|
->groups(['mock'])
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
|
|
@ -114,7 +114,7 @@ App::get('/v1/mock/tests/general/oauth2/success')
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/mock/tests/general/oauth2/failure')
|
Http::get('/v1/mock/tests/general/oauth2/failure')
|
||||||
->desc('OAuth2 Failure')
|
->desc('OAuth2 Failure')
|
||||||
->groups(['mock'])
|
->groups(['mock'])
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
|
|
@ -129,7 +129,7 @@ App::get('/v1/mock/tests/general/oauth2/failure')
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/mock/functions-v2')
|
Http::patch('/v1/mock/functions-v2')
|
||||||
->desc('Update Function Version to V2 (outdated code syntax)')
|
->desc('Update Function Version to V2 (outdated code syntax)')
|
||||||
->groups(['mock', 'api', 'functions'])
|
->groups(['mock', 'api', 'functions'])
|
||||||
->label('scope', 'functions.write')
|
->label('scope', 'functions.write')
|
||||||
|
|
@ -155,7 +155,7 @@ App::patch('/v1/mock/functions-v2')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/mock/api-key-unprefixed')
|
Http::post('/v1/mock/api-key-unprefixed')
|
||||||
->desc('Create API Key (without standard prefix)')
|
->desc('Create API Key (without standard prefix)')
|
||||||
->groups(['mock', 'api', 'projects'])
|
->groups(['mock', 'api', 'projects'])
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
|
|
@ -204,7 +204,7 @@ App::post('/v1/mock/api-key-unprefixed')
|
||||||
->dynamic($key, Response::MODEL_KEY);
|
->dynamic($key, Response::MODEL_KEY);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/mock/github/callback')
|
Http::get('/v1/mock/github/callback')
|
||||||
->desc('Create installation document using GitHub installation id')
|
->desc('Create installation document using GitHub installation id')
|
||||||
->groups(['mock', 'api', 'vcs'])
|
->groups(['mock', 'api', 'vcs'])
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
|
|
@ -264,15 +264,13 @@ App::get('/v1/mock/github/callback')
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::shutdown()
|
Http::shutdown()
|
||||||
->groups(['mock'])
|
->groups(['mock'])
|
||||||
->inject('utopia')
|
->inject('route')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('request')
|
->action(function (Route $route, Response $response) {
|
||||||
->action(function (App $utopia, Response $response, Request $request) {
|
|
||||||
|
|
||||||
$result = [];
|
$result = [];
|
||||||
$route = $utopia->getRoute();
|
|
||||||
$path = APP_STORAGE_CACHE . '/tests.json';
|
$path = APP_STORAGE_CACHE . '/tests.json';
|
||||||
$tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : [];
|
$tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : [];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,11 @@ use Appwrite\Event\Usage;
|
||||||
use Appwrite\Extend\Exception;
|
use Appwrite\Extend\Exception;
|
||||||
use Appwrite\Extend\Exception as AppwriteException;
|
use Appwrite\Extend\Exception as AppwriteException;
|
||||||
use Appwrite\Messaging\Adapter\Realtime;
|
use Appwrite\Messaging\Adapter\Realtime;
|
||||||
|
use Appwrite\Utopia\Queue\Connections;
|
||||||
use Appwrite\Utopia\Request;
|
use Appwrite\Utopia\Request;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Utopia\Abuse\Abuse;
|
use Utopia\Abuse\Abuse;
|
||||||
use Utopia\Abuse\Adapters\Database\TimeLimit;
|
use Utopia\Abuse\Adapters\Database\TimeLimit;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Cache\Adapter\Filesystem;
|
use Utopia\Cache\Adapter\Filesystem;
|
||||||
use Utopia\Cache\Cache;
|
use Utopia\Cache\Cache;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
|
|
@ -28,8 +28,11 @@ use Utopia\Database\DateTime;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Helpers\Role;
|
use Utopia\Database\Helpers\Role;
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\Database\Validator\Authorization;
|
||||||
|
use Utopia\Database\Validator\Authorization\Input;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Route;
|
||||||
|
use Utopia\Http\Validator\WhiteList;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\WhiteList;
|
|
||||||
|
|
||||||
$parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) {
|
$parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) {
|
||||||
preg_match_all('/{(.*?)}/', $label, $matches);
|
preg_match_all('/{(.*?)}/', $label, $matches);
|
||||||
|
|
@ -150,9 +153,9 @@ $databaseListener = function (string $event, Document $document, Document $proje
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
App::init()
|
Http::init()
|
||||||
->groups(['api'])
|
->groups(['api'])
|
||||||
->inject('utopia')
|
->inject('route')
|
||||||
->inject('request')
|
->inject('request')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
|
|
@ -161,9 +164,8 @@ App::init()
|
||||||
->inject('servers')
|
->inject('servers')
|
||||||
->inject('mode')
|
->inject('mode')
|
||||||
->inject('team')
|
->inject('team')
|
||||||
->action(function (App $utopia, Request $request, Database $dbForConsole, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team) {
|
->inject('authorization')
|
||||||
$route = $utopia->getRoute();
|
->action(function (Route $route, Request $request, Database $dbForConsole, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team, Authorization $authorization) {
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
@ -221,8 +223,8 @@ App::init()
|
||||||
$role = Auth::USER_ROLE_APPS;
|
$role = Auth::USER_ROLE_APPS;
|
||||||
$scopes = \array_merge($roles[$role]['scopes'], $tokenScopes);
|
$scopes = \array_merge($roles[$role]['scopes'], $tokenScopes);
|
||||||
|
|
||||||
Authorization::setRole(Auth::USER_ROLE_APPS);
|
$authorization->addRole(Auth::USER_ROLE_APPS);
|
||||||
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
|
$authorization->setDefaultStatus(false); // Cancel security segmentation for API keys.
|
||||||
}
|
}
|
||||||
} elseif ($keyType === API_KEY_STANDARD) {
|
} elseif ($keyType === API_KEY_STANDARD) {
|
||||||
// No underline means no prefix. Backwards compatibility.
|
// No underline means no prefix. Backwards compatibility.
|
||||||
|
|
@ -247,8 +249,8 @@ App::init()
|
||||||
throw new Exception(Exception::PROJECT_KEY_EXPIRED);
|
throw new Exception(Exception::PROJECT_KEY_EXPIRED);
|
||||||
}
|
}
|
||||||
|
|
||||||
Authorization::setRole(Auth::USER_ROLE_APPS);
|
$authorization->addRole(Auth::USER_ROLE_APPS);
|
||||||
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
|
$authorization->setDefaultStatus(false); // Cancel security segmentation for API keys.
|
||||||
|
|
||||||
$accessedAt = $key->getAttribute('accessedAt', '');
|
$accessedAt = $key->getAttribute('accessedAt', '');
|
||||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
|
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
|
||||||
|
|
@ -294,15 +296,15 @@ App::init()
|
||||||
foreach ($adminRoles as $role) {
|
foreach ($adminRoles as $role) {
|
||||||
$scopes = \array_merge($scopes, $roles[$role]['scopes']);
|
$scopes = \array_merge($scopes, $roles[$role]['scopes']);
|
||||||
}
|
}
|
||||||
|
$authorization->setDefaultStatus(false); // Cancel security segmentation for admin users.
|
||||||
Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$scopes = \array_unique($scopes);
|
$scopes = \array_unique($scopes);
|
||||||
|
|
||||||
Authorization::setRole($role);
|
$authorization->addRole($role);
|
||||||
foreach (Auth::getRoles($user) as $authRole) {
|
|
||||||
Authorization::setRole($authRole);
|
foreach (Auth::getRoles($user, $authorization) as $authRole) {
|
||||||
|
$authorization->addRole($authRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Do not allow access to disabled services */
|
/** Do not allow access to disabled services */
|
||||||
|
|
@ -311,7 +313,7 @@ App::init()
|
||||||
if (
|
if (
|
||||||
array_key_exists($service, $project->getAttribute('services', []))
|
array_key_exists($service, $project->getAttribute('services', []))
|
||||||
&& !$project->getAttribute('services', [])[$service]
|
&& !$project->getAttribute('services', [])[$service]
|
||||||
&& !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles()))
|
&& !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles()))
|
||||||
) {
|
) {
|
||||||
throw new Exception(Exception::GENERAL_SERVICE_DISABLED);
|
throw new Exception(Exception::GENERAL_SERVICE_DISABLED);
|
||||||
}
|
}
|
||||||
|
|
@ -346,9 +348,9 @@ App::init()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App::init()
|
Http::init()
|
||||||
->groups(['api'])
|
->groups(['api'])
|
||||||
->inject('utopia')
|
->inject('route')
|
||||||
->inject('request')
|
->inject('request')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
|
|
@ -362,14 +364,12 @@ App::init()
|
||||||
->inject('queueForUsage')
|
->inject('queueForUsage')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('mode')
|
->inject('mode')
|
||||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, string $mode) use ($databaseListener) {
|
->inject('authorization')
|
||||||
|
->action(function (Route $route, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, string $mode, Authorization $authorization) use ($databaseListener) {
|
||||||
$route = $utopia->getRoute();
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
array_key_exists('rest', $project->getAttribute('apis', []))
|
array_key_exists('rest', $project->getAttribute('apis', []))
|
||||||
&& !$project->getAttribute('apis', [])['rest']
|
&& !$project->getAttribute('apis', [])['rest']
|
||||||
&& !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles()))
|
&& !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles()))
|
||||||
) {
|
) {
|
||||||
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
|
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
|
||||||
}
|
}
|
||||||
|
|
@ -399,7 +399,7 @@ App::init()
|
||||||
|
|
||||||
$closestLimit = null;
|
$closestLimit = null;
|
||||||
|
|
||||||
$roles = Authorization::getRoles();
|
$roles = $authorization->getRoles();
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||||
$isAppUser = Auth::isAppUser($roles);
|
$isAppUser = Auth::isAppUser($roles);
|
||||||
|
|
||||||
|
|
@ -463,7 +463,7 @@ App::init()
|
||||||
$useCache = $route->getLabel('cache', false);
|
$useCache = $route->getLabel('cache', false);
|
||||||
if ($useCache) {
|
if ($useCache) {
|
||||||
$key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
|
$key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
|
||||||
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
|
$cacheLog = $authorization->skip(fn () => $dbForProject->getDocument('cache', $key));
|
||||||
$cache = new Cache(
|
$cache = new Cache(
|
||||||
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
|
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
|
||||||
);
|
);
|
||||||
|
|
@ -476,19 +476,18 @@ App::init()
|
||||||
|
|
||||||
if ($type === 'bucket') {
|
if ($type === 'bucket') {
|
||||||
$bucketId = $parts[1] ?? null;
|
$bucketId = $parts[1] ?? null;
|
||||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
|
||||||
|
|
||||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
|
||||||
|
$isAPIKey = Auth::isAppUser($authorization->getRoles());
|
||||||
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
|
|
||||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
|
||||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||||
$validator = new Authorization(Database::PERMISSION_READ);
|
$valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
|
||||||
$valid = $validator->isValid($bucket->getRead());
|
|
||||||
|
|
||||||
if (!$fileSecurity && !$valid) {
|
if (!$fileSecurity && !$valid) {
|
||||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
@ -499,7 +498,7 @@ App::init()
|
||||||
if ($fileSecurity && !$valid) {
|
if ($fileSecurity && !$valid) {
|
||||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||||
} else {
|
} else {
|
||||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
$file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($file->isEmpty()) {
|
if ($file->isEmpty()) {
|
||||||
|
|
@ -523,7 +522,7 @@ App::init()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App::init()
|
Http::init()
|
||||||
->groups(['session'])
|
->groups(['session'])
|
||||||
->inject('user')
|
->inject('user')
|
||||||
->inject('request')
|
->inject('request')
|
||||||
|
|
@ -543,14 +542,12 @@ App::init()
|
||||||
* Delete older sessions if the number of sessions have crossed
|
* Delete older sessions if the number of sessions have crossed
|
||||||
* the session limit set for the project
|
* the session limit set for the project
|
||||||
*/
|
*/
|
||||||
App::shutdown()
|
Http::shutdown()
|
||||||
->groups(['session'])
|
->groups(['session'])
|
||||||
->inject('utopia')
|
|
||||||
->inject('request')
|
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Database $dbForProject) {
|
->action(function (Response $response, Document $project, Database $dbForProject) {
|
||||||
$sessionLimit = $project->getAttribute('auths', [])['maxSessions'] ?? APP_LIMIT_USER_SESSIONS_DEFAULT;
|
$sessionLimit = $project->getAttribute('auths', [])['maxSessions'] ?? APP_LIMIT_USER_SESSIONS_DEFAULT;
|
||||||
$session = $response->getPayload();
|
$session = $response->getPayload();
|
||||||
$userId = $session['userId'] ?? '';
|
$userId = $session['userId'] ?? '';
|
||||||
|
|
@ -577,9 +574,9 @@ App::shutdown()
|
||||||
$dbForProject->purgeCachedDocument('users', $userId);
|
$dbForProject->purgeCachedDocument('users', $userId);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::shutdown()
|
Http::shutdown()
|
||||||
->groups(['api'])
|
->groups(['api'])
|
||||||
->inject('utopia')
|
->inject('route')
|
||||||
->inject('request')
|
->inject('request')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
|
|
@ -595,7 +592,29 @@ App::shutdown()
|
||||||
->inject('queueForFunctions')
|
->inject('queueForFunctions')
|
||||||
->inject('mode')
|
->inject('mode')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Usage $queueForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Database $dbForProject, Func $queueForFunctions, string $mode, Database $dbForConsole) use ($parseLabel) {
|
->inject('authorization')
|
||||||
|
->action(function (
|
||||||
|
Route $route,
|
||||||
|
Request $request,
|
||||||
|
Response $response,
|
||||||
|
Document $project,
|
||||||
|
Document $user,
|
||||||
|
Event $queueForEvents,
|
||||||
|
Audit $queueForAudits,
|
||||||
|
Usage $queueForUsage,
|
||||||
|
Delete $queueForDeletes,
|
||||||
|
EventDatabase $queueForDatabase,
|
||||||
|
Build $queueForBuilds,
|
||||||
|
Messaging $queueForMessaging,
|
||||||
|
Database $dbForProject,
|
||||||
|
Func $queueForFunctions,
|
||||||
|
string $mode,
|
||||||
|
Database $dbForConsole,
|
||||||
|
Authorization $authorization,
|
||||||
|
) use ($parseLabel) {
|
||||||
|
if (!empty($user) && !$user->isEmpty() && empty($user->getInternalId())) {
|
||||||
|
$user = $authorization->skip(fn () => $dbForProject->getDocument('users', $user->getId()));
|
||||||
|
}
|
||||||
|
|
||||||
$responsePayload = $response->getPayload();
|
$responsePayload = $response->getPayload();
|
||||||
|
|
||||||
|
|
@ -655,7 +674,6 @@ App::shutdown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$route = $utopia->getRoute();
|
|
||||||
$requestParams = $route->getParamsValues();
|
$requestParams = $route->getParamsValues();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -725,11 +743,11 @@ App::shutdown()
|
||||||
|
|
||||||
$key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
|
$key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
|
||||||
$signature = md5($data['payload']);
|
$signature = md5($data['payload']);
|
||||||
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
|
$cacheLog = $authorization->skip(fn () => $dbForProject->getDocument('cache', $key));
|
||||||
$accessedAt = $cacheLog->getAttribute('accessedAt', '');
|
$accessedAt = $cacheLog->getAttribute('accessedAt', '');
|
||||||
$now = DateTime::now();
|
$now = DateTime::now();
|
||||||
if ($cacheLog->isEmpty()) {
|
if ($cacheLog->isEmpty()) {
|
||||||
Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([
|
$authorization->skip(fn () => $dbForProject->createDocument('cache', new Document([
|
||||||
'$id' => $key,
|
'$id' => $key,
|
||||||
'resource' => $resource,
|
'resource' => $resource,
|
||||||
'resourceType' => $resourceType,
|
'resourceType' => $resourceType,
|
||||||
|
|
@ -739,7 +757,7 @@ App::shutdown()
|
||||||
])));
|
])));
|
||||||
} elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) {
|
} elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) {
|
||||||
$cacheLog->setAttribute('accessedAt', $now);
|
$cacheLog->setAttribute('accessedAt', $now);
|
||||||
Authorization::skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog));
|
$authorization->skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($signature !== $cacheLog->getAttribute('signature')) {
|
if ($signature !== $cacheLog->getAttribute('signature')) {
|
||||||
|
|
@ -751,10 +769,8 @@ App::shutdown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if ($project->getId() !== 'console') {
|
if ($project->getId() !== 'console') {
|
||||||
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
|
if (!Auth::isPrivilegedUser($authorization->getRoles())) {
|
||||||
$fileSize = 0;
|
$fileSize = 0;
|
||||||
$file = $request->getFiles('file');
|
$file = $request->getFiles('file');
|
||||||
if (!empty($file)) {
|
if (!empty($file)) {
|
||||||
|
|
@ -779,7 +795,7 @@ App::shutdown()
|
||||||
$accessedAt = $project->getAttribute('accessedAt', '');
|
$accessedAt = $project->getAttribute('accessedAt', '');
|
||||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
|
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
|
||||||
$project->setAttribute('accessedAt', DateTime::now());
|
$project->setAttribute('accessedAt', DateTime::now());
|
||||||
Authorization::skip(fn () => $dbForConsole->updateDocument('projects', $project->getId(), $project));
|
$authorization->skip(fn () => $dbForConsole->updateDocument('projects', $project->getId(), $project));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -800,10 +816,16 @@ App::shutdown()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App::init()
|
Http::init()
|
||||||
->groups(['usage'])
|
->groups(['usage'])
|
||||||
->action(function () {
|
->action(function () {
|
||||||
if (System::getEnv('_APP_USAGE_STATS', 'enabled') !== 'enabled') {
|
if (System::getEnv('_APP_USAGE_STATS', 'enabled') !== 'enabled') {
|
||||||
throw new Exception(Exception::GENERAL_USAGE_DISABLED);
|
throw new Exception(Exception::GENERAL_USAGE_DISABLED);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Http::shutdown()
|
||||||
|
->inject('connections')
|
||||||
|
->action(function (Connections $connections) {
|
||||||
|
$connections->reclaim();
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,14 @@ use Appwrite\Auth\Auth;
|
||||||
use Appwrite\Extend\Exception;
|
use Appwrite\Extend\Exception;
|
||||||
use Appwrite\Utopia\Request;
|
use Appwrite\Utopia\Request;
|
||||||
use MaxMind\Db\Reader;
|
use MaxMind\Db\Reader;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Database\DateTime;
|
use Utopia\Database\DateTime;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\Database\Validator\Authorization;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Route;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
|
|
||||||
App::init()
|
Http::init()
|
||||||
->groups(['mfaProtected'])
|
->groups(['mfaProtected'])
|
||||||
->inject('session')
|
->inject('session')
|
||||||
->action(function (Document $session) {
|
->action(function (Document $session) {
|
||||||
|
|
@ -29,13 +30,14 @@ App::init()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
App::init()
|
Http::init()
|
||||||
->groups(['auth'])
|
->groups(['auth'])
|
||||||
->inject('utopia')
|
->inject('route')
|
||||||
->inject('request')
|
->inject('request')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->action(function (App $utopia, Request $request, Document $project, Reader $geodb) {
|
->inject('authorization')
|
||||||
|
->action(function (Route $route, Request $request, Document $project, Reader $geodb, Authorization $authorization) {
|
||||||
$denylist = System::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', '');
|
$denylist = System::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', '');
|
||||||
if (!empty($denylist && $project->getId() === 'console')) {
|
if (!empty($denylist && $project->getId() === 'console')) {
|
||||||
$countries = explode(',', $denylist);
|
$countries = explode(',', $denylist);
|
||||||
|
|
@ -46,15 +48,17 @@ App::init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$route = $utopia->match($request);
|
$isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles());
|
||||||
|
$isAppUser = Auth::isAppUser($authorization->getRoles());
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
|
||||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
|
||||||
|
|
||||||
if ($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs
|
if ($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($route->getLabel('sdk.namespace', '') === 'graphql') { // Skip for graphQL recursive call
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$auths = $project->getAttribute('auths', []);
|
$auths = $project->getAttribute('auths', []);
|
||||||
switch ($route->getLabel('auth.type', '')) {
|
switch ($route->getLabel('auth.type', '')) {
|
||||||
case 'emailPassword':
|
case 'emailPassword':
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
use Appwrite\Utopia\Request;
|
use Appwrite\Utopia\Request;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Utopia\App;
|
use Utopia\Http\Http;
|
||||||
|
|
||||||
App::init()
|
Http::init()
|
||||||
->groups(['web'])
|
->groups(['web'])
|
||||||
->inject('request')
|
->inject('request')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
|
|
@ -16,7 +16,7 @@ App::init()
|
||||||
;
|
;
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/')
|
Http::get('/')
|
||||||
->alias('auth/*')
|
->alias('auth/*')
|
||||||
->alias('/invite')
|
->alias('/invite')
|
||||||
->alias('/login')
|
->alias('/login')
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
|
||||||
App::get('/versions')
|
Http::get('/versions')
|
||||||
->desc('Get Version')
|
->desc('Get Version')
|
||||||
->groups(['home', 'web'])
|
->groups(['home', 'web'])
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
|
|
|
||||||
515
app/http.php
515
app/http.php
|
|
@ -1,335 +1,242 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once __DIR__ . '/../vendor/autoload.php';
|
require_once __DIR__ . '/init.php';
|
||||||
|
require_once __DIR__ . '/controllers/general.php';
|
||||||
|
|
||||||
|
use Appwrite\Utopia\Queue\Connections;
|
||||||
use Appwrite\Utopia\Request;
|
use Appwrite\Utopia\Request;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Swoole\Constant;
|
|
||||||
use Swoole\Http\Request as SwooleRequest;
|
|
||||||
use Swoole\Http\Response as SwooleResponse;
|
|
||||||
use Swoole\Http\Server;
|
|
||||||
use Swoole\Process;
|
|
||||||
use Utopia\Abuse\Adapters\Database\TimeLimit;
|
use Utopia\Abuse\Adapters\Database\TimeLimit;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Audit\Audit;
|
use Utopia\Audit\Audit;
|
||||||
|
use Utopia\Cache\Cache;
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
|
use Utopia\Database\Adapter\MariaDB;
|
||||||
|
use Utopia\Database\Adapter\MySQL;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Helpers\ID;
|
use Utopia\Database\Helpers\ID;
|
||||||
use Utopia\Database\Helpers\Permission;
|
use Utopia\Database\Helpers\Permission;
|
||||||
use Utopia\Database\Helpers\Role;
|
use Utopia\Database\Helpers\Role;
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\Database\Validator\Authorization;
|
||||||
use Utopia\Logger\Log;
|
use Utopia\Http\Adapter\Swoole\Server;
|
||||||
use Utopia\Logger\Log\User;
|
use Utopia\Http\Http;
|
||||||
use Utopia\Pools\Group;
|
|
||||||
use Utopia\Swoole\Files;
|
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
|
|
||||||
$http = new Server(
|
global $registry, $container;
|
||||||
host: "0.0.0.0",
|
|
||||||
port: System::getEnv('PORT', 80),
|
|
||||||
mode: SWOOLE_PROCESS,
|
|
||||||
);
|
|
||||||
|
|
||||||
$payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing
|
$payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing
|
||||||
$workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6));
|
$workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6));
|
||||||
|
|
||||||
$http
|
$server = new Server('0.0.0.0', '80', [
|
||||||
->set([
|
'open_http2_protocol' => true,
|
||||||
'worker_num' => $workerNumber,
|
'http_compression' => true,
|
||||||
'open_http2_protocol' => true,
|
'http_compression_level' => 6,
|
||||||
'http_compression' => true,
|
'package_max_length' => $payloadSize,
|
||||||
'http_compression_level' => 6,
|
'buffer_output_size' => $payloadSize,
|
||||||
'package_max_length' => $payloadSize,
|
// Server
|
||||||
'buffer_output_size' => $payloadSize,
|
// 'log_level' => 0,
|
||||||
]);
|
'dispatch_mode' => 2,
|
||||||
|
'worker_num' => $workerNumber,
|
||||||
|
'reactor_num' => swoole_cpu_num() * 2,
|
||||||
|
'open_cpu_affinity' => true,
|
||||||
|
// Coroutine
|
||||||
|
'enable_coroutine' => true,
|
||||||
|
'send_yield' => true,
|
||||||
|
'tcp_fastopen' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) {
|
$http = new Http($server, $container, 'UTC');
|
||||||
Console::success('Worker ' . ++$workerId . ' started successfully');
|
$http->setRequestClass(Request::class);
|
||||||
});
|
$http->setResponseClass(Response::class);
|
||||||
|
|
||||||
$http->on(Constant::EVENT_BEFORE_RELOAD, function ($server, $workerId) {
|
|
||||||
Console::success('Starting reload...');
|
|
||||||
});
|
|
||||||
|
|
||||||
$http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) {
|
|
||||||
Console::success('Reload completed...');
|
|
||||||
});
|
|
||||||
|
|
||||||
include __DIR__ . '/controllers/general.php';
|
|
||||||
|
|
||||||
$http->on(Constant::EVENT_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);
|
|
||||||
|
|
||||||
// wait for database to be ready
|
|
||||||
$attempts = 0;
|
|
||||||
$max = 10;
|
|
||||||
$sleep = 1;
|
|
||||||
|
|
||||||
do {
|
|
||||||
try {
|
|
||||||
$attempts++;
|
|
||||||
$dbForConsole = $app->getResource('dbForConsole');
|
|
||||||
/** @var Utopia\Database\Database $dbForConsole */
|
|
||||||
break; // leave the do-while if successful
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Console::warning("Database not ready. Retrying connection ({$attempts})...");
|
|
||||||
if ($attempts >= $max) {
|
|
||||||
throw new \Exception('Failed to connect to database: ' . $e->getMessage());
|
|
||||||
}
|
|
||||||
sleep($sleep);
|
|
||||||
}
|
|
||||||
} while ($attempts < $max);
|
|
||||||
|
|
||||||
Console::success('[Setup] - Server database init started...');
|
|
||||||
|
|
||||||
|
Http::onStart()
|
||||||
|
->inject('authorization')
|
||||||
|
->inject('cache')
|
||||||
|
->inject('pools')
|
||||||
|
->inject('connections')
|
||||||
|
->action(function (Authorization $authorization, Cache $cache, array $pools, Connections $connections) {
|
||||||
try {
|
try {
|
||||||
Console::success('[Setup] - Creating database: appwrite...');
|
// wait for database to be ready
|
||||||
$dbForConsole->create();
|
$attempts = 0;
|
||||||
|
$max = 15;
|
||||||
|
$sleep = 2;
|
||||||
|
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
$attempts++;
|
||||||
|
$pool = $pools['pools-console-console']['pool'];
|
||||||
|
$dsn = $pools['pools-console-console']['dsn'];
|
||||||
|
$connection = $pool->get();
|
||||||
|
$connections->add($connection, $pool);
|
||||||
|
|
||||||
|
$adapter = match ($dsn->getScheme()) {
|
||||||
|
'mariadb' => new MariaDB($connection),
|
||||||
|
'mysql' => new MySQL($connection),
|
||||||
|
default => null
|
||||||
|
};
|
||||||
|
|
||||||
|
$adapter->setDatabase($dsn->getPath());
|
||||||
|
|
||||||
|
$dbForConsole = new Database($adapter, $cache);
|
||||||
|
$dbForConsole->setAuthorization($authorization);
|
||||||
|
|
||||||
|
$dbForConsole
|
||||||
|
->setNamespace('_console')
|
||||||
|
->setMetadata('host', \gethostname())
|
||||||
|
->setMetadata('project', 'console')
|
||||||
|
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS);
|
||||||
|
|
||||||
|
$dbForConsole->ping();
|
||||||
|
break; // leave the do-while if successful
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Console::warning("Database not ready. Retrying connection ({$attempts})...");
|
||||||
|
if ($attempts >= $max) {
|
||||||
|
throw new \Exception('Failed to connect to database: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
sleep($sleep);
|
||||||
|
}
|
||||||
|
} while ($attempts < $max);
|
||||||
|
|
||||||
|
Console::success('[Setup] - Server database init started...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
Console::success('[Setup] - Creating database: appwrite...');
|
||||||
|
$dbForConsole->create();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Console::success('[Setup] - Skip: metadata table already exists');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) {
|
||||||
|
$audit = new Audit($dbForConsole);
|
||||||
|
$audit->setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) {
|
||||||
|
$abuse = new TimeLimit("", 0, 1, $dbForConsole);
|
||||||
|
$abuse->setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var array $collections */
|
||||||
|
$collections = Config::getParam('collections', []);
|
||||||
|
$consoleCollections = $collections['console'];
|
||||||
|
foreach ($consoleCollections as $key => $collection) {
|
||||||
|
if (($collection['$collection'] ?? '') !== Database::METADATA) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!$dbForConsole->getCollection($key)->isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...');
|
||||||
|
|
||||||
|
$attributes = [];
|
||||||
|
$indexes = [];
|
||||||
|
|
||||||
|
foreach ($collection['attributes'] as $attribute) {
|
||||||
|
$attributes[] = new Document([
|
||||||
|
'$id' => ID::custom($attribute['$id']),
|
||||||
|
'type' => $attribute['type'],
|
||||||
|
'size' => $attribute['size'],
|
||||||
|
'required' => $attribute['required'],
|
||||||
|
'signed' => $attribute['signed'],
|
||||||
|
'array' => $attribute['array'],
|
||||||
|
'filters' => $attribute['filters'],
|
||||||
|
'default' => $attribute['default'] ?? null,
|
||||||
|
'format' => $attribute['format'] ?? ''
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($collection['indexes'] as $index) {
|
||||||
|
$indexes[] = new Document([
|
||||||
|
'$id' => ID::custom($index['$id']),
|
||||||
|
'type' => $index['type'],
|
||||||
|
'attributes' => $index['attributes'],
|
||||||
|
'lengths' => $index['lengths'],
|
||||||
|
'orders' => $index['orders'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbForConsole->createCollection($key, $attributes, $indexes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDatabase(), 'bucket_1')) {
|
||||||
|
Console::success('[Setup] - Creating default bucket...');
|
||||||
|
$dbForConsole->createDocument('buckets', new Document([
|
||||||
|
'$id' => ID::custom('default'),
|
||||||
|
'$collection' => ID::custom('buckets'),
|
||||||
|
'name' => 'Default',
|
||||||
|
'maximumFileSize' => (int) System::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB
|
||||||
|
'allowedFileExtensions' => [],
|
||||||
|
'enabled' => true,
|
||||||
|
'compression' => 'gzip',
|
||||||
|
'encryption' => true,
|
||||||
|
'antivirus' => true,
|
||||||
|
'fileSecurity' => true,
|
||||||
|
'$permissions' => [
|
||||||
|
Permission::create(Role::any()),
|
||||||
|
Permission::read(Role::any()),
|
||||||
|
Permission::update(Role::any()),
|
||||||
|
Permission::delete(Role::any()),
|
||||||
|
],
|
||||||
|
'search' => 'buckets Default',
|
||||||
|
]));
|
||||||
|
|
||||||
|
$bucket = $dbForConsole->getDocument('buckets', 'default');
|
||||||
|
|
||||||
|
Console::success('[Setup] - Creating files collection for default bucket...');
|
||||||
|
|
||||||
|
$files = $collections['buckets']['files'] ?? [];
|
||||||
|
if (empty($files)) {
|
||||||
|
throw new Exception('Files collection is not configured.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$attributes = [];
|
||||||
|
$indexes = [];
|
||||||
|
|
||||||
|
foreach ($files['attributes'] as $attribute) {
|
||||||
|
$attributes[] = new Document([
|
||||||
|
'$id' => ID::custom($attribute['$id']),
|
||||||
|
'type' => $attribute['type'],
|
||||||
|
'size' => $attribute['size'],
|
||||||
|
'required' => $attribute['required'],
|
||||||
|
'signed' => $attribute['signed'],
|
||||||
|
'array' => $attribute['array'],
|
||||||
|
'filters' => $attribute['filters'],
|
||||||
|
'default' => $attribute['default'] ?? null,
|
||||||
|
'format' => $attribute['format'] ?? ''
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($files['indexes'] as $index) {
|
||||||
|
$indexes[] = new Document([
|
||||||
|
'$id' => ID::custom($index['$id']),
|
||||||
|
'type' => $index['type'],
|
||||||
|
'attributes' => $index['attributes'],
|
||||||
|
'lengths' => $index['lengths'],
|
||||||
|
'orders' => $index['orders'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes);
|
||||||
|
}
|
||||||
|
|
||||||
|
$connections->reclaim();
|
||||||
|
|
||||||
|
Console::success('[Setup] - Server database init completed...');
|
||||||
|
Console::success('Server started successfully');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
Console::success('[Setup] - Skip: metadata table already exists');
|
Console::warning('Database not ready: ' . $e->getMessage());
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) {
|
|
||||||
$audit = new Audit($dbForConsole);
|
|
||||||
$audit->setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) {
|
|
||||||
$adapter = new TimeLimit("", 0, 1, $dbForConsole);
|
|
||||||
$adapter->setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var array $collections */
|
|
||||||
$collections = Config::getParam('collections', []);
|
|
||||||
$consoleCollections = $collections['console'];
|
|
||||||
foreach ($consoleCollections as $key => $collection) {
|
|
||||||
if (($collection['$collection'] ?? '') !== Database::METADATA) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!$dbForConsole->getCollection($key)->isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...');
|
|
||||||
|
|
||||||
$attributes = [];
|
|
||||||
$indexes = [];
|
|
||||||
|
|
||||||
foreach ($collection['attributes'] as $attribute) {
|
|
||||||
$attributes[] = new Document([
|
|
||||||
'$id' => ID::custom($attribute['$id']),
|
|
||||||
'type' => $attribute['type'],
|
|
||||||
'size' => $attribute['size'],
|
|
||||||
'required' => $attribute['required'],
|
|
||||||
'signed' => $attribute['signed'],
|
|
||||||
'array' => $attribute['array'],
|
|
||||||
'filters' => $attribute['filters'],
|
|
||||||
'default' => $attribute['default'] ?? null,
|
|
||||||
'format' => $attribute['format'] ?? ''
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($collection['indexes'] as $index) {
|
|
||||||
$indexes[] = new Document([
|
|
||||||
'$id' => ID::custom($index['$id']),
|
|
||||||
'type' => $index['type'],
|
|
||||||
'attributes' => $index['attributes'],
|
|
||||||
'lengths' => $index['lengths'],
|
|
||||||
'orders' => $index['orders'],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$dbForConsole->createCollection($key, $attributes, $indexes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDatabase(), 'bucket_1')) {
|
|
||||||
Console::success('[Setup] - Creating default bucket...');
|
|
||||||
$dbForConsole->createDocument('buckets', new Document([
|
|
||||||
'$id' => ID::custom('default'),
|
|
||||||
'$collection' => ID::custom('buckets'),
|
|
||||||
'name' => 'Default',
|
|
||||||
'maximumFileSize' => (int) System::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB
|
|
||||||
'allowedFileExtensions' => [],
|
|
||||||
'enabled' => true,
|
|
||||||
'compression' => 'gzip',
|
|
||||||
'encryption' => true,
|
|
||||||
'antivirus' => true,
|
|
||||||
'fileSecurity' => true,
|
|
||||||
'$permissions' => [
|
|
||||||
Permission::create(Role::any()),
|
|
||||||
Permission::read(Role::any()),
|
|
||||||
Permission::update(Role::any()),
|
|
||||||
Permission::delete(Role::any()),
|
|
||||||
],
|
|
||||||
'search' => 'buckets Default',
|
|
||||||
]));
|
|
||||||
|
|
||||||
$bucket = $dbForConsole->getDocument('buckets', 'default');
|
|
||||||
|
|
||||||
Console::success('[Setup] - Creating files collection for default bucket...');
|
|
||||||
$files = $collections['buckets']['files'] ?? [];
|
|
||||||
if (empty($files)) {
|
|
||||||
throw new Exception('Files collection is not configured.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$attributes = [];
|
|
||||||
$indexes = [];
|
|
||||||
|
|
||||||
foreach ($files['attributes'] as $attribute) {
|
|
||||||
$attributes[] = new Document([
|
|
||||||
'$id' => ID::custom($attribute['$id']),
|
|
||||||
'type' => $attribute['type'],
|
|
||||||
'size' => $attribute['size'],
|
|
||||||
'required' => $attribute['required'],
|
|
||||||
'signed' => $attribute['signed'],
|
|
||||||
'array' => $attribute['array'],
|
|
||||||
'filters' => $attribute['filters'],
|
|
||||||
'default' => $attribute['default'] ?? null,
|
|
||||||
'format' => $attribute['format'] ?? ''
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($files['indexes'] as $index) {
|
|
||||||
$indexes[] = new Document([
|
|
||||||
'$id' => ID::custom($index['$id']),
|
|
||||||
'type' => $index['type'],
|
|
||||||
'attributes' => $index['attributes'],
|
|
||||||
'lengths' => $index['lengths'],
|
|
||||||
'orders' => $index['orders'],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes);
|
|
||||||
}
|
|
||||||
|
|
||||||
$pools->reclaim();
|
|
||||||
|
|
||||||
Console::success('[Setup] - Server database init completed...');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Console::success('Server started successfully (max payload is ' . number_format($payloadSize) . ' bytes)');
|
Http::init()
|
||||||
Console::info("Master pid {$http->master_pid}, manager pid {$http->manager_pid}");
|
->inject('authorization')
|
||||||
|
->action(function (Authorization $authorization) {
|
||||||
// listen ctrl + c
|
$authorization->cleanRoles();
|
||||||
Process::signal(2, function () use ($http) {
|
$authorization->addRole(Role::any()->toString());
|
||||||
Console::log('Stop by Ctrl+C');
|
|
||||||
$http->shutdown();
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
$http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) use ($register) {
|
|
||||||
App::setResource('swooleRequest', fn () => $swooleRequest);
|
|
||||||
App::setResource('swooleResponse', fn () => $swooleResponse);
|
|
||||||
|
|
||||||
$request = new Request($swooleRequest);
|
|
||||||
$response = new Response($swooleResponse);
|
|
||||||
|
|
||||||
if (Files::isFileLoaded($request->getURI())) {
|
|
||||||
$time = (60 * 60 * 24 * 365 * 2); // 45 days cache
|
|
||||||
|
|
||||||
$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
|
|
||||||
->send(Files::getFileContents($request->getURI()));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$app = new App('UTC');
|
|
||||||
|
|
||||||
$pools = $register->get('pools');
|
|
||||||
App::setResource('pools', fn () => $pools);
|
|
||||||
|
|
||||||
try {
|
|
||||||
Authorization::cleanRoles();
|
|
||||||
Authorization::setRole(Role::any()->toString());
|
|
||||||
|
|
||||||
$app->run($request, $response);
|
|
||||||
} catch (\Throwable $th) {
|
|
||||||
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
|
||||||
|
|
||||||
$logger = $app->getResource("logger");
|
|
||||||
if ($logger) {
|
|
||||||
try {
|
|
||||||
/** @var Utopia\Database\Document $user */
|
|
||||||
$user = $app->getResource('user');
|
|
||||||
} catch (\Throwable $_th) {
|
|
||||||
// All good, user is optional information for logger
|
|
||||||
}
|
|
||||||
|
|
||||||
$route = $app->getRoute();
|
|
||||||
|
|
||||||
$log = $app->getResource("log");
|
|
||||||
|
|
||||||
if (isset($user) && !$user->isEmpty()) {
|
|
||||||
$log->setUser(new User($user->getId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
$log->setNamespace("http");
|
|
||||||
$log->setServer(\gethostname());
|
|
||||||
$log->setVersion($version);
|
|
||||||
$log->setType(Log::TYPE_ERROR);
|
|
||||||
$log->setMessage($th->getMessage());
|
|
||||||
|
|
||||||
$log->addTag('method', $route->getMethod());
|
|
||||||
$log->addTag('url', $route->getPath());
|
|
||||||
$log->addTag('verboseType', get_class($th));
|
|
||||||
$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->addExtra('file', $th->getFile());
|
|
||||||
$log->addExtra('line', $th->getLine());
|
|
||||||
$log->addExtra('trace', $th->getTraceAsString());
|
|
||||||
$log->addExtra('roles', Authorization::getRoles());
|
|
||||||
|
|
||||||
$action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD");
|
|
||||||
$log->setAction($action);
|
|
||||||
|
|
||||||
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
|
|
||||||
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$responseCode = $logger->addLog($log);
|
|
||||||
Console::info('Error log pushed with status code: ' . $responseCode);
|
|
||||||
} catch (Throwable $th) {
|
|
||||||
Console::error('Error pushing log: ' . $th->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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(),
|
|
||||||
'code' => 500,
|
|
||||||
'file' => $th->getFile(),
|
|
||||||
'line' => $th->getLine(),
|
|
||||||
'trace' => $th->getTrace(),
|
|
||||||
'version' => $version,
|
|
||||||
] : [
|
|
||||||
'message' => 'Error: Server Error',
|
|
||||||
'code' => 500,
|
|
||||||
'version' => $version,
|
|
||||||
];
|
|
||||||
|
|
||||||
$swooleResponse->end(\json_encode($output));
|
|
||||||
} finally {
|
|
||||||
$pools->reclaim();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$http->start();
|
$http->start();
|
||||||
|
|
|
||||||
1865
app/init.php
1865
app/init.php
File diff suppressed because it is too large
Load diff
37
app/init/config.php
Normal file
37
app/init/config.php
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
use Utopia\Config\Config;
|
||||||
|
|
||||||
|
Config::load('events', __DIR__ . '/../config/events.php');
|
||||||
|
Config::load('auth', __DIR__ . '/../config/auth.php');
|
||||||
|
Config::load('apis', __DIR__ . '/../config/apis.php');
|
||||||
|
Config::load('errors', __DIR__ . '/../config/errors.php');
|
||||||
|
Config::load('oAuthProviders', __DIR__ . '/../config/oAuthProviders.php');
|
||||||
|
Config::load('platforms', __DIR__ . '/../config/platforms.php');
|
||||||
|
Config::load('collections', __DIR__ . '/../config/collections.php');
|
||||||
|
Config::load('runtimes', __DIR__ . '/../config/runtimes.php');
|
||||||
|
Config::load('runtimes-v2', __DIR__ . '/../config/runtimes-v2.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('runtime-specifications', __DIR__ . '/../config/runtimes/specifications.php');
|
||||||
|
Config::load('function-templates', __DIR__ . '/../config/function-templates.php');
|
||||||
202
app/init/constants.php
Normal file
202
app/init/constants.php
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Appwrite\Functions\Specification;
|
||||||
|
|
||||||
|
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_MODE_DEFAULT = 'default';
|
||||||
|
const APP_MODE_ADMIN = 'admin';
|
||||||
|
const APP_PAGING_LIMIT = 12;
|
||||||
|
const APP_LIMIT_COUNT = 5000;
|
||||||
|
const APP_LIMIT_USERS = 10_000;
|
||||||
|
const APP_LIMIT_USER_PASSWORD_HISTORY = 20;
|
||||||
|
const APP_LIMIT_USER_SESSIONS_MAX = 100;
|
||||||
|
const APP_LIMIT_USER_SESSIONS_DEFAULT = 10;
|
||||||
|
const APP_LIMIT_ANTIVIRUS = 20_000_000; //20MB
|
||||||
|
const APP_LIMIT_ENCRYPTION = 20_000_000; //20MB
|
||||||
|
const APP_LIMIT_COMPRESSION = 20_000_000; //20MB
|
||||||
|
const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value
|
||||||
|
const APP_LIMIT_ARRAY_LABELS_SIZE = 1000; // Default maximum of how many labels elements can there be in API parameter that expects array value
|
||||||
|
const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length.
|
||||||
|
const APP_LIMIT_SUBQUERY = 1000;
|
||||||
|
const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1_000_000;
|
||||||
|
const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period
|
||||||
|
const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds
|
||||||
|
const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls
|
||||||
|
const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours
|
||||||
|
const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours
|
||||||
|
const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
|
||||||
|
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
|
||||||
|
const APP_CACHE_BUSTER = 4318;
|
||||||
|
const APP_VERSION_STABLE = '1.6.0';
|
||||||
|
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
||||||
|
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
|
||||||
|
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
|
||||||
|
const APP_DATABASE_ATTRIBUTE_DATETIME = 'datetime';
|
||||||
|
const APP_DATABASE_ATTRIBUTE_URL = 'url';
|
||||||
|
const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange';
|
||||||
|
const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange';
|
||||||
|
const APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH = 1_073_741_824; // 2^32 bits / 4 bits per char
|
||||||
|
const APP_DATABASE_TIMEOUT_MILLISECONDS = 15_000;
|
||||||
|
const APP_STORAGE_UPLOADS = '/storage/uploads';
|
||||||
|
const APP_STORAGE_FUNCTIONS = '/storage/functions';
|
||||||
|
const APP_STORAGE_BUILDS = '/storage/builds';
|
||||||
|
const APP_STORAGE_CACHE = '/storage/cache';
|
||||||
|
const APP_STORAGE_CERTIFICATES = '/storage/certificates';
|
||||||
|
const APP_STORAGE_CONFIG = '/storage/config';
|
||||||
|
const APP_STORAGE_READ_BUFFER = 20 * (1000 * 1000); //20MB other names `APP_STORAGE_MEMORY_LIMIT`, `APP_STORAGE_MEMORY_BUFFER`, `APP_STORAGE_READ_LIMIT`, `APP_STORAGE_BUFFER_LIMIT`
|
||||||
|
const APP_SOCIAL_TWITTER = 'https://twitter.com/appwrite';
|
||||||
|
const APP_SOCIAL_TWITTER_HANDLE = 'appwrite';
|
||||||
|
const APP_SOCIAL_FACEBOOK = 'https://www.facebook.com/appwrite.io';
|
||||||
|
const APP_SOCIAL_LINKEDIN = 'https://www.linkedin.com/company/appwrite';
|
||||||
|
const APP_SOCIAL_INSTAGRAM = 'https://www.instagram.com/appwrite.io';
|
||||||
|
const APP_SOCIAL_GITHUB = 'https://github.com/appwrite';
|
||||||
|
const APP_SOCIAL_DISCORD = 'https://appwrite.io/discord';
|
||||||
|
const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244';
|
||||||
|
const APP_SOCIAL_DEV = 'https://dev.to/appwrite';
|
||||||
|
const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
|
||||||
|
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1';
|
||||||
|
const APP_HOSTNAME_INTERNAL = 'appwrite';
|
||||||
|
const APP_FUNCTION_SPECIFICATION_DEFAULT = Specification::S_05VCPU_512MB;
|
||||||
|
const APP_FUNCTION_CPUS_DEFAULT = 0.5;
|
||||||
|
const APP_FUNCTION_MEMORY_DEFAULT = 512;
|
||||||
|
const APP_PLATFORM_SERVER = 'server';
|
||||||
|
const APP_PLATFORM_CLIENT = 'client';
|
||||||
|
const APP_PLATFORM_CONSOLE = 'console';
|
||||||
|
const DATABASE_SHARED_TABLES = 'database_db_fra1_self_hosted_16_0';
|
||||||
|
// Database Reconnect
|
||||||
|
const DATABASE_RECONNECT_SLEEP = 2;
|
||||||
|
const DATABASE_RECONNECT_MAX_ATTEMPTS = 10;
|
||||||
|
|
||||||
|
// Database Worker Types
|
||||||
|
const DATABASE_TYPE_CREATE_ATTRIBUTE = 'createAttribute';
|
||||||
|
const DATABASE_TYPE_CREATE_INDEX = 'createIndex';
|
||||||
|
const DATABASE_TYPE_DELETE_ATTRIBUTE = 'deleteAttribute';
|
||||||
|
const DATABASE_TYPE_DELETE_INDEX = 'deleteIndex';
|
||||||
|
const DATABASE_TYPE_DELETE_COLLECTION = 'deleteCollection';
|
||||||
|
const DATABASE_TYPE_DELETE_DATABASE = 'deleteDatabase';
|
||||||
|
|
||||||
|
// Build Worker Types
|
||||||
|
const BUILD_TYPE_DEPLOYMENT = 'deployment';
|
||||||
|
const BUILD_TYPE_RETRY = 'retry';
|
||||||
|
|
||||||
|
// Deletion Types
|
||||||
|
const DELETE_TYPE_DATABASES = 'databases';
|
||||||
|
const DELETE_TYPE_DOCUMENT = 'document';
|
||||||
|
const DELETE_TYPE_COLLECTIONS = 'collections';
|
||||||
|
const DELETE_TYPE_PROJECTS = 'projects';
|
||||||
|
const DELETE_TYPE_FUNCTIONS = 'functions';
|
||||||
|
const DELETE_TYPE_DEPLOYMENTS = 'deployments';
|
||||||
|
const DELETE_TYPE_USERS = 'users';
|
||||||
|
const DELETE_TYPE_TEAM_PROJECTS = 'teams_projects';
|
||||||
|
const DELETE_TYPE_EXECUTIONS = 'executions';
|
||||||
|
const DELETE_TYPE_AUDIT = 'audit';
|
||||||
|
const DELETE_TYPE_ABUSE = 'abuse';
|
||||||
|
const DELETE_TYPE_USAGE = 'usage';
|
||||||
|
const DELETE_TYPE_REALTIME = 'realtime';
|
||||||
|
const DELETE_TYPE_BUCKETS = 'buckets';
|
||||||
|
const DELETE_TYPE_INSTALLATIONS = 'installations';
|
||||||
|
const DELETE_TYPE_RULES = 'rules';
|
||||||
|
const DELETE_TYPE_SESSIONS = 'sessions';
|
||||||
|
const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp';
|
||||||
|
const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource';
|
||||||
|
const DELETE_TYPE_SCHEDULES = 'schedules';
|
||||||
|
const DELETE_TYPE_TOPIC = 'topic';
|
||||||
|
const DELETE_TYPE_TARGET = 'target';
|
||||||
|
const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets';
|
||||||
|
const DELETE_TYPE_SESSION_TARGETS = 'session_targets';
|
||||||
|
|
||||||
|
// Message types
|
||||||
|
const MESSAGE_SEND_TYPE_INTERNAL = 'internal';
|
||||||
|
const MESSAGE_SEND_TYPE_EXTERNAL = 'external';
|
||||||
|
// Mail Types
|
||||||
|
const MAIL_TYPE_VERIFICATION = 'verification';
|
||||||
|
const MAIL_TYPE_MAGIC_SESSION = 'magicSession';
|
||||||
|
const MAIL_TYPE_RECOVERY = 'recovery';
|
||||||
|
const MAIL_TYPE_INVITATION = 'invitation';
|
||||||
|
const MAIL_TYPE_CERTIFICATE = 'certificate';
|
||||||
|
// Auth Types
|
||||||
|
const APP_AUTH_TYPE_SESSION = 'Session';
|
||||||
|
const APP_AUTH_TYPE_JWT = 'JWT';
|
||||||
|
const APP_AUTH_TYPE_KEY = 'Key';
|
||||||
|
const APP_AUTH_TYPE_ADMIN = 'Admin';
|
||||||
|
// Response related
|
||||||
|
const MAX_OUTPUT_CHUNK_SIZE = 10 * 1024 * 1024; // 10MB
|
||||||
|
// Function headers
|
||||||
|
const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host'];
|
||||||
|
const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length'];
|
||||||
|
// Message types
|
||||||
|
const MESSAGE_TYPE_EMAIL = 'email';
|
||||||
|
const MESSAGE_TYPE_SMS = 'sms';
|
||||||
|
const MESSAGE_TYPE_PUSH = 'push';
|
||||||
|
// API key types
|
||||||
|
const API_KEY_STANDARD = 'standard';
|
||||||
|
const API_KEY_DYNAMIC = 'dynamic';
|
||||||
|
// Usage metrics
|
||||||
|
const METRIC_TEAMS = 'teams';
|
||||||
|
const METRIC_USERS = 'users';
|
||||||
|
const METRIC_AUTH_METHOD_PHONE = 'auth.method.phone';
|
||||||
|
const METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE = METRIC_AUTH_METHOD_PHONE . '.{countryCode}';
|
||||||
|
const METRIC_MESSAGES = 'messages';
|
||||||
|
const METRIC_MESSAGES_SENT = METRIC_MESSAGES . '.sent';
|
||||||
|
const METRIC_MESSAGES_FAILED = METRIC_MESSAGES . '.failed';
|
||||||
|
const METRIC_MESSAGES_TYPE = METRIC_MESSAGES . '.{type}';
|
||||||
|
const METRIC_MESSAGES_TYPE_SENT = METRIC_MESSAGES . '.{type}.sent';
|
||||||
|
const METRIC_MESSAGES_TYPE_FAILED = METRIC_MESSAGES . '.{type}.failed';
|
||||||
|
const METRIC_MESSAGES_TYPE_PROVIDER = METRIC_MESSAGES . '.{type}.{provider}';
|
||||||
|
const METRIC_MESSAGES_TYPE_PROVIDER_SENT = METRIC_MESSAGES . '.{type}.{provider}.sent';
|
||||||
|
const METRIC_MESSAGES_TYPE_PROVIDER_FAILED = METRIC_MESSAGES . '.{type}.{provider}.failed';
|
||||||
|
const METRIC_SESSIONS = 'sessions';
|
||||||
|
const METRIC_DATABASES = 'databases';
|
||||||
|
const METRIC_COLLECTIONS = 'collections';
|
||||||
|
const METRIC_DATABASES_STORAGE = 'databases.storage';
|
||||||
|
const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections';
|
||||||
|
const METRIC_DATABASE_ID_STORAGE = '{databaseInternalId}.databases.storage';
|
||||||
|
|
||||||
|
const METRIC_DOCUMENTS = 'documents';
|
||||||
|
const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents';
|
||||||
|
const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents';
|
||||||
|
const METRIC_DATABASE_ID_COLLECTION_ID_STORAGE = '{databaseInternalId}.{collectionInternalId}.databases.storage';
|
||||||
|
const METRIC_BUCKETS = 'buckets';
|
||||||
|
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_SUCCESS = 'builds.success';
|
||||||
|
const METRIC_BUILDS_FAILED = 'builds.failed';
|
||||||
|
|
||||||
|
const METRIC_BUILDS_STORAGE = 'builds.storage';
|
||||||
|
const METRIC_BUILDS_COMPUTE = 'builds.compute';
|
||||||
|
const METRIC_BUILDS_COMPUTE_SUCCESS = 'builds.compute.success';
|
||||||
|
const METRIC_BUILDS_COMPUTE_FAILED = 'builds.compute.failed';
|
||||||
|
const METRIC_BUILDS_MB_SECONDS = 'builds.mbSeconds';
|
||||||
|
|
||||||
|
const METRIC_FUNCTION_ID_BUILDS_COMPUTE_SUCCESS = '{functionInternalId}.builds.compute.success';
|
||||||
|
const METRIC_FUNCTION_ID_BUILDS_COMPUTE_FAILED = '{functionInternalId}.builds.compute.failed';
|
||||||
|
const METRIC_FUNCTION_ID_BUILDS = '{functionInternalId}.builds';
|
||||||
|
const METRIC_FUNCTION_ID_BUILDS_SUCCESS = '{functionInternalId}.builds.success';
|
||||||
|
const METRIC_FUNCTION_ID_BUILDS_FAILED = '{functionInternalId}.builds.failed';
|
||||||
|
|
||||||
|
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_FUNCTION_ID_BUILDS_MB_SECONDS = '{functionInternalId}.builds.mbSeconds';
|
||||||
|
|
||||||
|
const METRIC_EXECUTIONS = 'executions';
|
||||||
|
const METRIC_EXECUTIONS_COMPUTE = 'executions.compute';
|
||||||
|
const METRIC_EXECUTIONS_MB_SECONDS = 'executions.mbSeconds';
|
||||||
|
const METRIC_FUNCTION_ID_EXECUTIONS = '{functionInternalId}.executions';
|
||||||
|
const METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE = '{functionInternalId}.executions.compute';
|
||||||
|
const METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS = '{functionInternalId}.executions.mbSeconds';
|
||||||
|
const METRIC_NETWORK_REQUESTS = 'network.requests';
|
||||||
|
const METRIC_NETWORK_INBOUND = 'network.inbound';
|
||||||
|
const METRIC_NETWORK_OUTBOUND = 'network.outbound';
|
||||||
397
app/init/database/filters.php
Normal file
397
app/init/database/filters.php
Normal file
|
|
@ -0,0 +1,397 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Appwrite\OpenSSL\OpenSSL;
|
||||||
|
use Utopia\Database\Database;
|
||||||
|
use Utopia\Database\Document;
|
||||||
|
use Utopia\Database\Query;
|
||||||
|
use Utopia\System\System;
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'casting',
|
||||||
|
function (mixed $value) {
|
||||||
|
return json_encode(['value' => $value], JSON_PRESERVE_ZERO_FRACTION);
|
||||||
|
},
|
||||||
|
function (mixed $value) {
|
||||||
|
if (is_null($value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_decode($value, true)['value'];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'enum',
|
||||||
|
function (mixed $value, Document $attribute) {
|
||||||
|
if ($attribute->isSet('elements')) {
|
||||||
|
$attribute->removeAttribute('elements');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $attribute) {
|
||||||
|
$formatOptions = \json_decode($attribute->getAttribute('formatOptions', '[]'), true);
|
||||||
|
if (isset($formatOptions['elements'])) {
|
||||||
|
$attribute->setAttribute('elements', $formatOptions['elements']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'range',
|
||||||
|
function (mixed $value, Document $attribute) {
|
||||||
|
if ($attribute->isSet('min')) {
|
||||||
|
$attribute->removeAttribute('min');
|
||||||
|
}
|
||||||
|
if ($attribute->isSet('max')) {
|
||||||
|
$attribute->removeAttribute('max');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $attribute) {
|
||||||
|
$formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true);
|
||||||
|
if (isset($formatOptions['min']) || isset($formatOptions['max'])) {
|
||||||
|
$attribute
|
||||||
|
->setAttribute('min', $formatOptions['min'])
|
||||||
|
->setAttribute('max', $formatOptions['max'])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'subQueryAttributes',
|
||||||
|
function (mixed $value) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $document, Database $database) {
|
||||||
|
$attributes = $database->find('attributes', [
|
||||||
|
Query::equal('collectionInternalId', [$document->getInternalId()]),
|
||||||
|
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
|
||||||
|
Query::limit($database->getLimitForAttributes()),
|
||||||
|
]);
|
||||||
|
|
||||||
|
foreach ($attributes as $attribute) {
|
||||||
|
if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) {
|
||||||
|
$options = $attribute->getAttribute('options');
|
||||||
|
foreach ($options as $key => $value) {
|
||||||
|
$attribute->setAttribute($key, $value);
|
||||||
|
}
|
||||||
|
$attribute->removeAttribute('options');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $attributes;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'subQueryIndexes',
|
||||||
|
function (mixed $value) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $document, Database $database) {
|
||||||
|
return $database
|
||||||
|
->find('indexes', [
|
||||||
|
Query::equal('collectionInternalId', [$document->getInternalId()]),
|
||||||
|
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
|
||||||
|
Query::limit($database->getLimitForIndexes()),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'subQueryPlatforms',
|
||||||
|
function (mixed $value) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $document, Database $database) {
|
||||||
|
return $database
|
||||||
|
->find('platforms', [
|
||||||
|
Query::equal('projectInternalId', [$document->getInternalId()]),
|
||||||
|
Query::limit(APP_LIMIT_SUBQUERY),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'subQueryKeys',
|
||||||
|
function (mixed $value) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $document, Database $database) {
|
||||||
|
return $database
|
||||||
|
->find('keys', [
|
||||||
|
Query::equal('projectInternalId', [$document->getInternalId()]),
|
||||||
|
Query::limit(APP_LIMIT_SUBQUERY),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'subQueryWebhooks',
|
||||||
|
function (mixed $value) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $document, Database $database) {
|
||||||
|
return $database
|
||||||
|
->find('webhooks', [
|
||||||
|
Query::equal('projectInternalId', [$document->getInternalId()]),
|
||||||
|
Query::limit(APP_LIMIT_SUBQUERY),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'subQuerySessions',
|
||||||
|
function (mixed $value) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $document, Database $database) {
|
||||||
|
return $database->getAuthorization()->skip(fn () => $database->find('sessions', [
|
||||||
|
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||||
|
Query::limit(APP_LIMIT_SUBQUERY),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'subQueryTokens',
|
||||||
|
function (mixed $value) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $document, Database $database) {
|
||||||
|
return $database->getAuthorization()->skip(fn () => $database
|
||||||
|
->find('tokens', [
|
||||||
|
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||||
|
Query::limit(APP_LIMIT_SUBQUERY),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'subQueryChallenges',
|
||||||
|
function (mixed $value) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $document, Database $database) {
|
||||||
|
return $database->getAuthorization()->skip(fn () => $database
|
||||||
|
->find('challenges', [
|
||||||
|
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||||
|
Query::limit(APP_LIMIT_SUBQUERY),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'subQueryAuthenticators',
|
||||||
|
function (mixed $value) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $document, Database $database) {
|
||||||
|
return $database->getAuthorization()->skip(fn () => $database
|
||||||
|
->find('authenticators', [
|
||||||
|
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||||
|
Query::limit(APP_LIMIT_SUBQUERY),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'subQueryMemberships',
|
||||||
|
function (mixed $value) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $document, Database $database) {
|
||||||
|
return $database->getAuthorization()->skip(fn () => $database
|
||||||
|
->find('memberships', [
|
||||||
|
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||||
|
Query::limit(APP_LIMIT_SUBQUERY),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'subQueryVariables',
|
||||||
|
function (mixed $value) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $document, Database $database) {
|
||||||
|
return $database
|
||||||
|
->find('variables', [
|
||||||
|
Query::equal('resourceInternalId', [$document->getInternalId()]),
|
||||||
|
Query::equal('resourceType', ['function']),
|
||||||
|
Query::limit(APP_LIMIT_SUBQUERY),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'encrypt',
|
||||||
|
function (mixed $value) {
|
||||||
|
$key = System::getEnv('_APP_OPENSSL_KEY_V1');
|
||||||
|
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
|
||||||
|
$tag = null;
|
||||||
|
|
||||||
|
return json_encode([
|
||||||
|
'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
|
||||||
|
'method' => OpenSSL::CIPHER_AES_128_GCM,
|
||||||
|
'iv' => \bin2hex($iv),
|
||||||
|
'tag' => \bin2hex($tag ?? ''),
|
||||||
|
'version' => '1',
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
function (mixed $value) {
|
||||||
|
if (is_null($value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$value = json_decode($value, true);
|
||||||
|
$key = System::getEnv('_APP_OPENSSL_KEY_V' . $value['version']);
|
||||||
|
|
||||||
|
return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag']));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'subQueryProjectVariables',
|
||||||
|
function (mixed $value) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $document, Database $database) {
|
||||||
|
return $database
|
||||||
|
->find('variables', [
|
||||||
|
Query::equal('resourceType', ['project']),
|
||||||
|
Query::limit(APP_LIMIT_SUBQUERY)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'userSearch',
|
||||||
|
function (mixed $value, Document $user) {
|
||||||
|
$searchValues = [
|
||||||
|
$user->getId(),
|
||||||
|
$user->getAttribute('email', ''),
|
||||||
|
$user->getAttribute('name', ''),
|
||||||
|
$user->getAttribute('phone', '')
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($user->getAttribute('labels', []) as $label) {
|
||||||
|
$searchValues[] = 'label:' . $label;
|
||||||
|
}
|
||||||
|
|
||||||
|
$search = implode(' ', \array_filter($searchValues));
|
||||||
|
|
||||||
|
return $search;
|
||||||
|
},
|
||||||
|
function (mixed $value) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'subQueryTargets',
|
||||||
|
function (mixed $value) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $document, Database $database) {
|
||||||
|
return $database->getAuthorization()->skip(fn () => $database
|
||||||
|
->find('targets', [
|
||||||
|
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||||
|
Query::limit(APP_LIMIT_SUBQUERY)
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'subQueryTopicTargets',
|
||||||
|
function (mixed $value) {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $document, Database $database) {
|
||||||
|
$targetIds = $database->getAuthorization()->skip(fn () => \array_map(
|
||||||
|
fn ($document) => $document->getAttribute('targetInternalId'),
|
||||||
|
$database->find('subscribers', [
|
||||||
|
Query::equal('topicInternalId', [$document->getInternalId()]),
|
||||||
|
Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY)
|
||||||
|
])
|
||||||
|
));
|
||||||
|
if (\count($targetIds) > 0) {
|
||||||
|
return $database->find('targets', [
|
||||||
|
Query::equal('$internalId', $targetIds)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'providerSearch',
|
||||||
|
function (mixed $value, Document $provider) {
|
||||||
|
$searchValues = [
|
||||||
|
$provider->getId(),
|
||||||
|
$provider->getAttribute('name', ''),
|
||||||
|
$provider->getAttribute('provider', ''),
|
||||||
|
$provider->getAttribute('type', '')
|
||||||
|
];
|
||||||
|
|
||||||
|
$search = \implode(' ', \array_filter($searchValues));
|
||||||
|
|
||||||
|
return $search;
|
||||||
|
},
|
||||||
|
function (mixed $value) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'topicSearch',
|
||||||
|
function (mixed $value, Document $topic) {
|
||||||
|
$searchValues = [
|
||||||
|
$topic->getId(),
|
||||||
|
$topic->getAttribute('name', ''),
|
||||||
|
$topic->getAttribute('description', ''),
|
||||||
|
];
|
||||||
|
|
||||||
|
$search = \implode(' ', \array_filter($searchValues));
|
||||||
|
|
||||||
|
return $search;
|
||||||
|
},
|
||||||
|
function (mixed $value) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'messageSearch',
|
||||||
|
function (mixed $value, Document $message) {
|
||||||
|
$searchValues = [
|
||||||
|
$message->getId(),
|
||||||
|
$message->getAttribute('description', ''),
|
||||||
|
$message->getAttribute('status', ''),
|
||||||
|
];
|
||||||
|
|
||||||
|
$data = \json_decode($message->getAttribute('data', []), true);
|
||||||
|
$providerType = $message->getAttribute('providerType', '');
|
||||||
|
|
||||||
|
if ($providerType === MESSAGE_TYPE_EMAIL) {
|
||||||
|
$searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]);
|
||||||
|
} elseif ($providerType === MESSAGE_TYPE_SMS) {
|
||||||
|
$searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]);
|
||||||
|
} else {
|
||||||
|
$searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$search = \implode(' ', \array_filter($searchValues));
|
||||||
|
|
||||||
|
return $search;
|
||||||
|
},
|
||||||
|
function (mixed $value) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
);
|
||||||
43
app/init/database/formats.php
Normal file
43
app/init/database/formats.php
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Appwrite\Network\Validator\Email;
|
||||||
|
use Utopia\Database\Database;
|
||||||
|
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||||
|
use Utopia\Database\Validator\Structure;
|
||||||
|
use Utopia\Http\Validator\IP;
|
||||||
|
use Utopia\Http\Validator\Range;
|
||||||
|
use Utopia\Http\Validator\URL;
|
||||||
|
use Utopia\Http\Validator\WhiteList;
|
||||||
|
|
||||||
|
Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () {
|
||||||
|
return new Email();
|
||||||
|
}, Database::VAR_STRING);
|
||||||
|
|
||||||
|
Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () {
|
||||||
|
return new DatetimeValidator();
|
||||||
|
}, Database::VAR_DATETIME);
|
||||||
|
|
||||||
|
Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) {
|
||||||
|
$elements = $attribute['formatOptions']['elements'];
|
||||||
|
return new WhiteList($elements, true);
|
||||||
|
}, Database::VAR_STRING);
|
||||||
|
|
||||||
|
Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () {
|
||||||
|
return new IP();
|
||||||
|
}, Database::VAR_STRING);
|
||||||
|
|
||||||
|
Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () {
|
||||||
|
return new URL();
|
||||||
|
}, Database::VAR_STRING);
|
||||||
|
|
||||||
|
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);
|
||||||
23
app/init/locale.php
Normal file
23
app/init/locale.php
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Utopia\Config\Config;
|
||||||
|
use Utopia\Locale\Locale;
|
||||||
|
|
||||||
|
Locale::$exceptions = false;
|
||||||
|
|
||||||
|
$locales = Config::getParam('locale-codes', []);
|
||||||
|
|
||||||
|
foreach ($locales as $locale) {
|
||||||
|
$code = $locale['code'];
|
||||||
|
|
||||||
|
$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`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Locale::setLanguageFromJSON($code, $path);
|
||||||
|
}
|
||||||
1091
app/init/resources.php
Normal file
1091
app/init/resources.php
Normal file
File diff suppressed because it is too large
Load diff
277
app/realtime.php
277
app/realtime.php
|
|
@ -5,136 +5,47 @@ use Appwrite\Extend\Exception;
|
||||||
use Appwrite\Extend\Exception as AppwriteException;
|
use Appwrite\Extend\Exception as AppwriteException;
|
||||||
use Appwrite\Messaging\Adapter\Realtime;
|
use Appwrite\Messaging\Adapter\Realtime;
|
||||||
use Appwrite\Network\Validator\Origin;
|
use Appwrite\Network\Validator\Origin;
|
||||||
|
use Appwrite\Utopia\Queue\Connections;
|
||||||
use Appwrite\Utopia\Request;
|
use Appwrite\Utopia\Request;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Swoole\Http\Request as SwooleRequest;
|
use Swoole\Http\Request as SwooleRequest;
|
||||||
|
use Swoole\Http\Response as SwooleHttpResponse;
|
||||||
use Swoole\Http\Response as SwooleResponse;
|
use Swoole\Http\Response as SwooleResponse;
|
||||||
use Swoole\Runtime;
|
use Swoole\Runtime;
|
||||||
use Swoole\Table;
|
use Swoole\Table;
|
||||||
use Swoole\Timer;
|
use Swoole\Timer;
|
||||||
use Utopia\Abuse\Abuse;
|
use Utopia\Abuse\Abuse;
|
||||||
use Utopia\Abuse\Adapters\Database\TimeLimit;
|
use Utopia\Abuse\Adapters\Database\TimeLimit;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Cache\Adapter\Sharding;
|
|
||||||
use Utopia\Cache\Cache;
|
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Config\Config;
|
|
||||||
use Utopia\Database\Database;
|
|
||||||
use Utopia\Database\DateTime;
|
use Utopia\Database\DateTime;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Helpers\ID;
|
use Utopia\Database\Helpers\ID;
|
||||||
use Utopia\Database\Helpers\Role;
|
use Utopia\Database\Helpers\Role;
|
||||||
use Utopia\Database\Query;
|
use Utopia\Database\Query;
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\DI\Container;
|
||||||
use Utopia\DSN\DSN;
|
use Utopia\DI\Dependency;
|
||||||
|
use Utopia\Http\Adapter\Swoole\Request as UtopiaRequest;
|
||||||
|
use Utopia\Http\Adapter\Swoole\Response as HttpResponse;
|
||||||
|
use Utopia\Http\Adapter\Swoole\Response as UtopiaResponse;
|
||||||
|
use Utopia\Http\Http;
|
||||||
use Utopia\Logger\Log;
|
use Utopia\Logger\Log;
|
||||||
|
use Utopia\Pools\Connection;
|
||||||
|
use Utopia\Registry\Registry;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\WebSocket\Adapter;
|
use Utopia\WebSocket\Adapter;
|
||||||
use Utopia\WebSocket\Server;
|
use Utopia\WebSocket\Server;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Utopia\Registry\Registry $register
|
* @var Registry $registry
|
||||||
|
* @var Container $container
|
||||||
*/
|
*/
|
||||||
|
global $registry, $container;
|
||||||
|
|
||||||
|
|
||||||
require_once __DIR__ . '/init.php';
|
require_once __DIR__ . '/init.php';
|
||||||
|
|
||||||
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
||||||
|
|
||||||
// Allows overriding
|
|
||||||
if (!function_exists('getConsoleDB')) {
|
|
||||||
function getConsoleDB(): Database
|
|
||||||
{
|
|
||||||
global $register;
|
|
||||||
|
|
||||||
/** @var \Utopia\Pools\Group $pools */
|
|
||||||
$pools = $register->get('pools');
|
|
||||||
|
|
||||||
$dbAdapter = $pools
|
|
||||||
->get('console')
|
|
||||||
->pop()
|
|
||||||
->getResource()
|
|
||||||
;
|
|
||||||
|
|
||||||
$database = new Database($dbAdapter, getCache());
|
|
||||||
|
|
||||||
$database
|
|
||||||
->setNamespace('_console')
|
|
||||||
->setMetadata('host', \gethostname())
|
|
||||||
->setMetadata('project', '_console');
|
|
||||||
|
|
||||||
return $database;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allows overriding
|
|
||||||
if (!function_exists('getProjectDB')) {
|
|
||||||
function getProjectDB(Document $project): Database
|
|
||||||
{
|
|
||||||
global $register;
|
|
||||||
|
|
||||||
/** @var \Utopia\Pools\Group $pools */
|
|
||||||
$pools = $register->get('pools');
|
|
||||||
|
|
||||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
|
||||||
return getConsoleDB();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$dsn = new DSN($project->getAttribute('database'));
|
|
||||||
} catch (\InvalidArgumentException) {
|
|
||||||
// TODO: Temporary until all projects are using shared tables
|
|
||||||
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$adapter = $pools
|
|
||||||
->get($dsn->getHost())
|
|
||||||
->pop()
|
|
||||||
->getResource();
|
|
||||||
|
|
||||||
$database = new Database($adapter, getCache());
|
|
||||||
|
|
||||||
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
|
||||||
$database
|
|
||||||
->setSharedTables(true)
|
|
||||||
->setTenant($project->getInternalId())
|
|
||||||
->setNamespace($dsn->getParam('namespace'));
|
|
||||||
} else {
|
|
||||||
$database
|
|
||||||
->setSharedTables(false)
|
|
||||||
->setTenant(null)
|
|
||||||
->setNamespace('_' . $project->getInternalId());
|
|
||||||
}
|
|
||||||
|
|
||||||
$database
|
|
||||||
->setMetadata('host', \gethostname())
|
|
||||||
->setMetadata('project', $project->getId());
|
|
||||||
|
|
||||||
return $database;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allows overriding
|
|
||||||
if (!function_exists('getCache')) {
|
|
||||||
function getCache(): Cache
|
|
||||||
{
|
|
||||||
global $register;
|
|
||||||
|
|
||||||
$pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */
|
|
||||||
|
|
||||||
$list = Config::getParam('pools-cache', []);
|
|
||||||
$adapters = [];
|
|
||||||
|
|
||||||
foreach ($list as $value) {
|
|
||||||
$adapters[] = $pools
|
|
||||||
->get($value)
|
|
||||||
->pop()
|
|
||||||
->getResource()
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Cache(new Sharding($adapters));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('getRealtime')) {
|
if (!function_exists('getRealtime')) {
|
||||||
function getRealtime(): Realtime
|
function getRealtime(): Realtime
|
||||||
{
|
{
|
||||||
|
|
@ -166,8 +77,8 @@ $adapter
|
||||||
|
|
||||||
$server = new Server($adapter);
|
$server = new Server($adapter);
|
||||||
|
|
||||||
$logError = function (Throwable $error, string $action) use ($register) {
|
$logError = function (Throwable $error, string $action) use ($registry) {
|
||||||
$logger = $register->get('logger');
|
$logger = $registry->get('logger');
|
||||||
|
|
||||||
if ($logger && !$error instanceof Exception) {
|
if ($logger && !$error instanceof Exception) {
|
||||||
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||||
|
|
@ -207,16 +118,16 @@ $logError = function (Throwable $error, string $action) use ($register) {
|
||||||
|
|
||||||
$server->error($logError);
|
$server->error($logError);
|
||||||
|
|
||||||
$server->onStart(function () use ($stats, $register, $containerId, &$statsDocument, $logError) {
|
$server->onStart(function () use ($stats, $container, $containerId, &$statsDocument, $logError) {
|
||||||
sleep(5); // wait for the initial database schema to be ready
|
sleep(5); // wait for the initial database schema to be ready
|
||||||
Console::success('Server started successfully');
|
Console::success('Server started successfully');
|
||||||
|
$authorization = $container->get('authorization');
|
||||||
/**
|
/**
|
||||||
* Create document for this worker to share stats across Containers.
|
* Create document for this worker to share stats across Containers.
|
||||||
*/
|
*/
|
||||||
go(function () use ($register, $containerId, &$statsDocument) {
|
go(function () use ($container, $containerId, &$statsDocument) {
|
||||||
$attempts = 0;
|
$attempts = 0;
|
||||||
$database = getConsoleDB();
|
$database = $container->get('dbForConsole');
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try {
|
try {
|
||||||
|
|
@ -230,14 +141,15 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
||||||
'value' => '{}'
|
'value' => '{}'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$statsDocument = Authorization::skip(fn () => $database->createDocument('realtime', $document));
|
$authorization = $container->get('authorization');
|
||||||
|
$statsDocument = $authorization->skip(fn () => $database->createDocument('realtime', $document));
|
||||||
break;
|
break;
|
||||||
} catch (Throwable) {
|
} catch (Throwable) {
|
||||||
Console::warning("Collection not ready. Retrying connection ({$attempts})...");
|
Console::warning("Collection not ready. Retrying connection ({$attempts})...");
|
||||||
sleep(DATABASE_RECONNECT_SLEEP);
|
sleep(DATABASE_RECONNECT_SLEEP);
|
||||||
}
|
}
|
||||||
} while (true);
|
} while (true);
|
||||||
$register->get('pools')->reclaim();
|
($container->get('connections'))->reclaim();
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -245,7 +157,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
||||||
*/
|
*/
|
||||||
// TODO: Remove this if check once it doesn't cause issues for cloud
|
// TODO: Remove this if check once it doesn't cause issues for cloud
|
||||||
if (System::getEnv('_APP_EDITION', 'self-hosted') === 'self-hosted') {
|
if (System::getEnv('_APP_EDITION', 'self-hosted') === 'self-hosted') {
|
||||||
Timer::tick(5000, function () use ($register, $stats, &$statsDocument, $logError) {
|
Timer::tick(5000, function () use ($container, $stats, &$statsDocument, $logError, $authorization) {
|
||||||
$payload = [];
|
$payload = [];
|
||||||
foreach ($stats as $projectId => $value) {
|
foreach ($stats as $projectId => $value) {
|
||||||
$payload[$projectId] = $stats->get($projectId, 'connectionsTotal');
|
$payload[$projectId] = $stats->get($projectId, 'connectionsTotal');
|
||||||
|
|
@ -255,40 +167,43 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$database = getConsoleDB();
|
$database = $container->get('dbForConsole');
|
||||||
|
|
||||||
$statsDocument
|
$statsDocument
|
||||||
->setAttribute('timestamp', DateTime::now())
|
->setAttribute('timestamp', DateTime::now())
|
||||||
->setAttribute('value', json_encode($payload));
|
->setAttribute('value', json_encode($payload));
|
||||||
|
|
||||||
Authorization::skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument));
|
$authorization->skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument));
|
||||||
} catch (Throwable $th) {
|
} catch (Throwable $th) {
|
||||||
call_user_func($logError, $th, "updateWorkerDocument");
|
call_user_func($logError, $th, "updateWorkerDocument");
|
||||||
} finally {
|
} finally {
|
||||||
$register->get('pools')->reclaim();
|
($container->get('connections'))->reclaim();
|
||||||
|
$container->refresh('dbForConsole');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $realtime, $logError) {
|
$server->onWorkerStart(function (int $workerId) use ($server, $container, $stats, $realtime, $logError) {
|
||||||
Console::success('Worker ' . $workerId . ' started successfully');
|
Console::success('Worker ' . $workerId . ' started successfully');
|
||||||
|
|
||||||
$attempts = 0;
|
$attempts = 0;
|
||||||
$start = time();
|
$start = time();
|
||||||
|
|
||||||
Timer::tick(5000, function () use ($server, $register, $realtime, $stats, $logError) {
|
$authorization = $container->get('authorization');
|
||||||
|
|
||||||
|
Timer::tick(5000, function () use ($server, $container, $realtime, $stats, $logError, $authorization) {
|
||||||
/**
|
/**
|
||||||
* Sending current connections to project channels on the console project every 5 seconds.
|
* Sending current connections to project channels on the console project every 5 seconds.
|
||||||
*/
|
*/
|
||||||
// TODO: Remove this if check once it doesn't cause issues for cloud
|
// TODO: Remove this if check once it doesn't cause issues for cloud
|
||||||
if (System::getEnv('_APP_EDITION', 'self-hosted') === 'self-hosted') {
|
if (System::getEnv('_APP_EDITION', 'self-hosted') === 'self-hosted') {
|
||||||
if ($realtime->hasSubscriber('console', Role::users()->toString(), 'project')) {
|
if ($realtime->hasSubscriber('console', Role::users()->toString(), 'project')) {
|
||||||
$database = getConsoleDB();
|
$database = $container->get('dbForConsole');
|
||||||
|
|
||||||
$payload = [];
|
$payload = [];
|
||||||
|
|
||||||
$list = Authorization::skip(fn () => $database->find('realtime', [
|
$list = $authorization->skip(fn () => $database->find('realtime', [
|
||||||
Query::greaterThan('timestamp', DateTime::addSeconds(new \DateTime(), -15)),
|
Query::greaterThan('timestamp', DateTime::addSeconds(new \DateTime(), -15)),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
|
|
@ -328,8 +243,8 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
||||||
'data' => $event['data']
|
'data' => $event['data']
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
($container->get('connections'))->reclaim();
|
||||||
$register->get('pools')->reclaim();
|
$container->refresh('dbForConsole');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|
@ -359,13 +274,26 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
||||||
while ($attempts < 300) {
|
while ($attempts < 300) {
|
||||||
try {
|
try {
|
||||||
if ($attempts > 0) {
|
if ($attempts > 0) {
|
||||||
Console::error('Pub/sub connection lost (lasted ' . (time() - $start) . ' seconds, worker: ' . $workerId . ').
|
Console::error(
|
||||||
Attempting restart in 5 seconds (attempt #' . $attempts . ')');
|
'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
|
sleep(5); // 5 sec delay between connection attempts
|
||||||
}
|
}
|
||||||
|
|
||||||
$start = time();
|
$start = time();
|
||||||
|
|
||||||
$redis = $register->get('pools')->get('pubsub')->pop()->getResource(); /** @var Redis $redis */
|
$pools = $container->get('pools');
|
||||||
|
$pool = $pools['pools-pubsub-pubsub']['pool'];
|
||||||
|
|
||||||
|
/** @var Connections $connections */
|
||||||
|
$connections = $container->get('connections');
|
||||||
|
$connection = $pool->get();
|
||||||
|
$connections->add($connection, $pool);
|
||||||
|
|
||||||
|
$redis = $connection;
|
||||||
|
|
||||||
|
/** @var Redis $redis */
|
||||||
$redis->setOption(Redis::OPT_READ_TIMEOUT, -1);
|
$redis->setOption(Redis::OPT_READ_TIMEOUT, -1);
|
||||||
|
|
||||||
if ($redis->ping(true)) {
|
if ($redis->ping(true)) {
|
||||||
|
|
@ -375,7 +303,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
||||||
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) {
|
$redis->subscribe(['realtime'], function (Redis $redis, string $channel, string $payload) use ($server, $workerId, $stats, $realtime, $authorization, $container) {
|
||||||
$event = json_decode($payload, true);
|
$event = json_decode($payload, true);
|
||||||
|
|
||||||
if ($event['permissionsChanged'] && isset($event['userId'])) {
|
if ($event['permissionsChanged'] && isset($event['userId'])) {
|
||||||
|
|
@ -384,25 +312,24 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
||||||
|
|
||||||
if ($realtime->hasSubscriber($projectId, 'user:' . $userId)) {
|
if ($realtime->hasSubscriber($projectId, 'user:' . $userId)) {
|
||||||
$connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId]));
|
$connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId]));
|
||||||
$consoleDatabase = getConsoleDB();
|
$dbForConsole = $container->get('dbForConsole');
|
||||||
$project = Authorization::skip(fn () => $consoleDatabase->getDocument('projects', $projectId));
|
|
||||||
$database = getProjectDB($project);
|
|
||||||
|
|
||||||
$user = $database->getDocument('users', $userId);
|
$project = $authorization->skip(fn () => $dbForConsole->getDocument('projects', $projectId));
|
||||||
|
$dbForProject = $container->get('getProjectDB')($project);
|
||||||
|
|
||||||
$roles = Auth::getRoles($user);
|
$user = $dbForProject->getDocument('users', $userId);
|
||||||
|
|
||||||
|
$roles = Auth::getRoles($user, $authorization);
|
||||||
$channels = $realtime->connections[$connection]['channels'];
|
$channels = $realtime->connections[$connection]['channels'];
|
||||||
|
|
||||||
$realtime->unsubscribe($connection);
|
$realtime->unsubscribe($connection);
|
||||||
$realtime->subscribe($projectId, $connection, $roles, $channels);
|
$realtime->subscribe($projectId, $connection, $roles, $channels);
|
||||||
|
|
||||||
$register->get('pools')->reclaim();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$receivers = $realtime->getSubscribers($event);
|
$receivers = $realtime->getSubscribers($event);
|
||||||
|
|
||||||
if (App::isDevelopment() && !empty($receivers)) {
|
if (Http::isDevelopment() && !empty($receivers)) {
|
||||||
Console::log("[Debug][Worker {$workerId}] Receivers: " . count($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}] Receivers Connection IDs: " . json_encode($receivers));
|
||||||
Console::log("[Debug][Worker {$workerId}] Event: " . $payload);
|
Console::log("[Debug][Worker {$workerId}] Event: " . $payload);
|
||||||
|
|
@ -428,30 +355,40 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
||||||
sleep(DATABASE_RECONNECT_SLEEP);
|
sleep(DATABASE_RECONNECT_SLEEP);
|
||||||
continue;
|
continue;
|
||||||
} finally {
|
} finally {
|
||||||
$register->get('pools')->reclaim();
|
($container->get('connections'))->reclaim();
|
||||||
|
$container->refresh('dbForConsole');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Console::error('Failed to restart pub/sub...');
|
Console::error('Failed to restart pub/sub...');
|
||||||
});
|
});
|
||||||
|
|
||||||
$server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $register, $stats, &$realtime, $logError) {
|
$server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $container, $stats, &$realtime, $logError) {
|
||||||
$app = new App('UTC');
|
$authorization = $container->get('authorization');
|
||||||
$request = new Request($request);
|
|
||||||
$response = new Response(new SwooleResponse());
|
$request = new Request(new UtopiaRequest($request));
|
||||||
|
$response = new Response(new UtopiaResponse(new SwooleResponse()));
|
||||||
|
|
||||||
|
$requestInjection = new Dependency();
|
||||||
|
$responseInjection = new Dependency();
|
||||||
|
|
||||||
|
$requestInjection->setName('request')->setCallback(fn () => $request);
|
||||||
|
$responseInjection->setName('response')->setCallback(fn () => $response);
|
||||||
|
|
||||||
|
$container->set($requestInjection);
|
||||||
|
$container->set($responseInjection);
|
||||||
|
|
||||||
Console::info("Connection open (user: {$connection})");
|
Console::info("Connection open (user: {$connection})");
|
||||||
|
|
||||||
App::setResource('pools', fn () => $register->get('pools'));
|
|
||||||
App::setResource('request', fn () => $request);
|
|
||||||
App::setResource('response', fn () => $response);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
/** @var Document $project */
|
/** @var Document $project */
|
||||||
$project = $app->getResource('project');
|
$project = $container->refresh('project')->get('project');
|
||||||
|
|
||||||
|
$container->refresh('dbForProject');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Project Check
|
* Project Check
|
||||||
*/
|
*/
|
||||||
if (empty($project->getId())) {
|
if (empty($project->getId())) {
|
||||||
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, 'Missing or unknown project ID');
|
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, 'Missing or unknown project ID');
|
||||||
|
|
@ -460,15 +397,16 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||||
if (
|
if (
|
||||||
array_key_exists('realtime', $project->getAttribute('apis', []))
|
array_key_exists('realtime', $project->getAttribute('apis', []))
|
||||||
&& !$project->getAttribute('apis', [])['realtime']
|
&& !$project->getAttribute('apis', [])['realtime']
|
||||||
&& !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles()))
|
&& !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles()))
|
||||||
) {
|
) {
|
||||||
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
|
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
$dbForProject = getProjectDB($project);
|
$dbForProject = $container->get('getProjectDB')($project);
|
||||||
$console = $app->getResource('console'); /** @var Document $console */
|
/** @var Document $console */
|
||||||
$user = $app->getResource('user'); /** @var Document $user */
|
$console = $container->get('console');
|
||||||
|
/** @var Document $user */
|
||||||
|
$user = $container->refresh('user')->get('user');
|
||||||
/*
|
/*
|
||||||
* Abuse Check
|
* Abuse Check
|
||||||
*
|
*
|
||||||
|
|
@ -497,7 +435,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||||
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $originValidator->getDescription());
|
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $originValidator->getDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
$roles = Auth::getRoles($user);
|
$authorization = $container->get('authorization');
|
||||||
|
$roles = Auth::getRoles($user, $authorization);
|
||||||
|
|
||||||
$channels = Realtime::convertChannels($request->getQuery('channels', []), $user->getId());
|
$channels = Realtime::convertChannels($request->getQuery('channels', []), $user->getId());
|
||||||
|
|
||||||
|
|
@ -546,25 +485,33 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||||
$server->send([$connection], json_encode($response));
|
$server->send([$connection], json_encode($response));
|
||||||
$server->close($connection, $code);
|
$server->close($connection, $code);
|
||||||
|
|
||||||
if (App::isDevelopment()) {
|
if (Http::isDevelopment()) {
|
||||||
Console::error('[Error] Connection Error');
|
Console::error('[Error] Connection Error');
|
||||||
Console::error('[Error] Code: ' . $response['data']['code']);
|
Console::error('[Error] Code: ' . $response['data']['code']);
|
||||||
Console::error('[Error] Message: ' . $response['data']['message']);
|
Console::error('[Error] Message: ' . $response['data']['message']);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
$register->get('pools')->reclaim();
|
$connections = $container->get('connections');
|
||||||
|
$connections->reclaim();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$server->onMessage(function (int $connection, string $message) use ($server, $register, $realtime, $containerId) {
|
$server->onWorkerStop(function (int $workerId) use ($container) {
|
||||||
|
$connections = $container->get('connections');
|
||||||
|
$connections->reclaim();
|
||||||
|
});
|
||||||
|
|
||||||
|
$server->onMessage(function (int $connection, string $message) use ($server, $container, $realtime, $containerId) {
|
||||||
try {
|
try {
|
||||||
$response = new Response(new SwooleResponse());
|
$response = new Response(new HttpResponse(new SwooleHttpResponse()));
|
||||||
$projectId = $realtime->connections[$connection]['projectId'];
|
$projectId = $realtime->connections[$connection]['projectId'];
|
||||||
$database = getConsoleDB();
|
$database = $container->get('dbForConsole');
|
||||||
|
$authorization = $container->get('authorization');
|
||||||
|
$authentication = $container->get('authentication');
|
||||||
|
|
||||||
if ($projectId !== 'console') {
|
if ($projectId !== 'console') {
|
||||||
$project = Authorization::skip(fn () => $database->getDocument('projects', $projectId));
|
$project = $authorization->skip(fn () => $database->getDocument('projects', $projectId));
|
||||||
$database = getProjectDB($project);
|
$database = $container->get('getProjectDB')($project);
|
||||||
} else {
|
} else {
|
||||||
$project = null;
|
$project = null;
|
||||||
}
|
}
|
||||||
|
|
@ -602,20 +549,21 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
|
||||||
}
|
}
|
||||||
|
|
||||||
$session = Auth::decodeSession($message['data']['session']);
|
$session = Auth::decodeSession($message['data']['session']);
|
||||||
Auth::$unique = $session['id'] ?? '';
|
|
||||||
Auth::$secret = $session['secret'] ?? '';
|
|
||||||
|
|
||||||
$user = $database->getDocument('users', Auth::$unique);
|
$authentication->setUnique($session['id'] ?? '');
|
||||||
|
$authentication->setSecret($session['secret'] ?? '');
|
||||||
|
|
||||||
|
$user = $database->getDocument('users', $authentication->getUnique());
|
||||||
|
|
||||||
if (
|
if (
|
||||||
empty($user->getId()) // Check a document has been found in the DB
|
empty($user->getId()) // Check a document has been found in the DB
|
||||||
|| !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) // Validate user has valid login token
|
|| !Auth::sessionVerify($user->getAttribute('sessions', []), $authentication->getSecret()) // Validate user has valid login token
|
||||||
) {
|
) {
|
||||||
// cookie not valid
|
// cookie not valid
|
||||||
throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Session is not valid.');
|
throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Session is not valid.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$roles = Auth::getRoles($user);
|
$roles = Auth::getRoles($user, $authorization);
|
||||||
$channels = Realtime::convertChannels(array_flip($realtime->connections[$connection]['channels']), $user->getId());
|
$channels = Realtime::convertChannels(array_flip($realtime->connections[$connection]['channels']), $user->getId());
|
||||||
$realtime->subscribe($realtime->connections[$connection]['projectId'], $connection, $roles, $channels);
|
$realtime->subscribe($realtime->connections[$connection]['projectId'], $connection, $roles, $channels);
|
||||||
|
|
||||||
|
|
@ -649,7 +597,8 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
|
||||||
$server->close($connection, $th->getCode());
|
$server->close($connection, $th->getCode());
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
$register->get('pools')->reclaim();
|
($container->get('connections'))->reclaim();
|
||||||
|
$container->refresh('dbForConsole');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ $httpsPort = $this->getParam('httpsPort', '');
|
||||||
$version = $this->getParam('version', '');
|
$version = $this->getParam('version', '');
|
||||||
$organization = $this->getParam('organization', '');
|
$organization = $this->getParam('organization', '');
|
||||||
$image = $this->getParam('image', '');
|
$image = $this->getParam('image', '');
|
||||||
?>services:
|
?>
|
||||||
|
services:
|
||||||
traefik:
|
traefik:
|
||||||
image: traefik:2.11
|
image: traefik:2.11
|
||||||
container_name: appwrite-traefik
|
container_name: appwrite-traefik
|
||||||
|
|
@ -853,7 +854,7 @@ $image = $this->getParam('image', '');
|
||||||
- MYSQL_USER=${_APP_DB_USER}
|
- MYSQL_USER=${_APP_DB_USER}
|
||||||
- MYSQL_PASSWORD=${_APP_DB_PASS}
|
- MYSQL_PASSWORD=${_APP_DB_PASS}
|
||||||
- MARIADB_AUTO_UPGRADE=1
|
- MARIADB_AUTO_UPGRADE=1
|
||||||
command: 'mysqld --innodb-flush-method=fsync'
|
command: 'mysqld --innodb-flush-method=fsync --max_connections=5000'
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:7.2.4-alpine
|
image: redis:7.2.4-alpine
|
||||||
|
|
|
||||||
363
app/worker.php
363
app/worker.php
|
|
@ -2,278 +2,107 @@
|
||||||
|
|
||||||
require_once __DIR__ . '/init.php';
|
require_once __DIR__ . '/init.php';
|
||||||
|
|
||||||
use Appwrite\Event\Audit;
|
|
||||||
use Appwrite\Event\Build;
|
|
||||||
use Appwrite\Event\Certificate;
|
|
||||||
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\Messaging;
|
|
||||||
use Appwrite\Event\Migration;
|
|
||||||
use Appwrite\Event\Usage;
|
|
||||||
use Appwrite\Event\UsageDump;
|
use Appwrite\Event\UsageDump;
|
||||||
use Appwrite\Platform\Appwrite;
|
use Appwrite\Platform\Appwrite;
|
||||||
|
use Appwrite\Utopia\Queue\Connections;
|
||||||
use Swoole\Runtime;
|
use Swoole\Runtime;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Cache\Adapter\Sharding;
|
|
||||||
use Utopia\Cache\Cache;
|
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Config\Config;
|
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\DateTime;
|
use Utopia\Database\DateTime;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\Database\Validator\Authorization;
|
||||||
use Utopia\DSN\DSN;
|
use Utopia\DI\Dependency;
|
||||||
use Utopia\Logger\Log;
|
use Utopia\Logger\Log;
|
||||||
use Utopia\Logger\Logger;
|
use Utopia\Logger\Logger;
|
||||||
use Utopia\Platform\Service;
|
use Utopia\Platform\Service;
|
||||||
use Utopia\Pools\Group;
|
|
||||||
use Utopia\Queue\Connection;
|
use Utopia\Queue\Connection;
|
||||||
use Utopia\Queue\Message;
|
use Utopia\Queue\Message;
|
||||||
use Utopia\Queue\Server;
|
use Utopia\Queue\Worker;
|
||||||
use Utopia\Registry\Registry;
|
use Utopia\Storage\Device\Local;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
|
|
||||||
Authorization::disable();
|
global $registry, $container;
|
||||||
|
|
||||||
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
||||||
|
|
||||||
Server::setResource('register', fn () => $register);
|
$project = new Dependency();
|
||||||
|
$register = new Dependency();
|
||||||
|
$dbForProject = new Dependency();
|
||||||
|
$abuseRetention = new Dependency();
|
||||||
|
$deviceForCache = new Dependency();
|
||||||
|
$auditRetention = new Dependency();
|
||||||
|
$queueForUsageDump = new Dependency();
|
||||||
|
$executionRetention = new Dependency();
|
||||||
|
$deviceForLocalFiles = new Dependency();
|
||||||
|
|
||||||
Server::setResource('dbForConsole', function (Cache $cache, Registry $register) {
|
$register
|
||||||
$pools = $register->get('pools');
|
->setName('register')
|
||||||
$database = $pools
|
->setCallback(fn () => $registry);
|
||||||
->get('console')
|
|
||||||
->pop()
|
|
||||||
->getResource();
|
|
||||||
|
|
||||||
$adapter = new Database($database, $cache);
|
$project
|
||||||
$adapter->setNamespace('_console');
|
->setName('project')
|
||||||
|
->inject('message')
|
||||||
|
->inject('dbForConsole')
|
||||||
|
->setCallback(function (Message $message, Database $dbForConsole) {
|
||||||
|
$payload = $message->getPayload() ?? [];
|
||||||
|
$project = new Document($payload['project'] ?? []);
|
||||||
|
|
||||||
return $adapter;
|
if ($project->getId() === 'console' || $project->isEmpty() || ! empty($project->getInternalId())) {
|
||||||
}, ['cache', 'register']);
|
return $project;
|
||||||
|
|
||||||
Server::setResource('project', function (Message $message, Database $dbForConsole) {
|
|
||||||
$payload = $message->getPayload() ?? [];
|
|
||||||
$project = new Document($payload['project'] ?? []);
|
|
||||||
|
|
||||||
if ($project->getId() === 'console' || $project->isEmpty() || ! empty($project->getInternalId())) {
|
|
||||||
return $project;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $dbForConsole->getDocument('projects', $project->getId());
|
|
||||||
}, ['message', 'dbForConsole']);
|
|
||||||
|
|
||||||
Server::setResource('dbForProject', function (Cache $cache, Registry $register, Message $message, Document $project, Database $dbForConsole) {
|
|
||||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
|
||||||
return $dbForConsole;
|
|
||||||
}
|
|
||||||
|
|
||||||
$pools = $register->get('pools');
|
|
||||||
|
|
||||||
try {
|
|
||||||
$dsn = new DSN($project->getAttribute('database'));
|
|
||||||
} catch (\InvalidArgumentException) {
|
|
||||||
// TODO: Temporary until all projects are using shared tables
|
|
||||||
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$adapter = $pools
|
|
||||||
->get($dsn->getHost())
|
|
||||||
->pop()
|
|
||||||
->getResource();
|
|
||||||
|
|
||||||
$database = new Database($adapter, $cache);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$dsn = new DSN($project->getAttribute('database'));
|
|
||||||
} catch (\InvalidArgumentException) {
|
|
||||||
// TODO: Temporary until all projects are using shared tables
|
|
||||||
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
|
||||||
$database
|
|
||||||
->setSharedTables(true)
|
|
||||||
->setTenant($project->getInternalId())
|
|
||||||
->setNamespace($dsn->getParam('namespace'));
|
|
||||||
} else {
|
|
||||||
$database
|
|
||||||
->setSharedTables(false)
|
|
||||||
->setTenant(null)
|
|
||||||
->setNamespace('_' . $project->getInternalId());
|
|
||||||
}
|
|
||||||
|
|
||||||
return $database;
|
|
||||||
}, ['cache', 'register', 'message', 'project', 'dbForConsole']);
|
|
||||||
|
|
||||||
Server::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) {
|
|
||||||
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
|
|
||||||
|
|
||||||
return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases): Database {
|
|
||||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
|
||||||
return $dbForConsole;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return $dbForConsole->getDocument('projects', $project->getId());
|
||||||
$dsn = new DSN($project->getAttribute('database'));
|
});
|
||||||
} catch (\InvalidArgumentException) {
|
|
||||||
// TODO: Temporary until all projects are using shared tables
|
|
||||||
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($databases[$dsn->getHost()])) {
|
$abuseRetention
|
||||||
$database = $databases[$dsn->getHost()];
|
->setName('abuseRetention')
|
||||||
|
->setCallback(function () {
|
||||||
|
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400));
|
||||||
|
});
|
||||||
|
|
||||||
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
$auditRetention
|
||||||
$database
|
->setName('auditRetention')
|
||||||
->setSharedTables(true)
|
->setCallback(function () {
|
||||||
->setTenant($project->getInternalId())
|
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600));
|
||||||
->setNamespace($dsn->getParam('namespace'));
|
});
|
||||||
} else {
|
|
||||||
$database
|
|
||||||
->setSharedTables(false)
|
|
||||||
->setTenant(null)
|
|
||||||
->setNamespace('_' . $project->getInternalId());
|
|
||||||
}
|
|
||||||
|
|
||||||
return $database;
|
$executionRetention
|
||||||
}
|
->setName('executionRetention')
|
||||||
|
->setCallback(function () {
|
||||||
|
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600));
|
||||||
|
});
|
||||||
|
|
||||||
$dbAdapter = $pools
|
$queueForUsageDump
|
||||||
->get($dsn->getHost())
|
->setName('queueForUsageDump')
|
||||||
->pop()
|
->inject('queue')
|
||||||
->getResource();
|
->setCallback(function (Connection $queue) {
|
||||||
|
return new UsageDump($queue);
|
||||||
|
});
|
||||||
|
|
||||||
$database = new Database($dbAdapter, $cache);
|
$deviceForCache
|
||||||
|
->setName('deviceForCache')
|
||||||
|
->inject('project')
|
||||||
|
->setCallback(function (Document $project) {
|
||||||
|
return getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId());
|
||||||
|
});
|
||||||
|
|
||||||
$databases[$dsn->getHost()] = $database;
|
$deviceForLocalFiles
|
||||||
|
->setName('deviceForLocalFiles')
|
||||||
|
->inject('project')
|
||||||
|
->setCallback(function (Document $project) {
|
||||||
|
return new Local(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
|
||||||
|
});
|
||||||
|
|
||||||
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
$container->set($project);
|
||||||
$database
|
$container->set($register);
|
||||||
->setSharedTables(true)
|
$container->set($dbForProject);
|
||||||
->setTenant($project->getInternalId())
|
$container->set($abuseRetention);
|
||||||
->setNamespace($dsn->getParam('namespace'));
|
$container->set($auditRetention);
|
||||||
} else {
|
$container->set($deviceForCache);
|
||||||
$database
|
$container->set($queueForUsageDump);
|
||||||
->setSharedTables(false)
|
$container->set($executionRetention);
|
||||||
->setTenant(null)
|
$container->set($deviceForLocalFiles);
|
||||||
->setNamespace('_' . $project->getInternalId());
|
|
||||||
}
|
|
||||||
|
|
||||||
return $database;
|
|
||||||
};
|
|
||||||
}, ['pools', 'dbForConsole', 'cache']);
|
|
||||||
|
|
||||||
Server::setResource('abuseRetention', function () {
|
|
||||||
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400));
|
|
||||||
});
|
|
||||||
|
|
||||||
Server::setResource('auditRetention', function () {
|
|
||||||
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600));
|
|
||||||
});
|
|
||||||
|
|
||||||
Server::setResource('executionRetention', function () {
|
|
||||||
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600));
|
|
||||||
});
|
|
||||||
|
|
||||||
Server::setResource('cache', function (Registry $register) {
|
|
||||||
$pools = $register->get('pools');
|
|
||||||
$list = Config::getParam('pools-cache', []);
|
|
||||||
$adapters = [];
|
|
||||||
|
|
||||||
foreach ($list as $value) {
|
|
||||||
$adapters[] = $pools
|
|
||||||
->get($value)
|
|
||||||
->pop()
|
|
||||||
->getResource()
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Cache(new Sharding($adapters));
|
|
||||||
}, ['register']);
|
|
||||||
|
|
||||||
Server::setResource('log', fn () => new Log());
|
|
||||||
|
|
||||||
Server::setResource('queueForUsage', function (Connection $queue) {
|
|
||||||
return new Usage($queue);
|
|
||||||
}, ['queue']);
|
|
||||||
|
|
||||||
Server::setResource('queueForUsageDump', function (Connection $queue) {
|
|
||||||
return new UsageDump($queue);
|
|
||||||
}, ['queue']);
|
|
||||||
|
|
||||||
Server::setResource('queue', function (Group $pools) {
|
|
||||||
return $pools->get('queue')->pop()->getResource();
|
|
||||||
}, ['pools']);
|
|
||||||
|
|
||||||
Server::setResource('queueForDatabase', function (Connection $queue) {
|
|
||||||
return new EventDatabase($queue);
|
|
||||||
}, ['queue']);
|
|
||||||
|
|
||||||
Server::setResource('queueForMessaging', function (Connection $queue) {
|
|
||||||
return new Messaging($queue);
|
|
||||||
}, ['queue']);
|
|
||||||
|
|
||||||
Server::setResource('queueForMails', function (Connection $queue) {
|
|
||||||
return new Mail($queue);
|
|
||||||
}, ['queue']);
|
|
||||||
|
|
||||||
Server::setResource('queueForBuilds', function (Connection $queue) {
|
|
||||||
return new Build($queue);
|
|
||||||
}, ['queue']);
|
|
||||||
|
|
||||||
Server::setResource('queueForDeletes', function (Connection $queue) {
|
|
||||||
return new Delete($queue);
|
|
||||||
}, ['queue']);
|
|
||||||
|
|
||||||
Server::setResource('queueForEvents', function (Connection $queue) {
|
|
||||||
return new Event($queue);
|
|
||||||
}, ['queue']);
|
|
||||||
|
|
||||||
Server::setResource('queueForAudits', function (Connection $queue) {
|
|
||||||
return new Audit($queue);
|
|
||||||
}, ['queue']);
|
|
||||||
|
|
||||||
Server::setResource('queueForFunctions', function (Connection $queue) {
|
|
||||||
return new Func($queue);
|
|
||||||
}, ['queue']);
|
|
||||||
|
|
||||||
Server::setResource('queueForCertificates', function (Connection $queue) {
|
|
||||||
return new Certificate($queue);
|
|
||||||
}, ['queue']);
|
|
||||||
|
|
||||||
Server::setResource('queueForMigrations', function (Connection $queue) {
|
|
||||||
return new Migration($queue);
|
|
||||||
}, ['queue']);
|
|
||||||
|
|
||||||
Server::setResource('logger', function (Registry $register) {
|
|
||||||
return $register->get('logger');
|
|
||||||
}, ['register']);
|
|
||||||
|
|
||||||
Server::setResource('pools', function (Registry $register) {
|
|
||||||
return $register->get('pools');
|
|
||||||
}, ['register']);
|
|
||||||
|
|
||||||
Server::setResource('deviceForFunctions', function (Document $project) {
|
|
||||||
return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId());
|
|
||||||
}, ['project']);
|
|
||||||
|
|
||||||
Server::setResource('deviceForFiles', function (Document $project) {
|
|
||||||
return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
|
|
||||||
}, ['project']);
|
|
||||||
|
|
||||||
Server::setResource('deviceForBuilds', function (Document $project) {
|
|
||||||
return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId());
|
|
||||||
}, ['project']);
|
|
||||||
|
|
||||||
Server::setResource('deviceForCache', function (Document $project) {
|
|
||||||
return getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId());
|
|
||||||
}, ['project']);
|
|
||||||
|
|
||||||
|
|
||||||
$pools = $register->get('pools');
|
|
||||||
$platform = new Appwrite();
|
$platform = new Appwrite();
|
||||||
$args = $platform->getEnv('argv');
|
$args = $platform->getEnv('argv');
|
||||||
|
|
||||||
|
|
@ -292,6 +121,13 @@ if (\str_starts_with($workerName, 'databases')) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
$connection = new Connection\Redis(
|
||||||
|
System::getEnv('_APP_REDIS_HOST', 'redis'),
|
||||||
|
System::getEnv('_APP_REDIS_PORT', '6379'),
|
||||||
|
System::getEnv('_APP_REDIS_USER', ''),
|
||||||
|
System::getEnv('_APP_REDIS_PASS', '')
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Any worker can be configured with the following env vars:
|
* Any worker can be configured with the following env vars:
|
||||||
* - _APP_WORKERS_NUM The total number of worker processes
|
* - _APP_WORKERS_NUM The total number of worker processes
|
||||||
|
|
@ -300,32 +136,35 @@ try {
|
||||||
*/
|
*/
|
||||||
$platform->init(Service::TYPE_WORKER, [
|
$platform->init(Service::TYPE_WORKER, [
|
||||||
'workersNum' => System::getEnv('_APP_WORKERS_NUM', 1),
|
'workersNum' => System::getEnv('_APP_WORKERS_NUM', 1),
|
||||||
'connection' => $pools->get('queue')->pop()->getResource(),
|
'connection' => $connection,
|
||||||
'workerName' => strtolower($workerName) ?? null,
|
'workerName' => strtolower($workerName) ?? null,
|
||||||
'queueName' => $queueName
|
'queueName' => $queueName
|
||||||
]);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
Console::error($e->getMessage() . ', File: ' . $e->getFile() . ', Line: ' . $e->getLine());
|
Console::error($e->getMessage() . ', File: ' . $e->getFile() . ', Line: ' . $e->getLine());
|
||||||
}
|
}
|
||||||
|
|
||||||
$worker = $platform->getWorker();
|
Worker::init()
|
||||||
|
->inject('authorization')
|
||||||
$worker
|
->action(function (Authorization $authorization) {
|
||||||
->shutdown()
|
$authorization->disable();
|
||||||
->inject('pools')
|
|
||||||
->action(function (Group $pools) {
|
|
||||||
$pools->reclaim();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$worker
|
Worker::shutdown()
|
||||||
->error()
|
->inject('connections')
|
||||||
|
->action(function (Connections $connections) {
|
||||||
|
$connections->reclaim();
|
||||||
|
});
|
||||||
|
|
||||||
|
Worker::error()
|
||||||
->inject('error')
|
->inject('error')
|
||||||
->inject('logger')
|
->inject('logger')
|
||||||
->inject('log')
|
->inject('log')
|
||||||
->inject('pools')
|
->inject('connections')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($queueName) {
|
->inject('authorization')
|
||||||
$pools->reclaim();
|
->action(function (Throwable $error, ?Logger $logger, Log $log, Connections $connections, Document $project, Authorization $authorization) use ($queueName) {
|
||||||
|
$connections->reclaim();
|
||||||
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||||
|
|
||||||
if ($logger) {
|
if ($logger) {
|
||||||
|
|
@ -341,7 +180,7 @@ $worker
|
||||||
$log->addExtra('file', $error->getFile());
|
$log->addExtra('file', $error->getFile());
|
||||||
$log->addExtra('line', $error->getLine());
|
$log->addExtra('line', $error->getLine());
|
||||||
$log->addExtra('trace', $error->getTraceAsString());
|
$log->addExtra('trace', $error->getTraceAsString());
|
||||||
$log->addExtra('roles', Authorization::getRoles());
|
$log->addExtra('roles', $authorization->getRoles());
|
||||||
|
|
||||||
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
|
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
|
||||||
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
||||||
|
|
@ -360,9 +199,7 @@ $worker
|
||||||
Console::error('[Error] Line: ' . $error->getLine());
|
Console::error('[Error] Line: ' . $error->getLine());
|
||||||
});
|
});
|
||||||
|
|
||||||
$worker->workerStart()
|
$platform
|
||||||
->action(function () use ($workerName) {
|
->getWorker()
|
||||||
Console::info("Worker $workerName started");
|
->setContainer($container)
|
||||||
});
|
->start();
|
||||||
|
|
||||||
$worker->start();
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
"description": "End to end backend server for frontend and mobile apps.",
|
"description": "End to end backend server for frontend and mobile apps.",
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
|
"minimum-stability": "stable",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Eldad Fux",
|
"name": "Eldad Fux",
|
||||||
|
|
@ -14,7 +15,8 @@
|
||||||
"test": "vendor/bin/phpunit",
|
"test": "vendor/bin/phpunit",
|
||||||
"lint": "vendor/bin/pint --test",
|
"lint": "vendor/bin/pint --test",
|
||||||
"format": "vendor/bin/pint",
|
"format": "vendor/bin/pint",
|
||||||
"bench": "vendor/bin/phpbench run --report=benchmark"
|
"bench": "vendor/bin/phpbench run --report=benchmark",
|
||||||
|
"check": "./vendor/bin/phpstan analyse -c phpstan.neon --memory-limit 1G app src tests"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|
@ -45,33 +47,33 @@
|
||||||
"ext-sockets": "*",
|
"ext-sockets": "*",
|
||||||
"appwrite/php-runtimes": "0.15.*",
|
"appwrite/php-runtimes": "0.15.*",
|
||||||
"appwrite/php-clamav": "2.0.*",
|
"appwrite/php-clamav": "2.0.*",
|
||||||
"utopia-php/abuse": "0.43.0",
|
"utopia-php/abuse": "0.46.*",
|
||||||
"utopia-php/analytics": "0.10.*",
|
"utopia-php/analytics": "0.13.*",
|
||||||
"utopia-php/audit": "0.43.0",
|
"utopia-php/audit": "0.46.*",
|
||||||
"utopia-php/cache": "0.10.*",
|
"utopia-php/cache": "0.10.*",
|
||||||
"utopia-php/cli": "0.15.*",
|
"utopia-php/cli": "0.19.*",
|
||||||
"utopia-php/config": "0.2.*",
|
"utopia-php/config": "0.2.*",
|
||||||
"utopia-php/database": "0.53.5",
|
"utopia-php/database": "0.55.1",
|
||||||
"utopia-php/domains": "0.5.*",
|
"utopia-php/domains": "0.6.*",
|
||||||
"utopia-php/dsn": "0.2.1",
|
"utopia-php/dsn": "0.2.*",
|
||||||
"utopia-php/framework": "0.33.*",
|
"utopia-php/framework": "1.0.*",
|
||||||
"utopia-php/fetch": "0.2.*",
|
"utopia-php/fetch": "0.2.*",
|
||||||
"utopia-php/image": "0.7.*",
|
"utopia-php/image": "0.7.*",
|
||||||
"utopia-php/locale": "0.4.*",
|
"utopia-php/locale": "0.4.*",
|
||||||
"utopia-php/logger": "0.6.*",
|
"utopia-php/logger": "0.6.*",
|
||||||
"utopia-php/messaging": "0.12.*",
|
"utopia-php/messaging": "0.12.*",
|
||||||
"utopia-php/migration": "0.6.*",
|
"utopia-php/migration": "0.7.*",
|
||||||
"utopia-php/orchestration": "0.9.*",
|
"utopia-php/orchestration": "0.15.*",
|
||||||
"utopia-php/platform": "0.7.*",
|
"utopia-php/platform": "0.8.*",
|
||||||
|
"utopia-php/view": "0.2.*",
|
||||||
"utopia-php/pools": "0.5.*",
|
"utopia-php/pools": "0.5.*",
|
||||||
"utopia-php/preloader": "0.2.*",
|
"utopia-php/preloader": "0.2.*",
|
||||||
"utopia-php/queue": "0.7.*",
|
"utopia-php/queue": "0.8.*",
|
||||||
"utopia-php/registry": "0.5.*",
|
"utopia-php/registry": "0.5.*",
|
||||||
"utopia-php/storage": "0.18.*",
|
"utopia-php/storage": "0.19.*",
|
||||||
"utopia-php/swoole": "0.8.*",
|
|
||||||
"utopia-php/system": "0.8.*",
|
"utopia-php/system": "0.8.*",
|
||||||
"utopia-php/vcs": "0.8.*",
|
"utopia-php/vcs": "0.9.*",
|
||||||
"utopia-php/websocket": "0.1.*",
|
"utopia-php/websocket": "0.2.*",
|
||||||
"matomo/device-detector": "6.1.*",
|
"matomo/device-detector": "6.1.*",
|
||||||
"dragonmantank/cron-expression": "3.3.2",
|
"dragonmantank/cron-expression": "3.3.2",
|
||||||
"phpmailer/phpmailer": "6.9.1",
|
"phpmailer/phpmailer": "6.9.1",
|
||||||
|
|
@ -88,7 +90,8 @@
|
||||||
"swoole/ide-helper": "5.1.2",
|
"swoole/ide-helper": "5.1.2",
|
||||||
"textalk/websocket": "1.5.7",
|
"textalk/websocket": "1.5.7",
|
||||||
"laravel/pint": "^1.14",
|
"laravel/pint": "^1.14",
|
||||||
"phpbench/phpbench": "^1.2"
|
"phpbench/phpbench": "^1.2",
|
||||||
|
"phpstan/phpstan": "1.8.*"
|
||||||
},
|
},
|
||||||
"provide": {
|
"provide": {
|
||||||
"ext-phpiredis": "*"
|
"ext-phpiredis": "*"
|
||||||
|
|
|
||||||
517
composer.lock
generated
517
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "1884e3a2966762c4a955842426b64f6c",
|
"content-hash": "b808f001c3ff4c0396cf51f46e8b314a",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "adhocore/jwt",
|
"name": "adhocore/jwt",
|
||||||
|
|
@ -1430,16 +1430,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/abuse",
|
"name": "utopia-php/abuse",
|
||||||
"version": "0.43.0",
|
"version": "0.46.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/abuse.git",
|
"url": "https://github.com/utopia-php/abuse.git",
|
||||||
"reference": "6346a3b4c5177a43160035a7289e30fdfb0790d6"
|
"reference": "ab09ecf6ceac5420020e6046ec651c155005d3c3"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/6346a3b4c5177a43160035a7289e30fdfb0790d6",
|
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/ab09ecf6ceac5420020e6046ec651c155005d3c3",
|
||||||
"reference": "6346a3b4c5177a43160035a7289e30fdfb0790d6",
|
"reference": "ab09ecf6ceac5420020e6046ec651c155005d3c3",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -1447,7 +1447,7 @@
|
||||||
"ext-pdo": "*",
|
"ext-pdo": "*",
|
||||||
"ext-redis": "*",
|
"ext-redis": "*",
|
||||||
"php": ">=8.0",
|
"php": ">=8.0",
|
||||||
"utopia-php/database": "0.53.*"
|
"utopia-php/database": "0.55.*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"laravel/pint": "1.5.*",
|
"laravel/pint": "1.5.*",
|
||||||
|
|
@ -1475,27 +1475,28 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/abuse/issues",
|
"issues": "https://github.com/utopia-php/abuse/issues",
|
||||||
"source": "https://github.com/utopia-php/abuse/tree/0.43.0"
|
"source": "https://github.com/utopia-php/abuse/tree/0.46.0"
|
||||||
},
|
},
|
||||||
"time": "2024-08-30T05:17:23+00:00"
|
"time": "2024-10-07T19:09:29+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/analytics",
|
"name": "utopia-php/analytics",
|
||||||
"version": "0.10.2",
|
"version": "0.13.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/analytics.git",
|
"url": "https://github.com/utopia-php/analytics.git",
|
||||||
"reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f"
|
"reference": "3dace02af5d4190623f88fb6e02f5559a99f14c4"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f",
|
"url": "https://api.github.com/repos/utopia-php/analytics/zipball/3dace02af5d4190623f88fb6e02f5559a99f14c4",
|
||||||
"reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f",
|
"reference": "3dace02af5d4190623f88fb6e02f5559a99f14c4",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.0",
|
"php": ">=8.0",
|
||||||
"utopia-php/cli": "^0.15.0"
|
"utopia-php/cli": "0.19.*",
|
||||||
|
"utopia-php/system": "0.8.*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"laravel/pint": "dev-main",
|
"laravel/pint": "dev-main",
|
||||||
|
|
@ -1521,27 +1522,27 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/analytics/issues",
|
"issues": "https://github.com/utopia-php/analytics/issues",
|
||||||
"source": "https://github.com/utopia-php/analytics/tree/0.10.2"
|
"source": "https://github.com/utopia-php/analytics/tree/0.13.0"
|
||||||
},
|
},
|
||||||
"time": "2023-03-22T12:01:09+00:00"
|
"time": "2024-09-05T16:19:26+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/audit",
|
"name": "utopia-php/audit",
|
||||||
"version": "0.43.0",
|
"version": "0.46.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/audit.git",
|
"url": "https://github.com/utopia-php/audit.git",
|
||||||
"reference": "cef22b5dc6a6d28fcd522f41c7bf7ded4a4dfd3e"
|
"reference": "660bb322ca1e6a9fa121610a85930e65863e1e5d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/audit/zipball/cef22b5dc6a6d28fcd522f41c7bf7ded4a4dfd3e",
|
"url": "https://api.github.com/repos/utopia-php/audit/zipball/660bb322ca1e6a9fa121610a85930e65863e1e5d",
|
||||||
"reference": "cef22b5dc6a6d28fcd522f41c7bf7ded4a4dfd3e",
|
"reference": "660bb322ca1e6a9fa121610a85930e65863e1e5d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.0",
|
"php": ">=8.0",
|
||||||
"utopia-php/database": "0.53.*"
|
"utopia-php/database": "0.55.*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"laravel/pint": "1.5.*",
|
"laravel/pint": "1.5.*",
|
||||||
|
|
@ -1568,9 +1569,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/audit/issues",
|
"issues": "https://github.com/utopia-php/audit/issues",
|
||||||
"source": "https://github.com/utopia-php/audit/tree/0.43.0"
|
"source": "https://github.com/utopia-php/audit/tree/0.46.0"
|
||||||
},
|
},
|
||||||
"time": "2024-08-30T05:17:36+00:00"
|
"time": "2024-10-07T19:10:12+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/cache",
|
"name": "utopia-php/cache",
|
||||||
|
|
@ -1624,27 +1625,29 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/cli",
|
"name": "utopia-php/cli",
|
||||||
"version": "0.15.0",
|
"version": "0.19.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/cli.git",
|
"url": "https://github.com/utopia-php/cli.git",
|
||||||
"reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea"
|
"reference": "f8af1d6087f498bc1f0191750a118d357ded9948"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea",
|
"url": "https://api.github.com/repos/utopia-php/cli/zipball/f8af1d6087f498bc1f0191750a118d357ded9948",
|
||||||
"reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea",
|
"reference": "f8af1d6087f498bc1f0191750a118d357ded9948",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.4",
|
"php": ">=7.4",
|
||||||
"utopia-php/framework": "0.*.*"
|
"utopia-php/di": "0.1.*",
|
||||||
|
"utopia-php/framework": "1.0.*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"laravel/pint": "1.2.*",
|
"laravel/pint": "1.2.*",
|
||||||
|
"phpstan/phpstan": "^1.10",
|
||||||
"phpunit/phpunit": "^9.3",
|
"phpunit/phpunit": "^9.3",
|
||||||
"squizlabs/php_codesniffer": "^3.6",
|
"squizlabs/php_codesniffer": "^3.6",
|
||||||
"vimeo/psalm": "4.0.1"
|
"swoole/ide-helper": "4.8.8"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
|
@ -1667,9 +1670,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/cli/issues",
|
"issues": "https://github.com/utopia-php/cli/issues",
|
||||||
"source": "https://github.com/utopia-php/cli/tree/0.15.0"
|
"source": "https://github.com/utopia-php/cli/tree/0.19.0"
|
||||||
},
|
},
|
||||||
"time": "2023-03-01T05:55:14+00:00"
|
"time": "2024-09-05T15:46:56+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/config",
|
"name": "utopia-php/config",
|
||||||
|
|
@ -1724,16 +1727,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/database",
|
"name": "utopia-php/database",
|
||||||
"version": "0.53.5",
|
"version": "0.55.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/database.git",
|
"url": "https://github.com/utopia-php/database.git",
|
||||||
"reference": "689ba22063bf46def385da8695ba7a921e81e38d"
|
"reference": "6f44079999491feb0ef85686f6b2ac7ef54db828"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/689ba22063bf46def385da8695ba7a921e81e38d",
|
"url": "https://api.github.com/repos/utopia-php/database/zipball/6f44079999491feb0ef85686f6b2ac7ef54db828",
|
||||||
"reference": "689ba22063bf46def385da8695ba7a921e81e38d",
|
"reference": "6f44079999491feb0ef85686f6b2ac7ef54db828",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -1741,7 +1744,7 @@
|
||||||
"ext-pdo": "*",
|
"ext-pdo": "*",
|
||||||
"php": ">=8.0",
|
"php": ">=8.0",
|
||||||
"utopia-php/cache": "0.10.*",
|
"utopia-php/cache": "0.10.*",
|
||||||
"utopia-php/framework": "0.33.*",
|
"utopia-php/framework": "1.0.*",
|
||||||
"utopia-php/mongo": "0.3.*"
|
"utopia-php/mongo": "0.3.*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
|
@ -1752,7 +1755,7 @@
|
||||||
"phpunit/phpunit": "9.6.*",
|
"phpunit/phpunit": "9.6.*",
|
||||||
"rregeer/phpunit-coverage-check": "0.3.*",
|
"rregeer/phpunit-coverage-check": "0.3.*",
|
||||||
"swoole/ide-helper": "5.1.3",
|
"swoole/ide-helper": "5.1.3",
|
||||||
"utopia-php/cli": "0.14.*"
|
"utopia-php/cli": "0.19.*"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
|
@ -1774,30 +1777,79 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/database/issues",
|
"issues": "https://github.com/utopia-php/database/issues",
|
||||||
"source": "https://github.com/utopia-php/database/tree/0.53.5"
|
"source": "https://github.com/utopia-php/database/tree/0.55.1"
|
||||||
},
|
},
|
||||||
"time": "2024-09-24T08:43:10+00:00"
|
"time": "2024-10-07T18:52:26+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/domains",
|
"name": "utopia-php/di",
|
||||||
"version": "0.5.0",
|
"version": "0.1.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/domains.git",
|
"url": "https://github.com/utopia-php/di.git",
|
||||||
"reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c"
|
"reference": "22490c95f7ac3898ed1c33f1b1b5dd577305ee31"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/domains/zipball/bf07f60326f8389f378ddf6fcde86217e5cfe18c",
|
"url": "https://api.github.com/repos/utopia-php/di/zipball/22490c95f7ac3898ed1c33f1b1b5dd577305ee31",
|
||||||
"reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c",
|
"reference": "22490c95f7ac3898ed1c33f1b1b5dd577305ee31",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.2"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"laravel/pint": "^1.2",
|
||||||
|
"phpbench/phpbench": "^1.2",
|
||||||
|
"phpstan/phpstan": "^1.10",
|
||||||
|
"phpunit/phpunit": "^9.5.25",
|
||||||
|
"swoole/ide-helper": "4.8.3"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Utopia\\": "src/",
|
||||||
|
"Tests\\E2E\\": "tests/e2e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "A simple and lite library for managing dependency injections",
|
||||||
|
"keywords": [
|
||||||
|
"framework",
|
||||||
|
"http",
|
||||||
|
"php",
|
||||||
|
"upf"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/utopia-php/di/issues",
|
||||||
|
"source": "https://github.com/utopia-php/di/tree/0.1.0"
|
||||||
|
},
|
||||||
|
"time": "2024-08-08T14:35:19+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "utopia-php/domains",
|
||||||
|
"version": "0.6.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/utopia-php/domains.git",
|
||||||
|
"reference": "5c70b0f524deeb1fccc3962ad1da650ae2c94cda"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/utopia-php/domains/zipball/5c70b0f524deeb1fccc3962ad1da650ae2c94cda",
|
||||||
|
"reference": "5c70b0f524deeb1fccc3962ad1da650ae2c94cda",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.0",
|
"php": ">=8.0",
|
||||||
"utopia-php/framework": "0.*.*"
|
"utopia-php/framework": "1.0.*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"laravel/pint": "1.2.*",
|
"laravel/pint": "1.2.*",
|
||||||
|
"phpstan/phpstan": "1.9.x-dev",
|
||||||
"phpunit/phpunit": "^9.3"
|
"phpunit/phpunit": "^9.3"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
|
|
@ -1834,9 +1886,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/domains/issues",
|
"issues": "https://github.com/utopia-php/domains/issues",
|
||||||
"source": "https://github.com/utopia-php/domains/tree/0.5.0"
|
"source": "https://github.com/utopia-php/domains/tree/0.6.0"
|
||||||
},
|
},
|
||||||
"time": "2024-01-03T22:04:27+00:00"
|
"time": "2024-09-05T16:21:11+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/dsn",
|
"name": "utopia-php/dsn",
|
||||||
|
|
@ -1926,26 +1978,30 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/framework",
|
"name": "utopia-php/framework",
|
||||||
"version": "0.33.8",
|
"version": "1.0.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/http.git",
|
"url": "https://github.com/utopia-php/http.git",
|
||||||
"reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5"
|
"reference": "fc63ec61c720190a5ea5bb484c615145850951e6"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/http/zipball/a7f577540a25cb90896fef2b64767bf8d700f3c5",
|
"url": "https://api.github.com/repos/utopia-php/http/zipball/fc63ec61c720190a5ea5bb484c615145850951e6",
|
||||||
"reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5",
|
"reference": "fc63ec61c720190a5ea5bb484c615145850951e6",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.0"
|
"ext-swoole": "*",
|
||||||
|
"php": ">=8.0",
|
||||||
|
"utopia-php/servers": "0.1.*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
"ext-xdebug": "*",
|
||||||
"laravel/pint": "^1.2",
|
"laravel/pint": "^1.2",
|
||||||
"phpbench/phpbench": "^1.2",
|
"phpbench/phpbench": "^1.2",
|
||||||
"phpstan/phpstan": "^1.10",
|
"phpstan/phpstan": "^1.10",
|
||||||
"phpunit/phpunit": "^9.5.25"
|
"phpunit/phpunit": "^9.5.25",
|
||||||
|
"swoole/ide-helper": "4.8.3"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
|
@ -1957,17 +2013,18 @@
|
||||||
"license": [
|
"license": [
|
||||||
"MIT"
|
"MIT"
|
||||||
],
|
],
|
||||||
"description": "A simple, light and advanced PHP framework",
|
"description": "A simple, light and advanced PHP HTTP framework",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"framework",
|
"framework",
|
||||||
|
"http",
|
||||||
"php",
|
"php",
|
||||||
"upf"
|
"upf"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/http/issues",
|
"issues": "https://github.com/utopia-php/http/issues",
|
||||||
"source": "https://github.com/utopia-php/http/tree/0.33.8"
|
"source": "https://github.com/utopia-php/http/tree/1.0.2"
|
||||||
},
|
},
|
||||||
"time": "2024-08-15T14:10:09+00:00"
|
"time": "2024-09-10T09:04:19+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/image",
|
"name": "utopia-php/image",
|
||||||
|
|
@ -2175,16 +2232,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/migration",
|
"name": "utopia-php/migration",
|
||||||
"version": "0.6.4",
|
"version": "0.7.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/migration.git",
|
"url": "https://github.com/utopia-php/migration.git",
|
||||||
"reference": "e43ef283f1386084e11d1ffe093fb6c6d7a6ce6c"
|
"reference": "aa996ebfd3223fca2aa7d8b9aa31457c95344084"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/e43ef283f1386084e11d1ffe093fb6c6d7a6ce6c",
|
"url": "https://api.github.com/repos/utopia-php/migration/zipball/aa996ebfd3223fca2aa7d8b9aa31457c95344084",
|
||||||
"reference": "e43ef283f1386084e11d1ffe093fb6c6d7a6ce6c",
|
"reference": "aa996ebfd3223fca2aa7d8b9aa31457c95344084",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -2192,17 +2249,16 @@
|
||||||
"ext-curl": "*",
|
"ext-curl": "*",
|
||||||
"ext-openssl": "*",
|
"ext-openssl": "*",
|
||||||
"php": "8.3.*",
|
"php": "8.3.*",
|
||||||
"utopia-php/database": "0.53.*",
|
"utopia-php/database": "0.55.*",
|
||||||
"utopia-php/dsn": "0.2.*",
|
"utopia-php/dsn": "0.2.*",
|
||||||
"utopia-php/framework": "0.33.*",
|
"utopia-php/storage": "0.19.*"
|
||||||
"utopia-php/storage": "0.18.*"
|
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"ext-pdo": "*",
|
"ext-pdo": "*",
|
||||||
"laravel/pint": "1.17.*",
|
"laravel/pint": "1.17.*",
|
||||||
"phpstan/phpstan": "1.11.*",
|
"phpstan/phpstan": "1.11.*",
|
||||||
"phpunit/phpunit": "11.2.*",
|
"phpunit/phpunit": "11.2.*",
|
||||||
"utopia-php/cli": "0.16.*",
|
"utopia-php/cli": "0.19.*",
|
||||||
"vlucas/phpdotenv": "5.6.*"
|
"vlucas/phpdotenv": "5.6.*"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
|
|
@ -2225,9 +2281,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/migration/issues",
|
"issues": "https://github.com/utopia-php/migration/issues",
|
||||||
"source": "https://github.com/utopia-php/migration/tree/0.6.4"
|
"source": "https://github.com/utopia-php/migration/tree/0.7.0"
|
||||||
},
|
},
|
||||||
"time": "2024-10-02T15:16:36+00:00"
|
"time": "2024-10-07T19:00:18+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/mongo",
|
"name": "utopia-php/mongo",
|
||||||
|
|
@ -2291,26 +2347,26 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/orchestration",
|
"name": "utopia-php/orchestration",
|
||||||
"version": "0.9.1",
|
"version": "0.15.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/orchestration.git",
|
"url": "https://github.com/utopia-php/orchestration.git",
|
||||||
"reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0"
|
"reference": "cd55650ba5f13118c3580048e6dd86b604f9a5b3"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0",
|
"url": "https://api.github.com/repos/utopia-php/orchestration/zipball/cd55650ba5f13118c3580048e6dd86b604f9a5b3",
|
||||||
"reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0",
|
"reference": "cd55650ba5f13118c3580048e6dd86b604f9a5b3",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.0",
|
"php": ">=8.0",
|
||||||
"utopia-php/cli": "0.15.*"
|
"utopia-php/cli": "0.19.*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"laravel/pint": "^1.2",
|
"laravel/pint": "^1.2",
|
||||||
"phpunit/phpunit": "^9.3",
|
"phpstan/phpstan": "^1.10",
|
||||||
"vimeo/psalm": "4.0.1"
|
"phpunit/phpunit": "^9.3"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
|
@ -2335,31 +2391,32 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/orchestration/issues",
|
"issues": "https://github.com/utopia-php/orchestration/issues",
|
||||||
"source": "https://github.com/utopia-php/orchestration/tree/0.9.1"
|
"source": "https://github.com/utopia-php/orchestration/tree/0.15.0"
|
||||||
},
|
},
|
||||||
"time": "2023-03-17T15:05:06+00:00"
|
"time": "2024-09-05T16:28:02+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/platform",
|
"name": "utopia-php/platform",
|
||||||
"version": "0.7.0",
|
"version": "0.8.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/platform.git",
|
"url": "https://github.com/utopia-php/platform.git",
|
||||||
"reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52"
|
"reference": "95d57f38a4001c7189a66885c485ac635d305234"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/platform/zipball/beeea0f2c9bce14a6869fc5c87a1047cdecb5c52",
|
"url": "https://api.github.com/repos/utopia-php/platform/zipball/95d57f38a4001c7189a66885c485ac635d305234",
|
||||||
"reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52",
|
"reference": "95d57f38a4001c7189a66885c485ac635d305234",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"ext-redis": "*",
|
"ext-redis": "*",
|
||||||
"php": ">=8.0",
|
"php": ">=8.0",
|
||||||
"utopia-php/cli": "0.15.*",
|
"utopia-php/cli": "0.19.*",
|
||||||
"utopia-php/framework": "0.33.*",
|
"utopia-php/framework": "1.0.*",
|
||||||
"utopia-php/queue": "0.7.*"
|
"utopia-php/queue": "0.8.*",
|
||||||
|
"utopia-php/servers": "0.1.*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"laravel/pint": "1.2.*",
|
"laravel/pint": "1.2.*",
|
||||||
|
|
@ -2385,9 +2442,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/platform/issues",
|
"issues": "https://github.com/utopia-php/platform/issues",
|
||||||
"source": "https://github.com/utopia-php/platform/tree/0.7.0"
|
"source": "https://github.com/utopia-php/platform/tree/0.8.1"
|
||||||
},
|
},
|
||||||
"time": "2024-05-08T17:00:55+00:00"
|
"time": "2024-09-06T02:33:27+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/pools",
|
"name": "utopia-php/pools",
|
||||||
|
|
@ -2495,22 +2552,23 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/queue",
|
"name": "utopia-php/queue",
|
||||||
"version": "0.7.0",
|
"version": "0.8.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/queue.git",
|
"url": "https://github.com/utopia-php/queue.git",
|
||||||
"reference": "917565256eb94bcab7246f7a746b1a486813761b"
|
"reference": "a518b271f8c158d6e66e36972f767189111033c2"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/queue/zipball/917565256eb94bcab7246f7a746b1a486813761b",
|
"url": "https://api.github.com/repos/utopia-php/queue/zipball/a518b271f8c158d6e66e36972f767189111033c2",
|
||||||
"reference": "917565256eb94bcab7246f7a746b1a486813761b",
|
"reference": "a518b271f8c158d6e66e36972f767189111033c2",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.0",
|
"php": ">=8.0",
|
||||||
"utopia-php/cli": "0.15.*",
|
"utopia-php/cli": "0.19.*",
|
||||||
"utopia-php/framework": "0.*.*"
|
"utopia-php/di": "0.1.*",
|
||||||
|
"utopia-php/servers": "0.1.*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"laravel/pint": "^0.2.3",
|
"laravel/pint": "^0.2.3",
|
||||||
|
|
@ -2520,6 +2578,7 @@
|
||||||
"workerman/workerman": "^4.0"
|
"workerman/workerman": "^4.0"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
|
"ext-redis": "Needed to support Redis connections",
|
||||||
"ext-swoole": "Needed to support Swoole.",
|
"ext-swoole": "Needed to support Swoole.",
|
||||||
"workerman/workerman": "Needed to support Workerman."
|
"workerman/workerman": "Needed to support Workerman."
|
||||||
},
|
},
|
||||||
|
|
@ -2550,9 +2609,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/queue/issues",
|
"issues": "https://github.com/utopia-php/queue/issues",
|
||||||
"source": "https://github.com/utopia-php/queue/tree/0.7.0"
|
"source": "https://github.com/utopia-php/queue/tree/0.8.0"
|
||||||
},
|
},
|
||||||
"time": "2024-01-17T19:00:43+00:00"
|
"time": "2024-09-05T16:33:01+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/registry",
|
"name": "utopia-php/registry",
|
||||||
|
|
@ -2607,17 +2666,70 @@
|
||||||
"time": "2021-03-10T10:45:22+00:00"
|
"time": "2021-03-10T10:45:22+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/storage",
|
"name": "utopia-php/servers",
|
||||||
"version": "0.18.5",
|
"version": "0.1.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/storage.git",
|
"url": "https://github.com/utopia-php/servers.git",
|
||||||
"reference": "7d355c5e3ccc8ecebc0266f8ddd30088a43be919"
|
"reference": "fd5c8d32778f265256c1936372a071b944f5ba8a"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/storage/zipball/7d355c5e3ccc8ecebc0266f8ddd30088a43be919",
|
"url": "https://api.github.com/repos/utopia-php/servers/zipball/fd5c8d32778f265256c1936372a071b944f5ba8a",
|
||||||
"reference": "7d355c5e3ccc8ecebc0266f8ddd30088a43be919",
|
"reference": "fd5c8d32778f265256c1936372a071b944f5ba8a",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.0",
|
||||||
|
"utopia-php/di": "0.1.*"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"laravel/pint": "^0.2.3",
|
||||||
|
"phpstan/phpstan": "^1.8",
|
||||||
|
"phpunit/phpunit": "^9.5.5"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Utopia\\Servers\\": "src/Servers"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Team Appwrite",
|
||||||
|
"email": "team@appwrite.io"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A base library for building Utopia style servers.",
|
||||||
|
"keywords": [
|
||||||
|
"framework",
|
||||||
|
"php",
|
||||||
|
"servers",
|
||||||
|
"upf",
|
||||||
|
"utopia"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/utopia-php/servers/issues",
|
||||||
|
"source": "https://github.com/utopia-php/servers/tree/0.1.1"
|
||||||
|
},
|
||||||
|
"time": "2024-09-06T02:25:56+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "utopia-php/storage",
|
||||||
|
"version": "0.19.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/utopia-php/storage.git",
|
||||||
|
"reference": "5013b894a776874d6010753fc9349df2225c69af"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/utopia-php/storage/zipball/5013b894a776874d6010753fc9349df2225c69af",
|
||||||
|
"reference": "5013b894a776874d6010753fc9349df2225c69af",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -2629,8 +2741,8 @@
|
||||||
"ext-zlib": "*",
|
"ext-zlib": "*",
|
||||||
"ext-zstd": "*",
|
"ext-zstd": "*",
|
||||||
"php": ">=8.0",
|
"php": ">=8.0",
|
||||||
"utopia-php/framework": "0.*.*",
|
"utopia-php/framework": "1.0.*",
|
||||||
"utopia-php/system": "0.*.*"
|
"utopia-php/system": "0.8.*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"laravel/pint": "1.2.*",
|
"laravel/pint": "1.2.*",
|
||||||
|
|
@ -2657,60 +2769,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/storage/issues",
|
"issues": "https://github.com/utopia-php/storage/issues",
|
||||||
"source": "https://github.com/utopia-php/storage/tree/0.18.5"
|
"source": "https://github.com/utopia-php/storage/tree/0.19.0"
|
||||||
},
|
},
|
||||||
"time": "2024-09-04T08:57:27+00:00"
|
"time": "2024-09-05T17:00:24+00:00"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "utopia-php/swoole",
|
|
||||||
"version": "0.8.2",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/utopia-php/swoole.git",
|
|
||||||
"reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4",
|
|
||||||
"reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"ext-swoole": "*",
|
|
||||||
"php": ">=8.0",
|
|
||||||
"utopia-php/framework": "0.33.*"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"laravel/pint": "1.2.*",
|
|
||||||
"phpstan/phpstan": "^1.10",
|
|
||||||
"phpunit/phpunit": "^9.3",
|
|
||||||
"swoole/ide-helper": "5.0.2"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Utopia\\Swoole\\": "src/Swoole"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative",
|
|
||||||
"keywords": [
|
|
||||||
"framework",
|
|
||||||
"http",
|
|
||||||
"php",
|
|
||||||
"server",
|
|
||||||
"swoole",
|
|
||||||
"upf",
|
|
||||||
"utopia"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/utopia-php/swoole/issues",
|
|
||||||
"source": "https://github.com/utopia-php/swoole/tree/0.8.2"
|
|
||||||
},
|
|
||||||
"time": "2024-02-01T14:54:12+00:00"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/system",
|
"name": "utopia-php/system",
|
||||||
|
|
@ -2770,23 +2831,24 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/vcs",
|
"name": "utopia-php/vcs",
|
||||||
"version": "0.8.2",
|
"version": "0.9.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/vcs.git",
|
"url": "https://github.com/utopia-php/vcs.git",
|
||||||
"reference": "eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18"
|
"reference": "673abe2fef0750a841a4fa8fa6f99d4a602c68e7"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18",
|
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/673abe2fef0750a841a4fa8fa6f99d4a602c68e7",
|
||||||
"reference": "eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18",
|
"reference": "673abe2fef0750a841a4fa8fa6f99d4a602c68e7",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"adhocore/jwt": "^1.1",
|
"adhocore/jwt": "^1.1",
|
||||||
"php": ">=8.0",
|
"php": ">=8.0",
|
||||||
"utopia-php/cache": "^0.10.0",
|
"utopia-php/cache": "0.10.*",
|
||||||
"utopia-php/framework": "0.*.*"
|
"utopia-php/framework": "1.0.*",
|
||||||
|
"utopia-php/system": "0.8.*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"laravel/pint": "1.2.*",
|
"laravel/pint": "1.2.*",
|
||||||
|
|
@ -2813,32 +2875,76 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/vcs/issues",
|
"issues": "https://github.com/utopia-php/vcs/issues",
|
||||||
"source": "https://github.com/utopia-php/vcs/tree/0.8.2"
|
"source": "https://github.com/utopia-php/vcs/tree/0.9.0"
|
||||||
},
|
},
|
||||||
"time": "2024-08-13T14:36:30+00:00"
|
"time": "2024-09-05T16:44:48+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/websocket",
|
"name": "utopia-php/view",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/websocket.git",
|
"url": "https://github.com/utopia-php/view.git",
|
||||||
"reference": "51fcb86171400d8aa40d76c54593481fd273dab5"
|
"reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5",
|
"url": "https://api.github.com/repos/utopia-php/view/zipball/6ee55e83bc014c39ed6b69390f6d399116f65e88",
|
||||||
"reference": "51fcb86171400d8aa40d76c54593481fd273dab5",
|
"reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.0"
|
"php": ">=8.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
"laravel/pint": "^1.2",
|
||||||
|
"phpstan/phpstan": "^1.10",
|
||||||
|
"phpunit/phpunit": "^9.5.25"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Utopia\\View\\": "src/View"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "A simple, light and advanced PHP rendering engine",
|
||||||
|
"keywords": [
|
||||||
|
"php",
|
||||||
|
"view"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/utopia-php/view/issues",
|
||||||
|
"source": "https://github.com/utopia-php/view/tree/0.2.0"
|
||||||
|
},
|
||||||
|
"time": "2024-04-01T17:21:29+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "utopia-php/websocket",
|
||||||
|
"version": "0.2.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/utopia-php/websocket.git",
|
||||||
|
"reference": "e9d0919b321744a61f12563f5791c47ba9f57810"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/utopia-php/websocket/zipball/e9d0919b321744a61f12563f5791c47ba9f57810",
|
||||||
|
"reference": "e9d0919b321744a61f12563f5791c47ba9f57810",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"laravel/pint": "^1.15",
|
||||||
|
"phpstan/phpstan": "^1.8",
|
||||||
"phpunit/phpunit": "^9.5.5",
|
"phpunit/phpunit": "^9.5.5",
|
||||||
"swoole/ide-helper": "4.6.6",
|
"swoole/ide-helper": "5.1.2",
|
||||||
"textalk/websocket": "1.5.2",
|
"textalk/websocket": "1.5.2",
|
||||||
"vimeo/psalm": "^4.8.1",
|
|
||||||
"workerman/workerman": "^4.0"
|
"workerman/workerman": "^4.0"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
|
|
@ -2851,16 +2957,6 @@
|
||||||
"license": [
|
"license": [
|
||||||
"MIT"
|
"MIT"
|
||||||
],
|
],
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Eldad Fux",
|
|
||||||
"email": "eldad@appwrite.io"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Torsten Dittmann",
|
|
||||||
"email": "torsten@appwrite.io"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "A simple abstraction for WebSocket servers.",
|
"description": "A simple abstraction for WebSocket servers.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"framework",
|
"framework",
|
||||||
|
|
@ -2871,9 +2967,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/websocket/issues",
|
"issues": "https://github.com/utopia-php/websocket/issues",
|
||||||
"source": "https://github.com/utopia-php/websocket/tree/0.1.0"
|
"source": "https://github.com/utopia-php/websocket/tree/0.2.0"
|
||||||
},
|
},
|
||||||
"time": "2021-12-20T10:50:09+00:00"
|
"time": "2024-04-09T08:28:11+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "webmozart/assert",
|
"name": "webmozart/assert",
|
||||||
|
|
@ -4239,6 +4335,65 @@
|
||||||
},
|
},
|
||||||
"time": "2024-09-26T07:23:32+00:00"
|
"time": "2024-09-26T07:23:32+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "phpstan/phpstan",
|
||||||
|
"version": "1.8.11",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/phpstan/phpstan.git",
|
||||||
|
"reference": "46e223dd68a620da18855c23046ddb00940b4014"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/46e223dd68a620da18855c23046ddb00940b4014",
|
||||||
|
"reference": "46e223dd68a620da18855c23046ddb00940b4014",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2|^8.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"phpstan/phpstan-shim": "*"
|
||||||
|
},
|
||||||
|
"bin": [
|
||||||
|
"phpstan",
|
||||||
|
"phpstan.phar"
|
||||||
|
],
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "PHPStan - PHP Static Analysis Tool",
|
||||||
|
"keywords": [
|
||||||
|
"dev",
|
||||||
|
"static analysis"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||||
|
"source": "https://github.com/phpstan/phpstan/tree/1.8.11"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/ondrejmirtes",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/phpstan",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2022-10-24T15:45:13+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
"version": "9.2.32",
|
"version": "9.2.32",
|
||||||
|
|
|
||||||
|
|
@ -682,6 +682,7 @@ services:
|
||||||
entrypoint: maintenance
|
entrypoint: maintenance
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-task-maintenance
|
container_name: appwrite-task-maintenance
|
||||||
|
restart: unless-stopped
|
||||||
image: appwrite-dev
|
image: appwrite-dev
|
||||||
networks:
|
networks:
|
||||||
- appwrite
|
- appwrite
|
||||||
|
|
@ -782,6 +783,7 @@ services:
|
||||||
entrypoint: schedule-functions
|
entrypoint: schedule-functions
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-task-scheduler-functions
|
container_name: appwrite-task-scheduler-functions
|
||||||
|
restart: unless-stopped
|
||||||
image: appwrite-dev
|
image: appwrite-dev
|
||||||
networks:
|
networks:
|
||||||
- appwrite
|
- appwrite
|
||||||
|
|
@ -810,6 +812,7 @@ services:
|
||||||
entrypoint: schedule-executions
|
entrypoint: schedule-executions
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-task-scheduler-executions
|
container_name: appwrite-task-scheduler-executions
|
||||||
|
restart: unless-stopped
|
||||||
image: appwrite-dev
|
image: appwrite-dev
|
||||||
networks:
|
networks:
|
||||||
- appwrite
|
- appwrite
|
||||||
|
|
@ -837,6 +840,7 @@ services:
|
||||||
entrypoint: schedule-messages
|
entrypoint: schedule-messages
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-task-scheduler-messages
|
container_name: appwrite-task-scheduler-messages
|
||||||
|
restart: unless-stopped
|
||||||
image: appwrite-dev
|
image: appwrite-dev
|
||||||
networks:
|
networks:
|
||||||
- appwrite
|
- appwrite
|
||||||
|
|
@ -956,7 +960,7 @@ services:
|
||||||
- MYSQL_USER=${_APP_DB_USER}
|
- MYSQL_USER=${_APP_DB_USER}
|
||||||
- MYSQL_PASSWORD=${_APP_DB_PASS}
|
- MYSQL_PASSWORD=${_APP_DB_PASS}
|
||||||
- MARIADB_AUTO_UPGRADE=1
|
- MARIADB_AUTO_UPGRADE=1
|
||||||
command: "mysqld --innodb-flush-method=fsync"
|
command: 'mysqld --innodb-flush-method=fsync --max_connections=5000'
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:7.2.4-alpine
|
image: redis:7.2.4-alpine
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ Setting an alias allows the route to be also accessible from the alias URL.
|
||||||
The first parameter specifies the alias URL, the second parameter specifies default values for route parameters.
|
The first parameter specifies the alias URL, the second parameter specifies default values for route parameters.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
App::post('/v1/storage/buckets/:bucketId/files')
|
Http::post('/v1/storage/buckets/:bucketId/files')
|
||||||
->alias('/v1/storage/files', ['bucketId' => 'default'])
|
->alias('/v1/storage/files', ['bucketId' => 'default'])
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -17,7 +17,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||||
Used as an abstract description of the route.
|
Used as an abstract description of the route.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
App::post('/v1/storage/buckets/:bucketId/files')
|
Http::post('/v1/storage/buckets/:bucketId/files')
|
||||||
->desc('Create File')
|
->desc('Create File')
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -26,14 +26,14 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||||
Groups array is used to group one or more routes with one or more hooks functionality.
|
Groups array is used to group one or more routes with one or more hooks functionality.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
App::post('/v1/storage/buckets/:bucketId/files')
|
Http::post('/v1/storage/buckets/:bucketId/files')
|
||||||
->groups(['api'])
|
->groups(['api'])
|
||||||
```
|
```
|
||||||
|
|
||||||
In the above example groups() is used to define the current route as part of the routes that shares a common init middleware hook.
|
In the above example groups() is used to define the current route as part of the routes that shares a common init middleware hook.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
App::init()
|
Http::init()
|
||||||
->groups(['api'])
|
->groups(['api'])
|
||||||
->action(
|
->action(
|
||||||
some code.....
|
some code.....
|
||||||
|
|
@ -52,7 +52,7 @@ Appwrite uses different labels to achieve different things, for example:
|
||||||
- scope - Defines the route permissions scope.
|
- scope - Defines the route permissions scope.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
App::post('/v1/storage/buckets/:bucketId/files')
|
Http::post('/v1/storage/buckets/:bucketId/files')
|
||||||
->label('scope', 'files.write')
|
->label('scope', 'files.write')
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -66,7 +66,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||||
- audits.resource - Signals the extraction part of the resource.
|
- audits.resource - Signals the extraction part of the resource.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
App::post('/v1/account/create')
|
Http::post('/v1/account/create')
|
||||||
->label('audits.event', 'account.create')
|
->label('audits.event', 'account.create')
|
||||||
->label('audits.resource', 'user/{response.$id}')
|
->label('audits.resource', 'user/{response.$id}')
|
||||||
->label('audits.userId', '{response.$id}')
|
->label('audits.userId', '{response.$id}')
|
||||||
|
|
@ -84,7 +84,7 @@ App::post('/v1/account/create')
|
||||||
* sdk.offline.response.key - JSON property name that has the ID. Defaults to $id
|
* sdk.offline.response.key - JSON property name that has the ID. Defaults to $id
|
||||||
|
|
||||||
```php
|
```php
|
||||||
App::post('/v1/account/jwt')
|
Http::post('/v1/account/jwt')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION])
|
->label('sdk.auth', [APP_AUTH_TYPE_SESSION])
|
||||||
->label('sdk.namespace', 'account')
|
->label('sdk.namespace', 'account')
|
||||||
->label('sdk.method', 'createJWT')
|
->label('sdk.method', 'createJWT')
|
||||||
|
|
@ -100,7 +100,7 @@ App::post('/v1/account/jwt')
|
||||||
- cache.resource - Identifies the cached resource.
|
- cache.resource - Identifies the cached resource.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||||
->label('cache', true)
|
->label('cache', true)
|
||||||
->label('cache.resource', 'file/{request.fileId}')
|
->label('cache.resource', 'file/{request.fileId}')
|
||||||
```
|
```
|
||||||
|
|
@ -115,7 +115,7 @@ When using the example below, we configure the abuse mechanism to allow this key
|
||||||
constructed from the combination of the ip, http method, url, userId to hit the route maximum 60 times in 1 hour (60 seconds \* 60 minutes).
|
constructed from the combination of the ip, http method, url, userId to hit the route maximum 60 times in 1 hour (60 seconds \* 60 minutes).
|
||||||
|
|
||||||
```php
|
```php
|
||||||
App::post('/v1/storage/buckets/:bucketId/files')
|
Http::post('/v1/storage/buckets/:bucketId/files')
|
||||||
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
|
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
|
||||||
->label('abuse-limit', 60)
|
->label('abuse-limit', 60)
|
||||||
->label('abuse-time', 3600)
|
->label('abuse-time', 3600)
|
||||||
|
|
@ -127,7 +127,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||||
Placeholders marked as `[]` are parsed and replaced with their real values.
|
Placeholders marked as `[]` are parsed and replaced with their real values.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
App::post('/v1/storage/buckets/:bucketId/files')
|
Http::post('/v1/storage/buckets/:bucketId/files')
|
||||||
->label('event', 'buckets.[bucketId].files.[fileId].create')
|
->label('event', 'buckets.[bucketId].files.[fileId].create')
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -145,7 +145,7 @@ As the name implies, `param()` is used to define a request parameter.
|
||||||
- An array of injections
|
- An array of injections
|
||||||
|
|
||||||
```php
|
```php
|
||||||
App::get('/v1/account/logs')
|
Http::get('/v1/account/logs')
|
||||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -154,14 +154,14 @@ App::get('/v1/account/logs')
|
||||||
inject is used to inject dependencies pre-bounded to the app.
|
inject is used to inject dependencies pre-bounded to the app.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
App::post('/v1/storage/buckets/:bucketId/files')
|
Http::post('/v1/storage/buckets/:bucketId/files')
|
||||||
->inject('user')
|
->inject('user')
|
||||||
```
|
```
|
||||||
|
|
||||||
In the example above, the user object is injected into the route pre-bounded using `App::setResource()`.
|
In the example above, the user object is injected into the route pre-bounded using `Http::setResource()`.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
App::setResource('user', function() {
|
Http::setResource('user', function() {
|
||||||
some code...
|
some code...
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
@ -170,7 +170,7 @@ some code...
|
||||||
Action populates the actual route code and has to be very clear and understandable. A good route stays simple and doesn't contain complex logic. An action is where we describe our business needs in code, and combine different libraries to work together and tell our story.
|
Action populates the actual route code and has to be very clear and understandable. A good route stays simple and doesn't contain complex logic. An action is where we describe our business needs in code, and combine different libraries to work together and tell our story.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
App::post('/v1/account/sessions/anonymous')
|
Http::post('/v1/account/sessions/anonymous')
|
||||||
->action(function (Request $request) {
|
->action(function (Request $request) {
|
||||||
some code...
|
some code...
|
||||||
});
|
});
|
||||||
|
|
|
||||||
11
phpstan.neon
Normal file
11
phpstan.neon
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
parameters:
|
||||||
|
level: 0
|
||||||
|
scanDirectories:
|
||||||
|
- vendor/swoole/ide-helper
|
||||||
|
excludePaths:
|
||||||
|
- tests/resources
|
||||||
|
ignoreErrors:
|
||||||
|
- '#Parameter \$geodb of anonymous function has invalid type MaxMind\\Db\\Reader\.#'
|
||||||
|
- '#Parameter \$geodb of function router\(\) has invalid type MaxMind\\Db\\Reader\.#'
|
||||||
|
- '#Instantiated class MaxMind\\Db\\Reader not found.#'
|
||||||
|
- '#Function scrypt not found\.#'
|
||||||
|
|
@ -91,37 +91,6 @@ class Auth
|
||||||
*/
|
*/
|
||||||
public const MFA_RECENT_DURATION = 1800; // 30 mins
|
public const MFA_RECENT_DURATION = 1800; // 30 mins
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public static $cookieName = 'a_session';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User Unique ID.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public static $unique = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User Secret Key.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public static $secret = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set Cookie Name.
|
|
||||||
*
|
|
||||||
* @param $string
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function setCookieName($string)
|
|
||||||
{
|
|
||||||
return self::$cookieName = $string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encode Session.
|
* Encode Session.
|
||||||
*
|
*
|
||||||
|
|
@ -439,13 +408,14 @@ class Auth
|
||||||
* Returns all roles for a user.
|
* Returns all roles for a user.
|
||||||
*
|
*
|
||||||
* @param Document $user
|
* @param Document $user
|
||||||
|
* @param Authorization $auth
|
||||||
* @return array<string>
|
* @return array<string>
|
||||||
*/
|
*/
|
||||||
public static function getRoles(Document $user): array
|
public static function getRoles(Document $user, Authorization $auth): array
|
||||||
{
|
{
|
||||||
$roles = [];
|
$roles = [];
|
||||||
|
|
||||||
if (!self::isPrivilegedUser(Authorization::getRoles()) && !self::isAppUser(Authorization::getRoles())) {
|
if (!self::isPrivilegedUser($auth->getRoles()) && !self::isAppUser($auth->getRoles())) {
|
||||||
if ($user->getId()) {
|
if ($user->getId()) {
|
||||||
$roles[] = Role::user($user->getId())->toString();
|
$roles[] = Role::user($user->getId())->toString();
|
||||||
$roles[] = Role::users()->toString();
|
$roles[] = Role::users()->toString();
|
||||||
|
|
|
||||||
57
src/Appwrite/Auth/Authentication.php
Normal file
57
src/Appwrite/Auth/Authentication.php
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Auth;
|
||||||
|
|
||||||
|
class Authentication
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* User Unique ID.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $unique = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User Secret Key.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $secret = '';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $cookieName = 'a_session';
|
||||||
|
|
||||||
|
public function setCookieName($string): string
|
||||||
|
{
|
||||||
|
return $this->cookieName = $string;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCookieName(): string
|
||||||
|
{
|
||||||
|
return $this->cookieName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUnique(): string
|
||||||
|
{
|
||||||
|
return $this->unique;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUnique(string $unique): void
|
||||||
|
{
|
||||||
|
$this->unique = $unique;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSecret(): string
|
||||||
|
{
|
||||||
|
return $this->secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSecret(string $secret): void
|
||||||
|
{
|
||||||
|
$this->secret = $secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace Appwrite\Auth\Validator;
|
namespace Appwrite\Auth\Validator;
|
||||||
|
|
||||||
use Utopia\Validator;
|
use Utopia\Http\Validator;
|
||||||
use Utopia\Validator\Text;
|
use Utopia\Http\Validator\Text;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MockNumber.
|
* MockNumber.
|
||||||
|
|
@ -52,6 +52,7 @@ class MockNumber extends Validator
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->message = '';
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Appwrite\Auth\Validator;
|
namespace Appwrite\Auth\Validator;
|
||||||
|
|
||||||
use Utopia\Validator;
|
use Utopia\Http\Validator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Password.
|
* Password.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Appwrite\Auth\Validator;
|
namespace Appwrite\Auth\Validator;
|
||||||
|
|
||||||
use Utopia\Validator;
|
use Utopia\Http\Validator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Phone.
|
* Phone.
|
||||||
|
|
|
||||||
|
|
@ -175,9 +175,9 @@ class Func extends Event
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getData(): string
|
public function getBody(): string
|
||||||
{
|
{
|
||||||
return $this->data;
|
return $this->body;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
namespace Appwrite\Event\Validator;
|
namespace Appwrite\Event\Validator;
|
||||||
|
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Validator;
|
use Utopia\Http\Validator;
|
||||||
|
|
||||||
class Event extends Validator
|
class Event extends Validator
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Appwrite\Functions\Validator;
|
namespace Appwrite\Functions\Validator;
|
||||||
|
|
||||||
use Utopia\Validator;
|
use Utopia\Http\Validator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Headers.
|
* Headers.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Appwrite\Functions\Validator;
|
namespace Appwrite\Functions\Validator;
|
||||||
|
|
||||||
use Utopia\Validator\Text;
|
use Utopia\Http\Validator\Text;
|
||||||
|
|
||||||
class Payload extends Text
|
class Payload extends Text
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Appwrite\Functions\Validator;
|
namespace Appwrite\Functions\Validator;
|
||||||
|
|
||||||
use Utopia\Validator;
|
use Utopia\Http\Validator;
|
||||||
|
|
||||||
class RuntimeSpecification extends Validator
|
class RuntimeSpecification extends Validator
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,12 @@ use Appwrite\GraphQL\Exception as GQLException;
|
||||||
use Appwrite\Promises\Swoole;
|
use Appwrite\Promises\Swoole;
|
||||||
use Appwrite\Utopia\Request;
|
use Appwrite\Utopia\Request;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Utopia\App;
|
use Utopia\DI\Container;
|
||||||
use Utopia\Exception;
|
use Utopia\Exception;
|
||||||
use Utopia\Route;
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Request as UtopiaHttpRequest;
|
||||||
|
use Utopia\Http\Response as UtopiaHttpResponse;
|
||||||
|
use Utopia\Http\Route;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
|
|
||||||
class Resolvers
|
class Resolvers
|
||||||
|
|
@ -16,24 +19,21 @@ class Resolvers
|
||||||
/**
|
/**
|
||||||
* Create a resolver for a given API {@see Route}.
|
* Create a resolver for a given API {@see Route}.
|
||||||
*
|
*
|
||||||
* @param App $utopia
|
* @param Http $http
|
||||||
* @param ?Route $route
|
* @param ?Route $route
|
||||||
* @return callable
|
* @return callable
|
||||||
*/
|
*/
|
||||||
public static function api(
|
public function api(
|
||||||
App $utopia,
|
Http $http,
|
||||||
?Route $route,
|
?Route $route,
|
||||||
|
UtopiaHttpRequest $request,
|
||||||
|
UtopiaHttpResponse $response,
|
||||||
|
Container $container,
|
||||||
): callable {
|
): callable {
|
||||||
return static fn ($type, $args, $context, $info) => new Swoole(
|
$resolver = $this;
|
||||||
function (callable $resolve, callable $reject) use ($utopia, $route, $args, $context, $info) {
|
|
||||||
/** @var App $utopia */
|
|
||||||
/** @var Response $response */
|
|
||||||
/** @var Request $request */
|
|
||||||
|
|
||||||
$utopia = $utopia->getResource('utopia:graphql', true);
|
|
||||||
$request = $utopia->getResource('request', true);
|
|
||||||
$response = $utopia->getResource('response', true);
|
|
||||||
|
|
||||||
|
return fn ($type, $args, $context, $info) => new Swoole(
|
||||||
|
function (callable $resolve, callable $reject) use ($http, $route, $args, $context, $container, $info, $request, $response, $resolver) {
|
||||||
$path = $route->getPath();
|
$path = $route->getPath();
|
||||||
foreach ($args as $key => $value) {
|
foreach ($args as $key => $value) {
|
||||||
if (\str_contains($path, '/:' . $key)) {
|
if (\str_contains($path, '/:' . $key)) {
|
||||||
|
|
@ -46,14 +46,13 @@ class Resolvers
|
||||||
|
|
||||||
switch ($route->getMethod()) {
|
switch ($route->getMethod()) {
|
||||||
case 'GET':
|
case 'GET':
|
||||||
$request->setQueryString($args);
|
$request->setQuery($args);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$request->setPayload($args);
|
$request->setPayload($args);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
$resolver->resolve($http, $request, $response, $container, $resolve, $reject);
|
||||||
self::resolve($utopia, $request, $response, $resolve, $reject);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -61,20 +60,20 @@ class Resolvers
|
||||||
/**
|
/**
|
||||||
* Create a resolver for a document in a specified database and collection with a specific method type.
|
* Create a resolver for a document in a specified database and collection with a specific method type.
|
||||||
*
|
*
|
||||||
* @param App $utopia
|
* @param Http $http
|
||||||
* @param string $databaseId
|
* @param string $databaseId
|
||||||
* @param string $collectionId
|
* @param string $collectionId
|
||||||
* @param string $methodType
|
* @param string $methodType
|
||||||
* @return callable
|
* @return callable
|
||||||
*/
|
*/
|
||||||
public static function document(
|
public function document(
|
||||||
App $utopia,
|
Http $http,
|
||||||
string $databaseId,
|
string $databaseId,
|
||||||
string $collectionId,
|
string $collectionId,
|
||||||
string $methodType,
|
string $methodType,
|
||||||
): callable {
|
): callable {
|
||||||
return [self::class, 'document' . \ucfirst($methodType)](
|
return [self::class, 'document' . \ucfirst($methodType)](
|
||||||
$utopia,
|
$http,
|
||||||
$databaseId,
|
$databaseId,
|
||||||
$collectionId
|
$collectionId
|
||||||
);
|
);
|
||||||
|
|
@ -83,28 +82,28 @@ class Resolvers
|
||||||
/**
|
/**
|
||||||
* Create a resolver for getting a document in a specified database and collection.
|
* Create a resolver for getting a document in a specified database and collection.
|
||||||
*
|
*
|
||||||
* @param App $utopia
|
* @param Http $http
|
||||||
* @param string $databaseId
|
* @param string $databaseId
|
||||||
* @param string $collectionId
|
* @param string $collectionId
|
||||||
* @param callable $url
|
* @param callable $url
|
||||||
* @return callable
|
* @return callable
|
||||||
*/
|
*/
|
||||||
public static function documentGet(
|
public function documentGet(
|
||||||
App $utopia,
|
Http $http,
|
||||||
string $databaseId,
|
string $databaseId,
|
||||||
string $collectionId,
|
string $collectionId,
|
||||||
callable $url,
|
callable $url,
|
||||||
|
UtopiaHttpRequest $request,
|
||||||
|
UtopiaHttpResponse $response,
|
||||||
|
Container $container,
|
||||||
): callable {
|
): callable {
|
||||||
return static fn ($type, $args, $context, $info) => new Swoole(
|
$resolver = $this;
|
||||||
function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $type, $args) {
|
return fn ($type, $args, $context, $info) => new Swoole(
|
||||||
$utopia = $utopia->getResource('utopia:graphql', true);
|
function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container, $request, $response, $resolver) {
|
||||||
$request = $utopia->getResource('request', true);
|
|
||||||
$response = $utopia->getResource('response', true);
|
|
||||||
|
|
||||||
$request->setMethod('GET');
|
$request->setMethod('GET');
|
||||||
$request->setURI($url($databaseId, $collectionId, $args));
|
$request->setURI($url($databaseId, $collectionId, $args));
|
||||||
|
|
||||||
self::resolve($utopia, $request, $response, $resolve, $reject);
|
$resolver->resolve($http, $request, $response, $container, $resolve, $reject);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -112,35 +111,35 @@ class Resolvers
|
||||||
/**
|
/**
|
||||||
* Create a resolver for listing documents in a specified database and collection.
|
* Create a resolver for listing documents in a specified database and collection.
|
||||||
*
|
*
|
||||||
* @param App $utopia
|
* @param Http $http
|
||||||
* @param string $databaseId
|
* @param string $databaseId
|
||||||
* @param string $collectionId
|
* @param string $collectionId
|
||||||
* @param callable $url
|
* @param callable $url
|
||||||
* @param callable $params
|
* @param callable $params
|
||||||
* @return callable
|
* @return callable
|
||||||
*/
|
*/
|
||||||
public static function documentList(
|
public function documentList(
|
||||||
App $utopia,
|
Http $http,
|
||||||
string $databaseId,
|
string $databaseId,
|
||||||
string $collectionId,
|
string $collectionId,
|
||||||
callable $url,
|
callable $url,
|
||||||
callable $params,
|
callable $params,
|
||||||
|
UtopiaHttpRequest $request,
|
||||||
|
UtopiaHttpResponse $response,
|
||||||
|
Container $container,
|
||||||
): callable {
|
): callable {
|
||||||
return static fn ($type, $args, $context, $info) => new Swoole(
|
$resolver = $this;
|
||||||
function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $params, $type, $args) {
|
return fn ($type, $args, $context, $info) => new Swoole(
|
||||||
$utopia = $utopia->getResource('utopia:graphql', true);
|
function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) {
|
||||||
$request = $utopia->getResource('request', true);
|
|
||||||
$response = $utopia->getResource('response', true);
|
|
||||||
|
|
||||||
$request->setMethod('GET');
|
$request->setMethod('GET');
|
||||||
$request->setURI($url($databaseId, $collectionId, $args));
|
$request->setURI($url($databaseId, $collectionId, $args));
|
||||||
$request->setQueryString($params($databaseId, $collectionId, $args));
|
$request->setQuery($params($databaseId, $collectionId, $args));
|
||||||
|
|
||||||
$beforeResolve = function ($payload) {
|
$beforeResolve = function ($payload) {
|
||||||
return $payload['documents'];
|
return $payload['documents'];
|
||||||
};
|
};
|
||||||
|
|
||||||
self::resolve($utopia, $request, $response, $resolve, $reject, $beforeResolve);
|
$resolver->resolve($http, $request, $response, $container, $resolve, $reject, $beforeResolve);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -148,31 +147,31 @@ class Resolvers
|
||||||
/**
|
/**
|
||||||
* Create a resolver for creating a document in a specified database and collection.
|
* Create a resolver for creating a document in a specified database and collection.
|
||||||
*
|
*
|
||||||
* @param App $utopia
|
* @param Http $http
|
||||||
* @param string $databaseId
|
* @param string $databaseId
|
||||||
* @param string $collectionId
|
* @param string $collectionId
|
||||||
* @param callable $url
|
* @param callable $url
|
||||||
* @param callable $params
|
* @param callable $params
|
||||||
* @return callable
|
* @return callable
|
||||||
*/
|
*/
|
||||||
public static function documentCreate(
|
public function documentCreate(
|
||||||
App $utopia,
|
Http $http,
|
||||||
string $databaseId,
|
string $databaseId,
|
||||||
string $collectionId,
|
string $collectionId,
|
||||||
callable $url,
|
callable $url,
|
||||||
callable $params,
|
callable $params,
|
||||||
|
UtopiaHttpRequest $request,
|
||||||
|
UtopiaHttpResponse $response,
|
||||||
|
Container $container,
|
||||||
): callable {
|
): callable {
|
||||||
return static fn ($type, $args, $context, $info) => new Swoole(
|
$resolver = $this;
|
||||||
function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $params, $type, $args) {
|
return fn ($type, $args, $context, $info) => new Swoole(
|
||||||
$utopia = $utopia->getResource('utopia:graphql', true);
|
function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) {
|
||||||
$request = $utopia->getResource('request', true);
|
|
||||||
$response = $utopia->getResource('response', true);
|
|
||||||
|
|
||||||
$request->setMethod('POST');
|
$request->setMethod('POST');
|
||||||
$request->setURI($url($databaseId, $collectionId, $args));
|
$request->setURI($url($databaseId, $collectionId, $args));
|
||||||
$request->setPayload($params($databaseId, $collectionId, $args));
|
$request->setPayload($params($databaseId, $collectionId, $args));
|
||||||
|
|
||||||
self::resolve($utopia, $request, $response, $resolve, $reject);
|
$resolver->resolve($http, $request, $response, $container, $resolve, $reject);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -180,31 +179,31 @@ class Resolvers
|
||||||
/**
|
/**
|
||||||
* Create a resolver for updating a document in a specified database and collection.
|
* Create a resolver for updating a document in a specified database and collection.
|
||||||
*
|
*
|
||||||
* @param App $utopia
|
* @param Http $http
|
||||||
* @param string $databaseId
|
* @param string $databaseId
|
||||||
* @param string $collectionId
|
* @param string $collectionId
|
||||||
* @param callable $url
|
* @param callable $url
|
||||||
* @param callable $params
|
* @param callable $params
|
||||||
* @return callable
|
* @return callable
|
||||||
*/
|
*/
|
||||||
public static function documentUpdate(
|
public function documentUpdate(
|
||||||
App $utopia,
|
Http $http,
|
||||||
string $databaseId,
|
string $databaseId,
|
||||||
string $collectionId,
|
string $collectionId,
|
||||||
callable $url,
|
callable $url,
|
||||||
callable $params,
|
callable $params,
|
||||||
|
UtopiaHttpRequest $request,
|
||||||
|
UtopiaHttpResponse $response,
|
||||||
|
Container $container,
|
||||||
): callable {
|
): callable {
|
||||||
return static fn ($type, $args, $context, $info) => new Swoole(
|
$resolver = $this;
|
||||||
function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $params, $type, $args) {
|
return fn ($type, $args, $context, $info) => new Swoole(
|
||||||
$utopia = $utopia->getResource('utopia:graphql', true);
|
function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) {
|
||||||
$request = $utopia->getResource('request', true);
|
|
||||||
$response = $utopia->getResource('response', true);
|
|
||||||
|
|
||||||
$request->setMethod('PATCH');
|
$request->setMethod('PATCH');
|
||||||
$request->setURI($url($databaseId, $collectionId, $args));
|
$request->setURI($url($databaseId, $collectionId, $args));
|
||||||
$request->setPayload($params($databaseId, $collectionId, $args));
|
$request->setPayload($params($databaseId, $collectionId, $args));
|
||||||
|
|
||||||
self::resolve($utopia, $request, $response, $resolve, $reject);
|
$resolver->resolve($http, $request, $response, $container, $resolve, $reject);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -212,34 +211,34 @@ class Resolvers
|
||||||
/**
|
/**
|
||||||
* Create a resolver for deleting a document in a specified database and collection.
|
* Create a resolver for deleting a document in a specified database and collection.
|
||||||
*
|
*
|
||||||
* @param App $utopia
|
* @param Http $http
|
||||||
* @param string $databaseId
|
* @param string $databaseId
|
||||||
* @param string $collectionId
|
* @param string $collectionId
|
||||||
* @param callable $url
|
* @param callable $url
|
||||||
* @return callable
|
* @return callable
|
||||||
*/
|
*/
|
||||||
public static function documentDelete(
|
public function documentDelete(
|
||||||
App $utopia,
|
Http $http,
|
||||||
string $databaseId,
|
string $databaseId,
|
||||||
string $collectionId,
|
string $collectionId,
|
||||||
callable $url,
|
callable $url,
|
||||||
|
UtopiaHttpRequest $request,
|
||||||
|
UtopiaHttpResponse $response,
|
||||||
|
Container $container,
|
||||||
): callable {
|
): callable {
|
||||||
return static fn ($type, $args, $context, $info) => new Swoole(
|
$resolver = $this;
|
||||||
function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $type, $args) {
|
return fn ($type, $args, $context, $info) => new Swoole(
|
||||||
$utopia = $utopia->getResource('utopia:graphql', true);
|
function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container, $request, $response, $resolver) {
|
||||||
$request = $utopia->getResource('request', true);
|
|
||||||
$response = $utopia->getResource('response', true);
|
|
||||||
|
|
||||||
$request->setMethod('DELETE');
|
$request->setMethod('DELETE');
|
||||||
$request->setURI($url($databaseId, $collectionId, $args));
|
$request->setURI($url($databaseId, $collectionId, $args));
|
||||||
|
|
||||||
self::resolve($utopia, $request, $response, $resolve, $reject);
|
$resolver->resolve($http, $request, $response, $container, $resolve, $reject);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param App $utopia
|
* @param Http $http
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param Response $response
|
* @param Response $response
|
||||||
* @param callable $resolve
|
* @param callable $resolve
|
||||||
|
|
@ -249,10 +248,11 @@ class Resolvers
|
||||||
* @return void
|
* @return void
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
private static function resolve(
|
private function resolve(
|
||||||
App $utopia,
|
Http $http,
|
||||||
Request $request,
|
Request $request,
|
||||||
Response $response,
|
Response $response,
|
||||||
|
Container $context,
|
||||||
callable $resolve,
|
callable $resolve,
|
||||||
callable $reject,
|
callable $reject,
|
||||||
?callable $beforeResolve = null,
|
?callable $beforeResolve = null,
|
||||||
|
|
@ -263,14 +263,16 @@ class Resolvers
|
||||||
$request->removeHeader('content-type');
|
$request->removeHeader('content-type');
|
||||||
}
|
}
|
||||||
|
|
||||||
$request = clone $request;
|
|
||||||
$utopia->setResource('request', static fn () => $request);
|
|
||||||
$response->setContentType(Response::CONTENT_TYPE_NULL);
|
$response->setContentType(Response::CONTENT_TYPE_NULL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$route = $utopia->match($request, fresh: true);
|
$context
|
||||||
|
->refresh('cache')
|
||||||
|
->refresh('dbForProject')
|
||||||
|
->refresh('dbForConsole')
|
||||||
|
->refresh('getProjectDb');
|
||||||
|
|
||||||
$utopia->execute($route, $request, $response);
|
$http->run(clone $context);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
if ($beforeReject) {
|
if ($beforeReject) {
|
||||||
$e = $beforeReject($e);
|
$e = $beforeReject($e);
|
||||||
|
|
@ -285,10 +287,12 @@ class Resolvers
|
||||||
if ($beforeReject) {
|
if ($beforeReject) {
|
||||||
$payload = $beforeReject($payload);
|
$payload = $beforeReject($payload);
|
||||||
}
|
}
|
||||||
$reject(new GQLException(
|
$reject(
|
||||||
message: $payload['message'],
|
new GQLException(
|
||||||
code: $response->getStatusCode()
|
message: $payload['message'],
|
||||||
));
|
code: $response->getStatusCode()
|
||||||
|
)
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,54 +3,63 @@
|
||||||
namespace Appwrite\GraphQL;
|
namespace Appwrite\GraphQL;
|
||||||
|
|
||||||
use Appwrite\GraphQL\Types\Mapper;
|
use Appwrite\GraphQL\Types\Mapper;
|
||||||
|
use Appwrite\Utopia\Response\Models;
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
use GraphQL\Type\Schema as GQLSchema;
|
use GraphQL\Type\Schema as GQLSchema;
|
||||||
use Utopia\App;
|
use Utopia\DI\Container;
|
||||||
use Utopia\Exception;
|
use Utopia\Exception;
|
||||||
use Utopia\Route;
|
use Utopia\Http\Adapter\Swoole\Response as UtopiaSwooleResponse;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Request;
|
||||||
|
use Utopia\Http\Response as UtopiaHttpResponse;
|
||||||
|
use Utopia\Http\Route;
|
||||||
|
|
||||||
class Schema
|
class Schema
|
||||||
{
|
{
|
||||||
protected static ?GQLSchema $schema = null;
|
protected ?GQLSchema $schema = null;
|
||||||
protected static array $dirty = [];
|
protected array $dirty = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param App $utopia
|
* @param Http $http
|
||||||
* @param callable $complexity Function to calculate complexity
|
* @param callable $complexity Function to calculate complexity
|
||||||
* @param callable $attributes Function to get attributes
|
* @param callable $attributes Function to get attributes
|
||||||
* @param array $urls Array of functions to get urls for specific method types
|
* @param array $urls Array of functions to get urls for specific method types
|
||||||
* @param array $params Array of functions to build parameters for specific method types
|
* @param array $params Array of functions to build parameters for specific method types
|
||||||
* @return GQLSchema
|
* @return GQLSchema
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public static function build(
|
public function build(
|
||||||
App $utopia,
|
Http $http,
|
||||||
|
Request $request,
|
||||||
|
UtopiaHttpResponse $response,
|
||||||
|
Container $container,
|
||||||
callable $complexity,
|
callable $complexity,
|
||||||
callable $attributes,
|
callable $attributes,
|
||||||
array $urls,
|
array $urls,
|
||||||
array $params,
|
array $params,
|
||||||
): GQLSchema {
|
): GQLSchema {
|
||||||
App::setResource('utopia:graphql', static function () use ($utopia) {
|
if (!empty($this->schema)) {
|
||||||
return $utopia;
|
return $this->schema;
|
||||||
});
|
|
||||||
|
|
||||||
if (!empty(self::$schema)) {
|
|
||||||
return self::$schema;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$api = static::api(
|
$api = $this->api(
|
||||||
$utopia,
|
$http,
|
||||||
|
$request,
|
||||||
|
$response,
|
||||||
|
$container,
|
||||||
$complexity
|
$complexity
|
||||||
);
|
);
|
||||||
//$collections = static::collections(
|
// $collections = $this->collections(
|
||||||
// $utopia,
|
// $http,
|
||||||
// $complexity,
|
// $complexity,
|
||||||
// $attributes,
|
// $request,
|
||||||
// $urls,
|
// $response,
|
||||||
// $params,
|
// $attributes,
|
||||||
//);
|
// $urls,
|
||||||
|
// $params,
|
||||||
|
// );
|
||||||
|
|
||||||
$queries = \array_merge_recursive(
|
$queries = \array_merge_recursive(
|
||||||
$api['query'],
|
$api['query'],
|
||||||
|
|
@ -64,7 +73,7 @@ class Schema
|
||||||
\ksort($queries);
|
\ksort($queries);
|
||||||
\ksort($mutations);
|
\ksort($mutations);
|
||||||
|
|
||||||
return static::$schema = new GQLSchema([
|
return $this->schema = new GQLSchema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => $queries
|
'fields' => $queries
|
||||||
|
|
@ -80,21 +89,23 @@ class Schema
|
||||||
* This function iterates all API routes and builds a GraphQL
|
* This function iterates all API routes and builds a GraphQL
|
||||||
* schema defining types and resolvers for all response models.
|
* schema defining types and resolvers for all response models.
|
||||||
*
|
*
|
||||||
* @param App $utopia
|
* @param Http $http
|
||||||
|
* @param Request $request
|
||||||
|
* @param UtopiaSwooleResponse $response
|
||||||
* @param callable $complexity
|
* @param callable $complexity
|
||||||
* @return array
|
* @return array
|
||||||
* @throws Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
protected static function api(App $utopia, callable $complexity): array
|
protected function api(Http $http, Request $request, UtopiaHttpResponse $response, Container $container, callable $complexity): array
|
||||||
{
|
{
|
||||||
Mapper::init($utopia
|
Mapper::init(Models::getModels());
|
||||||
->getResource('response')
|
|
||||||
->getModels());
|
$mapper = new Mapper();
|
||||||
|
|
||||||
$queries = [];
|
$queries = [];
|
||||||
$mutations = [];
|
$mutations = [];
|
||||||
|
|
||||||
foreach ($utopia->getRoutes() as $routes) {
|
foreach ($http->getRoutes() as $routes) {
|
||||||
foreach ($routes as $route) {
|
foreach ($routes as $route) {
|
||||||
/** @var Route $route */
|
/** @var Route $route */
|
||||||
|
|
||||||
|
|
@ -106,7 +117,7 @@ class Schema
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Mapper::route($utopia, $route, $complexity) as $field) {
|
foreach ($mapper->route($http, $route, $request, $response, $container, $complexity) as $field) {
|
||||||
switch ($route->getMethod()) {
|
switch ($route->getMethod()) {
|
||||||
case 'GET':
|
case 'GET':
|
||||||
$queries[$name] = $field;
|
$queries[$name] = $field;
|
||||||
|
|
@ -134,7 +145,7 @@ class Schema
|
||||||
* Iterates all of a projects attributes and builds GraphQL
|
* Iterates all of a projects attributes and builds GraphQL
|
||||||
* queries and mutations for the collections they make up.
|
* queries and mutations for the collections they make up.
|
||||||
*
|
*
|
||||||
* @param App $utopia
|
* @param Http $http
|
||||||
* @param callable $complexity
|
* @param callable $complexity
|
||||||
* @param callable $attributes
|
* @param callable $attributes
|
||||||
* @param array $urls
|
* @param array $urls
|
||||||
|
|
@ -143,7 +154,7 @@ class Schema
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
protected static function collections(
|
protected static function collections(
|
||||||
App $utopia,
|
Http $http,
|
||||||
callable $complexity,
|
callable $complexity,
|
||||||
callable $attributes,
|
callable $attributes,
|
||||||
array $urls,
|
array $urls,
|
||||||
|
|
@ -194,36 +205,36 @@ class Schema
|
||||||
$queryFields[$collectionId . 'Get'] = [
|
$queryFields[$collectionId . 'Get'] = [
|
||||||
'type' => $objectType,
|
'type' => $objectType,
|
||||||
'args' => Mapper::args('id'),
|
'args' => Mapper::args('id'),
|
||||||
'resolve' => Resolvers::documentGet(
|
/*'resolve' => Resolvers::documentGet(
|
||||||
$utopia,
|
$http,
|
||||||
$databaseId,
|
$databaseId,
|
||||||
$collectionId,
|
$collectionId,
|
||||||
$urls['get'],
|
$urls['get'],
|
||||||
)
|
)*/
|
||||||
];
|
];
|
||||||
$queryFields[$collectionId . 'List'] = [
|
$queryFields[$collectionId . 'List'] = [
|
||||||
'type' => Type::listOf($objectType),
|
'type' => Type::listOf($objectType),
|
||||||
'args' => Mapper::args('list'),
|
'args' => Mapper::args('list'),
|
||||||
'resolve' => Resolvers::documentList(
|
/*'resolve' => Resolvers::documentList(
|
||||||
$utopia,
|
$http,
|
||||||
$databaseId,
|
$databaseId,
|
||||||
$collectionId,
|
$collectionId,
|
||||||
$urls['list'],
|
$urls['list'],
|
||||||
$params['list'],
|
$params['list'],
|
||||||
),
|
),*/
|
||||||
'complexity' => $complexity,
|
'complexity' => $complexity,
|
||||||
];
|
];
|
||||||
|
|
||||||
$mutationFields[$collectionId . 'Create'] = [
|
$mutationFields[$collectionId . 'Create'] = [
|
||||||
'type' => $objectType,
|
'type' => $objectType,
|
||||||
'args' => $attributes,
|
'args' => $attributes,
|
||||||
'resolve' => Resolvers::documentCreate(
|
/*'resolve' => Resolvers::documentCreate(
|
||||||
$utopia,
|
$http,
|
||||||
$databaseId,
|
$databaseId,
|
||||||
$collectionId,
|
$collectionId,
|
||||||
$urls['create'],
|
$urls['create'],
|
||||||
$params['create'],
|
$params['create'],
|
||||||
)
|
)*/
|
||||||
];
|
];
|
||||||
$mutationFields[$collectionId . 'Update'] = [
|
$mutationFields[$collectionId . 'Update'] = [
|
||||||
'type' => $objectType,
|
'type' => $objectType,
|
||||||
|
|
@ -234,23 +245,23 @@ class Schema
|
||||||
$attributes
|
$attributes
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
'resolve' => Resolvers::documentUpdate(
|
/*'resolve' => Resolvers::documentUpdate(
|
||||||
$utopia,
|
$http,
|
||||||
$databaseId,
|
$databaseId,
|
||||||
$collectionId,
|
$collectionId,
|
||||||
$urls['update'],
|
$urls['update'],
|
||||||
$params['update'],
|
$params['update'],
|
||||||
)
|
)*/
|
||||||
];
|
];
|
||||||
$mutationFields[$collectionId . 'Delete'] = [
|
$mutationFields[$collectionId . 'Delete'] = [
|
||||||
'type' => Mapper::model('none'),
|
'type' => Mapper::model('none'),
|
||||||
'args' => Mapper::args('id'),
|
'args' => Mapper::args('id'),
|
||||||
'resolve' => Resolvers::documentDelete(
|
/*'resolve' => Resolvers::documentDelete(
|
||||||
$utopia,
|
$http,
|
||||||
$databaseId,
|
$databaseId,
|
||||||
$collectionId,
|
$collectionId,
|
||||||
$urls['delete'],
|
$urls['delete'],
|
||||||
)
|
)*/
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$offset += $limit;
|
$offset += $limit;
|
||||||
|
|
@ -262,8 +273,8 @@ class Schema
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function setDirty(string $projectId): void
|
public function setDirty(string $projectId): void
|
||||||
{
|
{
|
||||||
self::$dirty[$projectId] = true;
|
$this->dirty[$projectId] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,13 @@ use Exception;
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
use GraphQL\Type\Definition\UnionType;
|
use GraphQL\Type\Definition\UnionType;
|
||||||
use Utopia\App;
|
use Utopia\DI\Container;
|
||||||
use Utopia\Route;
|
use Utopia\Http\Adapter\Swoole\Response as UtopiaSwooleResponse;
|
||||||
use Utopia\Validator;
|
use Utopia\Http\Http;
|
||||||
use Utopia\Validator\Nullable;
|
use Utopia\Http\Request;
|
||||||
|
use Utopia\Http\Route;
|
||||||
|
use Utopia\Http\Validator;
|
||||||
|
use Utopia\Http\Validator\Nullable;
|
||||||
|
|
||||||
class Mapper
|
class Mapper
|
||||||
{
|
{
|
||||||
|
|
@ -75,12 +78,15 @@ class Mapper
|
||||||
return self::$args[$key] ?? [];
|
return self::$args[$key] ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function route(
|
public function route(
|
||||||
App $utopia,
|
Http $http,
|
||||||
Route $route,
|
Route $route,
|
||||||
|
Request $request,
|
||||||
|
UtopiaSwooleResponse $response,
|
||||||
|
Container $container,
|
||||||
callable $complexity
|
callable $complexity
|
||||||
): iterable {
|
): iterable {
|
||||||
foreach (self::$blacklist as $blacklist) {
|
foreach (static::$blacklist as $blacklist) {
|
||||||
if (\str_starts_with($route->getPath(), $blacklist)) {
|
if (\str_starts_with($route->getPath(), $blacklist)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -102,7 +108,7 @@ class Mapper
|
||||||
$list = true;
|
$list = true;
|
||||||
}
|
}
|
||||||
$parameterType = Mapper::param(
|
$parameterType = Mapper::param(
|
||||||
$utopia,
|
$container,
|
||||||
$parameter['validator'],
|
$parameter['validator'],
|
||||||
!$parameter['optional'],
|
!$parameter['optional'],
|
||||||
$parameter['injections']
|
$parameter['injections']
|
||||||
|
|
@ -117,7 +123,7 @@ class Mapper
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
'description' => $description,
|
'description' => $description,
|
||||||
'args' => $params,
|
'args' => $params,
|
||||||
'resolve' => Resolvers::api($utopia, $route)
|
'resolve' => (new Resolvers())->api($http, $route, $request, $response, $container)
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($list) {
|
if ($list) {
|
||||||
|
|
@ -206,7 +212,7 @@ class Mapper
|
||||||
/**
|
/**
|
||||||
* Map a {@see Route} parameter to a GraphQL Type
|
* Map a {@see Route} parameter to a GraphQL Type
|
||||||
*
|
*
|
||||||
* @param App $utopia
|
* @param Container $container
|
||||||
* @param Validator|callable $validator
|
* @param Validator|callable $validator
|
||||||
* @param bool $required
|
* @param bool $required
|
||||||
* @param array $injections
|
* @param array $injections
|
||||||
|
|
@ -214,13 +220,13 @@ class Mapper
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public static function param(
|
public static function param(
|
||||||
App $utopia,
|
Container $container,
|
||||||
Validator|callable $validator,
|
Validator|callable $validator,
|
||||||
bool $required,
|
bool $required,
|
||||||
array $injections
|
array $injections
|
||||||
): Type {
|
): Type {
|
||||||
$validator = \is_callable($validator)
|
$validator = \is_callable($validator)
|
||||||
? \call_user_func_array($validator, $utopia->getResources($injections))
|
? \call_user_func_array($validator, array_map(fn ($injection) => $container->get($injection), $injections))
|
||||||
: $validator;
|
: $validator;
|
||||||
|
|
||||||
$isNullable = $validator instanceof Nullable;
|
$isNullable = $validator instanceof Nullable;
|
||||||
|
|
@ -233,20 +239,20 @@ class Mapper
|
||||||
case 'Appwrite\Network\Validator\CNAME':
|
case 'Appwrite\Network\Validator\CNAME':
|
||||||
case 'Appwrite\Task\Validator\Cron':
|
case 'Appwrite\Task\Validator\Cron':
|
||||||
case 'Appwrite\Utopia\Database\Validator\CustomId':
|
case 'Appwrite\Utopia\Database\Validator\CustomId':
|
||||||
case 'Utopia\Validator\Domain':
|
case 'Utopia\Http\Validator\Domain':
|
||||||
case 'Appwrite\Network\Validator\Email':
|
case 'Appwrite\Network\Validator\Email':
|
||||||
case 'Appwrite\Event\Validator\Event':
|
case 'Appwrite\Event\Validator\Event':
|
||||||
case 'Appwrite\Event\Validator\FunctionEvent':
|
case 'Appwrite\Event\Validator\FunctionEvent':
|
||||||
case 'Utopia\Validator\HexColor':
|
case 'Utopia\Http\Validator\HexColor':
|
||||||
case 'Utopia\Validator\Host':
|
case 'Utopia\Http\Validator\Host':
|
||||||
case 'Utopia\Validator\IP':
|
case 'Utopia\Http\Validator\IP':
|
||||||
case 'Utopia\Database\Validator\Key':
|
case 'Utopia\Database\Validator\Key':
|
||||||
case 'Utopia\Validator\Origin':
|
case 'Utopia\Http\Validator\Origin':
|
||||||
case 'Appwrite\Auth\Validator\Password':
|
case 'Appwrite\Auth\Validator\Password':
|
||||||
case 'Utopia\Validator\Text':
|
case 'Utopia\Http\Validator\Text':
|
||||||
case 'Utopia\Database\Validator\UID':
|
case 'Utopia\Database\Validator\UID':
|
||||||
case 'Utopia\Validator\URL':
|
case 'Utopia\Http\Validator\URL':
|
||||||
case 'Utopia\Validator\WhiteList':
|
case 'Utopia\Http\Validator\WhiteList':
|
||||||
default:
|
default:
|
||||||
$type = Type::string();
|
$type = Type::string();
|
||||||
break;
|
break;
|
||||||
|
|
@ -274,29 +280,29 @@ class Mapper
|
||||||
case 'Appwrite\Utopia\Database\Validator\Queries\Variables':
|
case 'Appwrite\Utopia\Database\Validator\Queries\Variables':
|
||||||
$type = Type::listOf(Type::string());
|
$type = Type::listOf(Type::string());
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Boolean':
|
case 'Utopia\Http\Validator\Boolean':
|
||||||
$type = Type::boolean();
|
$type = Type::boolean();
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\ArrayList':
|
case 'Utopia\Http\Validator\ArrayList':
|
||||||
$type = Type::listOf(self::param(
|
$type = Type::listOf(self::param(
|
||||||
$utopia,
|
$container,
|
||||||
$validator->getValidator(),
|
$validator->getValidator(),
|
||||||
$required,
|
$required,
|
||||||
$injections
|
$injections
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Integer':
|
case 'Utopia\Http\Validator\Integer':
|
||||||
case 'Utopia\Validator\Numeric':
|
case 'Utopia\Http\Validator\Numeric':
|
||||||
case 'Utopia\Validator\Range':
|
case 'Utopia\Http\Validator\Range':
|
||||||
$type = Type::int();
|
$type = Type::int();
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\FloatValidator':
|
case 'Utopia\Http\Validator\FloatValidator':
|
||||||
$type = Type::float();
|
$type = Type::float();
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Assoc':
|
case 'Utopia\Http\Validator\Assoc':
|
||||||
$type = Types::assoc();
|
$type = Types::assoc();
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\JSON':
|
case 'Utopia\Http\Validator\JSON':
|
||||||
$type = Types::json();
|
$type = Types::json();
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Storage\Validator\File':
|
case 'Utopia\Storage\Validator\File':
|
||||||
|
|
|
||||||
|
|
@ -98,10 +98,10 @@ abstract class Migration
|
||||||
*/
|
*/
|
||||||
protected array $collections;
|
protected array $collections;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct(Authorization $auth)
|
||||||
{
|
{
|
||||||
Authorization::disable();
|
$auth->disable();
|
||||||
Authorization::setDefaultStatus(false);
|
$auth->setDefaultStatus(false);
|
||||||
|
|
||||||
$this->collections = Config::getParam('collections', []);
|
$this->collections = Config::getParam('collections', []);
|
||||||
|
|
||||||
|
|
@ -176,25 +176,23 @@ abstract class Migration
|
||||||
Console::log('Migrating Collection ' . $collection['$id'] . ':');
|
Console::log('Migrating Collection ' . $collection['$id'] . ':');
|
||||||
|
|
||||||
foreach ($this->documentsIterator($collection['$id']) as $document) {
|
foreach ($this->documentsIterator($collection['$id']) as $document) {
|
||||||
go(function (Document $document, callable $callback) {
|
if (empty($document->getId()) || empty($document->getCollection())) {
|
||||||
if (empty($document->getId()) || empty($document->getCollection())) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$old = $document->getArrayCopy();
|
$old = $document->getArrayCopy();
|
||||||
$new = call_user_func($callback, $document);
|
$new = call_user_func($callback, $document);
|
||||||
|
|
||||||
if (is_null($new) || $new->getArrayCopy() == $old) {
|
if (is_null($new) || $new->getArrayCopy() == $old) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->projectDB->updateDocument($document->getCollection(), $document->getId(), $document);
|
$this->projectDB->updateDocument($document->getCollection(), $document->getId(), $document);
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $th) {
|
||||||
Console::error('Failed to update document: ' . $th->getMessage());
|
Console::error('Failed to update document: ' . $th->getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}, $document, $callback);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Appwrite\Network\Validator;
|
namespace Appwrite\Network\Validator;
|
||||||
|
|
||||||
use Utopia\Validator;
|
use Utopia\Http\Validator;
|
||||||
|
|
||||||
class CNAME extends Validator
|
class CNAME extends Validator
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
namespace Appwrite\Network\Validator;
|
namespace Appwrite\Network\Validator;
|
||||||
|
|
||||||
use Utopia\Validator;
|
use Utopia\Http\Validator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Email
|
* Email
|
||||||
*
|
*
|
||||||
* Validate that an variable is a valid email address
|
* Validate that an variable is a valid email address
|
||||||
*
|
*
|
||||||
* @package Utopia\Validator
|
* @package Utopia\Http\Validator
|
||||||
*/
|
*/
|
||||||
class Email extends Validator
|
class Email extends Validator
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace Appwrite\Network\Validator;
|
namespace Appwrite\Network\Validator;
|
||||||
|
|
||||||
use Utopia\Validator;
|
use Utopia\Http\Validator;
|
||||||
use Utopia\Validator\Hostname;
|
use Utopia\Http\Validator\Hostname;
|
||||||
|
|
||||||
class Origin extends Validator
|
class Origin extends Validator
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,17 @@
|
||||||
namespace Appwrite\Platform\Tasks;
|
namespace Appwrite\Platform\Tasks;
|
||||||
|
|
||||||
use Appwrite\ClamAV\Network;
|
use Appwrite\ClamAV\Network;
|
||||||
use Utopia\App;
|
use Appwrite\Utopia\Queue\Connections;
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
|
use Utopia\Database\Adapter\MariaDB;
|
||||||
|
use Utopia\Database\Adapter\MySQL;
|
||||||
use Utopia\Domains\Domain;
|
use Utopia\Domains\Domain;
|
||||||
use Utopia\DSN\DSN;
|
use Utopia\DSN\DSN;
|
||||||
|
use Utopia\Http\Http;
|
||||||
use Utopia\Logger\Logger;
|
use Utopia\Logger\Logger;
|
||||||
use Utopia\Platform\Action;
|
use Utopia\Platform\Action;
|
||||||
|
use Utopia\Queue\Connection\Redis;
|
||||||
use Utopia\Registry\Registry;
|
use Utopia\Registry\Registry;
|
||||||
use Utopia\Storage\Device\Local;
|
use Utopia\Storage\Device\Local;
|
||||||
use Utopia\Storage\Storage;
|
use Utopia\Storage\Storage;
|
||||||
|
|
@ -27,10 +31,11 @@ class Doctor extends Action
|
||||||
$this
|
$this
|
||||||
->desc('Validate server health')
|
->desc('Validate server health')
|
||||||
->inject('register')
|
->inject('register')
|
||||||
->callback(fn (Registry $register) => $this->action($register));
|
->inject('connections')
|
||||||
|
->callback(fn (Registry $register, Connections $connections) => $this->action($register, $connections));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function action(Registry $register): void
|
public function action(Registry $register, Connections $connections): void
|
||||||
{
|
{
|
||||||
Console::log(" __ ____ ____ _ _ ____ __ ____ ____ __ __
|
Console::log(" __ ____ ____ _ _ ____ __ ____ ____ __ __
|
||||||
/ _\ ( _ \( _ \/ )( \( _ \( )(_ _)( __) ( )/ \
|
/ _\ ( _ \( _ \/ )( \( _ \( )(_ _)( __) ( )/ \
|
||||||
|
|
@ -125,22 +130,41 @@ class Doctor extends Action
|
||||||
//throw $th;
|
//throw $th;
|
||||||
}
|
}
|
||||||
|
|
||||||
$pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */
|
/** @var array $pools */
|
||||||
|
$pools = $register->get('pools');
|
||||||
|
|
||||||
$configs = [
|
$configs = [
|
||||||
'Console.DB' => Config::getParam('pools-console'),
|
'Console.DB' => [
|
||||||
'Projects.DB' => Config::getParam('pools-database'),
|
'prefix' => 'console',
|
||||||
|
'databases' => Config::getParam('pools-console')
|
||||||
|
],
|
||||||
|
'Database.DB' => [
|
||||||
|
'prefix' => 'database',
|
||||||
|
'databases' => Config::getParam('pools-database')
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
foreach ($configs as $key => $config) {
|
foreach ($configs as $key => $config) {
|
||||||
foreach ($config as $database) {
|
foreach ($config['databases'] as $database) {
|
||||||
try {
|
try {
|
||||||
$adapter = $pools->get($database)->pop()->getResource();
|
$pool = $pools['pools-' . $config['prefix'] . '-' . $database]['pool'];
|
||||||
|
$dsn = $pools['pools-' . $config['prefix'] . '-' . $database]['dsn'];
|
||||||
|
|
||||||
|
$connection = $pool->get();
|
||||||
|
$connections->add($connection, $pool);
|
||||||
|
$adapter = match ($dsn->getScheme()) {
|
||||||
|
'mariadb' => new MariaDB($connection),
|
||||||
|
'mysql' => new MySQL($connection),
|
||||||
|
default => null
|
||||||
|
};
|
||||||
|
$adapter->setDatabase($dsn->getPath());
|
||||||
|
|
||||||
|
|
||||||
if ($adapter->ping()) {
|
if ($adapter->ping()) {
|
||||||
Console::success('🟢 ' . str_pad("{$key}({$database})", 50, '.') . 'connected');
|
Console::success('🟢 ' . str_pad("$key({$database})", 50, '.') . 'connected');
|
||||||
} else {
|
} else {
|
||||||
Console::error('🔴 ' . str_pad("{$key}({$database})", 47, '.') . 'disconnected');
|
Console::error('🔴 ' . str_pad("$key({$database})", 47, '.') . 'disconnected');
|
||||||
}
|
}
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $th) {
|
||||||
Console::error('🔴 ' . str_pad("{$key}.({$database})", 47, '.') . 'disconnected');
|
Console::error('🔴 ' . str_pad("{$key}.({$database})", 47, '.') . 'disconnected');
|
||||||
|
|
@ -148,25 +172,39 @@ class Doctor extends Action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */
|
/** @var array $pools */
|
||||||
|
$pools = $register->get('pools');
|
||||||
$configs = [
|
$configs = [
|
||||||
'Cache' => Config::getParam('pools-cache'),
|
'Cache' => [
|
||||||
'Queue' => Config::getParam('pools-queue'),
|
'prefix' => 'cache',
|
||||||
'PubSub' => Config::getParam('pools-pubsub'),
|
'databases' => Config::getParam('pools-cache')
|
||||||
|
],
|
||||||
|
'Queue' => [
|
||||||
|
'prefix' => 'queue',
|
||||||
|
'databases' => Config::getParam('pools-queue')
|
||||||
|
],
|
||||||
|
'PubSub' => [
|
||||||
|
'prefix' => 'pubsub',
|
||||||
|
'databases' => Config::getParam('pools-pubsub')
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($configs as $key => $config) {
|
foreach ($configs as $key => $config) {
|
||||||
foreach ($config as $pool) {
|
foreach ($config['databases'] as $database) {
|
||||||
try {
|
try {
|
||||||
$adapter = $pools->get($pool)->pop()->getResource();
|
$pool = $pools['pools-' . $config['prefix'] . '-' . $database]['pool'];
|
||||||
|
$dsn = $pools['pools-' . $config['prefix'] . '-' . $database]['dsn'];
|
||||||
|
$connection = $pool->get();
|
||||||
|
$connections->add($connection, $pool);
|
||||||
|
|
||||||
|
$adapter = new Redis($dsn->getHost(), $dsn->getPort());
|
||||||
|
|
||||||
if ($adapter->ping()) {
|
if ($adapter->ping()) {
|
||||||
Console::success('🟢 ' . str_pad("{$key}({$pool})", 50, '.') . 'connected');
|
Console::success('🟢 ' . str_pad("{$key}({$database})", 50, '.') . 'connected');
|
||||||
} else {
|
} else {
|
||||||
Console::error('🔴 ' . str_pad("{$key}({$pool})", 47, '.') . 'disconnected');
|
Console::error('🔴 ' . str_pad("{$key}({$database})", 47, '.') . 'disconnected');
|
||||||
}
|
}
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $th) {
|
||||||
Console::error('🔴 ' . str_pad("{$key}({$pool})", 47, '.') . 'disconnected');
|
Console::error('🔴 ' . str_pad("{$key}({$database})", 47, '.') . 'disconnected');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -258,7 +296,7 @@ class Doctor extends Action
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (App::isProduction()) {
|
if (Http::isProduction()) {
|
||||||
Console::log('');
|
Console::log('');
|
||||||
$version = \json_decode(@\file_get_contents(System::getEnv('_APP_HOME', 'http://localhost') . '/version'), true);
|
$version = \json_decode(@\file_get_contents(System::getEnv('_APP_HOME', 'http://localhost') . '/version'), true);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@ use Appwrite\Docker\Env;
|
||||||
use Appwrite\Utopia\View;
|
use Appwrite\Utopia\View;
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
|
use Utopia\Http\Validator\Boolean;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
use Utopia\Platform\Action;
|
use Utopia\Platform\Action;
|
||||||
use Utopia\Validator\Boolean;
|
|
||||||
use Utopia\Validator\Text;
|
|
||||||
|
|
||||||
class Install extends Action
|
class Install extends Action
|
||||||
{
|
{
|
||||||
|
|
@ -213,8 +213,7 @@ class Install extends Action
|
||||||
}
|
}
|
||||||
|
|
||||||
$env = '';
|
$env = '';
|
||||||
$stdout = '';
|
$output = '';
|
||||||
$stderr = '';
|
|
||||||
|
|
||||||
foreach ($input as $key => $value) {
|
foreach ($input as $key => $value) {
|
||||||
if ($value) {
|
if ($value) {
|
||||||
|
|
@ -225,13 +224,13 @@ class Install extends Action
|
||||||
$exit = 0;
|
$exit = 0;
|
||||||
if (!$noStart) {
|
if (!$noStart) {
|
||||||
Console::log("Running \"docker compose up -d --remove-orphans --renew-anon-volumes\"");
|
Console::log("Running \"docker compose up -d --remove-orphans --renew-anon-volumes\"");
|
||||||
$exit = Console::execute("$env docker compose --project-directory $this->path up -d --remove-orphans --renew-anon-volumes", '', $stdout, $stderr);
|
$exit = Console::execute("$env docker compose --project-directory $this->path up -d --remove-orphans --renew-anon-volumes", '', $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($exit !== 0) {
|
if ($exit !== 0) {
|
||||||
$message = 'Failed to install Appwrite dockers';
|
$message = 'Failed to install Appwrite dockers';
|
||||||
Console::error($message);
|
Console::error($message);
|
||||||
Console::error($stderr);
|
Console::error($output);
|
||||||
Console::exit($exit);
|
Console::exit($exit);
|
||||||
} else {
|
} else {
|
||||||
$message = 'Appwrite installed successfully';
|
$message = 'Appwrite installed successfully';
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ use Utopia\Database\Database;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Query;
|
use Utopia\Database\Query;
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\Database\Validator\Authorization;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
use Utopia\Platform\Action;
|
use Utopia\Platform\Action;
|
||||||
use Utopia\Registry\Registry;
|
use Utopia\Registry\Registry;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\Text;
|
|
||||||
|
|
||||||
class Migrate extends Action
|
class Migrate extends Action
|
||||||
{
|
{
|
||||||
|
|
@ -30,12 +30,15 @@ class Migrate extends Action
|
||||||
->desc('Migrate Appwrite to new version')
|
->desc('Migrate Appwrite to new version')
|
||||||
/** @TODO APP_VERSION_STABLE needs to be defined */
|
/** @TODO APP_VERSION_STABLE needs to be defined */
|
||||||
->param('version', APP_VERSION_STABLE, new Text(8), 'Version to migrate to.', true)
|
->param('version', APP_VERSION_STABLE, new Text(8), 'Version to migrate to.', true)
|
||||||
|
->inject('cache')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->inject('getProjectDB')
|
->inject('getProjectDB')
|
||||||
->inject('register')
|
->inject('register')
|
||||||
->callback(function ($version, $dbForConsole, $getProjectDB, Registry $register) {
|
->inject('authorization')
|
||||||
\Co\run(function () use ($version, $dbForConsole, $getProjectDB, $register) {
|
->inject('console')
|
||||||
$this->action($version, $dbForConsole, $getProjectDB, $register);
|
->callback(function ($version, $dbForConsole, $getProjectDB, Registry $register, Authorization $authorization, Document $console) {
|
||||||
|
\Co\run(function () use ($version, $dbForConsole, $getProjectDB, $register, $authorization, $console) {
|
||||||
|
$this->action($version, $dbForConsole, $getProjectDB, $register, $authorization, $console);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -58,13 +61,12 @@ class Migrate extends Action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function action(string $version, Database $dbForConsole, callable $getProjectDB, Registry $register)
|
public function action(string $version, Database $dbForConsole, callable $getProjectDB, Registry $register, Authorization $auth, Document $console)
|
||||||
{
|
{
|
||||||
Authorization::disable();
|
$auth->disable();
|
||||||
if (!array_key_exists($version, Migration::$versions)) {
|
if (!array_key_exists($version, Migration::$versions)) {
|
||||||
Console::error("Version {$version} not found.");
|
Console::error("Version {$version} not found.");
|
||||||
Console::exit(1);
|
Console::exit(1);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,12 +79,8 @@ class Migrate extends Action
|
||||||
10
|
10
|
||||||
);
|
);
|
||||||
|
|
||||||
$app = new App('UTC');
|
|
||||||
|
|
||||||
Console::success('Starting Data Migration to version ' . $version);
|
Console::success('Starting Data Migration to version ' . $version);
|
||||||
|
|
||||||
$console = $app->getResource('console');
|
|
||||||
|
|
||||||
$limit = 30;
|
$limit = 30;
|
||||||
$sum = 30;
|
$sum = 30;
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
|
|
@ -101,7 +99,7 @@ class Migrate extends Action
|
||||||
|
|
||||||
$class = 'Appwrite\\Migration\\Version\\' . Migration::$versions[$version];
|
$class = 'Appwrite\\Migration\\Version\\' . Migration::$versions[$version];
|
||||||
/** @var Migration $migration */
|
/** @var Migration $migration */
|
||||||
$migration = new $class();
|
$migration = new $class($auth, );
|
||||||
|
|
||||||
while (!empty($projects)) {
|
while (!empty($projects)) {
|
||||||
foreach ($projects as $project) {
|
foreach ($projects as $project) {
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@
|
||||||
namespace Appwrite\Platform\Tasks;
|
namespace Appwrite\Platform\Tasks;
|
||||||
|
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
|
use Utopia\Http\Validator\WhiteList;
|
||||||
use Utopia\Platform\Action;
|
use Utopia\Platform\Action;
|
||||||
use Utopia\Queue\Client;
|
use Utopia\Queue\Client;
|
||||||
use Utopia\Queue\Connection;
|
use Utopia\Queue\Connection;
|
||||||
use Utopia\Validator\Text;
|
|
||||||
use Utopia\Validator\WhiteList;
|
|
||||||
|
|
||||||
class QueueCount extends Action
|
class QueueCount extends Action
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@
|
||||||
namespace Appwrite\Platform\Tasks;
|
namespace Appwrite\Platform\Tasks;
|
||||||
|
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
|
use Utopia\Http\Validator\Wildcard;
|
||||||
use Utopia\Platform\Action;
|
use Utopia\Platform\Action;
|
||||||
use Utopia\Queue\Client;
|
use Utopia\Queue\Client;
|
||||||
use Utopia\Queue\Connection;
|
use Utopia\Queue\Connection;
|
||||||
use Utopia\Validator\Text;
|
|
||||||
use Utopia\Validator\Wildcard;
|
|
||||||
|
|
||||||
class QueueRetry extends Action
|
class QueueRetry extends Action
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ namespace Appwrite\Platform\Tasks;
|
||||||
use Appwrite\Event\Certificate;
|
use Appwrite\Event\Certificate;
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
|
use Utopia\Http\Validator\Boolean;
|
||||||
|
use Utopia\Http\Validator\Hostname;
|
||||||
use Utopia\Platform\Action;
|
use Utopia\Platform\Action;
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\Boolean;
|
|
||||||
use Utopia\Validator\Hostname;
|
|
||||||
|
|
||||||
class SSL extends Action
|
class SSL extends Action
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,41 +2,45 @@
|
||||||
|
|
||||||
namespace Appwrite\Platform\Tasks;
|
namespace Appwrite\Platform\Tasks;
|
||||||
|
|
||||||
|
use Appwrite\Utopia\Queue\Connections;
|
||||||
use Swoole\Timer;
|
use Swoole\Timer;
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Database\Database;
|
|
||||||
use Utopia\Database\DateTime;
|
use Utopia\Database\DateTime;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Exception;
|
use Utopia\Database\Exception;
|
||||||
use Utopia\Database\Query;
|
use Utopia\Database\Query;
|
||||||
use Utopia\Platform\Action;
|
use Utopia\Platform\Action;
|
||||||
use Utopia\Pools\Group;
|
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
|
|
||||||
use function Swoole\Coroutine\run;
|
|
||||||
|
|
||||||
abstract class ScheduleBase extends Action
|
abstract class ScheduleBase extends Action
|
||||||
{
|
{
|
||||||
protected const UPDATE_TIMER = 10; //seconds
|
protected const UPDATE_TIMER = 10; //seconds
|
||||||
protected const ENQUEUE_TIMER = 60; //seconds
|
protected const ENQUEUE_TIMER = 60; //seconds
|
||||||
|
|
||||||
protected array $schedules = [];
|
protected array $schedules = [];
|
||||||
|
protected Connections $connections;
|
||||||
|
|
||||||
abstract public static function getName(): string;
|
abstract public static function getName(): string;
|
||||||
|
|
||||||
abstract public static function getSupportedResource(): string;
|
abstract public static function getSupportedResource(): string;
|
||||||
abstract public static function getCollectionId(): string;
|
abstract public static function getCollectionId(): string;
|
||||||
abstract protected function enqueueResources(Group $pools, Database $dbForConsole, callable $getProjectDB): void;
|
|
||||||
|
abstract protected function enqueueResources(
|
||||||
|
array $pools,
|
||||||
|
callable $getConsoleDB
|
||||||
|
);
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
|
$this->connections = new Connections();
|
||||||
$type = static::getSupportedResource();
|
$type = static::getSupportedResource();
|
||||||
|
|
||||||
$this
|
$this
|
||||||
->desc("Execute {$type}s scheduled in Appwrite")
|
->desc("Execute {$type}s scheduled in Appwrite")
|
||||||
->inject('pools')
|
->inject('pools')
|
||||||
->inject('dbForConsole')
|
->inject('getConsoleDB')
|
||||||
->inject('getProjectDB')
|
->inject('getProjectDB')
|
||||||
->callback(fn (Group $pools, Database $dbForConsole, callable $getProjectDB) => $this->action($pools, $dbForConsole, $getProjectDB));
|
->callback(fn (array $pools, callable $getConsoleDB, callable $getProjectDB) => $this->action($pools, $getConsoleDB, $getProjectDB));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -44,11 +48,12 @@ abstract class ScheduleBase extends Action
|
||||||
* 2. Create timer that sync all changes from 'schedules' collection to local copy. Only reading changes thanks to 'resourceUpdatedAt' attribute
|
* 2. Create timer that sync all changes from 'schedules' collection to local copy. Only reading changes thanks to 'resourceUpdatedAt' attribute
|
||||||
* 3. Create timer that prepares coroutines for soon-to-execute schedules. When it's ready, coroutine sleeps until exact time before sending request to worker.
|
* 3. Create timer that prepares coroutines for soon-to-execute schedules. When it's ready, coroutine sleeps until exact time before sending request to worker.
|
||||||
*/
|
*/
|
||||||
public function action(Group $pools, Database $dbForConsole, callable $getProjectDB): void
|
public function action(array $pools, callable $getConsoleDB, callable $getProjectDB): void
|
||||||
{
|
{
|
||||||
Console::title(\ucfirst(static::getSupportedResource()) . ' scheduler V1');
|
Console::title(\ucfirst(static::getSupportedResource()) . ' scheduler V1');
|
||||||
Console::success(APP_NAME . ' ' . \ucfirst(static::getSupportedResource()) . ' scheduler v1 has started');
|
Console::success(APP_NAME . ' ' . \ucfirst(static::getSupportedResource()) . ' scheduler v1 has started');
|
||||||
|
|
||||||
|
[$_, $_, $dbForConsole] = $getConsoleDB();
|
||||||
/**
|
/**
|
||||||
* Extract only necessary attributes to lower memory used.
|
* Extract only necessary attributes to lower memory used.
|
||||||
*
|
*
|
||||||
|
|
@ -59,6 +64,12 @@ abstract class ScheduleBase extends Action
|
||||||
$getSchedule = function (Document $schedule) use ($dbForConsole, $getProjectDB): array {
|
$getSchedule = function (Document $schedule) use ($dbForConsole, $getProjectDB): array {
|
||||||
$project = $dbForConsole->getDocument('projects', $schedule->getAttribute('projectId'));
|
$project = $dbForConsole->getDocument('projects', $schedule->getAttribute('projectId'));
|
||||||
|
|
||||||
|
$collectionId = match ($schedule->getAttribute('resourceType')) {
|
||||||
|
'function' => 'functions',
|
||||||
|
'message' => 'messages',
|
||||||
|
'execution' => 'executions'
|
||||||
|
};
|
||||||
|
|
||||||
$resource = $getProjectDB($project)->getDocument(
|
$resource = $getProjectDB($project)->getDocument(
|
||||||
static::getCollectionId(),
|
static::getCollectionId(),
|
||||||
$schedule->getAttribute('resourceId')
|
$schedule->getAttribute('resourceId')
|
||||||
|
|
@ -113,76 +124,73 @@ abstract class ScheduleBase extends Action
|
||||||
$latestDocument = \end($results);
|
$latestDocument = \end($results);
|
||||||
}
|
}
|
||||||
|
|
||||||
$pools->reclaim();
|
|
||||||
|
|
||||||
Console::success("{$total} resources were loaded in " . (\microtime(true) - $loadStart) . " seconds");
|
Console::success("{$total} resources were loaded in " . (\microtime(true) - $loadStart) . " seconds");
|
||||||
|
|
||||||
Console::success("Starting timers at " . DateTime::now());
|
Console::success("Starting timers at " . DateTime::now());
|
||||||
|
|
||||||
run(function () use ($dbForConsole, &$lastSyncUpdate, $getSchedule, $pools, $getProjectDB) {
|
Timer::tick(static::UPDATE_TIMER * 1000, function () use ($getConsoleDB, &$lastSyncUpdate, $getSchedule, $pools) {
|
||||||
/**
|
[$connection,$pool, $dbForConsole] = $getConsoleDB();
|
||||||
* The timer synchronize $schedules copy with database collection.
|
$connections = new Connections();
|
||||||
*/
|
$connections->add($connection, $pool);
|
||||||
Timer::tick(static::UPDATE_TIMER * 1000, function () use ($dbForConsole, &$lastSyncUpdate, $getSchedule, $pools) {
|
|
||||||
$time = DateTime::now();
|
|
||||||
$timerStart = \microtime(true);
|
|
||||||
|
|
||||||
$limit = 1000;
|
$time = DateTime::now();
|
||||||
$sum = $limit;
|
$timerStart = \microtime(true);
|
||||||
$total = 0;
|
|
||||||
$latestDocument = null;
|
|
||||||
|
|
||||||
Console::log("Sync tick: Running at $time");
|
$limit = 1000;
|
||||||
|
$sum = $limit;
|
||||||
|
$total = 0;
|
||||||
|
$latestDocument = null;
|
||||||
|
|
||||||
while ($sum === $limit) {
|
Console::log("Sync tick: Running at $time");
|
||||||
$paginationQueries = [Query::limit($limit)];
|
|
||||||
|
|
||||||
if ($latestDocument) {
|
while ($sum === $limit) {
|
||||||
$paginationQueries[] = Query::cursorAfter($latestDocument);
|
$paginationQueries = [Query::limit($limit)];
|
||||||
}
|
|
||||||
|
|
||||||
$results = $dbForConsole->find('schedules', \array_merge($paginationQueries, [
|
if ($latestDocument) {
|
||||||
Query::equal('region', [System::getEnv('_APP_REGION', 'default')]),
|
$paginationQueries[] = Query::cursorAfter($latestDocument);
|
||||||
Query::equal('resourceType', [static::getSupportedResource()]),
|
|
||||||
Query::greaterThanEqual('resourceUpdatedAt', $lastSyncUpdate),
|
|
||||||
]));
|
|
||||||
|
|
||||||
$sum = count($results);
|
|
||||||
$total = $total + $sum;
|
|
||||||
|
|
||||||
foreach ($results as $document) {
|
|
||||||
$localDocument = $this->schedules[$document->getInternalId()] ?? null;
|
|
||||||
|
|
||||||
// Check if resource has been updated since last sync
|
|
||||||
$org = $localDocument !== null ? \strtotime($localDocument['resourceUpdatedAt']) : null;
|
|
||||||
$new = \strtotime($document['resourceUpdatedAt']);
|
|
||||||
|
|
||||||
if (!$document['active']) {
|
|
||||||
Console::info("Removing: {$document['resourceType']}::{$document['resourceId']}");
|
|
||||||
unset($this->schedules[$document->getInternalId()]);
|
|
||||||
} elseif ($new !== $org) {
|
|
||||||
Console::info("Updating: {$document['resourceType']}::{$document['resourceId']}");
|
|
||||||
$this->schedules[$document->getInternalId()] = $getSchedule($document);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$latestDocument = \end($results);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$lastSyncUpdate = $time;
|
$results = $dbForConsole->find('schedules', \array_merge($paginationQueries, [
|
||||||
$timerEnd = \microtime(true);
|
Query::equal('region', [System::getEnv('_APP_REGION', 'default')]),
|
||||||
|
Query::equal('resourceType', [static::getSupportedResource()]),
|
||||||
|
Query::greaterThanEqual('resourceUpdatedAt', $lastSyncUpdate),
|
||||||
|
]));
|
||||||
|
|
||||||
$pools->reclaim();
|
$sum = count($results);
|
||||||
|
$total = $total + $sum;
|
||||||
|
|
||||||
Console::log("Sync tick: {$total} schedules were updated in " . ($timerEnd - $timerStart) . " seconds");
|
foreach ($results as $document) {
|
||||||
});
|
$localDocument = $this->schedules[$document->getInternalId()] ?? null;
|
||||||
|
|
||||||
Timer::tick(
|
// Check if resource has been updated since last sync
|
||||||
static::ENQUEUE_TIMER * 1000,
|
$org = $localDocument !== null ? \strtotime($localDocument['resourceUpdatedAt']) : null;
|
||||||
fn () => $this->enqueueResources($pools, $dbForConsole, $getProjectDB)
|
$new = \strtotime($document['resourceUpdatedAt']);
|
||||||
);
|
|
||||||
|
|
||||||
$this->enqueueResources($pools, $dbForConsole, $getProjectDB);
|
if (!$document['active']) {
|
||||||
|
Console::info("Removing: {$document['resourceType']}::{$document['resourceId']}");
|
||||||
|
unset($this->schedules[$document->getInternalId()]);
|
||||||
|
} elseif ($new !== $org) {
|
||||||
|
Console::info("Updating: {$document['resourceType']}::{$document['resourceId']}");
|
||||||
|
$this->schedules[$document->getInternalId()] = $getSchedule($document);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$latestDocument = \end($results);
|
||||||
|
}
|
||||||
|
|
||||||
|
$lastSyncUpdate = $time;
|
||||||
|
$timerEnd = \microtime(true);
|
||||||
|
|
||||||
|
$connections->reclaim();
|
||||||
|
Console::log("Sync tick: {$total} schedules were updated in " . ($timerEnd - $timerStart) . " seconds");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Timer::tick(
|
||||||
|
static::ENQUEUE_TIMER * 1000,
|
||||||
|
fn () => $this->enqueueResources($pools, $getConsoleDB)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->enqueueResources($pools, $getConsoleDB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@ namespace Appwrite\Platform\Tasks;
|
||||||
|
|
||||||
use Appwrite\Event\Func;
|
use Appwrite\Event\Func;
|
||||||
use Swoole\Coroutine as Co;
|
use Swoole\Coroutine as Co;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Queue\Connection\Redis;
|
||||||
use Utopia\Pools\Group;
|
|
||||||
|
|
||||||
class ScheduleExecutions extends ScheduleBase
|
class ScheduleExecutions extends ScheduleBase
|
||||||
{
|
{
|
||||||
|
|
@ -27,11 +26,16 @@ class ScheduleExecutions extends ScheduleBase
|
||||||
return 'executions';
|
return 'executions';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function enqueueResources(Group $pools, Database $dbForConsole, callable $getProjectDB): void
|
protected function enqueueResources(array $pools, callable $getConsoleDB): void
|
||||||
{
|
{
|
||||||
$queue = $pools->get('queue')->pop();
|
[$connection,$pool, $dbForConsole] = $getConsoleDB();
|
||||||
$connection = $queue->getResource();
|
$this->connections->add($connection, $pool);
|
||||||
$queueForFunctions = new Func($connection);
|
|
||||||
|
$queuePool = $pools['pools-queue-queue']['pool'];
|
||||||
|
$queueConnection = $queuePool->get();
|
||||||
|
$this->connections->add($queueConnection, $queuePool);
|
||||||
|
|
||||||
|
$queueForFunctions = new Func(new Redis($queueConnection));
|
||||||
$intervalEnd = (new \DateTime())->modify('+' . self::ENQUEUE_TIMER . ' seconds');
|
$intervalEnd = (new \DateTime())->modify('+' . self::ENQUEUE_TIMER . ' seconds');
|
||||||
|
|
||||||
foreach ($this->schedules as $schedule) {
|
foreach ($this->schedules as $schedule) {
|
||||||
|
|
@ -57,7 +61,7 @@ class ScheduleExecutions extends ScheduleBase
|
||||||
|
|
||||||
$delay = $scheduledAt->getTimestamp() - (new \DateTime())->getTimestamp();
|
$delay = $scheduledAt->getTimestamp() - (new \DateTime())->getTimestamp();
|
||||||
|
|
||||||
\go(function () use ($queueForFunctions, $schedule, $delay, $data) {
|
\go(function () use ($queueForFunctions, $schedule, $delay, $data, $dbForConsole) {
|
||||||
Co::sleep($delay);
|
Co::sleep($delay);
|
||||||
|
|
||||||
$queueForFunctions->setType('schedule')
|
$queueForFunctions->setType('schedule')
|
||||||
|
|
@ -72,16 +76,16 @@ class ScheduleExecutions extends ScheduleBase
|
||||||
->setProject($schedule['project'])
|
->setProject($schedule['project'])
|
||||||
->setUserId($data['userId'] ?? '')
|
->setUserId($data['userId'] ?? '')
|
||||||
->trigger();
|
->trigger();
|
||||||
});
|
|
||||||
|
|
||||||
$dbForConsole->deleteDocument(
|
$dbForConsole->deleteDocument(
|
||||||
'schedules',
|
'schedules',
|
||||||
$schedule['$id'],
|
$schedule['$id'],
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
unset($this->schedules[$schedule['$internalId']]);
|
unset($this->schedules[$schedule['$internalId']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$queue->reclaim();
|
$this->connections->reclaim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,8 @@ namespace Appwrite\Platform\Tasks;
|
||||||
use Appwrite\Event\Func;
|
use Appwrite\Event\Func;
|
||||||
use Cron\CronExpression;
|
use Cron\CronExpression;
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Database\Database;
|
|
||||||
use Utopia\Database\DateTime;
|
use Utopia\Database\DateTime;
|
||||||
use Utopia\Pools\Group;
|
use Utopia\Queue\Connection\Redis;
|
||||||
|
|
||||||
class ScheduleFunctions extends ScheduleBase
|
class ScheduleFunctions extends ScheduleBase
|
||||||
{
|
{
|
||||||
|
|
@ -31,7 +30,7 @@ class ScheduleFunctions extends ScheduleBase
|
||||||
return 'functions';
|
return 'functions';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function enqueueResources(Group $pools, Database $dbForConsole, callable $getProjectDB): void
|
protected function enqueueResources(array $pools, callable $getConsoleDB): void
|
||||||
{
|
{
|
||||||
$timerStart = \microtime(true);
|
$timerStart = \microtime(true);
|
||||||
$time = DateTime::now();
|
$time = DateTime::now();
|
||||||
|
|
@ -73,8 +72,11 @@ class ScheduleFunctions extends ScheduleBase
|
||||||
\go(function () use ($delay, $scheduleKeys, $pools) {
|
\go(function () use ($delay, $scheduleKeys, $pools) {
|
||||||
\sleep($delay); // in seconds
|
\sleep($delay); // in seconds
|
||||||
|
|
||||||
$queue = $pools->get('queue')->pop();
|
$pool = $pools['pools-queue-queue']['pool'];
|
||||||
$connection = $queue->getResource();
|
$connection = $pool->get();
|
||||||
|
$this->connections->add($connection, $pool);
|
||||||
|
|
||||||
|
$queueConnection = new Redis($connection);
|
||||||
|
|
||||||
foreach ($scheduleKeys as $scheduleKey) {
|
foreach ($scheduleKeys as $scheduleKey) {
|
||||||
// Ensure schedule was not deleted
|
// Ensure schedule was not deleted
|
||||||
|
|
@ -84,7 +86,7 @@ class ScheduleFunctions extends ScheduleBase
|
||||||
|
|
||||||
$schedule = $this->schedules[$scheduleKey];
|
$schedule = $this->schedules[$scheduleKey];
|
||||||
|
|
||||||
$queueForFunctions = new Func($connection);
|
$queueForFunctions = new Func($queueConnection);
|
||||||
|
|
||||||
$queueForFunctions
|
$queueForFunctions
|
||||||
->setType('schedule')
|
->setType('schedule')
|
||||||
|
|
@ -95,7 +97,8 @@ class ScheduleFunctions extends ScheduleBase
|
||||||
->trigger();
|
->trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
$queue->reclaim();
|
$this->connections->reclaim();
|
||||||
|
// $queue->reclaim(); // TODO: Do in try/catch/finally, or add to connectons resource
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,7 @@
|
||||||
namespace Appwrite\Platform\Tasks;
|
namespace Appwrite\Platform\Tasks;
|
||||||
|
|
||||||
use Appwrite\Event\Messaging;
|
use Appwrite\Event\Messaging;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Queue\Connection\Redis;
|
||||||
use Utopia\Pools\Group;
|
|
||||||
|
|
||||||
class ScheduleMessages extends ScheduleBase
|
class ScheduleMessages extends ScheduleBase
|
||||||
{
|
{
|
||||||
|
|
@ -26,8 +25,11 @@ class ScheduleMessages extends ScheduleBase
|
||||||
return 'messages';
|
return 'messages';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function enqueueResources(Group $pools, Database $dbForConsole, callable $getProjectDB): void
|
protected function enqueueResources(array $pools, callable $getConsoleDB): void
|
||||||
{
|
{
|
||||||
|
[$connection,$pool, $dbForConsole] = $getConsoleDB();
|
||||||
|
$this->connections->add($connection, $pool);
|
||||||
|
|
||||||
foreach ($this->schedules as $schedule) {
|
foreach ($this->schedules as $schedule) {
|
||||||
if (!$schedule['active']) {
|
if (!$schedule['active']) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -40,10 +42,15 @@ class ScheduleMessages extends ScheduleBase
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
\go(function () use ($schedule, $pools, $dbForConsole) {
|
\go(function () use ($now, $schedule, $pools, $dbForConsole) {
|
||||||
$queue = $pools->get('queue')->pop();
|
$pool = $pools['pools-queue-queue']['pool'];
|
||||||
$connection = $queue->getResource();
|
$dsn = $pools['pools-queue-queue']['dsn'];
|
||||||
$queueForMessaging = new Messaging($connection);
|
$connection = $pool->get();
|
||||||
|
$this->connections->add($connection, $pool);
|
||||||
|
|
||||||
|
$queueConnection = new Redis($dsn->getHost(), $dsn->getPort());
|
||||||
|
|
||||||
|
$queueForMessaging = new Messaging($queueConnection);
|
||||||
|
|
||||||
$queueForMessaging
|
$queueForMessaging
|
||||||
->setType(MESSAGE_SEND_TYPE_EXTERNAL)
|
->setType(MESSAGE_SEND_TYPE_EXTERNAL)
|
||||||
|
|
@ -56,8 +63,7 @@ class ScheduleMessages extends ScheduleBase
|
||||||
$schedule['$id'],
|
$schedule['$id'],
|
||||||
);
|
);
|
||||||
|
|
||||||
$queue->reclaim();
|
$this->connections->reclaim();
|
||||||
|
|
||||||
unset($this->schedules[$schedule['$internalId']]);
|
unset($this->schedules[$schedule['$internalId']]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,25 +5,27 @@ namespace Appwrite\Platform\Tasks;
|
||||||
use Appwrite\Specification\Format\OpenAPI3;
|
use Appwrite\Specification\Format\OpenAPI3;
|
||||||
use Appwrite\Specification\Format\Swagger2;
|
use Appwrite\Specification\Format\Swagger2;
|
||||||
use Appwrite\Specification\Specification;
|
use Appwrite\Specification\Specification;
|
||||||
use Appwrite\Utopia\Request as AppwriteRequest;
|
use Appwrite\Utopia\Response;
|
||||||
use Appwrite\Utopia\Response as AppwriteResponse;
|
use Appwrite\Utopia\Response\Models;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Swoole\Http\Request as SwooleRequest;
|
use Swoole\Http\Request as SwooleHttpRequest;
|
||||||
use Swoole\Http\Response as SwooleResponse;
|
use Swoole\Http\Response as SwooleHttpResponse;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Cache\Adapter\None;
|
use Utopia\Cache\Adapter\None;
|
||||||
use Utopia\Cache\Cache;
|
use Utopia\Cache\Cache;
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Database\Adapter\MySQL;
|
use Utopia\Database\Adapter\MySQL;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
|
use Utopia\DI\Container;
|
||||||
|
use Utopia\DI\Dependency;
|
||||||
|
use Utopia\Http\Adapter\FPM\Server;
|
||||||
|
use Utopia\Http\Adapter\Swoole\Request;
|
||||||
|
use Utopia\Http\Adapter\Swoole\Response as HttpResponse;
|
||||||
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Validator\Text;
|
||||||
|
use Utopia\Http\Validator\WhiteList;
|
||||||
use Utopia\Platform\Action;
|
use Utopia\Platform\Action;
|
||||||
use Utopia\Registry\Registry;
|
|
||||||
use Utopia\Request as UtopiaRequest;
|
|
||||||
use Utopia\Response as UtopiaResponse;
|
|
||||||
use Utopia\System\System;
|
use Utopia\System\System;
|
||||||
use Utopia\Validator\Text;
|
|
||||||
use Utopia\Validator\WhiteList;
|
|
||||||
|
|
||||||
class Specs extends Action
|
class Specs extends Action
|
||||||
{
|
{
|
||||||
|
|
@ -32,37 +34,38 @@ class Specs extends Action
|
||||||
return 'specs';
|
return 'specs';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRequest(): UtopiaRequest
|
|
||||||
{
|
|
||||||
return new AppwriteRequest(new SwooleRequest());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getResponse(): UtopiaResponse
|
|
||||||
{
|
|
||||||
return new AppwriteResponse(new SwooleResponse());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this
|
$this
|
||||||
->desc('Generate Appwrite API specifications')
|
->desc('Generate Appwrite API specifications')
|
||||||
->param('version', 'latest', new Text(16), 'Spec version', true)
|
->param('version', 'latest', new Text(16), 'Spec version', true)
|
||||||
->param('mode', 'normal', new WhiteList(['normal', 'mocks']), 'Spec Mode', true)
|
->param('mode', 'normal', new WhiteList(['normal', 'mocks']), 'Spec Mode', true)
|
||||||
->inject('register')
|
->inject('context')
|
||||||
->callback(fn (string $version, string $mode, Registry $register) => $this->action($version, $mode, $register));
|
->callback(fn (string $version, string $mode, Container $context) => $this->action($version, $mode, $context));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function action(string $version, string $mode, Registry $register): void
|
public function action(string $version, string $mode, Container $container): void
|
||||||
{
|
{
|
||||||
$appRoutes = App::getRoutes();
|
$appRoutes = Http::getRoutes();
|
||||||
$response = $this->getResponse();
|
$response = new Response(new HttpResponse(new SwooleHttpResponse()));
|
||||||
$mocks = ($mode === 'mocks');
|
$mocks = ($mode === 'mocks');
|
||||||
|
|
||||||
|
$requestDependency = new Dependency();
|
||||||
|
$responseDependency = new Dependency();
|
||||||
|
$dbForConsole = new Dependency();
|
||||||
|
$dbForProject = new Dependency();
|
||||||
|
|
||||||
// Mock dependencies
|
// Mock dependencies
|
||||||
App::setResource('request', fn () => $this->getRequest());
|
$requestDependency->setName('request')->setCallback(fn () => new Request(new SwooleHttpRequest()));
|
||||||
App::setResource('response', fn () => $response);
|
$responseDependency->setName('response')->setCallback(fn () => $response);
|
||||||
App::setResource('dbForConsole', fn () => new Database(new MySQL(''), new Cache(new None())));
|
$dbForConsole->setName('dbForConsole')->setCallback(fn () => new Database(new MySQL(''), new Cache(new None())));
|
||||||
App::setResource('dbForProject', fn () => new Database(new MySQL(''), new Cache(new None())));
|
$dbForProject->setName('dbForProject')->setCallback(fn () => new Database(new MySQL(''), new Cache(new None())));
|
||||||
|
|
||||||
|
$container
|
||||||
|
->set($requestDependency)
|
||||||
|
->set($responseDependency)
|
||||||
|
->set($dbForProject)
|
||||||
|
->set($dbForConsole);
|
||||||
|
|
||||||
$platforms = [
|
$platforms = [
|
||||||
'client' => APP_PLATFORM_CLIENT,
|
'client' => APP_PLATFORM_CLIENT,
|
||||||
|
|
@ -252,7 +255,7 @@ class Specs extends Action
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$models = $response->getModels();
|
$models = Models::getModels();
|
||||||
|
|
||||||
foreach ($models as $key => $value) {
|
foreach ($models as $key => $value) {
|
||||||
if ($platform !== APP_PLATFORM_CONSOLE && !$value->isPublic()) {
|
if ($platform !== APP_PLATFORM_CONSOLE && !$value->isPublic()) {
|
||||||
|
|
@ -260,7 +263,7 @@ class Specs extends Action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$arguments = [new App('UTC'), $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0];
|
$arguments = [new Http(new Server(), $container, 'UTC'), $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0];
|
||||||
foreach (['swagger2', 'open-api3'] as $format) {
|
foreach (['swagger2', 'open-api3'] as $format) {
|
||||||
$formatInstance = match ($format) {
|
$formatInstance = match ($format) {
|
||||||
'swagger2' => new Swagger2(...$arguments),
|
'swagger2' => new Swagger2(...$arguments),
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
namespace Appwrite\Platform\Tasks;
|
namespace Appwrite\Platform\Tasks;
|
||||||
|
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Validator\Boolean;
|
use Utopia\Http\Validator\Boolean;
|
||||||
use Utopia\Validator\Text;
|
use Utopia\Http\Validator\Text;
|
||||||
|
|
||||||
class Upgrade extends Install
|
class Upgrade extends Install
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use Utopia\Database\Database;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Exception\Authorization;
|
use Utopia\Database\Exception\Authorization;
|
||||||
use Utopia\Database\Exception\Structure;
|
use Utopia\Database\Exception\Structure;
|
||||||
|
use Utopia\Database\Validator\Authorization as ValidatorAuthorization;
|
||||||
use Utopia\Platform\Action;
|
use Utopia\Platform\Action;
|
||||||
use Utopia\Queue\Message;
|
use Utopia\Queue\Message;
|
||||||
|
|
||||||
|
|
@ -28,7 +29,8 @@ class Audits extends Action
|
||||||
->desc('Audits worker')
|
->desc('Audits worker')
|
||||||
->inject('message')
|
->inject('message')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->callback(fn ($message, $dbForProject) => $this->action($message, $dbForProject));
|
->inject('authorization')
|
||||||
|
->callback(fn ($message, $dbForProject, ValidatorAuthorization $authorization) => $this->action($message, $dbForProject, $authorization));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -41,7 +43,7 @@ class Audits extends Action
|
||||||
* @throws Authorization
|
* @throws Authorization
|
||||||
* @throws Structure
|
* @throws Structure
|
||||||
*/
|
*/
|
||||||
public function action(Message $message, Database $dbForProject): void
|
public function action(Message $message, Database $dbForProject, ValidatorAuthorization $auth): void
|
||||||
{
|
{
|
||||||
|
|
||||||
$payload = $message->getPayload() ?? [];
|
$payload = $message->getPayload() ?? [];
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,8 @@ class Builds extends Action
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('deviceForFunctions')
|
->inject('deviceForFunctions')
|
||||||
->inject('log')
|
->inject('log')
|
||||||
->callback(fn ($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $usage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $usage, $cache, $dbForProject, $deviceForFunctions, $log));
|
->inject('authorization')
|
||||||
|
->callback(fn ($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $usage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log, Authorization $authorization) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $usage, $cache, $dbForProject, $deviceForFunctions, $log, $authorization));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -67,10 +68,11 @@ class Builds extends Action
|
||||||
* @param Database $dbForProject
|
* @param Database $dbForProject
|
||||||
* @param Device $deviceForFunctions
|
* @param Device $deviceForFunctions
|
||||||
* @param Log $log
|
* @param Log $log
|
||||||
|
* @param Authorization $auth
|
||||||
* @return void
|
* @return void
|
||||||
* @throws \Utopia\Database\Exception
|
* @throws \Utopia\Database\Exception
|
||||||
*/
|
*/
|
||||||
public function action(Message $message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log): void
|
public function action(Message $message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log, Authorization $auth): void
|
||||||
{
|
{
|
||||||
$payload = $message->getPayload() ?? [];
|
$payload = $message->getPayload() ?? [];
|
||||||
|
|
||||||
|
|
@ -92,7 +94,7 @@ class Builds extends Action
|
||||||
case BUILD_TYPE_RETRY:
|
case BUILD_TYPE_RETRY:
|
||||||
Console::info('Creating build for deployment: ' . $deployment->getId());
|
Console::info('Creating build for deployment: ' . $deployment->getId());
|
||||||
$github = new GitHub($cache);
|
$github = new GitHub($cache);
|
||||||
$this->buildDeployment($deviceForFunctions, $queueForFunctions, $queueForEvents, $queueForUsage, $dbForConsole, $dbForProject, $github, $project, $resource, $deployment, $template, $log);
|
$this->buildDeployment($deviceForFunctions, $queueForFunctions, $queueForEvents, $queueForUsage, $dbForConsole, $dbForProject, $github, $project, $resource, $deployment, $template, $log, $auth);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -113,11 +115,12 @@ class Builds extends Action
|
||||||
* @param Document $deployment
|
* @param Document $deployment
|
||||||
* @param Document $template
|
* @param Document $template
|
||||||
* @param Log $log
|
* @param Log $log
|
||||||
|
* @param Authorization $auth
|
||||||
* @return void
|
* @return void
|
||||||
* @throws \Utopia\Database\Exception
|
* @throws \Utopia\Database\Exception
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
protected function buildDeployment(Device $deviceForFunctions, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, Database $dbForProject, GitHub $github, Document $project, Document $function, Document $deployment, Document $template, Log $log): void
|
protected function buildDeployment(Device $deviceForFunctions, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, Database $dbForProject, GitHub $github, Document $project, Document $function, Document $deployment, Document $template, Log $log, Authorization $auth): void
|
||||||
{
|
{
|
||||||
$executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST'));
|
$executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST'));
|
||||||
|
|
||||||
|
|
@ -221,20 +224,18 @@ class Builds extends Action
|
||||||
$templateRootDirectory = \ltrim($templateRootDirectory, '/');
|
$templateRootDirectory = \ltrim($templateRootDirectory, '/');
|
||||||
|
|
||||||
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateVersion)) {
|
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateVersion)) {
|
||||||
$stdout = '';
|
$output = '';
|
||||||
$stderr = '';
|
|
||||||
|
|
||||||
// Clone template repo
|
// Clone template repo
|
||||||
$tmpTemplateDirectory = '/tmp/builds/' . $buildId . '-template';
|
$tmpTemplateDirectory = '/tmp/builds/' . $buildId . '-template';
|
||||||
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateVersion, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory);
|
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateVersion, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory);
|
||||||
$exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr);
|
$exit = Console::execute($gitCloneCommandForTemplate, '', $output);
|
||||||
|
|
||||||
if ($exit !== 0) {
|
if ($exit !== 0) {
|
||||||
throw new \Exception('Unable to clone code repository: ' . $stderr);
|
throw new \Exception('Unable to clone code repository: ' . $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure directories
|
// Ensure directories
|
||||||
Console::execute('mkdir -p ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory), '', $stdout, $stderr);
|
Console::execute('mkdir -p ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory), '', $output);
|
||||||
|
|
||||||
$tmpPathFile = $tmpTemplateDirectory . '/code.tar.gz';
|
$tmpPathFile = $tmpTemplateDirectory . '/code.tar.gz';
|
||||||
|
|
||||||
|
|
@ -245,7 +246,7 @@ class Builds extends Action
|
||||||
}
|
}
|
||||||
|
|
||||||
$tarParamDirectory = \escapeshellarg($tmpTemplateDirectory . (empty($templateRootDirectory) ? '' : '/' . $templateRootDirectory));
|
$tarParamDirectory = \escapeshellarg($tmpTemplateDirectory . (empty($templateRootDirectory) ? '' : '/' . $templateRootDirectory));
|
||||||
Console::execute('tar --exclude code.tar.gz -czf ' . \escapeshellarg($tmpPathFile) . ' -C ' . \escapeshellcmd($tarParamDirectory) . ' .', '', $stdout, $stderr); // TODO: Replace escapeshellcmd with escapeshellarg if we find a way that doesnt break syntax
|
Console::execute('tar --exclude code.tar.gz -czf ' . \escapeshellarg($tmpPathFile) . ' -C ' . \escapeshellcmd($tarParamDirectory) . ' .', '', $output); // TODO: Replace escapeshellcmd with escapeshellarg if we find a way that doesnt break syntax
|
||||||
|
|
||||||
$source = $deviceForFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
|
$source = $deviceForFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
|
||||||
$result = $localDevice->transfer($tmpPathFile, $source, $deviceForFunctions);
|
$result = $localDevice->transfer($tmpPathFile, $source, $deviceForFunctions);
|
||||||
|
|
@ -254,7 +255,7 @@ class Builds extends Action
|
||||||
throw new \Exception("Unable to move file");
|
throw new \Exception("Unable to move file");
|
||||||
}
|
}
|
||||||
|
|
||||||
Console::execute('rm -rf ' . \escapeshellarg($tmpTemplateDirectory), '', $stdout, $stderr);
|
Console::execute('rm -rf ' . \escapeshellarg($tmpTemplateDirectory), '', $output);
|
||||||
|
|
||||||
$directorySize = $deviceForFunctions->getFileSize($source);
|
$directorySize = $deviceForFunctions->getFileSize($source);
|
||||||
$build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source));
|
$build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source));
|
||||||
|
|
@ -276,6 +277,7 @@ class Builds extends Action
|
||||||
|
|
||||||
$branchName = $deployment->getAttribute('providerBranch');
|
$branchName = $deployment->getAttribute('providerBranch');
|
||||||
$commitHash = $deployment->getAttribute('providerCommitHash', '');
|
$commitHash = $deployment->getAttribute('providerCommitHash', '');
|
||||||
|
$output = '';
|
||||||
|
|
||||||
$cloneVersion = $branchName;
|
$cloneVersion = $branchName;
|
||||||
$cloneType = GitHub::CLONE_TYPE_BRANCH;
|
$cloneType = GitHub::CLONE_TYPE_BRANCH;
|
||||||
|
|
@ -283,22 +285,18 @@ class Builds extends Action
|
||||||
$cloneVersion = $commitHash;
|
$cloneVersion = $commitHash;
|
||||||
$cloneType = GitHub::CLONE_TYPE_COMMIT;
|
$cloneType = GitHub::CLONE_TYPE_COMMIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
$gitCloneCommand = $github->generateCloneCommand($cloneOwner, $cloneRepository, $cloneVersion, $cloneType, $tmpDirectory, $rootDirectory);
|
$gitCloneCommand = $github->generateCloneCommand($cloneOwner, $cloneRepository, $cloneVersion, $cloneType, $tmpDirectory, $rootDirectory);
|
||||||
$stdout = '';
|
Console::execute('mkdir -p ' . \escapeshellarg('/tmp/builds/' . $buildId), '', $output);
|
||||||
$stderr = '';
|
|
||||||
|
|
||||||
Console::execute('mkdir -p ' . \escapeshellarg('/tmp/builds/' . $buildId), '', $stdout, $stderr);
|
|
||||||
|
|
||||||
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
|
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
|
||||||
Console::info('Build has been canceled');
|
Console::info('Build has been canceled');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$exit = Console::execute($gitCloneCommand, '', $stdout, $stderr);
|
$exit = Console::execute($gitCloneCommand, '', $output);
|
||||||
|
|
||||||
if ($exit !== 0) {
|
if ($exit !== 0) {
|
||||||
throw new \Exception('Unable to clone code repository: ' . $stderr);
|
throw new \Exception('Unable to clone code repository: ' . $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Local refactoring for function folder with spaces
|
// Local refactoring for function folder with spaces
|
||||||
|
|
@ -306,10 +304,10 @@ class Builds extends Action
|
||||||
$rootDirectoryWithoutSpaces = str_replace(' ', '', $rootDirectory);
|
$rootDirectoryWithoutSpaces = str_replace(' ', '', $rootDirectory);
|
||||||
$from = $tmpDirectory . '/' . $rootDirectory;
|
$from = $tmpDirectory . '/' . $rootDirectory;
|
||||||
$to = $tmpDirectory . '/' . $rootDirectoryWithoutSpaces;
|
$to = $tmpDirectory . '/' . $rootDirectoryWithoutSpaces;
|
||||||
$exit = Console::execute('mv "' . \escapeshellarg($from) . '" "' . \escapeshellarg($to) . '"', '', $stdout, $stderr);
|
$exit = Console::execute('mv "' . \escapeshellarg($from) . '" "' . \escapeshellarg($to) . '"', '', $output);
|
||||||
|
|
||||||
if ($exit !== 0) {
|
if ($exit !== 0) {
|
||||||
throw new \Exception('Unable to move function with spaces' . $stderr);
|
throw new \Exception('Unable to move function with spaces' . $output);
|
||||||
}
|
}
|
||||||
$rootDirectory = $rootDirectoryWithoutSpaces;
|
$rootDirectory = $rootDirectoryWithoutSpaces;
|
||||||
}
|
}
|
||||||
|
|
@ -330,33 +328,33 @@ class Builds extends Action
|
||||||
$tmpTemplateDirectory = '/tmp/builds/' . $buildId . '/template';
|
$tmpTemplateDirectory = '/tmp/builds/' . $buildId . '/template';
|
||||||
|
|
||||||
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateVersion, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory);
|
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateVersion, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory);
|
||||||
$exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr);
|
$exit = Console::execute($gitCloneCommandForTemplate, '', $output);
|
||||||
|
|
||||||
if ($exit !== 0) {
|
if ($exit !== 0) {
|
||||||
throw new \Exception('Unable to clone code repository: ' . $stderr);
|
throw new \Exception('Unable to clone code repository: ' . $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure directories
|
// Ensure directories
|
||||||
Console::execute('mkdir -p ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory), '', $stdout, $stderr);
|
Console::execute('mkdir -p ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory), '', $output);
|
||||||
Console::execute('mkdir -p ' . \escapeshellarg($tmpDirectory . '/' . $rootDirectory), '', $stdout, $stderr);
|
Console::execute('mkdir -p ' . \escapeshellarg($tmpDirectory . '/' . $rootDirectory), '', $output);
|
||||||
|
|
||||||
// Merge template into user repo
|
// Merge template into user repo
|
||||||
Console::execute('rsync -av --exclude \'.git\' ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory . '/') . ' ' . \escapeshellarg($tmpDirectory . '/' . $rootDirectory), '', $stdout, $stderr);
|
Console::execute('rsync -av --exclude \'.git\' ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory . '/') . ' ' . \escapeshellarg($tmpDirectory . '/' . $rootDirectory), '', $output);
|
||||||
|
|
||||||
// Commit and push
|
// Commit and push
|
||||||
$exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . \escapeshellarg($tmpDirectory) . ' && git add . && git commit -m "Create ' . \escapeshellarg($function->getAttribute('name', '')) . ' function" && git push origin ' . \escapeshellarg($branchName), '', $stdout, $stderr);
|
$exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . \escapeshellarg($tmpDirectory) . ' && git add . && git commit -m "Create ' . \escapeshellarg($function->getAttribute('name', '')) . ' function" && git push origin ' . \escapeshellarg($branchName), '', $output);
|
||||||
|
|
||||||
if ($exit !== 0) {
|
if ($exit !== 0) {
|
||||||
throw new \Exception('Unable to push code repository: ' . $stderr);
|
throw new \Exception('Unable to push code repository: ' . $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
$exit = Console::execute('cd ' . \escapeshellarg($tmpDirectory) . ' && git rev-parse HEAD', '', $stdout, $stderr);
|
$exit = Console::execute('cd ' . \escapeshellarg($tmpDirectory) . ' && git rev-parse HEAD', '', $output);
|
||||||
|
|
||||||
if ($exit !== 0) {
|
if ($exit !== 0) {
|
||||||
throw new \Exception('Unable to get vcs commit SHA: ' . $stderr);
|
throw new \Exception('Unable to get vcs commit SHA: ' . $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
$providerCommitHash = \trim($stdout);
|
$providerCommitHash = \trim($output);
|
||||||
$authorUrl = "https://github.com/$cloneOwner";
|
$authorUrl = "https://github.com/$cloneOwner";
|
||||||
|
|
||||||
$deployment->setAttribute('providerCommitHash', $providerCommitHash ?? '');
|
$deployment->setAttribute('providerCommitHash', $providerCommitHash ?? '');
|
||||||
|
|
@ -399,7 +397,7 @@ class Builds extends Action
|
||||||
}
|
}
|
||||||
|
|
||||||
$tarParamDirectory = '/tmp/builds/' . $buildId . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory);
|
$tarParamDirectory = '/tmp/builds/' . $buildId . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory);
|
||||||
Console::execute('tar --exclude code.tar.gz -czf ' . \escapeshellarg($tmpPathFile) . ' -C ' . \escapeshellcmd($tarParamDirectory) . ' .', '', $stdout, $stderr); // TODO: Replace escapeshellcmd with escapeshellarg if we find a way that doesnt break syntax
|
Console::execute('tar --exclude code.tar.gz -czf ' . \escapeshellarg($tmpPathFile) . ' -C ' . \escapeshellcmd($tarParamDirectory) . ' .', '', $output); // TODO: Replace escapeshellcmd with escapeshellarg if we find a way that doesnt break syntax
|
||||||
|
|
||||||
$source = $deviceForFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
|
$source = $deviceForFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
|
||||||
$result = $localDevice->transfer($tmpPathFile, $source, $deviceForFunctions);
|
$result = $localDevice->transfer($tmpPathFile, $source, $deviceForFunctions);
|
||||||
|
|
@ -408,7 +406,7 @@ class Builds extends Action
|
||||||
throw new \Exception("Unable to move file");
|
throw new \Exception("Unable to move file");
|
||||||
}
|
}
|
||||||
|
|
||||||
Console::execute('rm -rf ' . \escapeshellarg($tmpPath), '', $stdout, $stderr);
|
Console::execute('rm -rf ' . \escapeshellarg($tmpPath), '', $output);
|
||||||
|
|
||||||
$build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source));
|
$build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source));
|
||||||
|
|
||||||
|
|
@ -663,7 +661,7 @@ class Builds extends Action
|
||||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||||
->setAttribute('schedule', $function->getAttribute('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));
|
$auth->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $th) {
|
||||||
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
|
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
|
||||||
Console::info('Build has been canceled');
|
Console::info('Build has been canceled');
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ use Appwrite\Template\Template;
|
||||||
use Appwrite\Utopia\Response\Model\Rule;
|
use Appwrite\Utopia\Response\Model\Rule;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\DateTime;
|
use Utopia\Database\DateTime;
|
||||||
|
|
@ -22,6 +21,7 @@ use Utopia\Database\Exception\Structure;
|
||||||
use Utopia\Database\Helpers\ID;
|
use Utopia\Database\Helpers\ID;
|
||||||
use Utopia\Database\Query;
|
use Utopia\Database\Query;
|
||||||
use Utopia\Domains\Domain;
|
use Utopia\Domains\Domain;
|
||||||
|
use Utopia\Http\Http;
|
||||||
use Utopia\Locale\Locale;
|
use Utopia\Locale\Locale;
|
||||||
use Utopia\Logger\Log;
|
use Utopia\Logger\Log;
|
||||||
use Utopia\Platform\Action;
|
use Utopia\Platform\Action;
|
||||||
|
|
@ -126,7 +126,7 @@ class Certificates extends Action
|
||||||
$certificate = $dbForConsole->findOne('certificates', [Query::equal('domain', [$domain->get()])]);
|
$certificate = $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 we don't have certificate for domain yet, let's create new document. At the end we save it
|
||||||
if (!$certificate) {
|
if ($certificate->isEmpty()) {
|
||||||
$certificate = new Document();
|
$certificate = new Document();
|
||||||
$certificate->setAttribute('domain', $domain->get());
|
$certificate->setAttribute('domain', $domain->get());
|
||||||
}
|
}
|
||||||
|
|
@ -216,7 +216,7 @@ class Certificates extends Action
|
||||||
{
|
{
|
||||||
// Check if update or insert required
|
// Check if update or insert required
|
||||||
$certificateDocument = $dbForConsole->findOne('certificates', [Query::equal('domain', [$domain])]);
|
$certificateDocument = $dbForConsole->findOne('certificates', [Query::equal('domain', [$domain])]);
|
||||||
if (!empty($certificateDocument) && !$certificateDocument->isEmpty()) {
|
if (!$certificateDocument->isEmpty()) {
|
||||||
// Merge new data with current data
|
// Merge new data with current data
|
||||||
$certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy()));
|
$certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy()));
|
||||||
$certificate = $dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate);
|
$certificate = $dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate);
|
||||||
|
|
@ -330,30 +330,26 @@ class Certificates extends Action
|
||||||
*
|
*
|
||||||
* @param string $folder Folder into which certificates should be generated
|
* @param string $folder Folder into which certificates should be generated
|
||||||
* @param string $domain Domain to generate certificate for
|
* @param string $domain Domain to generate certificate for
|
||||||
* @return array Named array with keys 'stdout' and 'stderr', both string
|
* @return string output
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
private function issueCertificate(string $folder, string $domain, string $email): array
|
private function issueCertificate(string $folder, string $domain, string $email): string
|
||||||
{
|
{
|
||||||
$stdout = '';
|
$output = '';
|
||||||
$stderr = '';
|
|
||||||
|
|
||||||
$staging = (App::isProduction()) ? '' : ' --dry-run';
|
$staging = (Http::isProduction()) ? '' : ' --dry-run';
|
||||||
$exit = Console::execute("certbot certonly -v --webroot --noninteractive --agree-tos{$staging}"
|
$exit = Console::execute("certbot certonly -v --webroot --noninteractive --agree-tos{$staging}"
|
||||||
. " --email " . $email
|
. " --email " . $email
|
||||||
. " --cert-name " . $folder
|
. " --cert-name " . $folder
|
||||||
. " -w " . APP_STORAGE_CERTIFICATES
|
. " -w " . APP_STORAGE_CERTIFICATES
|
||||||
. " -d {$domain}", '', $stdout, $stderr);
|
. " -d {$domain}", '', $output);
|
||||||
|
|
||||||
// Unexpected error, usually 5XX, API limits, ...
|
// Unexpected error, usually 5XX, API limits, ...
|
||||||
if ($exit !== 0) {
|
if ($exit !== 0) {
|
||||||
throw new Exception('Failed to issue a certificate with message: ' . $stderr);
|
throw new Exception('Failed to issue a certificate with message: ' . $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return $output;
|
||||||
'stdout' => $stdout,
|
|
||||||
'stderr' => $stderr
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -381,7 +377,7 @@ class Certificates extends Action
|
||||||
* @return void
|
* @return void
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
private function applyCertificateFiles(string $folder, string $domain, array $letsEncryptData): void
|
private function applyCertificateFiles(string $folder, string $domain, string $letsEncryptData): void
|
||||||
{
|
{
|
||||||
|
|
||||||
// Prepare folder in storage for domain
|
// Prepare folder in storage for domain
|
||||||
|
|
@ -394,19 +390,19 @@ class Certificates extends Action
|
||||||
|
|
||||||
// Move generated files
|
// Move generated files
|
||||||
if (!@\rename('/etc/letsencrypt/live/' . $folder . '/cert.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem')) {
|
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']);
|
throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!@\rename('/etc/letsencrypt/live/' . $folder . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/chain.pem')) {
|
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']);
|
throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!@\rename('/etc/letsencrypt/live/' . $folder . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/fullchain.pem')) {
|
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']);
|
throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!@\rename('/etc/letsencrypt/live/' . $folder . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/privkey.pem')) {
|
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']);
|
throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData);
|
||||||
}
|
}
|
||||||
|
|
||||||
$config = \implode(PHP_EOL, [
|
$config = \implode(PHP_EOL, [
|
||||||
|
|
@ -482,7 +478,7 @@ class Certificates extends Action
|
||||||
Query::equal('domain', [$domain]),
|
Query::equal('domain', [$domain]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($rule !== false && !$rule->isEmpty()) {
|
if (!$rule->isEmpty()) {
|
||||||
$rule->setAttribute('certificateId', $certificateId);
|
$rule->setAttribute('certificateId', $certificateId);
|
||||||
$rule->setAttribute('status', $success ? 'verified' : 'unverified');
|
$rule->setAttribute('status', $success ? 'verified' : 'unverified');
|
||||||
$dbForConsole->updateDocument('rules', $rule->getId(), $rule);
|
$dbForConsole->updateDocument('rules', $rule->getId(), $rule);
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ use Utopia\Database\Exception\Conflict;
|
||||||
use Utopia\Database\Exception\Restricted;
|
use Utopia\Database\Exception\Restricted;
|
||||||
use Utopia\Database\Exception\Structure;
|
use Utopia\Database\Exception\Structure;
|
||||||
use Utopia\Database\Query;
|
use Utopia\Database\Query;
|
||||||
|
use Utopia\Database\Validator\Authorization as ValidatorAuthorization;
|
||||||
use Utopia\DSN\DSN;
|
use Utopia\DSN\DSN;
|
||||||
use Utopia\Logger\Log;
|
use Utopia\Logger\Log;
|
||||||
use Utopia\Platform\Action;
|
use Utopia\Platform\Action;
|
||||||
|
|
@ -54,14 +55,15 @@ class Deletes extends Action
|
||||||
->inject('executionRetention')
|
->inject('executionRetention')
|
||||||
->inject('auditRetention')
|
->inject('auditRetention')
|
||||||
->inject('log')
|
->inject('log')
|
||||||
->callback(fn ($message, $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log) => $this->action($message, $dbForConsole, $getProjectDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $abuseRetention, $executionRetention, $auditRetention, $log));
|
->inject('authorization')
|
||||||
|
->callback(fn ($message, $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log, ValidatorAuthorization $authorization) => $this->action($message, $dbForConsole, $getProjectDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $abuseRetention, $executionRetention, $auditRetention, $log, $authorization));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
* @throws Throwable
|
* @throws Throwable
|
||||||
*/
|
*/
|
||||||
public function action(Message $message, Database $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log): void
|
public function action(Message $message, Database $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log, ValidatorAuthorization $auth): void
|
||||||
{
|
{
|
||||||
$payload = $message->getPayload() ?? [];
|
$payload = $message->getPayload() ?? [];
|
||||||
|
|
||||||
|
|
@ -117,7 +119,7 @@ class Deletes extends Action
|
||||||
break;
|
break;
|
||||||
case DELETE_TYPE_AUDIT:
|
case DELETE_TYPE_AUDIT:
|
||||||
if (!$project->isEmpty()) {
|
if (!$project->isEmpty()) {
|
||||||
$this->deleteAuditLogs($project, $getProjectDB, $auditRetention);
|
$this->deleteAuditLogs($project, $getProjectDB, $auditRetention, $auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$document->isEmpty()) {
|
if (!$document->isEmpty()) {
|
||||||
|
|
@ -125,7 +127,7 @@ class Deletes extends Action
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DELETE_TYPE_ABUSE:
|
case DELETE_TYPE_ABUSE:
|
||||||
$this->deleteAbuseLogs($project, $getProjectDB, $abuseRetention);
|
$this->deleteAbuseLogs($project, $getProjectDB, $abuseRetention, $auth);
|
||||||
break;
|
break;
|
||||||
case DELETE_TYPE_REALTIME:
|
case DELETE_TYPE_REALTIME:
|
||||||
$this->deleteRealtimeUsage($dbForConsole, $datetime);
|
$this->deleteRealtimeUsage($dbForConsole, $datetime);
|
||||||
|
|
@ -699,7 +701,7 @@ class Deletes extends Action
|
||||||
* @return void
|
* @return void
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
private function deleteAbuseLogs(Document $project, callable $getProjectDB, string $abuseRetention): void
|
private function deleteAbuseLogs(Document $project, callable $getProjectDB, string $abuseRetention, ValidatorAuthorization $auth): void
|
||||||
{
|
{
|
||||||
$projectId = $project->getId();
|
$projectId = $project->getId();
|
||||||
$dbForProject = $getProjectDB($project);
|
$dbForProject = $getProjectDB($project);
|
||||||
|
|
@ -720,7 +722,7 @@ class Deletes extends Action
|
||||||
* @return void
|
* @return void
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
private function deleteAuditLogs(Document $project, callable $getProjectDB, string $auditRetention): void
|
private function deleteAuditLogs(Document $project, callable $getProjectDB, string $auditRetention, ValidatorAuthorization $auth): void
|
||||||
{
|
{
|
||||||
$projectId = $project->getId();
|
$projectId = $project->getId();
|
||||||
$dbForProject = $getProjectDB($project);
|
$dbForProject = $getProjectDB($project);
|
||||||
|
|
|
||||||
|
|
@ -84,13 +84,13 @@ class Mails extends Action
|
||||||
$bodyTemplate = __DIR__ . '/../../../../app/config/locale/templates/email-base.tpl';
|
$bodyTemplate = __DIR__ . '/../../../../app/config/locale/templates/email-base.tpl';
|
||||||
}
|
}
|
||||||
$bodyTemplate = Template::fromFile($bodyTemplate);
|
$bodyTemplate = Template::fromFile($bodyTemplate);
|
||||||
$bodyTemplate->setParam('{{body}}', $body, escapeHtml: false);
|
$bodyTemplate->setParam('{{body}}', $body, escape: false);
|
||||||
foreach ($variables as $key => $value) {
|
foreach ($variables as $key => $value) {
|
||||||
// TODO: hotfix for redirect param
|
// TODO: hotfix for redirect param
|
||||||
$bodyTemplate->setParam('{{' . $key . '}}', $value, escapeHtml: $key !== 'redirect');
|
$bodyTemplate->setParam('{{' . $key . '}}', $value, escape: $key !== 'redirect');
|
||||||
}
|
}
|
||||||
foreach ($this->richTextParams as $key => $value) {
|
foreach ($this->richTextParams as $key => $value) {
|
||||||
$bodyTemplate->setParam('{{' . $key . '}}', $value, escapeHtml: false);
|
$bodyTemplate->setParam('{{' . $key . '}}', $value, escape: false);
|
||||||
}
|
}
|
||||||
$body = $bodyTemplate->render();
|
$body = $bodyTemplate->render();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@ class Messaging extends Action
|
||||||
Query::equal('type', [$providerType]),
|
Query::equal('type', [$providerType]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($default === false || $default->isEmpty()) {
|
if ($default->isEmpty()) {
|
||||||
$dbForProject->updateDocument('messages', $message->getId(), $message->setAttributes([
|
$dbForProject->updateDocument('messages', $message->getId(), $message->setAttributes([
|
||||||
'status' => MessageStatus::FAILED,
|
'status' => MessageStatus::FAILED,
|
||||||
'deliveryErrors' => ['No enabled provider found.']
|
'deliveryErrors' => ['No enabled provider found.']
|
||||||
|
|
@ -275,7 +275,7 @@ class Messaging extends Action
|
||||||
Query::equal('identifier', [$result['recipient']])
|
Query::equal('identifier', [$result['recipient']])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($target instanceof Document && !$target->isEmpty()) {
|
if (!$target->isEmpty()) {
|
||||||
$dbForProject->updateDocument(
|
$dbForProject->updateDocument(
|
||||||
'targets',
|
'targets',
|
||||||
$target->getId(),
|
$target->getId(),
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ abstract class Promise
|
||||||
|
|
||||||
private mixed $result;
|
private mixed $result;
|
||||||
|
|
||||||
public function __construct(?callable $executor = null)
|
final public function __construct(?callable $executor = null)
|
||||||
{
|
{
|
||||||
if (\is_null($executor)) {
|
if (\is_null($executor)) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -6,23 +6,16 @@ use Swoole\Coroutine\Channel;
|
||||||
|
|
||||||
class Swoole extends Promise
|
class Swoole extends Promise
|
||||||
{
|
{
|
||||||
public function __construct(?callable $executor = null)
|
|
||||||
{
|
|
||||||
parent::__construct($executor);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function execute(
|
protected function execute(
|
||||||
callable $executor,
|
callable $executor,
|
||||||
callable $resolve,
|
callable $resolve,
|
||||||
callable $reject
|
callable $reject
|
||||||
): void {
|
): void {
|
||||||
\go(function () use ($executor, $resolve, $reject) {
|
try {
|
||||||
try {
|
$executor($resolve, $reject);
|
||||||
$executor($resolve, $reject);
|
} catch (\Throwable $exception) {
|
||||||
} catch (\Throwable $exception) {
|
$reject($exception);
|
||||||
$reject($exception);
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
namespace Appwrite\Specification;
|
namespace Appwrite\Specification;
|
||||||
|
|
||||||
use Appwrite\Utopia\Response\Model;
|
use Appwrite\Utopia\Response\Model;
|
||||||
use Utopia\App;
|
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Route;
|
use Utopia\Http\Http;
|
||||||
|
use Utopia\Http\Route;
|
||||||
|
|
||||||
abstract class Format
|
abstract class Format
|
||||||
{
|
{
|
||||||
protected App $app;
|
protected Http $http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Route[]
|
* @var Route[]
|
||||||
|
|
@ -50,9 +50,9 @@ abstract class Format
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(App $app, array $services, array $routes, array $models, array $keys, int $authCount)
|
public function __construct(Http $http, array $services, array $routes, array $models, array $keys, int $authCount)
|
||||||
{
|
{
|
||||||
$this->app = $app;
|
$this->http = $http;
|
||||||
$this->services = $services;
|
$this->services = $services;
|
||||||
$this->routes = $routes;
|
$this->routes = $routes;
|
||||||
$this->models = $models;
|
$this->models = $models;
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,11 @@ use Appwrite\Template\Template;
|
||||||
use Appwrite\Utopia\Response\Model;
|
use Appwrite\Utopia\Response\Model;
|
||||||
use Utopia\Database\Helpers\Permission;
|
use Utopia\Database\Helpers\Permission;
|
||||||
use Utopia\Database\Helpers\Role;
|
use Utopia\Database\Helpers\Role;
|
||||||
use Utopia\Validator;
|
use Utopia\Http\Validator;
|
||||||
use Utopia\Validator\ArrayList;
|
use Utopia\Http\Validator\ArrayList;
|
||||||
use Utopia\Validator\Nullable;
|
use Utopia\Http\Validator\Nullable;
|
||||||
use Utopia\Validator\Range;
|
use Utopia\Http\Validator\Range;
|
||||||
use Utopia\Validator\WhiteList;
|
use Utopia\Http\Validator\WhiteList;
|
||||||
|
|
||||||
class OpenAPI3 extends Format
|
class OpenAPI3 extends Format
|
||||||
{
|
{
|
||||||
|
|
@ -238,8 +238,11 @@ class OpenAPI3 extends Format
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($route->getLabel('sdk.response.code', 500) === 204) {
|
if ($route->getLabel('sdk.response.code', 500) === 204) {
|
||||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['description'] = 'No content';
|
$labelCode = (string)$route->getLabel('sdk.response.code', '500');
|
||||||
unset($temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['schema']);
|
$temp['responses'][$labelCode]['description'] = 'No content';
|
||||||
|
if (isset($temp['responses'][$labelCode]['schema'])) {
|
||||||
|
unset($temp['responses'][$labelCode]['schema']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!empty($scope))) { // && 'public' != $scope
|
if ((!empty($scope))) { // && 'public' != $scope
|
||||||
|
|
@ -269,10 +272,10 @@ class OpenAPI3 extends Format
|
||||||
$bodyRequired = [];
|
$bodyRequired = [];
|
||||||
|
|
||||||
foreach ($route->getParams() as $name => $param) { // Set params
|
foreach ($route->getParams() as $name => $param) { // Set params
|
||||||
/**
|
$injections = array_map(fn ($injection) => $this->http->getContainer()->get($injection), $param['injections'] ?? []);
|
||||||
* @var \Utopia\Validator $validator
|
|
||||||
*/
|
/** @var Validator $validator */
|
||||||
$validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['injections'])) : $param['validator'];
|
$validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $injections) : $param['validator'];
|
||||||
|
|
||||||
$node = [
|
$node = [
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
|
|
@ -289,11 +292,11 @@ class OpenAPI3 extends Format
|
||||||
|
|
||||||
switch ((!empty($validator)) ? \get_class($validator) : '') {
|
switch ((!empty($validator)) ? \get_class($validator) : '') {
|
||||||
case 'Utopia\Database\Validator\UID':
|
case 'Utopia\Database\Validator\UID':
|
||||||
case 'Utopia\Validator\Text':
|
case 'Utopia\Http\Validator\Text':
|
||||||
$node['schema']['type'] = $validator->getType();
|
$node['schema']['type'] = $validator->getType();
|
||||||
$node['schema']['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>';
|
$node['schema']['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Boolean':
|
case 'Utopia\Http\Validator\Boolean':
|
||||||
$node['schema']['type'] = $validator->getType();
|
$node['schema']['type'] = $validator->getType();
|
||||||
$node['schema']['x-example'] = false;
|
$node['schema']['x-example'] = false;
|
||||||
break;
|
break;
|
||||||
|
|
@ -314,15 +317,15 @@ class OpenAPI3 extends Format
|
||||||
$node['schema']['format'] = 'email';
|
$node['schema']['format'] = 'email';
|
||||||
$node['schema']['x-example'] = 'email@example.com';
|
$node['schema']['x-example'] = 'email@example.com';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Host':
|
case 'Utopia\Http\Validator\Host':
|
||||||
case 'Utopia\Validator\URL':
|
case 'Utopia\Http\Validator\URL':
|
||||||
$node['schema']['type'] = $validator->getType();
|
$node['schema']['type'] = $validator->getType();
|
||||||
$node['schema']['format'] = 'url';
|
$node['schema']['format'] = 'url';
|
||||||
$node['schema']['x-example'] = 'https://example.com';
|
$node['schema']['x-example'] = 'https://example.com';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\JSON':
|
case 'Utopia\Http\Validator\JSON':
|
||||||
case 'Utopia\Validator\Mock':
|
case 'Utopia\Http\Validator\Mock':
|
||||||
case 'Utopia\Validator\Assoc':
|
case 'Utopia\Http\Validator\Assoc':
|
||||||
$param['default'] = (empty($param['default'])) ? new \stdClass() : $param['default'];
|
$param['default'] = (empty($param['default'])) ? new \stdClass() : $param['default'];
|
||||||
$node['schema']['type'] = 'object';
|
$node['schema']['type'] = 'object';
|
||||||
$node['schema']['x-example'] = '{}';
|
$node['schema']['x-example'] = '{}';
|
||||||
|
|
@ -332,7 +335,7 @@ class OpenAPI3 extends Format
|
||||||
$node['schema']['type'] = $validator->getType();
|
$node['schema']['type'] = $validator->getType();
|
||||||
$node['schema']['format'] = 'binary';
|
$node['schema']['format'] = 'binary';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\ArrayList':
|
case 'Utopia\Http\Validator\ArrayList':
|
||||||
/** @var ArrayList $validator */
|
/** @var ArrayList $validator */
|
||||||
$node['schema']['type'] = 'array';
|
$node['schema']['type'] = 'array';
|
||||||
$node['schema']['items'] = [
|
$node['schema']['items'] = [
|
||||||
|
|
@ -394,25 +397,25 @@ class OpenAPI3 extends Format
|
||||||
$node['schema']['format'] = 'phone';
|
$node['schema']['format'] = 'phone';
|
||||||
$node['schema']['x-example'] = '+12065550100'; // In the US, 555 is reserved like example.com
|
$node['schema']['x-example'] = '+12065550100'; // In the US, 555 is reserved like example.com
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Range':
|
case 'Utopia\Http\Validator\Range':
|
||||||
/** @var Range $validator */
|
/** @var Range $validator */
|
||||||
$node['schema']['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number' : $validator->getType();
|
$node['schema']['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number' : $validator->getType();
|
||||||
$node['schema']['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float';
|
$node['schema']['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float';
|
||||||
$node['schema']['x-example'] = $validator->getMin();
|
$node['schema']['x-example'] = $validator->getMin();
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Numeric':
|
case 'Utopia\Http\Validator\Numeric':
|
||||||
case 'Utopia\Validator\Integer':
|
case 'Utopia\Http\Validator\Integer':
|
||||||
$node['schema']['type'] = $validator->getType();
|
$node['schema']['type'] = $validator->getType();
|
||||||
$node['schema']['format'] = 'int32';
|
$node['schema']['format'] = 'int32';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\FloatValidator':
|
case 'Utopia\Http\Validator\FloatValidator':
|
||||||
$node['schema']['type'] = 'number';
|
$node['schema']['type'] = 'number';
|
||||||
$node['schema']['format'] = 'float';
|
$node['schema']['format'] = 'float';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Length':
|
case 'Utopia\Http\Validator\Length':
|
||||||
$node['schema']['type'] = $validator->getType();
|
$node['schema']['type'] = $validator->getType();
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\WhiteList':
|
case 'Utopia\Http\Validator\WhiteList':
|
||||||
/** @var WhiteList $validator */
|
/** @var WhiteList $validator */
|
||||||
$node['schema']['type'] = $validator->getType();
|
$node['schema']['type'] = $validator->getType();
|
||||||
$node['schema']['x-example'] = $validator->getList()[0];
|
$node['schema']['x-example'] = $validator->getList()[0];
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ use Appwrite\Template\Template;
|
||||||
use Appwrite\Utopia\Response\Model;
|
use Appwrite\Utopia\Response\Model;
|
||||||
use Utopia\Database\Helpers\Permission;
|
use Utopia\Database\Helpers\Permission;
|
||||||
use Utopia\Database\Helpers\Role;
|
use Utopia\Database\Helpers\Role;
|
||||||
use Utopia\Validator;
|
use Utopia\Http\Validator;
|
||||||
use Utopia\Validator\ArrayList;
|
use Utopia\Http\Validator\ArrayList;
|
||||||
use Utopia\Validator\Nullable;
|
use Utopia\Http\Validator\Nullable;
|
||||||
use Utopia\Validator\Range;
|
use Utopia\Http\Validator\Range;
|
||||||
|
|
||||||
class Swagger2 extends Format
|
class Swagger2 extends Format
|
||||||
{
|
{
|
||||||
|
|
@ -270,8 +270,10 @@ class Swagger2 extends Format
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ($parameters as $name => $param) { // Set params
|
foreach ($parameters as $name => $param) { // Set params
|
||||||
|
$injections = array_map(fn ($injection) => $this->http->getContainer()->get($injection), $param['injections'] ?? []);
|
||||||
|
|
||||||
/** @var Validator $validator */
|
/** @var Validator $validator */
|
||||||
$validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['injections'])) : $param['validator'];
|
$validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $injections) : $param['validator'];
|
||||||
|
|
||||||
$node = [
|
$node = [
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
|
|
@ -306,12 +308,12 @@ class Swagger2 extends Format
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($class) {
|
switch ($class) {
|
||||||
case 'Utopia\Validator\Text':
|
case 'Utopia\Http\Validator\Text':
|
||||||
case 'Utopia\Database\Validator\UID':
|
case 'Utopia\Database\Validator\UID':
|
||||||
$node['type'] = $validator->getType();
|
$node['type'] = $validator->getType();
|
||||||
$node['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>';
|
$node['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Boolean':
|
case 'Utopia\Http\Validator\Boolean':
|
||||||
$node['type'] = $validator->getType();
|
$node['type'] = $validator->getType();
|
||||||
$node['x-example'] = false;
|
$node['x-example'] = false;
|
||||||
break;
|
break;
|
||||||
|
|
@ -332,13 +334,13 @@ class Swagger2 extends Format
|
||||||
$node['format'] = 'email';
|
$node['format'] = 'email';
|
||||||
$node['x-example'] = 'email@example.com';
|
$node['x-example'] = 'email@example.com';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Host':
|
case 'Utopia\Http\Validator\Host':
|
||||||
case 'Utopia\Validator\URL':
|
case 'Utopia\Http\Validator\URL':
|
||||||
$node['type'] = $validator->getType();
|
$node['type'] = $validator->getType();
|
||||||
$node['format'] = 'url';
|
$node['format'] = 'url';
|
||||||
$node['x-example'] = 'https://example.com';
|
$node['x-example'] = 'https://example.com';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\ArrayList':
|
case 'Utopia\Http\Validator\ArrayList':
|
||||||
/** @var ArrayList $validator */
|
/** @var ArrayList $validator */
|
||||||
$node['type'] = 'array';
|
$node['type'] = 'array';
|
||||||
$node['collectionFormat'] = 'multi';
|
$node['collectionFormat'] = 'multi';
|
||||||
|
|
@ -346,9 +348,9 @@ class Swagger2 extends Format
|
||||||
'type' => $validator->getValidator()->getType(),
|
'type' => $validator->getValidator()->getType(),
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\JSON':
|
case 'Utopia\Http\Validator\JSON':
|
||||||
case 'Utopia\Validator\Mock':
|
case 'Utopia\Http\Validator\Mock':
|
||||||
case 'Utopia\Validator\Assoc':
|
case 'Utopia\Http\Validator\Assoc':
|
||||||
$node['type'] = 'object';
|
$node['type'] = 'object';
|
||||||
$node['default'] = (empty($param['default'])) ? new \stdClass() : $param['default'];
|
$node['default'] = (empty($param['default'])) ? new \stdClass() : $param['default'];
|
||||||
$node['x-example'] = '{}';
|
$node['x-example'] = '{}';
|
||||||
|
|
@ -397,26 +399,26 @@ class Swagger2 extends Format
|
||||||
$node['format'] = 'phone';
|
$node['format'] = 'phone';
|
||||||
$node['x-example'] = '+12065550100';
|
$node['x-example'] = '+12065550100';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Range':
|
case 'Utopia\Http\Validator\Range':
|
||||||
/** @var Range $validator */
|
/** @var Range $validator */
|
||||||
$node['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number' : $validator->getType();
|
$node['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number' : $validator->getType();
|
||||||
$node['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float';
|
$node['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float';
|
||||||
$node['x-example'] = $validator->getMin();
|
$node['x-example'] = $validator->getMin();
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Numeric':
|
case 'Utopia\Http\Validator\Numeric':
|
||||||
case 'Utopia\Validator\Integer':
|
case 'Utopia\Http\Validator\Integer':
|
||||||
$node['type'] = $validator->getType();
|
$node['type'] = $validator->getType();
|
||||||
$node['format'] = 'int32';
|
$node['format'] = 'int32';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\FloatValidator':
|
case 'Utopia\Http\Validator\FloatValidator':
|
||||||
$node['type'] = 'number';
|
$node['type'] = 'number';
|
||||||
$node['format'] = 'float';
|
$node['format'] = 'float';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Length':
|
case 'Utopia\Http\Validator\Length':
|
||||||
$node['type'] = $validator->getType();
|
$node['type'] = $validator->getType();
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\WhiteList':
|
case 'Utopia\Http\Validator\WhiteList':
|
||||||
/** @var \Utopia\Validator\WhiteList $validator */
|
/** @var \Utopia\Http\Validator\WhiteList $validator */
|
||||||
$node['type'] = $validator->getType();
|
$node['type'] = $validator->getType();
|
||||||
$node['x-example'] = $validator->getList()[0];
|
$node['x-example'] = $validator->getList()[0];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
namespace Appwrite\Task\Validator;
|
namespace Appwrite\Task\Validator;
|
||||||
|
|
||||||
use Cron\CronExpression;
|
use Cron\CronExpression;
|
||||||
use Utopia\Validator;
|
use Utopia\Http\Validator;
|
||||||
|
|
||||||
class Cron extends Validator
|
class Cron extends Validator
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
namespace Appwrite\Utopia\Database\Validator;
|
namespace Appwrite\Utopia\Database\Validator;
|
||||||
|
|
||||||
use Utopia\Database\Validator\UID;
|
use Utopia\Database\Validator\UID;
|
||||||
use Utopia\Validator;
|
use Utopia\Http\Validator;
|
||||||
|
|
||||||
class CompoundUID extends Validator
|
class CompoundUID extends Validator
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Appwrite\Utopia\Database\Validator;
|
namespace Appwrite\Utopia\Database\Validator;
|
||||||
|
|
||||||
use Utopia\Validator;
|
use Utopia\Http\Validator;
|
||||||
|
|
||||||
class ProjectId extends Validator
|
class ProjectId extends Validator
|
||||||
{
|
{
|
||||||
|
|
|
||||||
36
src/Appwrite/Utopia/Queue/Connections.php
Normal file
36
src/Appwrite/Utopia/Queue/Connections.php
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Queue;
|
||||||
|
|
||||||
|
class Connections
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected array $connections = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $connection
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function add(mixed $connection, $pool): self
|
||||||
|
{
|
||||||
|
$this->connections[] = ['connection' => $connection, 'pool' => $pool];
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function reclaim(): self
|
||||||
|
{
|
||||||
|
foreach ($this->connections as $id => $resource) {
|
||||||
|
$pool = $resource['pool'];
|
||||||
|
$connection = $resource['connection'];
|
||||||
|
$pool->put($connection);
|
||||||
|
unset($this->connections[$id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,11 +3,10 @@
|
||||||
namespace Appwrite\Utopia;
|
namespace Appwrite\Utopia;
|
||||||
|
|
||||||
use Appwrite\Utopia\Request\Filter;
|
use Appwrite\Utopia\Request\Filter;
|
||||||
use Swoole\Http\Request as SwooleRequest;
|
use Utopia\Http\Adapter\Swoole\Request as HttpRequest;
|
||||||
use Utopia\Route;
|
use Utopia\Http\Route;
|
||||||
use Utopia\Swoole\Request as UtopiaRequest;
|
|
||||||
|
|
||||||
class Request extends UtopiaRequest
|
class Request extends HttpRequest
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var array<Filter>
|
* @var array<Filter>
|
||||||
|
|
@ -15,9 +14,12 @@ class Request extends UtopiaRequest
|
||||||
private array $filters = [];
|
private array $filters = [];
|
||||||
private static ?Route $route = null;
|
private static ?Route $route = null;
|
||||||
|
|
||||||
public function __construct(SwooleRequest $request)
|
/**
|
||||||
|
* Request constructor.
|
||||||
|
*/
|
||||||
|
public function __construct(HttpRequest $request)
|
||||||
{
|
{
|
||||||
parent::__construct($request);
|
parent::__construct($request->swoole);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -113,6 +115,16 @@ class Request extends UtopiaRequest
|
||||||
return self::$route !== null;
|
return self::$route !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function removeHeader(string $key): static
|
||||||
|
{
|
||||||
|
if (isset($this->headers[$key])) {
|
||||||
|
unset($this->headers[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::removeHeader($key);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get headers
|
* Get headers
|
||||||
*
|
*
|
||||||
|
|
@ -122,14 +134,18 @@ class Request extends UtopiaRequest
|
||||||
*/
|
*/
|
||||||
public function getHeaders(): array
|
public function getHeaders(): array
|
||||||
{
|
{
|
||||||
|
if ($this->headers !== null) {
|
||||||
|
return $this->headers;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$headers = $this->generateHeaders();
|
$this->headers = $this->generateHeaders();
|
||||||
} catch (\Throwable) {
|
} catch (\Throwable) {
|
||||||
$headers = [];
|
$this->headers = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($this->swoole->cookie)) {
|
if (empty($this->swoole->cookie)) {
|
||||||
return $headers;
|
return $this->headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
$cookieHeaders = [];
|
$cookieHeaders = [];
|
||||||
|
|
@ -138,10 +154,10 @@ class Request extends UtopiaRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($cookieHeaders)) {
|
if (!empty($cookieHeaders)) {
|
||||||
$headers['cookie'] = \implode('; ', $cookieHeaders);
|
$this->headers['cookie'] = \implode('; ', $cookieHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $headers;
|
return $this->headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -4,122 +4,20 @@ namespace Appwrite\Utopia;
|
||||||
|
|
||||||
use Appwrite\Utopia\Fetch\BodyMultipart;
|
use Appwrite\Utopia\Fetch\BodyMultipart;
|
||||||
use Appwrite\Utopia\Response\Filter;
|
use Appwrite\Utopia\Response\Filter;
|
||||||
use Appwrite\Utopia\Response\Model;
|
use Appwrite\Utopia\Response\Models;
|
||||||
use Appwrite\Utopia\Response\Model\Account;
|
|
||||||
use Appwrite\Utopia\Response\Model\AlgoArgon2;
|
|
||||||
use Appwrite\Utopia\Response\Model\AlgoBcrypt;
|
|
||||||
use Appwrite\Utopia\Response\Model\AlgoMd5;
|
|
||||||
use Appwrite\Utopia\Response\Model\AlgoPhpass;
|
|
||||||
use Appwrite\Utopia\Response\Model\AlgoScrypt;
|
|
||||||
use Appwrite\Utopia\Response\Model\AlgoScryptModified;
|
|
||||||
use Appwrite\Utopia\Response\Model\AlgoSha;
|
|
||||||
use Appwrite\Utopia\Response\Model\Any;
|
|
||||||
use Appwrite\Utopia\Response\Model\Attribute;
|
|
||||||
use Appwrite\Utopia\Response\Model\AttributeBoolean;
|
|
||||||
use Appwrite\Utopia\Response\Model\AttributeDatetime;
|
|
||||||
use Appwrite\Utopia\Response\Model\AttributeEmail;
|
|
||||||
use Appwrite\Utopia\Response\Model\AttributeEnum;
|
|
||||||
use Appwrite\Utopia\Response\Model\AttributeFloat;
|
|
||||||
use Appwrite\Utopia\Response\Model\AttributeInteger;
|
|
||||||
use Appwrite\Utopia\Response\Model\AttributeIP;
|
|
||||||
use Appwrite\Utopia\Response\Model\AttributeList;
|
|
||||||
use Appwrite\Utopia\Response\Model\AttributeRelationship;
|
|
||||||
use Appwrite\Utopia\Response\Model\AttributeString;
|
|
||||||
use Appwrite\Utopia\Response\Model\AttributeURL;
|
|
||||||
use Appwrite\Utopia\Response\Model\AuthProvider;
|
|
||||||
use Appwrite\Utopia\Response\Model\BaseList;
|
|
||||||
use Appwrite\Utopia\Response\Model\Branch;
|
|
||||||
use Appwrite\Utopia\Response\Model\Bucket;
|
|
||||||
use Appwrite\Utopia\Response\Model\Build;
|
|
||||||
use Appwrite\Utopia\Response\Model\Collection;
|
|
||||||
use Appwrite\Utopia\Response\Model\ConsoleVariables;
|
|
||||||
use Appwrite\Utopia\Response\Model\Continent;
|
|
||||||
use Appwrite\Utopia\Response\Model\Country;
|
|
||||||
use Appwrite\Utopia\Response\Model\Currency;
|
|
||||||
use Appwrite\Utopia\Response\Model\Database;
|
|
||||||
use Appwrite\Utopia\Response\Model\Deployment;
|
|
||||||
use Appwrite\Utopia\Response\Model\Detection;
|
|
||||||
use Appwrite\Utopia\Response\Model\Document as ModelDocument;
|
|
||||||
use Appwrite\Utopia\Response\Model\Error;
|
|
||||||
use Appwrite\Utopia\Response\Model\ErrorDev;
|
|
||||||
use Appwrite\Utopia\Response\Model\Execution;
|
|
||||||
use Appwrite\Utopia\Response\Model\File;
|
|
||||||
use Appwrite\Utopia\Response\Model\Func;
|
|
||||||
use Appwrite\Utopia\Response\Model\Headers;
|
|
||||||
use Appwrite\Utopia\Response\Model\HealthAntivirus;
|
|
||||||
use Appwrite\Utopia\Response\Model\HealthCertificate;
|
|
||||||
use Appwrite\Utopia\Response\Model\HealthQueue;
|
|
||||||
use Appwrite\Utopia\Response\Model\HealthStatus;
|
|
||||||
use Appwrite\Utopia\Response\Model\HealthTime;
|
|
||||||
use Appwrite\Utopia\Response\Model\HealthVersion;
|
|
||||||
use Appwrite\Utopia\Response\Model\Identity;
|
|
||||||
use Appwrite\Utopia\Response\Model\Index;
|
|
||||||
use Appwrite\Utopia\Response\Model\Installation;
|
|
||||||
use Appwrite\Utopia\Response\Model\JWT;
|
|
||||||
use Appwrite\Utopia\Response\Model\Key;
|
|
||||||
use Appwrite\Utopia\Response\Model\Language;
|
|
||||||
use Appwrite\Utopia\Response\Model\Locale;
|
|
||||||
use Appwrite\Utopia\Response\Model\LocaleCode;
|
|
||||||
use Appwrite\Utopia\Response\Model\Log;
|
|
||||||
use Appwrite\Utopia\Response\Model\Membership;
|
|
||||||
use Appwrite\Utopia\Response\Model\Message;
|
|
||||||
use Appwrite\Utopia\Response\Model\Metric;
|
|
||||||
use Appwrite\Utopia\Response\Model\MetricBreakdown;
|
|
||||||
use Appwrite\Utopia\Response\Model\MFAChallenge;
|
|
||||||
use Appwrite\Utopia\Response\Model\MFAFactors;
|
|
||||||
use Appwrite\Utopia\Response\Model\MFARecoveryCodes;
|
|
||||||
use Appwrite\Utopia\Response\Model\MFAType;
|
|
||||||
use Appwrite\Utopia\Response\Model\Migration;
|
|
||||||
use Appwrite\Utopia\Response\Model\MigrationFirebaseProject;
|
|
||||||
use Appwrite\Utopia\Response\Model\MigrationReport;
|
|
||||||
use Appwrite\Utopia\Response\Model\Mock;
|
|
||||||
use Appwrite\Utopia\Response\Model\MockNumber;
|
|
||||||
use Appwrite\Utopia\Response\Model\None;
|
|
||||||
use Appwrite\Utopia\Response\Model\Phone;
|
|
||||||
use Appwrite\Utopia\Response\Model\Platform;
|
|
||||||
use Appwrite\Utopia\Response\Model\Preferences;
|
|
||||||
use Appwrite\Utopia\Response\Model\Project;
|
|
||||||
use Appwrite\Utopia\Response\Model\Provider;
|
|
||||||
use Appwrite\Utopia\Response\Model\ProviderRepository;
|
|
||||||
use Appwrite\Utopia\Response\Model\Rule;
|
|
||||||
use Appwrite\Utopia\Response\Model\Runtime;
|
|
||||||
use Appwrite\Utopia\Response\Model\Session;
|
|
||||||
use Appwrite\Utopia\Response\Model\Specification;
|
|
||||||
use Appwrite\Utopia\Response\Model\Subscriber;
|
|
||||||
use Appwrite\Utopia\Response\Model\Target;
|
|
||||||
use Appwrite\Utopia\Response\Model\Team;
|
|
||||||
use Appwrite\Utopia\Response\Model\TemplateEmail;
|
|
||||||
use Appwrite\Utopia\Response\Model\TemplateFunction;
|
|
||||||
use Appwrite\Utopia\Response\Model\TemplateRuntime;
|
|
||||||
use Appwrite\Utopia\Response\Model\TemplateSMS;
|
|
||||||
use Appwrite\Utopia\Response\Model\TemplateVariable;
|
|
||||||
use Appwrite\Utopia\Response\Model\Token;
|
|
||||||
use Appwrite\Utopia\Response\Model\Topic;
|
|
||||||
use Appwrite\Utopia\Response\Model\UsageBuckets;
|
|
||||||
use Appwrite\Utopia\Response\Model\UsageCollection;
|
|
||||||
use Appwrite\Utopia\Response\Model\UsageDatabase;
|
|
||||||
use Appwrite\Utopia\Response\Model\UsageDatabases;
|
|
||||||
use Appwrite\Utopia\Response\Model\UsageFunction;
|
|
||||||
use Appwrite\Utopia\Response\Model\UsageFunctions;
|
|
||||||
use Appwrite\Utopia\Response\Model\UsageProject;
|
|
||||||
use Appwrite\Utopia\Response\Model\UsageStorage;
|
|
||||||
use Appwrite\Utopia\Response\Model\UsageUsers;
|
|
||||||
use Appwrite\Utopia\Response\Model\User;
|
|
||||||
use Appwrite\Utopia\Response\Model\Variable;
|
|
||||||
use Appwrite\Utopia\Response\Model\VcsContent;
|
|
||||||
use Appwrite\Utopia\Response\Model\Webhook;
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use JsonException;
|
use JsonException;
|
||||||
use Swoole\Http\Response as SwooleHTTPResponse;
|
|
||||||
// Keep last
|
// Keep last
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Swoole\Response as SwooleResponse;
|
use Utopia\Http\Adapter\Swoole\Response as HttpResponse;
|
||||||
|
|
||||||
|
// Keep last
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @method int getStatusCode()
|
* @method int getStatusCode()
|
||||||
* @method Response setStatusCode(int $code = 200)
|
* @method Response setStatusCode(int $code = 200)
|
||||||
*/
|
*/
|
||||||
class Response extends SwooleResponse
|
class Response extends HttpResponse
|
||||||
{
|
{
|
||||||
// General
|
// General
|
||||||
public const MODEL_NONE = 'none';
|
public const MODEL_NONE = 'none';
|
||||||
|
|
@ -330,162 +228,9 @@ class Response extends SwooleResponse
|
||||||
*
|
*
|
||||||
* @param float $time
|
* @param float $time
|
||||||
*/
|
*/
|
||||||
public function __construct(SwooleHTTPResponse $response)
|
public function __construct(HttpResponse $response)
|
||||||
{
|
{
|
||||||
$this
|
parent::__construct($response->swoole);
|
||||||
// General
|
|
||||||
->setModel(new None())
|
|
||||||
->setModel(new Any())
|
|
||||||
->setModel(new Error())
|
|
||||||
->setModel(new ErrorDev())
|
|
||||||
// Lists
|
|
||||||
->setModel(new BaseList('Documents List', self::MODEL_DOCUMENT_LIST, 'documents', self::MODEL_DOCUMENT))
|
|
||||||
->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'collections', self::MODEL_COLLECTION))
|
|
||||||
->setModel(new BaseList('Databases List', self::MODEL_DATABASE_LIST, 'databases', self::MODEL_DATABASE))
|
|
||||||
->setModel(new BaseList('Indexes List', self::MODEL_INDEX_LIST, 'indexes', self::MODEL_INDEX))
|
|
||||||
->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER))
|
|
||||||
->setModel(new BaseList('Sessions List', self::MODEL_SESSION_LIST, 'sessions', self::MODEL_SESSION))
|
|
||||||
->setModel(new BaseList('Identities List', self::MODEL_IDENTITY_LIST, 'identities', self::MODEL_IDENTITY))
|
|
||||||
->setModel(new BaseList('Logs List', self::MODEL_LOG_LIST, 'logs', self::MODEL_LOG))
|
|
||||||
->setModel(new BaseList('Files List', self::MODEL_FILE_LIST, 'files', self::MODEL_FILE))
|
|
||||||
->setModel(new BaseList('Buckets List', self::MODEL_BUCKET_LIST, 'buckets', self::MODEL_BUCKET))
|
|
||||||
->setModel(new BaseList('Teams List', self::MODEL_TEAM_LIST, 'teams', self::MODEL_TEAM))
|
|
||||||
->setModel(new BaseList('Memberships List', self::MODEL_MEMBERSHIP_LIST, 'memberships', self::MODEL_MEMBERSHIP))
|
|
||||||
->setModel(new BaseList('Functions List', self::MODEL_FUNCTION_LIST, 'functions', self::MODEL_FUNCTION))
|
|
||||||
->setModel(new BaseList('Function Templates List', self::MODEL_TEMPLATE_FUNCTION_LIST, 'templates', self::MODEL_TEMPLATE_FUNCTION))
|
|
||||||
->setModel(new BaseList('Installations List', self::MODEL_INSTALLATION_LIST, 'installations', self::MODEL_INSTALLATION))
|
|
||||||
->setModel(new BaseList('Provider Repositories List', self::MODEL_PROVIDER_REPOSITORY_LIST, 'providerRepositories', self::MODEL_PROVIDER_REPOSITORY))
|
|
||||||
->setModel(new BaseList('Branches List', self::MODEL_BRANCH_LIST, 'branches', self::MODEL_BRANCH))
|
|
||||||
->setModel(new BaseList('Runtimes List', self::MODEL_RUNTIME_LIST, 'runtimes', self::MODEL_RUNTIME))
|
|
||||||
->setModel(new BaseList('Deployments List', self::MODEL_DEPLOYMENT_LIST, 'deployments', self::MODEL_DEPLOYMENT))
|
|
||||||
->setModel(new BaseList('Executions List', self::MODEL_EXECUTION_LIST, 'executions', self::MODEL_EXECUTION))
|
|
||||||
->setModel(new BaseList('Builds List', self::MODEL_BUILD_LIST, 'builds', self::MODEL_BUILD)) // Not used anywhere yet
|
|
||||||
->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT, true, false))
|
|
||||||
->setModel(new BaseList('Webhooks List', self::MODEL_WEBHOOK_LIST, 'webhooks', self::MODEL_WEBHOOK, true, false))
|
|
||||||
->setModel(new BaseList('API Keys List', self::MODEL_KEY_LIST, 'keys', self::MODEL_KEY, true, false))
|
|
||||||
->setModel(new BaseList('Auth Providers List', self::MODEL_AUTH_PROVIDER_LIST, 'platforms', self::MODEL_AUTH_PROVIDER, true, false))
|
|
||||||
->setModel(new BaseList('Platforms List', self::MODEL_PLATFORM_LIST, 'platforms', self::MODEL_PLATFORM, true, false))
|
|
||||||
->setModel(new BaseList('Countries List', self::MODEL_COUNTRY_LIST, 'countries', self::MODEL_COUNTRY))
|
|
||||||
->setModel(new BaseList('Continents List', self::MODEL_CONTINENT_LIST, 'continents', self::MODEL_CONTINENT))
|
|
||||||
->setModel(new BaseList('Languages List', self::MODEL_LANGUAGE_LIST, 'languages', self::MODEL_LANGUAGE))
|
|
||||||
->setModel(new BaseList('Currencies List', self::MODEL_CURRENCY_LIST, 'currencies', self::MODEL_CURRENCY))
|
|
||||||
->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE))
|
|
||||||
->setModel(new BaseList('Metric List', self::MODEL_METRIC_LIST, 'metrics', self::MODEL_METRIC, true, false))
|
|
||||||
->setModel(new BaseList('Variables List', self::MODEL_VARIABLE_LIST, 'variables', self::MODEL_VARIABLE))
|
|
||||||
->setModel(new BaseList('Status List', self::MODEL_HEALTH_STATUS_LIST, 'statuses', self::MODEL_HEALTH_STATUS))
|
|
||||||
->setModel(new BaseList('Rule List', self::MODEL_PROXY_RULE_LIST, 'rules', self::MODEL_PROXY_RULE))
|
|
||||||
->setModel(new BaseList('Locale codes list', self::MODEL_LOCALE_CODE_LIST, 'localeCodes', self::MODEL_LOCALE_CODE))
|
|
||||||
->setModel(new BaseList('Provider list', self::MODEL_PROVIDER_LIST, 'providers', self::MODEL_PROVIDER))
|
|
||||||
->setModel(new BaseList('Message list', self::MODEL_MESSAGE_LIST, 'messages', self::MODEL_MESSAGE))
|
|
||||||
->setModel(new BaseList('Topic list', self::MODEL_TOPIC_LIST, 'topics', self::MODEL_TOPIC))
|
|
||||||
->setModel(new BaseList('Subscriber list', self::MODEL_SUBSCRIBER_LIST, 'subscribers', self::MODEL_SUBSCRIBER))
|
|
||||||
->setModel(new BaseList('Target list', self::MODEL_TARGET_LIST, 'targets', self::MODEL_TARGET))
|
|
||||||
->setModel(new BaseList('Migrations List', self::MODEL_MIGRATION_LIST, 'migrations', self::MODEL_MIGRATION))
|
|
||||||
->setModel(new BaseList('Migrations Firebase Projects List', self::MODEL_MIGRATION_FIREBASE_PROJECT_LIST, 'projects', self::MODEL_MIGRATION_FIREBASE_PROJECT))
|
|
||||||
->setModel(new BaseList('Specifications List', self::MODEL_SPECIFICATION_LIST, 'specifications', self::MODEL_SPECIFICATION))
|
|
||||||
->setModel(new BaseList('VCS Content List', self::MODEL_VCS_CONTENT_LIST, 'contents', self::MODEL_VCS_CONTENT))
|
|
||||||
// Entities
|
|
||||||
->setModel(new Database())
|
|
||||||
->setModel(new Collection())
|
|
||||||
->setModel(new Attribute())
|
|
||||||
->setModel(new AttributeList())
|
|
||||||
->setModel(new AttributeString())
|
|
||||||
->setModel(new AttributeInteger())
|
|
||||||
->setModel(new AttributeFloat())
|
|
||||||
->setModel(new AttributeBoolean())
|
|
||||||
->setModel(new AttributeEmail())
|
|
||||||
->setModel(new AttributeEnum())
|
|
||||||
->setModel(new AttributeIP())
|
|
||||||
->setModel(new AttributeURL())
|
|
||||||
->setModel(new AttributeDatetime())
|
|
||||||
->setModel(new AttributeRelationship())
|
|
||||||
->setModel(new Index())
|
|
||||||
->setModel(new ModelDocument())
|
|
||||||
->setModel(new Log())
|
|
||||||
->setModel(new User())
|
|
||||||
->setModel(new AlgoMd5())
|
|
||||||
->setModel(new AlgoSha())
|
|
||||||
->setModel(new AlgoPhpass())
|
|
||||||
->setModel(new AlgoBcrypt())
|
|
||||||
->setModel(new AlgoScrypt())
|
|
||||||
->setModel(new AlgoScryptModified())
|
|
||||||
->setModel(new AlgoArgon2())
|
|
||||||
->setModel(new Account())
|
|
||||||
->setModel(new Preferences())
|
|
||||||
->setModel(new Session())
|
|
||||||
->setModel(new Identity())
|
|
||||||
->setModel(new Token())
|
|
||||||
->setModel(new JWT())
|
|
||||||
->setModel(new Locale())
|
|
||||||
->setModel(new LocaleCode())
|
|
||||||
->setModel(new File())
|
|
||||||
->setModel(new Bucket())
|
|
||||||
->setModel(new Team())
|
|
||||||
->setModel(new Membership())
|
|
||||||
->setModel(new Func())
|
|
||||||
->setModel(new TemplateFunction())
|
|
||||||
->setModel(new TemplateRuntime())
|
|
||||||
->setModel(new TemplateVariable())
|
|
||||||
->setModel(new Installation())
|
|
||||||
->setModel(new ProviderRepository())
|
|
||||||
->setModel(new Detection())
|
|
||||||
->setModel(new VcsContent())
|
|
||||||
->setModel(new Branch())
|
|
||||||
->setModel(new Runtime())
|
|
||||||
->setModel(new Deployment())
|
|
||||||
->setModel(new Execution())
|
|
||||||
->setModel(new Build())
|
|
||||||
->setModel(new Project())
|
|
||||||
->setModel(new Webhook())
|
|
||||||
->setModel(new Key())
|
|
||||||
->setModel(new MockNumber())
|
|
||||||
->setModel(new AuthProvider())
|
|
||||||
->setModel(new Platform())
|
|
||||||
->setModel(new Variable())
|
|
||||||
->setModel(new Country())
|
|
||||||
->setModel(new Continent())
|
|
||||||
->setModel(new Language())
|
|
||||||
->setModel(new Currency())
|
|
||||||
->setModel(new Phone())
|
|
||||||
->setModel(new HealthAntivirus())
|
|
||||||
->setModel(new HealthQueue())
|
|
||||||
->setModel(new HealthStatus())
|
|
||||||
->setModel(new HealthCertificate())
|
|
||||||
->setModel(new HealthTime())
|
|
||||||
->setModel(new HealthVersion())
|
|
||||||
->setModel(new Metric())
|
|
||||||
->setModel(new MetricBreakdown())
|
|
||||||
->setModel(new UsageDatabases())
|
|
||||||
->setModel(new UsageDatabase())
|
|
||||||
->setModel(new UsageCollection())
|
|
||||||
->setModel(new UsageUsers())
|
|
||||||
->setModel(new UsageStorage())
|
|
||||||
->setModel(new UsageBuckets())
|
|
||||||
->setModel(new UsageFunctions())
|
|
||||||
->setModel(new UsageFunction())
|
|
||||||
->setModel(new UsageProject())
|
|
||||||
->setModel(new Headers())
|
|
||||||
->setModel(new Specification())
|
|
||||||
->setModel(new Rule())
|
|
||||||
->setModel(new TemplateSMS())
|
|
||||||
->setModel(new TemplateEmail())
|
|
||||||
->setModel(new ConsoleVariables())
|
|
||||||
->setModel(new MFAChallenge())
|
|
||||||
->setModel(new MFARecoveryCodes())
|
|
||||||
->setModel(new MFAType())
|
|
||||||
->setModel(new MFAFactors())
|
|
||||||
->setModel(new Provider())
|
|
||||||
->setModel(new Message())
|
|
||||||
->setModel(new Topic())
|
|
||||||
->setModel(new Subscriber())
|
|
||||||
->setModel(new Target())
|
|
||||||
->setModel(new Migration())
|
|
||||||
->setModel(new MigrationReport())
|
|
||||||
->setModel(new MigrationFirebaseProject())
|
|
||||||
// Tests (keep last)
|
|
||||||
->setModel(new Mock());
|
|
||||||
|
|
||||||
parent::__construct($response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -495,49 +240,6 @@ class Response extends SwooleResponse
|
||||||
public const CONTENT_TYPE_NULL = 'null';
|
public const CONTENT_TYPE_NULL = 'null';
|
||||||
public const CONTENT_TYPE_MULTIPART = 'multipart/form-data';
|
public const CONTENT_TYPE_MULTIPART = 'multipart/form-data';
|
||||||
|
|
||||||
/**
|
|
||||||
* List of defined output objects
|
|
||||||
*/
|
|
||||||
protected $models = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set Model Object
|
|
||||||
*
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
public function setModel(Model $instance)
|
|
||||||
{
|
|
||||||
$this->models[$instance->getType()] = $instance;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get Model Object
|
|
||||||
*
|
|
||||||
* @param string $key
|
|
||||||
* @return Model
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function getModel(string $key): Model
|
|
||||||
{
|
|
||||||
if (!isset($this->models[$key])) {
|
|
||||||
throw new Exception('Undefined model: ' . $key);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->models[$key];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get Models List
|
|
||||||
*
|
|
||||||
* @return Model[]
|
|
||||||
*/
|
|
||||||
public function getModels(): array
|
|
||||||
{
|
|
||||||
return $this->models;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function applyFilters(array $data, string $model): array
|
public function applyFilters(array $data, string $model): array
|
||||||
{
|
{
|
||||||
foreach ($this->filters as $filter) {
|
foreach ($this->filters as $filter) {
|
||||||
|
|
@ -605,7 +307,7 @@ class Response extends SwooleResponse
|
||||||
public function output(Document $document, string $model): array
|
public function output(Document $document, string $model): array
|
||||||
{
|
{
|
||||||
$data = clone $document;
|
$data = clone $document;
|
||||||
$model = $this->getModel($model);
|
$model = Models::getModel($model);
|
||||||
$output = [];
|
$output = [];
|
||||||
|
|
||||||
$data = $model->filter($data);
|
$data = $model->filter($data);
|
||||||
|
|
@ -640,7 +342,7 @@ class Response extends SwooleResponse
|
||||||
if (\is_array($rule['type'])) {
|
if (\is_array($rule['type'])) {
|
||||||
foreach ($rule['type'] as $type) {
|
foreach ($rule['type'] as $type) {
|
||||||
$condition = false;
|
$condition = false;
|
||||||
foreach ($this->getModel($type)->conditions as $attribute => $val) {
|
foreach (Models::getModel($type)->conditions as $attribute => $val) {
|
||||||
$condition = $item->getAttribute($attribute) === $val;
|
$condition = $item->getAttribute($attribute) === $val;
|
||||||
if (!$condition) {
|
if (!$condition) {
|
||||||
break;
|
break;
|
||||||
|
|
@ -655,7 +357,7 @@ class Response extends SwooleResponse
|
||||||
$ruleType = $rule['type'];
|
$ruleType = $rule['type'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!array_key_exists($ruleType, $this->models)) {
|
if (!array_key_exists($ruleType, Models::getModels())) {
|
||||||
throw new Exception('Missing model for rule: ' . $ruleType);
|
throw new Exception('Missing model for rule: ' . $ruleType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
304
src/Appwrite/Utopia/Response/Models.php
Normal file
304
src/Appwrite/Utopia/Response/Models.php
Normal file
|
|
@ -0,0 +1,304 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Response;
|
||||||
|
|
||||||
|
use Appwrite\Utopia\Response;
|
||||||
|
use Appwrite\Utopia\Response\Model\Account;
|
||||||
|
use Appwrite\Utopia\Response\Model\AlgoArgon2;
|
||||||
|
use Appwrite\Utopia\Response\Model\AlgoBcrypt;
|
||||||
|
use Appwrite\Utopia\Response\Model\AlgoMd5;
|
||||||
|
use Appwrite\Utopia\Response\Model\AlgoPhpass;
|
||||||
|
use Appwrite\Utopia\Response\Model\AlgoScrypt;
|
||||||
|
use Appwrite\Utopia\Response\Model\AlgoScryptModified;
|
||||||
|
use Appwrite\Utopia\Response\Model\AlgoSha;
|
||||||
|
use Appwrite\Utopia\Response\Model\Any;
|
||||||
|
use Appwrite\Utopia\Response\Model\Attribute;
|
||||||
|
use Appwrite\Utopia\Response\Model\AttributeBoolean;
|
||||||
|
use Appwrite\Utopia\Response\Model\AttributeDatetime;
|
||||||
|
use Appwrite\Utopia\Response\Model\AttributeEmail;
|
||||||
|
use Appwrite\Utopia\Response\Model\AttributeEnum;
|
||||||
|
use Appwrite\Utopia\Response\Model\AttributeFloat;
|
||||||
|
use Appwrite\Utopia\Response\Model\AttributeInteger;
|
||||||
|
use Appwrite\Utopia\Response\Model\AttributeIP;
|
||||||
|
use Appwrite\Utopia\Response\Model\AttributeList;
|
||||||
|
use Appwrite\Utopia\Response\Model\AttributeRelationship;
|
||||||
|
use Appwrite\Utopia\Response\Model\AttributeString;
|
||||||
|
use Appwrite\Utopia\Response\Model\AttributeURL;
|
||||||
|
use Appwrite\Utopia\Response\Model\AuthProvider;
|
||||||
|
use Appwrite\Utopia\Response\Model\BaseList;
|
||||||
|
use Appwrite\Utopia\Response\Model\Branch;
|
||||||
|
use Appwrite\Utopia\Response\Model\Bucket;
|
||||||
|
use Appwrite\Utopia\Response\Model\Build;
|
||||||
|
use Appwrite\Utopia\Response\Model\Collection;
|
||||||
|
use Appwrite\Utopia\Response\Model\ConsoleVariables;
|
||||||
|
use Appwrite\Utopia\Response\Model\Continent;
|
||||||
|
use Appwrite\Utopia\Response\Model\Country;
|
||||||
|
use Appwrite\Utopia\Response\Model\Currency;
|
||||||
|
use Appwrite\Utopia\Response\Model\Database;
|
||||||
|
use Appwrite\Utopia\Response\Model\Deployment;
|
||||||
|
use Appwrite\Utopia\Response\Model\Detection;
|
||||||
|
use Appwrite\Utopia\Response\Model\Document as ModelDocument;
|
||||||
|
use Appwrite\Utopia\Response\Model\Error;
|
||||||
|
use Appwrite\Utopia\Response\Model\ErrorDev;
|
||||||
|
use Appwrite\Utopia\Response\Model\Execution;
|
||||||
|
use Appwrite\Utopia\Response\Model\File;
|
||||||
|
use Appwrite\Utopia\Response\Model\Func;
|
||||||
|
use Appwrite\Utopia\Response\Model\Headers;
|
||||||
|
use Appwrite\Utopia\Response\Model\HealthAntivirus;
|
||||||
|
use Appwrite\Utopia\Response\Model\HealthCertificate;
|
||||||
|
use Appwrite\Utopia\Response\Model\HealthQueue;
|
||||||
|
use Appwrite\Utopia\Response\Model\HealthStatus;
|
||||||
|
use Appwrite\Utopia\Response\Model\HealthTime;
|
||||||
|
use Appwrite\Utopia\Response\Model\HealthVersion;
|
||||||
|
use Appwrite\Utopia\Response\Model\Identity;
|
||||||
|
use Appwrite\Utopia\Response\Model\Index;
|
||||||
|
use Appwrite\Utopia\Response\Model\Installation;
|
||||||
|
use Appwrite\Utopia\Response\Model\JWT;
|
||||||
|
use Appwrite\Utopia\Response\Model\Key;
|
||||||
|
use Appwrite\Utopia\Response\Model\Language;
|
||||||
|
use Appwrite\Utopia\Response\Model\Locale;
|
||||||
|
use Appwrite\Utopia\Response\Model\LocaleCode;
|
||||||
|
use Appwrite\Utopia\Response\Model\Log;
|
||||||
|
use Appwrite\Utopia\Response\Model\Membership;
|
||||||
|
use Appwrite\Utopia\Response\Model\Message;
|
||||||
|
use Appwrite\Utopia\Response\Model\Metric;
|
||||||
|
use Appwrite\Utopia\Response\Model\MetricBreakdown;
|
||||||
|
use Appwrite\Utopia\Response\Model\MFAChallenge;
|
||||||
|
use Appwrite\Utopia\Response\Model\MFAFactors;
|
||||||
|
use Appwrite\Utopia\Response\Model\MFARecoveryCodes;
|
||||||
|
use Appwrite\Utopia\Response\Model\MFAType;
|
||||||
|
use Appwrite\Utopia\Response\Model\Migration;
|
||||||
|
use Appwrite\Utopia\Response\Model\MigrationFirebaseProject;
|
||||||
|
use Appwrite\Utopia\Response\Model\MigrationReport;
|
||||||
|
use Appwrite\Utopia\Response\Model\Mock;
|
||||||
|
use Appwrite\Utopia\Response\Model\MockNumber;
|
||||||
|
use Appwrite\Utopia\Response\Model\None;
|
||||||
|
use Appwrite\Utopia\Response\Model\Phone;
|
||||||
|
use Appwrite\Utopia\Response\Model\Platform;
|
||||||
|
use Appwrite\Utopia\Response\Model\Preferences;
|
||||||
|
use Appwrite\Utopia\Response\Model\Project;
|
||||||
|
use Appwrite\Utopia\Response\Model\Provider;
|
||||||
|
use Appwrite\Utopia\Response\Model\ProviderRepository;
|
||||||
|
use Appwrite\Utopia\Response\Model\Rule;
|
||||||
|
use Appwrite\Utopia\Response\Model\Runtime;
|
||||||
|
use Appwrite\Utopia\Response\Model\Session;
|
||||||
|
use Appwrite\Utopia\Response\Model\Specification;
|
||||||
|
use Appwrite\Utopia\Response\Model\Subscriber;
|
||||||
|
use Appwrite\Utopia\Response\Model\Target;
|
||||||
|
use Appwrite\Utopia\Response\Model\Team;
|
||||||
|
use Appwrite\Utopia\Response\Model\TemplateEmail;
|
||||||
|
use Appwrite\Utopia\Response\Model\TemplateFunction;
|
||||||
|
use Appwrite\Utopia\Response\Model\TemplateRuntime;
|
||||||
|
use Appwrite\Utopia\Response\Model\TemplateSMS;
|
||||||
|
use Appwrite\Utopia\Response\Model\TemplateVariable;
|
||||||
|
use Appwrite\Utopia\Response\Model\Token;
|
||||||
|
use Appwrite\Utopia\Response\Model\Topic;
|
||||||
|
use Appwrite\Utopia\Response\Model\UsageBuckets;
|
||||||
|
use Appwrite\Utopia\Response\Model\UsageCollection;
|
||||||
|
use Appwrite\Utopia\Response\Model\UsageDatabase;
|
||||||
|
use Appwrite\Utopia\Response\Model\UsageDatabases;
|
||||||
|
use Appwrite\Utopia\Response\Model\UsageFunction;
|
||||||
|
use Appwrite\Utopia\Response\Model\UsageFunctions;
|
||||||
|
use Appwrite\Utopia\Response\Model\UsageProject;
|
||||||
|
use Appwrite\Utopia\Response\Model\UsageStorage;
|
||||||
|
use Appwrite\Utopia\Response\Model\UsageUsers;
|
||||||
|
use Appwrite\Utopia\Response\Model\User;
|
||||||
|
use Appwrite\Utopia\Response\Model\Variable;
|
||||||
|
use Appwrite\Utopia\Response\Model\VcsContent;
|
||||||
|
use Appwrite\Utopia\Response\Model\Webhook;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
// Keep last
|
||||||
|
|
||||||
|
class Models
|
||||||
|
{
|
||||||
|
public static function init()
|
||||||
|
{
|
||||||
|
// General
|
||||||
|
self::setModel(new None());
|
||||||
|
self::setModel(new Any());
|
||||||
|
self::setModel(new Error());
|
||||||
|
self::setModel(new ErrorDev());
|
||||||
|
// Lists
|
||||||
|
self::setModel(new BaseList('Documents List', Response::MODEL_DOCUMENT_LIST, 'documents', Response::MODEL_DOCUMENT));
|
||||||
|
self::setModel(new BaseList('Collections List', Response::MODEL_COLLECTION_LIST, 'collections', Response::MODEL_COLLECTION));
|
||||||
|
self::setModel(new BaseList('Databases List', Response::MODEL_DATABASE_LIST, 'databases', Response::MODEL_DATABASE));
|
||||||
|
self::setModel(new BaseList('Indexes List', Response::MODEL_INDEX_LIST, 'indexes', Response::MODEL_INDEX));
|
||||||
|
self::setModel(new BaseList('Users List', Response::MODEL_USER_LIST, 'users', Response::MODEL_USER));
|
||||||
|
self::setModel(new BaseList('Sessions List', Response::MODEL_SESSION_LIST, 'sessions', Response::MODEL_SESSION));
|
||||||
|
self::setModel(new BaseList('Identities List', Response::MODEL_IDENTITY_LIST, 'identities', Response::MODEL_IDENTITY));
|
||||||
|
self::setModel(new BaseList('Logs List', Response::MODEL_LOG_LIST, 'logs', Response::MODEL_LOG));
|
||||||
|
self::setModel(new BaseList('Files List', Response::MODEL_FILE_LIST, 'files', Response::MODEL_FILE));
|
||||||
|
self::setModel(new BaseList('Buckets List', Response::MODEL_BUCKET_LIST, 'buckets', Response::MODEL_BUCKET));
|
||||||
|
self::setModel(new BaseList('Teams List', Response::MODEL_TEAM_LIST, 'teams', Response::MODEL_TEAM));
|
||||||
|
self::setModel(new BaseList('Memberships List', Response::MODEL_MEMBERSHIP_LIST, 'memberships', Response::MODEL_MEMBERSHIP));
|
||||||
|
self::setModel(new BaseList('Functions List', Response::MODEL_FUNCTION_LIST, 'functions', Response::MODEL_FUNCTION));
|
||||||
|
self::setModel(new BaseList('Function Templates List', Response::MODEL_TEMPLATE_FUNCTION_LIST, 'templates', Response::MODEL_TEMPLATE_FUNCTION));
|
||||||
|
self::setModel(new BaseList('Installations List', Response::MODEL_INSTALLATION_LIST, 'installations', Response::MODEL_INSTALLATION));
|
||||||
|
self::setModel(new BaseList('Provider Repositories List', Response::MODEL_PROVIDER_REPOSITORY_LIST, 'providerRepositories', Response::MODEL_PROVIDER_REPOSITORY));
|
||||||
|
self::setModel(new BaseList('Branches List', Response::MODEL_BRANCH_LIST, 'branches', Response::MODEL_BRANCH));
|
||||||
|
self::setModel(new BaseList('Runtimes List', Response::MODEL_RUNTIME_LIST, 'runtimes', Response::MODEL_RUNTIME));
|
||||||
|
self::setModel(new BaseList('Deployments List', Response::MODEL_DEPLOYMENT_LIST, 'deployments', Response::MODEL_DEPLOYMENT));
|
||||||
|
self::setModel(new BaseList('Executions List', Response::MODEL_EXECUTION_LIST, 'executions', Response::MODEL_EXECUTION));
|
||||||
|
self::setModel(new BaseList('Builds List', Response::MODEL_BUILD_LIST, 'builds', Response::MODEL_BUILD)); // Not used anywhere yet;
|
||||||
|
self::setModel(new BaseList('Projects List', Response::MODEL_PROJECT_LIST, 'projects', Response::MODEL_PROJECT, true, false));
|
||||||
|
self::setModel(new BaseList('Webhooks List', Response::MODEL_WEBHOOK_LIST, 'webhooks', Response::MODEL_WEBHOOK, true, false));
|
||||||
|
self::setModel(new BaseList('API Keys List', Response::MODEL_KEY_LIST, 'keys', Response::MODEL_KEY, true, false));
|
||||||
|
self::setModel(new BaseList('Auth Providers List', Response::MODEL_AUTH_PROVIDER_LIST, 'platforms', Response::MODEL_AUTH_PROVIDER, true, false));
|
||||||
|
self::setModel(new BaseList('Platforms List', Response::MODEL_PLATFORM_LIST, 'platforms', Response::MODEL_PLATFORM, true, false));
|
||||||
|
self::setModel(new BaseList('Countries List', Response::MODEL_COUNTRY_LIST, 'countries', Response::MODEL_COUNTRY));
|
||||||
|
self::setModel(new BaseList('Continents List', Response::MODEL_CONTINENT_LIST, 'continents', Response::MODEL_CONTINENT));
|
||||||
|
self::setModel(new BaseList('Languages List', Response::MODEL_LANGUAGE_LIST, 'languages', Response::MODEL_LANGUAGE));
|
||||||
|
self::setModel(new BaseList('Currencies List', Response::MODEL_CURRENCY_LIST, 'currencies', Response::MODEL_CURRENCY));
|
||||||
|
self::setModel(new BaseList('Phones List', Response::MODEL_PHONE_LIST, 'phones', Response::MODEL_PHONE));
|
||||||
|
self::setModel(new BaseList('Metric List', Response::MODEL_METRIC_LIST, 'metrics', Response::MODEL_METRIC, true, false));
|
||||||
|
self::setModel(new BaseList('Variables List', Response::MODEL_VARIABLE_LIST, 'variables', Response::MODEL_VARIABLE));
|
||||||
|
self::setModel(new BaseList('Status List', Response::MODEL_HEALTH_STATUS_LIST, 'statuses', Response::MODEL_HEALTH_STATUS));
|
||||||
|
self::setModel(new BaseList('Rule List', Response::MODEL_PROXY_RULE_LIST, 'rules', Response::MODEL_PROXY_RULE));
|
||||||
|
self::setModel(new BaseList('Locale codes list', Response::MODEL_LOCALE_CODE_LIST, 'localeCodes', Response::MODEL_LOCALE_CODE));
|
||||||
|
self::setModel(new BaseList('Provider list', Response::MODEL_PROVIDER_LIST, 'providers', Response::MODEL_PROVIDER));
|
||||||
|
self::setModel(new BaseList('Message list', Response::MODEL_MESSAGE_LIST, 'messages', Response::MODEL_MESSAGE));
|
||||||
|
self::setModel(new BaseList('Topic list', Response::MODEL_TOPIC_LIST, 'topics', Response::MODEL_TOPIC));
|
||||||
|
self::setModel(new BaseList('Subscriber list', Response::MODEL_SUBSCRIBER_LIST, 'subscribers', Response::MODEL_SUBSCRIBER));
|
||||||
|
self::setModel(new BaseList('Target list', Response::MODEL_TARGET_LIST, 'targets', Response::MODEL_TARGET));
|
||||||
|
self::setModel(new BaseList('Migrations List', Response::MODEL_MIGRATION_LIST, 'migrations', Response::MODEL_MIGRATION));
|
||||||
|
self::setModel(new BaseList('Migrations Firebase Projects List', Response::MODEL_MIGRATION_FIREBASE_PROJECT_LIST, 'projects', Response::MODEL_MIGRATION_FIREBASE_PROJECT));
|
||||||
|
self::setModel(new BaseList('Specifications List', Response::MODEL_SPECIFICATION_LIST, 'specifications', Response::MODEL_SPECIFICATION));
|
||||||
|
self::setModel(new BaseList('VCS Content List', Response::MODEL_VCS_CONTENT_LIST, 'contents', Response::MODEL_VCS_CONTENT));
|
||||||
|
// Entities
|
||||||
|
self::setModel(new Database());
|
||||||
|
self::setModel(new Collection());
|
||||||
|
self::setModel(new Attribute());
|
||||||
|
self::setModel(new AttributeList());
|
||||||
|
self::setModel(new AttributeString());
|
||||||
|
self::setModel(new AttributeInteger());
|
||||||
|
self::setModel(new AttributeFloat());
|
||||||
|
self::setModel(new AttributeBoolean());
|
||||||
|
self::setModel(new AttributeEmail());
|
||||||
|
self::setModel(new AttributeEnum());
|
||||||
|
self::setModel(new AttributeIP());
|
||||||
|
self::setModel(new AttributeURL());
|
||||||
|
self::setModel(new AttributeDatetime());
|
||||||
|
self::setModel(new AttributeRelationship());
|
||||||
|
self::setModel(new Index());
|
||||||
|
self::setModel(new ModelDocument());
|
||||||
|
self::setModel(new Log());
|
||||||
|
self::setModel(new User());
|
||||||
|
self::setModel(new AlgoMd5());
|
||||||
|
self::setModel(new AlgoSha());
|
||||||
|
self::setModel(new AlgoPhpass());
|
||||||
|
self::setModel(new AlgoBcrypt());
|
||||||
|
self::setModel(new AlgoScrypt());
|
||||||
|
self::setModel(new AlgoScryptModified());
|
||||||
|
self::setModel(new AlgoArgon2());
|
||||||
|
self::setModel(new Account());
|
||||||
|
self::setModel(new Preferences());
|
||||||
|
self::setModel(new Session());
|
||||||
|
self::setModel(new Identity());
|
||||||
|
self::setModel(new Token());
|
||||||
|
self::setModel(new JWT());
|
||||||
|
self::setModel(new Locale());
|
||||||
|
self::setModel(new LocaleCode());
|
||||||
|
self::setModel(new File());
|
||||||
|
self::setModel(new Bucket());
|
||||||
|
self::setModel(new Team());
|
||||||
|
self::setModel(new Membership());
|
||||||
|
self::setModel(new Func());
|
||||||
|
self::setModel(new TemplateFunction());
|
||||||
|
self::setModel(new TemplateRuntime());
|
||||||
|
self::setModel(new TemplateVariable());
|
||||||
|
self::setModel(new Installation());
|
||||||
|
self::setModel(new ProviderRepository());
|
||||||
|
self::setModel(new Detection());
|
||||||
|
self::setModel(new VcsContent());
|
||||||
|
self::setModel(new Branch());
|
||||||
|
self::setModel(new Runtime());
|
||||||
|
self::setModel(new Deployment());
|
||||||
|
self::setModel(new Execution());
|
||||||
|
self::setModel(new Build());
|
||||||
|
self::setModel(new Project());
|
||||||
|
self::setModel(new Webhook());
|
||||||
|
self::setModel(new Key());
|
||||||
|
self::setModel(new MockNumber());
|
||||||
|
self::setModel(new AuthProvider());
|
||||||
|
self::setModel(new Platform());
|
||||||
|
self::setModel(new Variable());
|
||||||
|
self::setModel(new Country());
|
||||||
|
self::setModel(new Continent());
|
||||||
|
self::setModel(new Language());
|
||||||
|
self::setModel(new Currency());
|
||||||
|
self::setModel(new Phone());
|
||||||
|
self::setModel(new HealthAntivirus());
|
||||||
|
self::setModel(new HealthQueue());
|
||||||
|
self::setModel(new HealthStatus());
|
||||||
|
self::setModel(new HealthCertificate());
|
||||||
|
self::setModel(new HealthTime());
|
||||||
|
self::setModel(new HealthVersion());
|
||||||
|
self::setModel(new Metric());
|
||||||
|
self::setModel(new MetricBreakdown());
|
||||||
|
self::setModel(new UsageDatabases());
|
||||||
|
self::setModel(new UsageDatabase());
|
||||||
|
self::setModel(new UsageCollection());
|
||||||
|
self::setModel(new UsageUsers());
|
||||||
|
self::setModel(new UsageStorage());
|
||||||
|
self::setModel(new UsageBuckets());
|
||||||
|
self::setModel(new UsageFunctions());
|
||||||
|
self::setModel(new UsageFunction());
|
||||||
|
self::setModel(new UsageProject());
|
||||||
|
self::setModel(new Headers());
|
||||||
|
self::setModel(new Specification());
|
||||||
|
self::setModel(new Rule());
|
||||||
|
self::setModel(new TemplateSMS());
|
||||||
|
self::setModel(new TemplateEmail());
|
||||||
|
self::setModel(new ConsoleVariables());
|
||||||
|
self::setModel(new MFAChallenge());
|
||||||
|
self::setModel(new MFARecoveryCodes());
|
||||||
|
self::setModel(new MFAType());
|
||||||
|
self::setModel(new MFAFactors());
|
||||||
|
self::setModel(new Provider());
|
||||||
|
self::setModel(new Message());
|
||||||
|
self::setModel(new Topic());
|
||||||
|
self::setModel(new Subscriber());
|
||||||
|
self::setModel(new Target());
|
||||||
|
self::setModel(new Migration());
|
||||||
|
self::setModel(new MigrationReport());
|
||||||
|
self::setModel(new MigrationFirebaseProject());
|
||||||
|
// Tests (keep last)
|
||||||
|
self::setModel(new Mock());
|
||||||
|
;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* List of defined output objects
|
||||||
|
* @var Model[]
|
||||||
|
*/
|
||||||
|
protected static array $models = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Model Object
|
||||||
|
*/
|
||||||
|
public static function setModel(Model $instance): void
|
||||||
|
{
|
||||||
|
self::$models[$instance->getType()] = $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Model Object
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return Model
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static function getModel(string $key): Model
|
||||||
|
{
|
||||||
|
if (!isset(self::$models[$key])) {
|
||||||
|
throw new Exception('Undefined model: ' . $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$models[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getModels(): array
|
||||||
|
{
|
||||||
|
return self::$models;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Appwrite\Utopia;
|
namespace Appwrite\Utopia;
|
||||||
|
|
||||||
use Utopia\View as OldView;
|
use Utopia\View\View as OldView;
|
||||||
|
|
||||||
class View extends OldView
|
class View extends OldView
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use Utopia\Database\Helpers\Permission;
|
||||||
use Utopia\Database\Helpers\Role;
|
use Utopia\Database\Helpers\Role;
|
||||||
use Utopia\Database\Query;
|
use Utopia\Database\Query;
|
||||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||||
use Utopia\Validator\JSON;
|
use Utopia\Http\Validator\JSON;
|
||||||
|
|
||||||
trait DatabasesBase
|
trait DatabasesBase
|
||||||
{
|
{
|
||||||
|
|
@ -2869,7 +2869,7 @@ trait DatabasesBase
|
||||||
$this->assertEquals('Invalid document structure: Attribute "floatRange" has invalid format. Value must be a valid range between 1 and 1', $badFloatRange['body']['message']);
|
$this->assertEquals('Invalid document structure: Attribute "floatRange" has invalid format. Value must be a valid range between 1 and 1', $badFloatRange['body']['message']);
|
||||||
$this->assertEquals('Invalid document structure: Attribute "probability" has invalid format. Value must be a valid range between 0 and 1', $badProbability['body']['message']);
|
$this->assertEquals('Invalid document structure: Attribute "probability" has invalid format. Value must be a valid range between 0 and 1', $badProbability['body']['message']);
|
||||||
$this->assertEquals('Invalid document structure: Attribute "upperBound" has invalid format. Value must be a valid range between -9,223,372,036,854,775,808 and 10', $tooHigh['body']['message']);
|
$this->assertEquals('Invalid document structure: Attribute "upperBound" has invalid format. Value must be a valid range between -9,223,372,036,854,775,808 and 10', $tooHigh['body']['message']);
|
||||||
$this->assertEquals('Invalid document structure: Attribute "lowerBound" has invalid format. Value must be a valid range between 5 and 9,223,372,036,854,775,807', $tooLow['body']['message']);
|
$this->assertEquals('Invalid document structure: Attribute "lowerBound" has invalid format. Value must be a valid range between 5 and '.\number_format(PHP_INT_MAX), $tooLow['body']['message']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,10 @@ class DatabasesCustomClientTest extends Scope
|
||||||
'required' => true,
|
'required' => true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(202, $response['headers']['status-code']);
|
||||||
|
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
|
||||||
$this->assertEquals(202, $response['headers']['status-code']);
|
|
||||||
|
|
||||||
// Document aliases write to update, delete
|
// Document aliases write to update, delete
|
||||||
$document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents', array_merge([
|
$document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents', array_merge([
|
||||||
|
|
@ -82,6 +83,7 @@ class DatabasesCustomClientTest extends Scope
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $document1['headers']['status-code']);
|
||||||
$this->assertNotContains(Permission::create(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']);
|
$this->assertNotContains(Permission::create(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']);
|
||||||
$this->assertContains(Permission::update(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']);
|
$this->assertContains(Permission::update(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']);
|
||||||
$this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']);
|
$this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,14 @@ class DatabasesPermissionsGuestTest extends Scope
|
||||||
use SideClient;
|
use SideClient;
|
||||||
use DatabasesPermissionsScope;
|
use DatabasesPermissionsScope;
|
||||||
|
|
||||||
|
protected Authorization $auth;
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->auth = new Authorization();
|
||||||
|
}
|
||||||
|
|
||||||
public function createCollection(): array
|
public function createCollection(): array
|
||||||
{
|
{
|
||||||
$database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([
|
$database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([
|
||||||
|
|
@ -111,8 +119,8 @@ class DatabasesPermissionsGuestTest extends Scope
|
||||||
$this->assertEquals(201, $publicResponse['headers']['status-code']);
|
$this->assertEquals(201, $publicResponse['headers']['status-code']);
|
||||||
$this->assertEquals(201, $privateResponse['headers']['status-code']);
|
$this->assertEquals(201, $privateResponse['headers']['status-code']);
|
||||||
|
|
||||||
$roles = Authorization::getRoles();
|
$roles = $this->auth->getRoles();
|
||||||
Authorization::cleanRoles();
|
$this->auth->cleanRoles();
|
||||||
|
|
||||||
$publicDocuments = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents', [
|
$publicDocuments = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents', [
|
||||||
'content-type' => 'application/json',
|
'content-type' => 'application/json',
|
||||||
|
|
@ -134,7 +142,7 @@ class DatabasesPermissionsGuestTest extends Scope
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($roles as $role) {
|
foreach ($roles as $role) {
|
||||||
Authorization::setRole($role);
|
$this->auth->addRole($role);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,8 +153,8 @@ class DatabasesPermissionsGuestTest extends Scope
|
||||||
$privateCollectionId = $data['privateCollectionId'];
|
$privateCollectionId = $data['privateCollectionId'];
|
||||||
$databaseId = $data['databaseId'];
|
$databaseId = $data['databaseId'];
|
||||||
|
|
||||||
$roles = Authorization::getRoles();
|
$roles = $this->auth->getRoles();
|
||||||
Authorization::cleanRoles();
|
$this->auth->cleanRoles();
|
||||||
|
|
||||||
$publicResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents', [
|
$publicResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents', [
|
||||||
'content-type' => 'application/json',
|
'content-type' => 'application/json',
|
||||||
|
|
@ -222,7 +230,7 @@ class DatabasesPermissionsGuestTest extends Scope
|
||||||
$this->assertEquals(401, $privateDocument['headers']['status-code']);
|
$this->assertEquals(401, $privateDocument['headers']['status-code']);
|
||||||
|
|
||||||
foreach ($roles as $role) {
|
foreach ($roles as $role) {
|
||||||
Authorization::setRole($role);
|
$this->auth->addRole($role);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,7 @@ trait FunctionsBase
|
||||||
{
|
{
|
||||||
use Async;
|
use Async;
|
||||||
|
|
||||||
protected string $stdout = '';
|
protected string $output = '';
|
||||||
protected string $stderr = '';
|
|
||||||
|
|
||||||
protected function setupFunction(mixed $params): string
|
protected function setupFunction(mixed $params): string
|
||||||
{
|
{
|
||||||
|
|
@ -146,8 +145,7 @@ trait FunctionsBase
|
||||||
{
|
{
|
||||||
$folderPath = realpath(__DIR__ . '/../../../resources/functions') . "/$function";
|
$folderPath = realpath(__DIR__ . '/../../../resources/functions') . "/$function";
|
||||||
$tarPath = "$folderPath/code.tar.gz";
|
$tarPath = "$folderPath/code.tar.gz";
|
||||||
|
Console::execute("cd $folderPath && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->output);
|
||||||
Console::execute("cd $folderPath && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr);
|
|
||||||
|
|
||||||
if (filesize($tarPath) > 1024 * 1024 * 5) {
|
if (filesize($tarPath) > 1024 * 1024 * 5) {
|
||||||
throw new \Exception('Code package is too large. Use the chunked upload method instead.');
|
throw new \Exception('Code package is too large. Use the chunked upload method instead.');
|
||||||
|
|
|
||||||
|
|
@ -553,7 +553,7 @@ class FunctionsCustomServerTest extends Scope
|
||||||
|
|
||||||
$folder = 'php-large';
|
$folder = 'php-large';
|
||||||
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
|
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
|
||||||
Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr);
|
Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->output);
|
||||||
|
|
||||||
$chunkSize = 5 * 1024 * 1024;
|
$chunkSize = 5 * 1024 * 1024;
|
||||||
$handle = @fopen($code, "rb");
|
$handle = @fopen($code, "rb");
|
||||||
|
|
@ -1859,10 +1859,11 @@ class FunctionsCustomServerTest extends Scope
|
||||||
'x-appwrite-response-format' => '1.4.0', // Set response format for 1.4 syntax
|
'x-appwrite-response-format' => '1.4.0', // Set response format for 1.4 syntax
|
||||||
], $this->getHeaders()),
|
], $this->getHeaders()),
|
||||||
[
|
[
|
||||||
'queries' => [ 'equal("name", ["Test2"])' ]
|
'queries' => [
|
||||||
|
Query::equal('name', ['Test2'])->toString(),
|
||||||
|
]
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals(200, $response['headers']['status-code']);
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
$this->assertCount(1, $response['body']['functions']);
|
$this->assertCount(1, $response['body']['functions']);
|
||||||
$this->assertEquals('Test2', $response['body']['functions'][0]['name']);
|
$this->assertEquals('Test2', $response['body']['functions'][0]['name']);
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Tests\E2E\Services\Functions;
|
namespace Tests\E2E\Services\FunctionsSchedule;
|
||||||
|
|
||||||
use Appwrite\ID;
|
use Appwrite\ID;
|
||||||
use Tests\E2E\Client;
|
use Tests\E2E\Client;
|
||||||
use Tests\E2E\Scopes\ProjectCustom;
|
use Tests\E2E\Scopes\ProjectCustom;
|
||||||
use Tests\E2E\Scopes\Scope;
|
use Tests\E2E\Scopes\Scope;
|
||||||
use Tests\E2E\Scopes\SideServer;
|
use Tests\E2E\Scopes\SideServer;
|
||||||
|
use Tests\E2E\Services\Functions\FunctionsBase;
|
||||||
use Utopia\Database\Helpers\Role;
|
use Utopia\Database\Helpers\Role;
|
||||||
|
|
||||||
class FunctionsScheduleTest extends Scope
|
class FunctionsScheduleTest extends Scope
|
||||||
|
|
|
||||||
|
|
@ -2502,7 +2502,7 @@ trait Base
|
||||||
$folderPath = realpath(__DIR__ . '/../../../resources/functions') . "/$function";
|
$folderPath = realpath(__DIR__ . '/../../../resources/functions') . "/$function";
|
||||||
$tarPath = "$folderPath/code.tar.gz";
|
$tarPath = "$folderPath/code.tar.gz";
|
||||||
|
|
||||||
Console::execute("cd $folderPath && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr);
|
Console::execute("cd $folderPath && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout);
|
||||||
|
|
||||||
if (filesize($tarPath) > 1024 * 1024 * 5) {
|
if (filesize($tarPath) > 1024 * 1024 * 5) {
|
||||||
throw new \Exception('Code package is too large. Use the chunked upload method instead.');
|
throw new \Exception('Code package is too large. Use the chunked upload method instead.');
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,6 @@ class StorageClientTest extends Scope
|
||||||
/**
|
/**
|
||||||
* @depends testCreateFile
|
* @depends testCreateFile
|
||||||
* @param $file
|
* @param $file
|
||||||
* @return array
|
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function testGetFileDownload($file)
|
public function testGetFileDownload($file)
|
||||||
|
|
|
||||||
|
|
@ -232,7 +232,6 @@ class StorageServerTest extends Scope
|
||||||
/**
|
/**
|
||||||
* @depends testCreateFile
|
* @depends testCreateFile
|
||||||
* @param $file
|
* @param $file
|
||||||
* @return array
|
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function testGetFileDownload($file)
|
public function testGetFileDownload($file)
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue