Compare commits

..
Author SHA1 Message Date
fogelito 472efa55e5 version 2025-04-01 14:58:37 +03:00
fogelito f97dfc8d1b Test bind params PR 2025-04-01 14:55:07 +03:00
17221 changed files with 29208 additions and 44017 deletions
+1 -2
View File
@@ -15,7 +15,6 @@ _APP_SYSTEM_TEAM_EMAIL=team@appwrite.io
_APP_EMAIL_SECURITY=security@appwrite.io
_APP_EMAIL_CERTIFICATES=certificates@appwrite.io
_APP_SYSTEM_RESPONSE_FORMAT=
_APP_CUSTOM_DOMAIN_DENY_LIST=
_APP_OPTIONS_ABUSE=disabled
_APP_OPTIONS_ROUTER_PROTECTION=disabled
_APP_OPTIONS_FORCE_HTTPS=disabled
@@ -82,7 +81,7 @@ _APP_EXECUTOR_SECRET=your-secret-key
_APP_EXECUTOR_HOST=http://proxy/v1
_APP_FUNCTIONS_RUNTIMES=php-8.0,node-18.0,python-3.9,ruby-3.1
_APP_MAINTENANCE_INTERVAL=86400
_APP_MAINTENANCE_START_TIME=12:00
_APP_MAINTENANCE_DELAY=
_APP_MAINTENANCE_RETENTION_CACHE=2592000
_APP_MAINTENANCE_RETENTION_EXECUTION=1209600
_APP_MAINTENANCE_RETENTION_ABUSE=86400
+8 -1
View File
@@ -7,7 +7,6 @@ tasks:
docker pull composer
command: |
docker run --rm --interactive --tty \
--user "$(id -u):$(id -g)" \
--volume $PWD:/app \
composer install \
--ignore-platform-reqs \
@@ -24,3 +23,11 @@ vscode:
extensions:
- ms-azuretools.vscode-docker
- zobo.php-intellisense
github:
# https://www.gitpod.io/docs/prebuilds#github-specific-configuration
prebuilds:
# enable for pull requests coming from forks (defaults to false)
pullRequestsFromForks: true
# add a check to pull requests (defaults to true)
addCheck: false
+1
View File
@@ -86,6 +86,7 @@ RUN chmod +x /usr/local/bin/doctor && \
chmod +x /usr/local/bin/worker-migrations && \
chmod +x /usr/local/bin/worker-webhooks && \
chmod +x /usr/local/bin/worker-stats-usage && \
chmod +x /usr/local/bin/worker-stats-usage-dump && \
chmod +x /usr/local/bin/stats-resources && \
chmod +x /usr/local/bin/worker-stats-resources
+8 -39
View File
@@ -9,8 +9,6 @@ use Appwrite\Event\StatsResources;
use Appwrite\Event\StatsUsage;
use Appwrite\Platform\Appwrite;
use Appwrite\Runtimes\Runtimes;
use Executor\Executor;
use Swoole\Timer;
use Utopia\Cache\Adapter\Sharding;
use Utopia\Cache\Cache;
use Utopia\CLI\CLI;
@@ -26,9 +24,6 @@ use Utopia\Pools\Group;
use Utopia\Queue\Publisher;
use Utopia\Registry\Registry;
use Utopia\System\System;
use Utopia\Telemetry\Adapter\None as NoTelemetry;
use function Swoole\Coroutine\run;
// Overwriting runtimes to be architecture agnostic for CLI
Config::setParam('runtimes', (new Runtimes('v4'))->getAll(supported: false));
@@ -203,7 +198,7 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
};
}, ['pools', 'cache']);
CLI::setResource('queueForStatsUsage', function (Publisher $publisher) {
CLI::setResource('queueForStatsUsage', function (Connection $publisher) {
return new StatsUsage($publisher);
}, ['publisher']);
CLI::setResource('queueForStatsResources', function (Publisher $publisher) {
@@ -223,13 +218,6 @@ CLI::setResource('queueForCertificates', function (Publisher $publisher) {
}, ['publisher']);
CLI::setResource('logError', function (Registry $register) {
return function (Throwable $error, string $namespace, string $action) use ($register) {
Console::error('[Error] Timestamp: ' . date('c', time()));
Console::error('[Error] Type: ' . get_class($error));
Console::error('[Error] Message: ' . $error->getMessage());
Console::error('[Error] File: ' . $error->getFile());
Console::error('[Error] Line: ' . $error->getLine());
Console::error('[Error] Trace: ' . $error->getTraceAsString());
$logger = $register->get('logger');
if ($logger) {
@@ -248,7 +236,6 @@ CLI::setResource('logError', function (Registry $register) {
$log->addExtra('file', $error->getFile());
$log->addExtra('line', $error->getLine());
$log->addExtra('trace', $error->getTraceAsString());
$log->addExtra('detailedTrace', $error->getTrace());
$log->setAction($action);
@@ -262,40 +249,22 @@ CLI::setResource('logError', function (Registry $register) {
Console::error('Error pushing log: ' . $th->getMessage());
}
}
Console::warning("Failed: {$error->getMessage()}");
Console::warning($error->getTraceAsString());
};
}, ['register']);
CLI::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
CLI::setResource('telemetry', fn () => new NoTelemetry());
$platform = new Appwrite();
$args = $platform->getEnv('argv');
if (!isset($args[0])) {
Console::error('Missing task name');
Console::exit(1);
}
\array_shift($args);
$taskName = $args[0];
$platform->init(Service::TYPE_TASK);
$cli = $platform->getCli();
$cli
->error()
->inject('error')
->inject('logError')
->action(function (Throwable $error, callable $logError) use ($taskName) {
call_user_func_array($logError, [
$error,
'Task',
$taskName,
]);
Timer::clearAll();
->action(function (Throwable $error) {
Console::error($error->getMessage());
});
$cli->shutdown()->action(fn () => Timer::clearAll());
run($cli->run(...));
$cli->run();
-14
View File
@@ -2352,20 +2352,6 @@ return [
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_expired'),
'type' => Database::INDEX_KEY,
'attributes' => ['expired'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_session_internal_id'),
'type' => Database::INDEX_KEY,
'attributes' => ['sessionInternalId'],
'lengths' => [],
'orders' => [],
],
],
],
+2 -51
View File
@@ -356,21 +356,7 @@ return [
'attributes' => ['pingedAt'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_database'),
'type' => Database::INDEX_KEY,
'attributes' => ['database'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_region_accessed_at'),
'type' => Database::INDEX_KEY,
'attributes' => ['region', 'accessedAt'],
'lengths' => [],
'orders' => [],
],
]
],
],
@@ -494,20 +480,6 @@ return [
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_project_id_region'),
'type' => Database::INDEX_KEY,
'attributes' => ['projectId', 'region'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_region_rt_active'),
'type' => Database::INDEX_KEY,
'attributes' => ['region', 'resourceType', 'active'],
'lengths' => [],
'orders' => [],
],
],
],
@@ -1175,13 +1147,6 @@ return [
'lengths' => [16],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_piid_riid_rt'),
'type' => Database::INDEX_KEY,
'attributes' => ['projectInternalId', 'resourceInternalId', 'resourceType'],
'lengths' => [],
'orders' => [],
],
],
],
@@ -1477,14 +1442,7 @@ return [
'attributes' => ['resourceType'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_piid_riid_rt'),
'type' => Database::INDEX_KEY,
'attributes' => ['projectInternalId', 'resourceInternalId', 'resourceType'],
'lengths' => [],
'orders' => [],
],
]
],
],
@@ -1632,13 +1590,6 @@ return [
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_piid_prid_rt'),
'type' => Database::INDEX_KEY,
'attributes' => ['projectInternalId', 'providerRepositoryId'],
'lengths' => [],
'orders' => [],
],
],
],
+1 -29
View File
@@ -1182,13 +1182,6 @@ return [
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_resource_internal_id'),
'type' => Database::INDEX_KEY,
'attributes' => ['resourceInternalId'],
'lengths' => [],
'orders' => [],
],
],
],
@@ -1337,14 +1330,7 @@ return [
'attributes' => ['deploymentId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_deployment_internal_id'),
'type' => Database::INDEX_KEY,
'attributes' => ['deploymentInternalId'],
'lengths' => [],
'orders' => [],
],
]
],
],
@@ -1616,13 +1602,6 @@ return [
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_function_internal_id'),
'type' => Database::INDEX_KEY,
'attributes' => ['functionInternalId'],
'lengths' => [],
'orders' => [],
],
],
],
@@ -1741,13 +1720,6 @@ return [
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_resource_internal_id_resource_type'),
'type' => Database::INDEX_KEY,
'attributes' => ['resourceInternalId', 'resourceType'],
'lengths' => [],
'orders' => [],
],
],
],
-5
View File
@@ -593,11 +593,6 @@ return [
'description' => 'Database timed out. Try adjusting your queries or adding an index.',
'code' => 408
],
Exception::DATABASE_QUERY_ORDER_NULL => [
'name' => Exception::DATABASE_QUERY_ORDER_NULL,
'description' => 'The order attribute had a null value. Cursor pagination requires all documents order attribute values are non-null.',
'code' => 400,
],
/** Collections */
Exception::COLLECTION_NOT_FOUND => [
-10
View File
@@ -142,16 +142,6 @@ return [
'beta' => false,
'mock' => false,
],
'figma' => [
'name' => 'Figma',
'developers' => 'https://www.figma.com/developers/api#oauth2',
'icon' => 'icon-figma',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'github' => [
'name' => 'GitHub',
'developers' => 'https://developer.github.com/',
+16 -16
View File
@@ -11,7 +11,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
'version' => '17.0.2',
'version' => '17.0.1',
'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true,
@@ -59,7 +59,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '15.0.2',
'version' => '15.0.0',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@@ -77,7 +77,7 @@ return [
[
'key' => 'apple',
'name' => 'Apple',
'version' => '9.0.1',
'version' => '9.0.0',
'url' => 'https://github.com/appwrite/sdk-for-apple',
'package' => 'https://github.com/appwrite/sdk-for-apple',
'enabled' => true,
@@ -112,7 +112,7 @@ return [
[
'key' => 'android',
'name' => 'Android',
'version' => '7.0.1',
'version' => '6.1.0',
'url' => 'https://github.com/appwrite/sdk-for-android',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
'enabled' => true,
@@ -134,7 +134,7 @@ return [
[
'key' => 'react-native',
'name' => 'React Native',
'version' => '0.7.3',
'version' => '0.7.1',
'url' => 'https://github.com/appwrite/sdk-for-react-native',
'package' => 'https://npmjs.com/package/react-native-appwrite',
'enabled' => true,
@@ -217,7 +217,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '6.2.3',
'version' => '6.2.2',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@@ -245,7 +245,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '16.1.0-rc.1',
'version' => '15.0.1',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@@ -263,7 +263,7 @@ return [
[
'key' => 'deno',
'name' => 'Deno',
'version' => '14.1.0-rc.1',
'version' => '12.2.0',
'url' => 'https://github.com/appwrite/sdk-for-deno',
'package' => 'https://deno.land/x/appwrite',
'enabled' => true,
@@ -281,7 +281,7 @@ return [
[
'key' => 'php',
'name' => 'PHP',
'version' => '14.1.0-rc.1',
'version' => '12.2.0',
'url' => 'https://github.com/appwrite/sdk-for-php',
'package' => 'https://packagist.org/packages/appwrite/appwrite',
'enabled' => true,
@@ -299,7 +299,7 @@ return [
[
'key' => 'python',
'name' => 'Python',
'version' => '10.1.0-rc.1',
'version' => '9.0.2',
'url' => 'https://github.com/appwrite/sdk-for-python',
'package' => 'https://pypi.org/project/appwrite/',
'enabled' => true,
@@ -317,7 +317,7 @@ return [
[
'key' => 'ruby',
'name' => 'Ruby',
'version' => '15.1.0-rc.1',
'version' => '12.2.0',
'url' => 'https://github.com/appwrite/sdk-for-ruby',
'package' => 'https://rubygems.org/gems/appwrite',
'enabled' => true,
@@ -335,7 +335,7 @@ return [
[
'key' => 'go',
'name' => 'Go',
'version' => '0.6.0-rc.1',
'version' => '0.3.0',
'url' => 'https://github.com/appwrite/sdk-for-go',
'package' => 'https://github.com/appwrite/sdk-for-go',
'enabled' => true,
@@ -353,7 +353,7 @@ return [
[
'key' => 'dotnet',
'name' => '.NET',
'version' => '0.13.0-rc.1',
'version' => '0.11.0',
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => true,
@@ -371,7 +371,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '15.1.0-rc.1',
'version' => '14.0.0',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
@@ -389,7 +389,7 @@ return [
[
'key' => 'kotlin',
'name' => 'Kotlin',
'version' => '8.1.0-rc.1',
'version' => '6.2.0',
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
'enabled' => true,
@@ -411,7 +411,7 @@ return [
[
'key' => 'swift',
'name' => 'Swift',
'version' => '9.1.0-rc.1',
'version' => '8.0.0',
'url' => 'https://github.com/appwrite/sdk-for-swift',
'package' => 'https://github.com/appwrite/sdk-for-swift',
'enabled' => true,
+3
View File
@@ -65,6 +65,9 @@ return [
'tests' => false,
'optional' => true,
'icon' => '/images/services/databases.png',
'globalAttributes' => [
'databaseId'
]
],
'locale' => [
'key' => 'locale',
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+3 -21
View File
@@ -79,15 +79,6 @@ return [
'question' => 'Enter your Appwrite hostname',
'filter' => ''
],
[
'name' => '_APP_CUSTOM_DOMAIN_DENY_LIST',
'description' => 'List of reserved or prohibited domains when configuring custom domains.',
'introduction' => '',
'default' => 'example.com,test.com,app.example.com',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DOMAIN_FUNCTIONS',
'description' => 'A domain to use for function preview URLs. Setting to empty turns off function preview URLs.',
@@ -800,7 +791,7 @@ return [
],
[
'name' => '_APP_FUNCTIONS_CPUS',
'description' => 'The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is empty. When it\'s empty or 0, CPU limit will be disabled.',
'description' => 'The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is empty. When it\'s empty, CPU limit will be disabled.',
'introduction' => '0.7.0',
'default' => '0',
'required' => false,
@@ -809,7 +800,7 @@ return [
],
[
'name' => '_APP_FUNCTIONS_MEMORY',
'description' => 'The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is empty. When it\'s empty or 0, memory limit will be disabled.',
'description' => 'The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is empty. When it\'s empty, memory limit will be disabled.',
'introduction' => '0.7.0',
'default' => '0',
'required' => false,
@@ -1030,22 +1021,13 @@ return [
],
[
'name' => '_APP_MAINTENANCE_DELAY',
'description' => 'Deprecated with 1.6.2 use _APP_MAINTENANCE_START_TIME instead to run the maintenance at a specific time per day.',
'description' => 'Delay value containing the number of seconds that the Appwrite maintenance process should wait before executing system cleanups and optimizations. The default value is 0 seconds.',
'introduction' => '1.5.0',
'default' => '0',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_MAINTENANCE_START_TIME',
'description' => 'The time of day (in 24-hour format) when the maintenance process should start. The default value is 00:00.',
'introduction' => '1.6.2',
'default' => '00:00',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_MAINTENANCE_RETENTION_CACHE',
'description' => 'The maximum duration (in seconds) upto which to retain cached files. The default value is 2592000 seconds (30 days).',
+109 -90
View File
@@ -43,7 +43,6 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -156,6 +155,9 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails) {
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
/** @var Utopia\Database\Document $user */
$userFromRequest = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId));
@@ -271,7 +273,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
->setAttribute('current', true)
->setAttribute('countryName', $countryName)
->setAttribute('expire', $expire)
->setAttribute('secret', Auth::encodeSession($user->getId(), $sessionSecret))
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $sessionSecret) : '')
;
$response->dynamic($session, Response::MODEL_SESSION);
@@ -287,7 +289,6 @@ App::post('/v1/account')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'create',
description: '/docs/references/account/create.md',
auth: [],
@@ -431,7 +432,6 @@ App::get('/v1/account')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'get',
description: '/docs/references/account/get.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -461,7 +461,6 @@ App::delete('/v1/account')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'delete',
description: '/docs/references/account/delete.md',
auth: [AuthType::ADMIN],
@@ -514,7 +513,6 @@ App::get('/v1/account/sessions')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'listSessions',
description: '/docs/references/account/list-sessions.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -532,6 +530,9 @@ App::get('/v1/account/sessions')
->inject('project')
->action(function (Response $response, Document $user, Locale $locale, Document $project) {
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$sessions = $user->getAttribute('sessions', []);
$current = Auth::sessionVerify($sessions, Auth::$secret);
@@ -541,7 +542,7 @@ App::get('/v1/account/sessions')
$session->setAttribute('countryName', $countryName);
$session->setAttribute('current', ($current == $session->getId()) ? true : false);
$session->setAttribute('secret', $session->getAttribute('secret', ''));
$session->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $session->getAttribute('secret', '') : '');
$sessions[$key] = $session;
}
@@ -561,7 +562,6 @@ App::delete('/v1/account/sessions')
->label('audits.resource', 'user/{user.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'deleteSessions',
description: '/docs/references/account/delete-sessions.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -631,7 +631,6 @@ App::get('/v1/account/sessions/:sessionId')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'getSession',
description: '/docs/references/account/get-session.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -650,6 +649,10 @@ App::get('/v1/account/sessions/:sessionId')
->inject('project')
->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Document $project) {
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$sessions = $user->getAttribute('sessions', []);
$sessionId = ($sessionId === 'current')
? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret)
@@ -662,7 +665,7 @@ App::get('/v1/account/sessions/:sessionId')
$session
->setAttribute('current', ($session->getAttribute('secret') == Auth::hash(Auth::$secret)))
->setAttribute('countryName', $countryName)
->setAttribute('secret', $session->getAttribute('secret', ''))
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $session->getAttribute('secret', '') : '')
;
return $response->dynamic($session, Response::MODEL_SESSION);
@@ -681,7 +684,6 @@ App::delete('/v1/account/sessions/:sessionId')
->label('audits.resource', 'user/{user.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'deleteSession',
description: '/docs/references/account/delete-session.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -719,7 +721,9 @@ App::delete('/v1/account/sessions/:sessionId')
continue;
}
$dbForProject->deleteDocument('sessions', $session->getId());
$dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $session) {
return $dbForProject->deleteDocument('sessions', $session->getId());
});
unset($sessions[$key]);
@@ -768,7 +772,6 @@ App::patch('/v1/account/sessions/:sessionId')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'updateSession',
description: '/docs/references/account/update-session.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -853,7 +856,6 @@ App::post('/v1/account/sessions/email')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'createEmailPasswordSession',
description: '/docs/references/account/create-session-email-password.md',
auth: [],
@@ -895,6 +897,10 @@ App::post('/v1/account/sessions/email')
throw new Exception(Exception::USER_BLOCKED); // User is in status blocked
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$user->setAttributes($profile->getArrayCopy());
$hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]);
@@ -960,7 +966,7 @@ App::post('/v1/account/sessions/email')
$session
->setAttribute('current', true)
->setAttribute('countryName', $countryName)
->setAttribute('secret', Auth::encodeSession($user->getId(), $secret))
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '')
;
$queueForEvents
@@ -990,7 +996,6 @@ App::post('/v1/account/sessions/anonymous')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'createAnonymousSession',
description: '/docs/references/account/create-session-anonymous.md',
auth: [],
@@ -1014,6 +1019,9 @@ App::post('/v1/account/sessions/anonymous')
->inject('queueForEvents')
->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents) {
$protocol = $request->getProtocol();
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
if ('console' === $project->getId()) {
throw new Exception(Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED, 'Failed to create anonymous user');
@@ -1115,7 +1123,7 @@ App::post('/v1/account/sessions/anonymous')
$session
->setAttribute('current', true)
->setAttribute('countryName', $countryName)
->setAttribute('secret', Auth::encodeSession($user->getId(), $secret))
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '')
;
$response->dynamic($session, Response::MODEL_SESSION);
@@ -1131,7 +1139,6 @@ App::post('/v1/account/sessions/token')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'createSession',
description: '/docs/references/account/create-session.md',
auth: [],
@@ -1165,7 +1172,6 @@ App::get('/v1/account/sessions/oauth2/:provider')
->label('scope', 'sessions.write')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'createOAuth2Session',
description: '/docs/references/account/create-session-oauth2.md',
type: MethodType::WEBAUTH,
@@ -1237,7 +1243,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
});
App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->desc('Get OAuth2 callback')
->desc('OAuth2 callback')
->groups(['account'])
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('scope', 'public')
@@ -1267,7 +1273,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
});
App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->desc('Create OAuth2 callback')
->desc('OAuth2 callback')
->groups(['account'])
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('scope', 'public')
@@ -1298,7 +1304,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
});
App::get('/v1/account/sessions/oauth2/:provider/redirect')
->desc('Get OAuth2 redirect')
->desc('OAuth2 redirect')
->groups(['api', 'account', 'session'])
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('event', 'users.[userId].sessions.[sessionId].create')
@@ -1443,7 +1449,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
Query::notEqual('userInternalId', $user->getInternalId()),
]);
if (!$identityWithMatchingEmail->isEmpty()) {
$failureRedirect(Exception::USER_ALREADY_EXISTS);
throw new Exception(Exception::USER_ALREADY_EXISTS);
}
$userWithMatchingEmail = $dbForProject->find('users', [
@@ -1451,7 +1457,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
Query::notEqual('$id', $userId),
]);
if (!empty($userWithMatchingEmail)) {
$failureRedirect(Exception::USER_ALREADY_EXISTS);
throw new Exception(Exception::USER_ALREADY_EXISTS);
}
$sessionUpgrade = true;
@@ -1480,7 +1486,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
if ($user === false || $user->isEmpty()) { // No user logged in or with OAuth2 provider ID, create new one or connect with account with same email
if (empty($email)) {
$failureRedirect(Exception::USER_UNAUTHORIZED, 'OAuth provider failed to return email.');
throw new Exception(Exception::USER_UNAUTHORIZED, 'OAuth provider failed to return email.');
}
/**
@@ -1523,7 +1529,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
Query::equal('providerEmail', [$email]),
]);
if (!$identityWithMatchingEmail->isEmpty()) {
$failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
}
try {
@@ -1595,7 +1601,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
Query::notEqual('userInternalId', $user->getInternalId()),
]);
if (!empty($identitiesWithMatchingEmail)) {
$failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
}
$dbForProject->createDocument('identities', new Document([
@@ -1763,7 +1769,6 @@ App::get('/v1/account/tokens/oauth2/:provider')
->label('scope', 'sessions.write')
->label('sdk', new Method(
namespace: 'account',
group: 'tokens',
name: 'createOAuth2Token',
description: '/docs/references/account/create-token-oauth2.md',
auth: [],
@@ -1844,7 +1849,6 @@ App::post('/v1/account/tokens/magic-url')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'tokens',
name: 'createMagicURLToken',
description: '/docs/references/account/create-token-magic-url.md',
auth: [],
@@ -1880,6 +1884,9 @@ App::post('/v1/account/tokens/magic-url')
$phrase = Phrase::generate();
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$result = $dbForProject->findOne('users', [Query::equal('email', [$email])]);
if (!$result->isEmpty()) {
@@ -1999,7 +2006,6 @@ App::post('/v1/account/tokens/magic-url')
$senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
$replyTo = "";
if ($smtpEnabled) {
@@ -2062,11 +2068,17 @@ App::post('/v1/account/tokens/magic-url')
->setRecipient($email)
->trigger();
// Set to unhashed secret for events and server responses
$token->setAttribute('secret', $tokenSecret);
$queueForEvents
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
if (!$isPrivilegedUser && !$isAppUser) {
$token->setAttribute('secret', '');
}
if (!empty($phrase)) {
$token->setAttribute('phrase', $phrase);
}
@@ -2086,7 +2098,6 @@ App::post('/v1/account/tokens/email')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'tokens',
name: 'createEmailToken',
description: '/docs/references/account/create-token-email.md',
auth: [],
@@ -2099,7 +2110,7 @@ App::post('/v1/account/tokens/email')
contentType: ContentType::JSON,
))
->label('abuse-limit', 10)
->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}'])
->label('abuse-key', 'url:{url},email:{param-email}')
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('email', '', new Email(), 'User email.')
->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true)
@@ -2120,6 +2131,10 @@ App::post('/v1/account/tokens/email')
$phrase = Phrase::generate();
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$result = $dbForProject->findOne('users', [Query::equal('email', [$email])]);
if (!$result->isEmpty()) {
$user->setAttributes($result->getArrayCopy());
@@ -2288,11 +2303,17 @@ App::post('/v1/account/tokens/email')
->setRecipient($email)
->trigger();
// Set to unhashed secret for events and server responses
$token->setAttribute('secret', $tokenSecret);
$queueForEvents
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
if (!$isPrivilegedUser && !$isAppUser) {
$token->setAttribute('secret', '');
}
if (!empty($phrase)) {
$token->setAttribute('phrase', $phrase);
}
@@ -2312,7 +2333,6 @@ App::put('/v1/account/sessions/magic-url')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'updateMagicURLSession',
description: '/docs/references/account/create-session.md',
auth: [],
@@ -2350,7 +2370,6 @@ App::put('/v1/account/sessions/phone')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'updatePhoneSession',
description: '/docs/references/account/create-session.md',
auth: [],
@@ -2389,7 +2408,6 @@ App::post('/v1/account/tokens/phone')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'tokens',
name: 'createPhoneToken',
description: '/docs/references/account/create-token-phone.md',
auth: [],
@@ -2421,6 +2439,10 @@ App::post('/v1/account/tokens/phone')
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$result = $dbForProject->findOne('users', [Query::equal('phone', [$phone])]);
if (!$result->isEmpty()) {
$user->setAttributes($result->getArrayCopy());
@@ -2572,13 +2594,14 @@ App::post('/v1/account/tokens/phone')
}
}
// Set to unhashed secret for events and server responses
$token->setAttribute('secret', $secret);
$queueForEvents
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
// Encode secret for clients
$token->setAttribute('secret', Auth::encodeSession($user->getId(), $secret));
// Hide secret for clients
$token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '');
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -2593,7 +2616,6 @@ App::post('/v1/account/jwts')
->label('auth.type', 'jwt')
->label('sdk', new Method(
namespace: 'account',
group: 'tokens',
name: 'createJWT',
description: '/docs/references/account/create-jwt.md',
auth: [],
@@ -2642,7 +2664,6 @@ App::get('/v1/account/prefs')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'getPrefs',
description: '/docs/references/account/get-prefs.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -2669,7 +2690,6 @@ App::get('/v1/account/logs')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'logs',
name: 'listLogs',
description: '/docs/references/account/list-logs.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -2746,7 +2766,6 @@ App::patch('/v1/account/name')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updateName',
description: '/docs/references/account/update-name.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -2768,7 +2787,7 @@ App::patch('/v1/account/name')
$user->setAttribute('name', $name);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
$queueForEvents->setParam('userId', $user->getId());
@@ -2785,7 +2804,6 @@ App::patch('/v1/account/password')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updatePassword',
description: '/docs/references/account/update-password.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -2843,7 +2861,7 @@ App::patch('/v1/account/password')
->setAttribute('hash', Auth::DEFAULT_ALGO)
->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
$queueForEvents->setParam('userId', $user->getId());
@@ -2859,7 +2877,6 @@ App::patch('/v1/account/email')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updateEmail',
description: '/docs/references/account/update-email.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -2928,7 +2945,7 @@ App::patch('/v1/account/email')
}
try {
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
/**
* @var Document $oldTarget
*/
@@ -2956,7 +2973,6 @@ App::patch('/v1/account/phone')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updatePhone',
description: '/docs/references/account/update-phone.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3014,7 +3030,7 @@ App::patch('/v1/account/phone')
}
try {
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
/**
* @var Document $oldTarget
*/
@@ -3042,7 +3058,6 @@ App::patch('/v1/account/prefs')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updatePrefs',
description: '/docs/references/account/update-prefs.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3064,7 +3079,7 @@ App::patch('/v1/account/prefs')
$user->setAttribute('prefs', $prefs);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
$queueForEvents->setParam('userId', $user->getId());
@@ -3080,7 +3095,6 @@ App::patch('/v1/account/status')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updateStatus',
description: '/docs/references/account/update-status.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3102,7 +3116,7 @@ App::patch('/v1/account/status')
$user->setAttribute('status', false);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
$queueForEvents
->setParam('userId', $user->getId())
@@ -3131,7 +3145,6 @@ App::post('/v1/account/recovery')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'recovery',
name: 'createRecovery',
description: '/docs/references/account/create-recovery.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3161,6 +3174,11 @@ App::post('/v1/account/recovery')
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
}
$url = htmlentities($url);
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$email = \strtolower($email);
$profile = $dbForProject->findOne('users', [
@@ -3284,13 +3302,19 @@ App::post('/v1/account/recovery')
->setSubject($subject)
->trigger();
// Set to unhashed secret for events and server responses
$recovery->setAttribute('secret', $secret);
$queueForEvents
->setParam('userId', $profile->getId())
->setParam('tokenId', $recovery->getId())
->setUser($profile)
->setPayload(Response::showSensitive(fn () => $response->output($recovery, Response::MODEL_TOKEN)), sensitive: ['secret']);
->setPayload($response->output($recovery, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
if (!$isPrivilegedUser && !$isAppUser) {
$recovery->setAttribute('secret', '');
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -3298,7 +3322,7 @@ App::post('/v1/account/recovery')
});
App::put('/v1/account/recovery')
->desc('Update password recovery (confirmation)')
->desc('Create password recovery (confirmation)')
->groups(['api', 'account'])
->label('scope', 'sessions.write')
->label('event', 'users.[userId].recovery.[tokenId].update')
@@ -3307,7 +3331,6 @@ App::put('/v1/account/recovery')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'recovery',
name: 'updateRecovery',
description: '/docs/references/account/update-recovery.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3384,7 +3407,7 @@ App::put('/v1/account/recovery')
$queueForEvents
->setParam('userId', $profile->getId())
->setParam('tokenId', $recoveryDocument->getId())
->setPayload(Response::showSensitive(fn () => $response->output($recoveryDocument, Response::MODEL_TOKEN)), sensitive: ['secret']);
;
$response->dynamic($recoveryDocument, Response::MODEL_TOKEN);
});
@@ -3398,7 +3421,6 @@ App::post('/v1/account/verification')
->label('audits.resource', 'user/{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'verification',
name: 'createVerification',
description: '/docs/references/account/create-email-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3432,6 +3454,9 @@ App::post('/v1/account/verification')
throw new Exception(Exception::USER_EMAIL_ALREADY_VERIFIED);
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$verificationSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_VERIFICATION);
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
@@ -3540,12 +3565,18 @@ App::post('/v1/account/verification')
->setName($user->getAttribute('name') ?? '')
->trigger();
// Set to unhashed secret for events and server responses
$verification->setAttribute('secret', $verificationSecret);
$queueForEvents
->setParam('userId', $user->getId())
->setParam('tokenId', $verification->getId())
->setPayload(Response::showSensitive(fn () => $response->output($verification, Response::MODEL_TOKEN)), sensitive: ['secret']);
->setPayload($response->output($verification, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
if (!$isPrivilegedUser && !$isAppUser) {
$verification->setAttribute('secret', '');
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -3553,7 +3584,7 @@ App::post('/v1/account/verification')
});
App::put('/v1/account/verification')
->desc('Update email verification (confirmation)')
->desc('Create email verification (confirmation)')
->groups(['api', 'account'])
->label('scope', 'public')
->label('event', 'users.[userId].verification.[tokenId].update')
@@ -3561,7 +3592,6 @@ App::put('/v1/account/verification')
->label('audits.resource', 'user/{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'verification',
name: 'updateVerification',
description: '/docs/references/account/update-email-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3613,8 +3643,7 @@ App::put('/v1/account/verification')
$queueForEvents
->setParam('userId', $userId)
->setParam('tokenId', $verification->getId())
->setPayload(Response::showSensitive(fn () => $response->output($verification, Response::MODEL_TOKEN)), sensitive: ['secret']);
->setParam('tokenId', $verification->getId());
$response->dynamic($verification, Response::MODEL_TOKEN);
});
@@ -3629,7 +3658,6 @@ App::post('/v1/account/verification/phone')
->label('audits.resource', 'user/{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'verification',
name: 'createPhoneVerification',
description: '/docs/references/account/create-phone-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3668,6 +3696,10 @@ App::post('/v1/account/verification/phone')
throw new Exception(Exception::USER_PHONE_ALREADY_VERIFIED);
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$secret = null;
$sendSMS = true;
$mockNumbers = $project->getAttribute('auths', [])['mockNumbers'] ?? [];
@@ -3756,6 +3788,7 @@ App::post('/v1/account/verification/phone')
}
}
// Set to unhashed secret for events and server responses
$verification->setAttribute('secret', $secret);
$queueForEvents
@@ -3763,6 +3796,11 @@ App::post('/v1/account/verification/phone')
->setParam('tokenId', $verification->getId())
->setPayload($response->output($verification, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
if (!$isPrivilegedUser && !$isAppUser) {
$verification->setAttribute('secret', '');
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($verification, Response::MODEL_TOKEN);
@@ -3777,7 +3815,6 @@ App::put('/v1/account/verification/phone')
->label('audits.resource', 'user/{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'verification',
name: 'updatePhoneVerification',
description: '/docs/references/account/update-phone-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3843,7 +3880,6 @@ App::patch('/v1/account/mfa')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMFA',
description: '/docs/references/account/update-mfa.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3866,7 +3902,7 @@ App::patch('/v1/account/mfa')
$user->setAttribute('mfa', $mfa);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
if ($mfa) {
$factors = $session->getAttribute('factors', []);
@@ -3897,7 +3933,6 @@ App::get('/v1/account/mfa/factors')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'listMfaFactors',
description: '/docs/references/account/list-mfa-factors.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3938,7 +3973,6 @@ App::post('/v1/account/mfa/authenticators/:type')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'createMfaAuthenticator',
description: '/docs/references/account/create-mfa-authenticator.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4006,7 +4040,7 @@ App::post('/v1/account/mfa/authenticators/:type')
});
App::put('/v1/account/mfa/authenticators/:type')
->desc('Update authenticator (confirmation)')
->desc('Verify authenticator')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
@@ -4015,7 +4049,6 @@ App::put('/v1/account/mfa/authenticators/:type')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMfaAuthenticator',
description: '/docs/references/account/update-mfa-authenticator.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4085,7 +4118,6 @@ App::post('/v1/account/mfa/recovery-codes')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'createMfaRecoveryCodes',
description: '/docs/references/account/create-mfa-recovery-codes.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4123,7 +4155,7 @@ App::post('/v1/account/mfa/recovery-codes')
});
App::patch('/v1/account/mfa/recovery-codes')
->desc('Update MFA recovery codes (regenerate)')
->desc('Regenerate MFA recovery codes')
->groups(['api', 'account', 'mfaProtected'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
@@ -4132,7 +4164,6 @@ App::patch('/v1/account/mfa/recovery-codes')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMfaRecoveryCodes',
description: '/docs/references/account/update-mfa-recovery-codes.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4169,12 +4200,11 @@ App::patch('/v1/account/mfa/recovery-codes')
});
App::get('/v1/account/mfa/recovery-codes')
->desc('List MFA recovery codes')
->desc('Get MFA recovery codes')
->groups(['api', 'account', 'mfaProtected'])
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'getMfaRecoveryCodes',
description: '/docs/references/account/get-mfa-recovery-codes.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4213,7 +4243,6 @@ App::delete('/v1/account/mfa/authenticators/:type')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'deleteMfaAuthenticator',
description: '/docs/references/account/delete-mfa-authenticator.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4259,7 +4288,6 @@ App::post('/v1/account/mfa/challenge')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'createMfaChallenge',
description: '/docs/references/account/create-mfa-challenge.md',
auth: [],
@@ -4470,7 +4498,7 @@ App::post('/v1/account/mfa/challenge')
});
App::put('/v1/account/mfa/challenge')
->desc('Update MFA challenge (confirmation)')
->desc('Create MFA challenge (confirmation)')
->groups(['api', 'account', 'mfa'])
->label('scope', 'account')
->label('event', 'users.[userId].sessions.[sessionId].create')
@@ -4479,7 +4507,6 @@ App::put('/v1/account/mfa/challenge')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMfaChallenge',
description: '/docs/references/account/update-mfa-challenge.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4573,7 +4600,6 @@ App::post('/v1/account/targets/push')
->label('event', 'users.[userId].targets.[targetId].create')
->label('sdk', new Method(
namespace: 'account',
group: 'pushTargets',
name: 'createPushTarget',
description: '/docs/references/account/create-push-target.md',
auth: [AuthType::SESSION],
@@ -4654,7 +4680,6 @@ App::put('/v1/account/targets/:targetId/push')
->label('event', 'users.[userId].targets.[targetId].update')
->label('sdk', new Method(
namespace: 'account',
group: 'pushTargets',
name: 'updatePushTarget',
description: '/docs/references/account/update-push-target.md',
auth: [AuthType::SESSION],
@@ -4719,7 +4744,6 @@ App::delete('/v1/account/targets/:targetId/push')
->label('event', 'users.[userId].targets.[targetId].delete')
->label('sdk', new Method(
namespace: 'account',
group: 'pushTargets',
name: 'deletePushTarget',
description: '/docs/references/account/delete-push-target.md',
auth: [AuthType::SESSION],
@@ -4770,7 +4794,6 @@ App::get('/v1/account/identities')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'identities',
name: 'listIdentities',
description: '/docs/references/account/list-identities.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4822,11 +4845,8 @@ App::get('/v1/account/identities')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$results = $dbForProject->find('identities', $queries);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$results = $dbForProject->find('identities', $queries);
$total = $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT);
$response->dynamic(new Document([
@@ -4845,7 +4865,6 @@ App::delete('/v1/account/identities/:identityId')
->label('audits.userId', '{user.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'identities',
name: 'deleteIdentity',
description: '/docs/references/account/delete-identity.md',
auth: [AuthType::SESSION, AuthType::JWT],
-7
View File
@@ -171,7 +171,6 @@ App::get('/v1/avatars/credit-cards/:code')
->label('cache.resource', 'avatar/credit-card')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getCreditCard',
description: '/docs/references/avatars/get-credit-card.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -199,7 +198,6 @@ App::get('/v1/avatars/browsers/:code')
->label('cache.resource', 'avatar/browser')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getBrowser',
description: '/docs/references/avatars/get-browser.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -227,7 +225,6 @@ App::get('/v1/avatars/flags/:code')
->label('cache.resource', 'avatar/flag')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getFlag',
description: '/docs/references/avatars/get-flag.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -255,7 +252,6 @@ App::get('/v1/avatars/image')
->label('cache.resource', 'avatar/image')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getImage',
description: '/docs/references/avatars/get-image.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -326,7 +322,6 @@ App::get('/v1/avatars/favicon')
->label('cache.resource', 'avatar/favicon')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getFavicon',
description: '/docs/references/avatars/get-favicon.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -477,7 +472,6 @@ App::get('/v1/avatars/qr')
->label('scope', 'avatars.read')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getQR',
description: '/docs/references/avatars/get-qr.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -526,7 +520,6 @@ App::get('/v1/avatars/initials')
->label('cache.resource', 'avatar/initials')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getInitials',
description: '/docs/references/avatars/get-initials.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
+1 -3
View File
@@ -27,7 +27,6 @@ App::get('/v1/console/variables')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'console',
group: 'console',
name: 'variables',
description: '/docs/references/console/variables.md',
auth: [AuthType::ADMIN],
@@ -68,12 +67,11 @@ App::get('/v1/console/variables')
});
App::post('/v1/console/assistant')
->desc('Create assistant query')
->desc('Ask query')
->groups(['api', 'assistant'])
->label('scope', 'assistant.read')
->label('sdk', new Method(
namespace: 'assistant',
group: 'console',
name: 'chat',
description: '/docs/references/assistant/chat.md',
auth: [AuthType::ADMIN],
File diff suppressed because it is too large Load Diff
+51 -66
View File
@@ -37,7 +37,6 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -151,7 +150,6 @@ App::post('/v1/functions')
->label('audits.resource', 'function/{response.$id}')
->label('sdk', new Method(
namespace: 'functions',
group: 'functions',
name: 'create',
description: '/docs/references/functions/create-function.md',
auth: [AuthType::KEY],
@@ -186,8 +184,8 @@ App::post('/v1/functions')
->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification(
$plan,
Config::getParam('runtime-specifications', []),
System::getEnv('_APP_FUNCTIONS_CPUS', 0),
System::getEnv('_APP_FUNCTIONS_MEMORY', 0)
App::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT),
App::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT)
), 'Runtime specification for the function and builds.', true, ['plan'])
->inject('request')
->inject('response')
@@ -433,7 +431,6 @@ App::get('/v1/functions')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'functions',
name: 'list',
description: '/docs/references/functions/list-functions.md',
auth: [AuthType::KEY],
@@ -486,15 +483,10 @@ App::get('/v1/functions')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$functions = $dbForProject->find('functions', $queries);
$total = $dbForProject->count('functions', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'functions' => $functions,
'total' => $total,
'functions' => $dbForProject->find('functions', $queries),
'total' => $dbForProject->count('functions', $filterQueries, APP_LIMIT_COUNT),
]), Response::MODEL_FUNCTION_LIST);
});
@@ -505,7 +497,6 @@ App::get('/v1/functions/runtimes')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'runtimes',
name: 'listRuntimes',
description: '/docs/references/functions/list-runtimes.md',
auth: [AuthType::KEY],
@@ -545,7 +536,6 @@ App::get('/v1/functions/specifications')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'runtimes',
name: 'listSpecifications',
description: '/docs/references/functions/list-specifications.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -569,12 +559,8 @@ App::get('/v1/functions/specifications')
$spec['enabled'] = in_array($spec['slug'], $plan['runtimeSpecifications']);
}
$maxCpus = System::getEnv('_APP_FUNCTIONS_CPUS', 0);
$maxMemory = System::getEnv('_APP_FUNCTIONS_MEMORY', 0);
// Only add specs that are within the limits set by environment variables
// Treat 0 as no limit
if ((empty($maxCpus) || $spec['cpus'] <= $maxCpus) && (empty($maxMemory) || $spec['memory'] <= $maxMemory)) {
if ($spec['cpus'] <= System::getEnv('_APP_FUNCTIONS_CPUS', 1) && $spec['memory'] <= System::getEnv('_APP_FUNCTIONS_MEMORY', 512)) {
$runtimeSpecs[] = $spec;
}
}
@@ -592,7 +578,6 @@ App::get('/v1/functions/:functionId')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'functions',
name: 'get',
description: '/docs/references/functions/get-function.md',
auth: [AuthType::KEY],
@@ -623,7 +608,6 @@ App::get('/v1/functions/:functionId/usage')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: null,
name: 'getFunctionUsage',
description: '/docs/references/functions/get-function-usage.md',
auth: [AuthType::ADMIN],
@@ -735,7 +719,6 @@ App::get('/v1/functions/usage')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: null,
name: 'getUsage',
description: '/docs/references/functions/get-functions-usage.md',
auth: [AuthType::ADMIN],
@@ -845,7 +828,6 @@ App::put('/v1/functions/:functionId')
->label('audits.resource', 'function/{response.$id}')
->label('sdk', new Method(
namespace: 'functions',
group: 'functions',
name: 'update',
description: '/docs/references/functions/update-function.md',
auth: [AuthType::KEY],
@@ -876,8 +858,8 @@ App::put('/v1/functions/:functionId')
->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification(
$plan,
Config::getParam('runtime-specifications', []),
System::getEnv('_APP_FUNCTIONS_CPUS', 0),
System::getEnv('_APP_FUNCTIONS_MEMORY', 0)
App::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT),
App::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT)
), 'Runtime specification for the function and builds.', true, ['plan'])
->inject('request')
->inject('response')
@@ -887,8 +869,7 @@ App::put('/v1/functions/:functionId')
->inject('queueForBuilds')
->inject('dbForPlatform')
->inject('gitHub')
->inject('executor')
->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 $dbForPlatform, GitHub $github, Executor $executor) use ($redeployVcs) {
->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 $dbForPlatform, GitHub $github) use ($redeployVcs) {
// TODO: If only branch changes, re-deploy
$function = $dbForProject->getDocument('functions', $functionId);
@@ -991,6 +972,7 @@ App::put('/v1/functions/:functionId')
// Enforce Cold Start if spec limits change.
if ($function->getAttribute('specification') !== $specification && !empty($function->getAttribute('deployment'))) {
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
try {
$executor->deleteRuntime($project->getId(), $function->getAttribute('deployment'));
} catch (\Throwable $th) {
@@ -1046,12 +1028,11 @@ App::put('/v1/functions/:functionId')
App::get('/v1/functions/:functionId/deployments/:deploymentId/download')
->groups(['api', 'functions'])
->desc('Get deployment download')
->desc('Download deployment')
->label('scope', 'functions.read')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'deployments',
name: 'getDeploymentDownload',
description: '/docs/references/functions/get-deployment-download.md',
auth: [AuthType::KEY, AuthType::JWT],
@@ -1148,7 +1129,6 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'deployments',
name: 'updateDeployment',
description: '/docs/references/functions/update-function-deployment.md',
auth: [AuthType::KEY],
@@ -1217,7 +1197,6 @@ App::delete('/v1/functions/:functionId')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'functions',
name: 'delete',
description: '/docs/references/functions/delete-function.md',
auth: [AuthType::KEY],
@@ -1273,7 +1252,6 @@ App::post('/v1/functions/:functionId/deployments')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'deployments',
name: 'createDeployment',
description: '/docs/references/functions/create-deployment.md',
auth: [AuthType::KEY],
@@ -1285,7 +1263,7 @@ App::post('/v1/functions/:functionId/deployments')
],
type: MethodType::UPLOAD,
packaging: true,
requestType: ContentType::MULTIPART,
requestType: 'multipart/form-data',
))
->param('functionId', '', new UID(), 'Function ID.')
->param('entrypoint', null, new Text(1028), 'Entrypoint File.', true)
@@ -1496,7 +1474,6 @@ App::get('/v1/functions/:functionId/deployments')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'deployments',
name: 'listDeployments',
description: '/docs/references/functions/list-deployments.md',
auth: [AuthType::KEY],
@@ -1560,12 +1537,9 @@ App::get('/v1/functions/:functionId/deployments')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$results = $dbForProject->find('deployments', $queries);
$total = $dbForProject->count('deployments', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$results = $dbForProject->find('deployments', $queries);
$total = $dbForProject->count('deployments', $filterQueries, APP_LIMIT_COUNT);
foreach ($results as $result) {
$build = $dbForProject->getDocument('builds', $result->getAttribute('buildId', ''));
@@ -1589,7 +1563,6 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'deployments',
name: 'getDeployment',
description: '/docs/references/functions/get-deployment.md',
auth: [AuthType::KEY],
@@ -1642,7 +1615,6 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'deployments',
name: 'deleteDeployment',
description: '/docs/references/functions/delete-deployment.md',
auth: [AuthType::KEY],
@@ -1708,7 +1680,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
->alias('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
->groups(['api', 'functions'])
->desc('Create deployment build')
->desc('Rebuild deployment')
->label('scope', 'functions.write')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('event', 'functions.[functionId].deployments.[deploymentId].update')
@@ -1716,7 +1688,6 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'deployments',
name: 'createBuild',
description: '/docs/references/functions/create-build.md',
auth: [AuthType::KEY],
@@ -1792,7 +1763,6 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'deployments',
name: 'updateDeploymentBuild',
description: '/docs/references/functions/update-deployment-build.md',
auth: [AuthType::KEY],
@@ -1809,8 +1779,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
->inject('dbForProject')
->inject('project')
->inject('queueForEvents')
->inject('executor')
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Executor $executor) {
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
@@ -1865,6 +1834,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
$dbForProject->purgeCachedDocument('deployments', $deployment->getId());
try {
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
$executor->deleteRuntime($project->getId(), $deploymentId . "-build");
} catch (\Throwable $th) {
// Don't throw if the deployment doesn't exist
@@ -1888,7 +1858,6 @@ App::post('/v1/functions/:functionId/executions')
->label('event', 'functions.[functionId].executions.[executionId].create')
->label('sdk', new Method(
namespace: 'functions',
group: 'executions',
name: 'createExecution',
description: '/docs/references/functions/create-execution.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1899,6 +1868,7 @@ App::post('/v1/functions/:functionId/executions')
)
],
contentType: ContentType::MULTIPART,
requestType: 'application/json',
))
->param('functionId', '', new UID(), 'Function ID.')
->param('body', '', new Text(10485760, 0), 'HTTP body of execution. Default value is empty string.', true)
@@ -1916,9 +1886,8 @@ App::post('/v1/functions/:functionId/executions')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->inject('queueForFunctions')
->inject('executor')
->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 $dbForPlatform, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $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 $dbForPlatform, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb) {
$async = \strval($async) === 'true' || \strval($async) === '1';
if (!$async && !is_null($scheduledAt)) {
@@ -2191,6 +2160,7 @@ App::post('/v1/functions/:functionId/executions')
]);
/** Execute function */
$executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST'));
try {
$version = $function->getAttribute('version', 'v2');
$command = $runtime['startCommand'];
@@ -2256,6 +2226,15 @@ App::post('/v1/functions/:functionId/executions')
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
if (!$isPrivilegedUser && !$isAppUser) {
$execution->setAttribute('logs', '');
$execution->setAttribute('errors', '');
}
$headers = [];
foreach (($executionResponse['headers'] ?? []) as $key => $value) {
$headers[] = ['name' => $key, 'value' => $value];
@@ -2287,7 +2266,6 @@ App::get('/v1/functions/:functionId/executions')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'executions',
name: 'listExecutions',
description: '/docs/references/functions/list-executions.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -2353,11 +2331,19 @@ App::get('/v1/functions/:functionId/executions')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$results = $dbForProject->find('executions', $queries);
$total = $dbForProject->count('executions', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
$results = $dbForProject->find('executions', $queries);
$total = $dbForProject->count('executions', $filterQueries, APP_LIMIT_COUNT);
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
if (!$isPrivilegedUser && !$isAppUser) {
$results = array_map(function ($execution) {
$execution->setAttribute('logs', '');
$execution->setAttribute('errors', '');
return $execution;
}, $results);
}
$response->dynamic(new Document([
@@ -2373,7 +2359,6 @@ App::get('/v1/functions/:functionId/executions/:executionId')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'executions',
name: 'getExecution',
description: '/docs/references/functions/get-execution.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -2409,6 +2394,14 @@ App::get('/v1/functions/:functionId/executions/:executionId')
throw new Exception(Exception::EXECUTION_NOT_FOUND);
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
if (!$isPrivilegedUser && !$isAppUser) {
$execution->setAttribute('logs', '');
$execution->setAttribute('errors', '');
}
$response->dynamic($execution, Response::MODEL_EXECUTION);
});
@@ -2422,7 +2415,6 @@ App::delete('/v1/functions/:functionId/executions/:executionId')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'executions',
name: 'deleteExecution',
description: '/docs/references/functions/delete-execution.md',
auth: [AuthType::KEY],
@@ -2500,7 +2492,6 @@ App::post('/v1/functions/:functionId/variables')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'variables',
name: 'createVariable',
description: '/docs/references/functions/create-variable.md',
auth: [AuthType::KEY],
@@ -2571,7 +2562,6 @@ App::get('/v1/functions/:functionId/variables')
'sdk',
new Method(
namespace: 'functions',
group: 'variables',
name: 'listVariables',
description: '/docs/references/functions/list-variables.md',
auth: [AuthType::KEY],
@@ -2608,7 +2598,6 @@ App::get('/v1/functions/:functionId/variables/:variableId')
'sdk',
new Method(
namespace: 'functions',
group: 'variables',
name: 'getVariable',
description: '/docs/references/functions/get-variable.md',
auth: [AuthType::KEY],
@@ -2657,7 +2646,6 @@ App::put('/v1/functions/:functionId/variables/:variableId')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'variables',
name: 'updateVariable',
description: '/docs/references/functions/update-variable.md',
auth: [AuthType::KEY],
@@ -2725,7 +2713,6 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'variables',
name: 'deleteVariable',
description: '/docs/references/functions/delete-variable.md',
auth: [AuthType::KEY],
@@ -2780,7 +2767,6 @@ App::get('/v1/functions/templates')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'templates',
name: 'listTemplates',
description: '/docs/references/functions/list-templates.md',
auth: [AuthType::ADMIN],
@@ -2824,7 +2810,6 @@ App::get('/v1/functions/templates/:templateId')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'templates',
name: 'getTemplate',
description: '/docs/references/functions/get-template.md',
auth: [AuthType::ADMIN],
-3
View File
@@ -44,7 +44,6 @@ App::get('/v1/graphql')
->label('scope', 'graphql')
->label('sdk', new Method(
namespace: 'graphql',
group: 'graphql',
name: 'get',
auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT],
hide: true,
@@ -91,7 +90,6 @@ App::post('/v1/graphql/mutation')
->label('scope', 'graphql')
->label('sdk', new Method(
namespace: 'graphql',
group: 'graphql',
name: 'mutation',
auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT],
description: '/docs/references/graphql/post.md',
@@ -142,7 +140,6 @@ App::post('/v1/graphql')
->label('scope', 'graphql')
->label('sdk', new Method(
namespace: 'graphql',
group: 'graphql',
name: 'query',
auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT],
description: '/docs/references/graphql/post.md',
+36 -22
View File
@@ -32,7 +32,6 @@ App::get('/v1/health')
->label('scope', 'health.read')
->label('sdk', new Method(
namespace: 'health',
group: 'health',
name: 'get',
auth: [AuthType::KEY],
description: '/docs/references/health/get.md',
@@ -72,7 +71,6 @@ App::get('/v1/health/db')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getDB',
description: '/docs/references/health/get-db.md',
responses: [
@@ -133,7 +131,6 @@ App::get('/v1/health/cache')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getCache',
description: '/docs/references/health/get-cache.md',
responses: [
@@ -198,7 +195,6 @@ App::get('/v1/health/pubsub')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getPubSub',
description: '/docs/references/health/get-pubsub.md',
responses: [
@@ -263,7 +259,6 @@ App::get('/v1/health/time')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getTime',
description: '/docs/references/health/get-time.md',
responses: [
@@ -327,7 +322,6 @@ App::get('/v1/health/queue/webhooks')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueWebhooks',
description: '/docs/references/health/get-queue-webhooks.md',
responses: [
@@ -360,7 +354,6 @@ App::get('/v1/health/queue/logs')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueLogs',
description: '/docs/references/health/get-queue-logs.md',
responses: [
@@ -393,7 +386,6 @@ App::get('/v1/health/certificate')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getCertificate',
description: '/docs/references/health/get-certificate.md',
responses: [
@@ -450,7 +442,6 @@ App::get('/v1/health/queue/certificates')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueCertificates',
description: '/docs/references/health/get-queue-certificates.md',
responses: [
@@ -483,7 +474,6 @@ App::get('/v1/health/queue/builds')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueBuilds',
description: '/docs/references/health/get-queue-builds.md',
responses: [
@@ -516,7 +506,6 @@ App::get('/v1/health/queue/databases')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueDatabases',
description: '/docs/references/health/get-queue-databases.md',
responses: [
@@ -550,7 +539,6 @@ App::get('/v1/health/queue/deletes')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueDeletes',
description: '/docs/references/health/get-queue-deletes.md',
responses: [
@@ -583,7 +571,6 @@ App::get('/v1/health/queue/mails')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueMails',
description: '/docs/references/health/get-queue-mails.md',
responses: [
@@ -616,7 +603,6 @@ App::get('/v1/health/queue/messaging')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueMessaging',
description: '/docs/references/health/get-queue-messaging.md',
responses: [
@@ -649,7 +635,6 @@ App::get('/v1/health/queue/migrations')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueMigrations',
description: '/docs/references/health/get-queue-migrations.md',
responses: [
@@ -682,7 +667,6 @@ App::get('/v1/health/queue/functions')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueFunctions',
description: '/docs/references/health/get-queue-functions.md',
responses: [
@@ -715,7 +699,6 @@ App::get('/v1/health/queue/stats-resources')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueStatsResources',
description: '/docs/references/health/get-queue-stats-resources.md',
responses: [
@@ -748,7 +731,6 @@ App::get('/v1/health/queue/stats-usage')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueUsage',
description: '/docs/references/health/get-queue-stats-usage.md',
responses: [
@@ -774,6 +756,38 @@ App::get('/v1/health/queue/stats-usage')
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
});
App::get('/v1/health/queue/stats-usage-dump')
->desc('Get usage dump queue')
->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
name: 'getQueueStatsUsageDump',
description: '/docs/references/health/get-queue-stats-usage-dump.md',
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_HEALTH_QUEUE,
)
],
contentType: ContentType::JSON
))
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisher')
->inject('response')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::STATS_USAGE_DUMP_QUEUE_NAME));
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
}
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
});
App::get('/v1/health/storage/local')
->desc('Get local storage')
->groups(['api', 'health'])
@@ -781,7 +795,6 @@ App::get('/v1/health/storage/local')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'storage',
name: 'getStorageLocal',
description: '/docs/references/health/get-storage-local.md',
responses: [
@@ -831,7 +844,6 @@ App::get('/v1/health/storage')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'storage',
name: 'getStorage',
description: '/docs/references/health/get-storage.md',
responses: [
@@ -879,7 +891,6 @@ App::get('/v1/health/anti-virus')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getAntivirus',
description: '/docs/references/health/get-storage-anti-virus.md',
responses: [
@@ -925,7 +936,6 @@ App::get('/v1/health/queue/failed/:name')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getFailedJobs',
description: '/docs/references/health/get-failed-queue-jobs.md',
responses: [
@@ -944,6 +954,7 @@ App::get('/v1/health/queue/failed/:name')
Event::FUNCTIONS_QUEUE_NAME,
Event::STATS_RESOURCES_QUEUE_NAME,
Event::STATS_USAGE_QUEUE_NAME,
Event::STATS_USAGE_DUMP_QUEUE_NAME,
Event::WEBHOOK_QUEUE_NAME,
Event::CERTIFICATES_QUEUE_NAME,
Event::BUILDS_QUEUE_NAME,
@@ -969,6 +980,9 @@ App::get('/v1/health/stats') // Currently only used internally
->desc('Get system stats')
->groups(['api', 'health'])
->label('scope', 'root')
// ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
// ->label('sdk.namespace', 'health')
// ->label('sdk.method', 'getStats')
->label('docs', false)
->inject('response')
->inject('register')
-8
View File
@@ -17,7 +17,6 @@ App::get('/v1/locale')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'get',
description: '/docs/references/locale/get-locale.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -81,7 +80,6 @@ App::get('/v1/locale/codes')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listCodes',
description: '/docs/references/locale/list-locale-codes.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -107,7 +105,6 @@ App::get('/v1/locale/countries')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listCountries',
description: '/docs/references/locale/list-countries.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -144,7 +141,6 @@ App::get('/v1/locale/countries/eu')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listCountriesEU',
description: '/docs/references/locale/list-countries-eu.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -183,7 +179,6 @@ App::get('/v1/locale/countries/phones')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listCountriesPhones',
description: '/docs/references/locale/list-countries-phones.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -221,7 +216,6 @@ App::get('/v1/locale/continents')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listContinents',
description: '/docs/references/locale/list-continents.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -257,7 +251,6 @@ App::get('/v1/locale/currencies')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listCurrencies',
description: '/docs/references/locale/list-currencies.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -284,7 +277,6 @@ App::get('/v1/locale/languages')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listLanguages',
description: '/docs/references/locale/list-languages.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
+14 -84
View File
@@ -30,7 +30,6 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
@@ -64,7 +63,6 @@ App::post('/v1/messaging/providers/mailgun')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createMailgunProvider',
description: '/docs/references/messaging/create-mailgun-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -158,7 +156,6 @@ App::post('/v1/messaging/providers/sendgrid')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createSendgridProvider',
description: '/docs/references/messaging/create-sendgrid-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -240,7 +237,6 @@ App::post('/v1/messaging/providers/smtp')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createSmtpProvider',
description: '/docs/references/messaging/create-smtp-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -335,7 +331,6 @@ App::post('/v1/messaging/providers/msg91')
->label('event', 'providers.[providerId].create')
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createMsg91Provider',
description: '/docs/references/messaging/create-msg91-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -418,7 +413,6 @@ App::post('/v1/messaging/providers/telesign')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createTelesignProvider',
description: '/docs/references/messaging/create-telesign-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -502,7 +496,6 @@ App::post('/v1/messaging/providers/textmagic')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createTextmagicProvider',
description: '/docs/references/messaging/create-textmagic-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -586,7 +579,6 @@ App::post('/v1/messaging/providers/twilio')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createTwilioProvider',
description: '/docs/references/messaging/create-twilio-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -670,7 +662,6 @@ App::post('/v1/messaging/providers/vonage')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createVonageProvider',
description: '/docs/references/messaging/create-vonage-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -754,7 +745,6 @@ App::post('/v1/messaging/providers/fcm')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createFcmProvider',
description: '/docs/references/messaging/create-fcm-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -824,7 +814,6 @@ App::post('/v1/messaging/providers/apns')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createApnsProvider',
description: '/docs/references/messaging/create-apns-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -914,7 +903,6 @@ App::get('/v1/messaging/providers')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'listProviders',
description: '/docs/references/messaging/list-providers.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -963,15 +951,10 @@ App::get('/v1/messaging/providers')
$cursor->setValue($cursorDocument);
}
try {
$providers = $dbForProject->find('providers', $queries);
$total = $dbForProject->count('providers', $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'providers' => $providers,
'total' => $total,
'providers' => $dbForProject->find('providers', $queries),
'total' => $dbForProject->count('providers', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_PROVIDER_LIST);
});
@@ -982,7 +965,6 @@ App::get('/v1/messaging/providers/:providerId/logs')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'listProviderLogs',
description: '/docs/references/messaging/list-provider-logs.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1079,7 +1061,6 @@ App::get('/v1/messaging/providers/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'getProvider',
description: '/docs/references/messaging/get-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1113,7 +1094,6 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateMailgunProvider',
description: '/docs/references/messaging/update-mailgun-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1226,7 +1206,6 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateSendgridProvider',
description: '/docs/references/messaging/update-sendgrid-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1324,7 +1303,6 @@ App::patch('/v1/messaging/providers/smtp/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateSmtpProvider',
description: '/docs/references/messaging/update-smtp-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1453,7 +1431,6 @@ App::patch('/v1/messaging/providers/msg91/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateMsg91Provider',
description: '/docs/references/messaging/update-msg91-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1540,7 +1517,6 @@ App::patch('/v1/messaging/providers/telesign/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateTelesignProvider',
description: '/docs/references/messaging/update-telesign-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1629,7 +1605,6 @@ App::patch('/v1/messaging/providers/textmagic/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateTextmagicProvider',
description: '/docs/references/messaging/update-textmagic-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1718,7 +1693,6 @@ App::patch('/v1/messaging/providers/twilio/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateTwilioProvider',
description: '/docs/references/messaging/update-twilio-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1807,7 +1781,6 @@ App::patch('/v1/messaging/providers/vonage/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateVonageProvider',
description: '/docs/references/messaging/update-vonage-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1896,7 +1869,6 @@ App::patch('/v1/messaging/providers/fcm/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateFcmProvider',
description: '/docs/references/messaging/update-fcm-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1972,7 +1944,6 @@ App::patch('/v1/messaging/providers/apns/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateApnsProvider',
description: '/docs/references/messaging/update-apns-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2074,7 +2045,6 @@ App::delete('/v1/messaging/providers/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'deleteProvider',
description: '/docs/references/messaging/delete-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2117,7 +2087,6 @@ App::post('/v1/messaging/topics')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'createTopic',
description: '/docs/references/messaging/create-topic.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2164,7 +2133,6 @@ App::get('/v1/messaging/topics')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'listTopics',
description: '/docs/references/messaging/list-topics.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2213,15 +2181,10 @@ App::get('/v1/messaging/topics')
$cursor->setValue($cursorDocument[0]);
}
try {
$topics = $dbForProject->find('topics', $queries);
$total = $dbForProject->count('topics', $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'topics' => $topics,
'total' => $total,
'topics' => $dbForProject->find('topics', $queries),
'total' => $dbForProject->count('topics', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_TOPIC_LIST);
});
@@ -2232,7 +2195,6 @@ App::get('/v1/messaging/topics/:topicId/logs')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'listTopicLogs',
description: '/docs/references/messaging/list-topic-logs.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2330,7 +2292,6 @@ App::get('/v1/messaging/topics/:topicId')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'getTopic',
description: '/docs/references/messaging/get-topic.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2365,7 +2326,6 @@ App::patch('/v1/messaging/topics/:topicId')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'updateTopic',
description: '/docs/references/messaging/update-topic.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2416,7 +2376,6 @@ App::delete('/v1/messaging/topics/:topicId')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'deleteTopic',
description: '/docs/references/messaging/delete-topic.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2464,7 +2423,6 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'subscribers',
name: 'createSubscriber',
description: '/docs/references/messaging/create-subscriber.md',
auth: [AuthType::JWT, AuthType::SESSION, AuthType::ADMIN, AuthType::KEY],
@@ -2564,7 +2522,6 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'subscribers',
name: 'listSubscribers',
description: '/docs/references/messaging/list-subscribers.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2622,11 +2579,8 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
$cursor->setValue($cursorDocument);
}
try {
$subscribers = $dbForProject->find('subscribers', $queries);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$subscribers = $dbForProject->find('subscribers', $queries);
$subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject) {
return function () use ($subscriber, $dbForProject) {
@@ -2653,7 +2607,6 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'subscribers',
name: 'listSubscriberLogs',
description: '/docs/references/messaging/list-subscriber-logs.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2751,7 +2704,6 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'subscribers',
name: 'getSubscriber',
description: '/docs/references/messaging/get-subscriber.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2800,7 +2752,6 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'subscribers',
name: 'deleteSubscriber',
description: '/docs/references/messaging/delete-subscriber.md',
auth: [AuthType::JWT, AuthType::SESSION, AuthType::ADMIN, AuthType::KEY],
@@ -2867,7 +2818,6 @@ App::post('/v1/messaging/messages/email')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'createEmail',
description: '/docs/references/messaging/create-email.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3026,7 +2976,6 @@ App::post('/v1/messaging/messages/sms')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'createSms',
description: '/docs/references/messaging/create-sms.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3149,7 +3098,6 @@ App::post('/v1/messaging/messages/push')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'createPush',
description: '/docs/references/messaging/create-push.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3364,7 +3312,6 @@ App::get('/v1/messaging/messages')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'listMessages',
description: '/docs/references/messaging/list-messages.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3413,15 +3360,10 @@ App::get('/v1/messaging/messages')
$cursor->setValue($cursorDocument);
}
try {
$messages = $dbForProject->find('messages', $queries);
$total = $dbForProject->count('messages', $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'messages' => $messages,
'total' => $total,
'messages' => $dbForProject->find('messages', $queries),
'total' => $dbForProject->count('messages', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_MESSAGE_LIST);
});
@@ -3432,7 +3374,6 @@ App::get('/v1/messaging/messages/:messageId/logs')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'logs',
name: 'listMessageLogs',
description: '/docs/references/messaging/list-message-logs.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3530,7 +3471,6 @@ App::get('/v1/messaging/messages/:messageId/targets')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'listTargets',
description: '/docs/references/messaging/list-message-targets.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3593,15 +3533,10 @@ App::get('/v1/messaging/messages/:messageId/targets')
$cursor->setValue($cursorDocument);
}
try {
$targets = $dbForProject->find('targets', $queries);
$total = $dbForProject->count('targets', $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'targets' => $targets,
'total' => $total,
'targets' => $dbForProject->find('targets', $queries),
'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_TARGET_LIST);
});
@@ -3612,7 +3547,6 @@ App::get('/v1/messaging/messages/:messageId')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'getMessage',
description: '/docs/references/messaging/get-message.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3646,7 +3580,6 @@ App::patch('/v1/messaging/messages/email/:messageId')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'updateEmail',
description: '/docs/references/messaging/update-email.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3853,7 +3786,6 @@ App::patch('/v1/messaging/messages/sms/:messageId')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'updateSms',
description: '/docs/references/messaging/update-sms.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -4015,7 +3947,6 @@ App::patch('/v1/messaging/messages/push/:messageId')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'updatePush',
description: '/docs/references/messaging/update-push.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -4275,7 +4206,6 @@ App::delete('/v1/messaging/messages/:messageId')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'delete',
description: '/docs/references/messaging/delete-message.md',
auth: [AuthType::ADMIN, AuthType::KEY],
+12 -31
View File
@@ -12,7 +12,6 @@ use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
@@ -32,13 +31,12 @@ include_once __DIR__ . '/../shared/api.php';
App::post('/v1/migrations/appwrite')
->groups(['api', 'migrations'])
->desc('Create Appwrite migration')
->desc('Migrate Appwrite data')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'createAppwriteMigration',
description: '/docs/references/migrations/migration-appwrite.md',
auth: [AuthType::ADMIN],
@@ -94,13 +92,12 @@ App::post('/v1/migrations/appwrite')
App::post('/v1/migrations/firebase')
->groups(['api', 'migrations'])
->desc('Create Firebase migration')
->desc('Migrate Firebase data')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'createFirebaseMigration',
description: '/docs/references/migrations/migration-firebase.md',
auth: [AuthType::ADMIN],
@@ -161,13 +158,12 @@ App::post('/v1/migrations/firebase')
App::post('/v1/migrations/supabase')
->groups(['api', 'migrations'])
->desc('Create Supabase migration')
->desc('Migrate Supabase data')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'createSupabaseMigration',
description: '/docs/references/migrations/migration-supabase.md',
auth: [AuthType::ADMIN],
@@ -228,13 +224,12 @@ App::post('/v1/migrations/supabase')
App::post('/v1/migrations/nhost')
->groups(['api', 'migrations'])
->desc('Create NHost migration')
->desc('Migrate NHost data')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'createNHostMigration',
description: '/docs/references/migrations/migration-nhost.md',
auth: [AuthType::ADMIN],
@@ -301,7 +296,6 @@ App::get('/v1/migrations')
->label('scope', 'migrations.read')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'list',
description: '/docs/references/migrations/list-migrations.md',
auth: [AuthType::ADMIN],
@@ -353,15 +347,10 @@ App::get('/v1/migrations')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$migrations = $dbForProject->find('migrations', $queries);
$total = $dbForProject->count('migrations', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'migrations' => $migrations,
'total' => $total,
'migrations' => $dbForProject->find('migrations', $queries),
'total' => $dbForProject->count('migrations', $filterQueries, APP_LIMIT_COUNT),
]), Response::MODEL_MIGRATION_LIST);
});
@@ -371,7 +360,6 @@ App::get('/v1/migrations/:migrationId')
->label('scope', 'migrations.read')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'get',
description: '/docs/references/migrations/get-migration.md',
auth: [AuthType::ADMIN],
@@ -397,11 +385,10 @@ App::get('/v1/migrations/:migrationId')
App::get('/v1/migrations/appwrite/report')
->groups(['api', 'migrations'])
->desc('Get Appwrite migration report')
->desc('Generate a report on Appwrite data')
->label('scope', 'migrations.write')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'getAppwriteReport',
description: '/docs/references/migrations/migration-appwrite-report.md',
auth: [AuthType::ADMIN],
@@ -421,7 +408,6 @@ App::get('/v1/migrations/appwrite/report')
->inject('project')
->inject('user')
->action(function (array $resources, string $endpoint, string $projectID, string $key, Response $response) {
$appwrite = new Appwrite($projectID, $endpoint, $key);
try {
@@ -446,11 +432,10 @@ App::get('/v1/migrations/appwrite/report')
App::get('/v1/migrations/firebase/report')
->groups(['api', 'migrations'])
->desc('Get Firebase migration report')
->desc('Generate a report on Firebase data')
->label('scope', 'migrations.write')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'getFirebaseReport',
description: '/docs/references/migrations/migration-firebase-report.md',
auth: [AuthType::ADMIN],
@@ -499,11 +484,10 @@ App::get('/v1/migrations/firebase/report')
App::get('/v1/migrations/supabase/report')
->groups(['api', 'migrations'])
->desc('Get Supabase migration report')
->desc('Generate a report on Supabase Data')
->label('scope', 'migrations.write')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'getSupabaseReport',
description: '/docs/references/migrations/migration-supabase-report.md',
auth: [AuthType::ADMIN],
@@ -548,11 +532,10 @@ App::get('/v1/migrations/supabase/report')
App::get('/v1/migrations/nhost/report')
->groups(['api', 'migrations'])
->desc('Get NHost migration report')
->desc('Generate a report on NHost Data')
->label('scope', 'migrations.write')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'getNHostReport',
description: '/docs/references/migrations/migration-nhost-report.md',
auth: [AuthType::ADMIN],
@@ -597,14 +580,13 @@ App::get('/v1/migrations/nhost/report')
App::patch('/v1/migrations/:migrationId')
->groups(['api', 'migrations'])
->desc('Update retry migration')
->desc('Retry migration')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].retry')
->label('audits.event', 'migration.retry')
->label('audits.resource', 'migrations/{request.migrationId}')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'retry',
description: '/docs/references/migrations/retry-migration.md',
auth: [AuthType::ADMIN],
@@ -655,7 +637,6 @@ App::delete('/v1/migrations/:migrationId')
->label('audits.resource', 'migrations/{request.migrationId}')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'delete',
description: '/docs/references/migrations/delete-migration.md',
auth: [AuthType::ADMIN],
-6
View File
@@ -26,7 +26,6 @@ App::get('/v1/project/usage')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'getUsage',
description: '/docs/references/project/get-usage.md',
auth: [AuthType::ADMIN],
@@ -388,7 +387,6 @@ App::post('/v1/project/variables')
->label('audits.event', 'variable.create')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'createVariable',
description: '/docs/references/project/create-variable.md',
auth: [AuthType::ADMIN],
@@ -448,7 +446,6 @@ App::get('/v1/project/variables')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'listVariables',
description: '/docs/references/project/list-variables.md',
auth: [AuthType::ADMIN],
@@ -479,7 +476,6 @@ App::get('/v1/project/variables/:variableId')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'getVariable',
description: '/docs/references/project/get-variable.md',
auth: [AuthType::ADMIN],
@@ -509,7 +505,6 @@ App::put('/v1/project/variables/:variableId')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'updateVariable',
description: '/docs/references/project/update-variable.md',
auth: [AuthType::ADMIN],
@@ -561,7 +556,6 @@ App::delete('/v1/project/variables/:variableId')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'deleteVariable',
description: '/docs/references/project/delete-variable.md',
auth: [AuthType::ADMIN],
+8 -68
View File
@@ -28,7 +28,6 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -69,7 +68,6 @@ App::post('/v1/projects')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'create',
description: '/docs/references/projects/create.md',
auth: [AuthType::ADMIN],
@@ -140,14 +138,6 @@ App::post('/v1/projects')
$databases = Config::getParam('pools-database', []);
if ($region !== 'default') {
$databaseKeys = System::getEnv('_APP_DATABASE_KEYS', '');
$keys = explode(',', $databaseKeys);
$databases = array_filter($keys, function ($value) use ($region) {
return str_contains($value, $region);
});
}
$databaseOverride = System::getEnv('_APP_DATABASE_OVERRIDE');
$index = \array_search($databaseOverride, $databases);
if ($index !== false) {
@@ -215,17 +205,17 @@ App::post('/v1/projects')
$dsn = new DSN('mysql://' . $dsn);
}
$adapter = $pools->get($dsn->getHost())->pop()->getResource();
$dbForProject = new Database($adapter, $cache);
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
$sharedTablesV1 = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES_V1', ''));
$projectTables = !\in_array($dsn->getHost(), $sharedTables);
$sharedTablesV1 = \in_array($dsn->getHost(), $sharedTablesV1);
$sharedTablesV2 = !$projectTables && !$sharedTablesV1;
$sharedTables = $sharedTablesV1 || $sharedTablesV2;
if (!$sharedTablesV2) {
$adapter = $pools->get($dsn->getHost())->pop()->getResource();
$dbForProject = new Database($adapter, $cache);
if ($sharedTables) {
$dbForProject
->setSharedTables(true)
@@ -307,7 +297,6 @@ App::get('/v1/projects')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'list',
description: '/docs/references/projects/list.md',
auth: [AuthType::ADMIN],
@@ -360,15 +349,10 @@ App::get('/v1/projects')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$projects = $dbForPlatform->find('projects', $queries);
$total = $dbForPlatform->count('projects', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'projects' => $projects,
'total' => $total,
'projects' => $dbForPlatform->find('projects', $queries),
'total' => $dbForPlatform->count('projects', $filterQueries, APP_LIMIT_COUNT),
]), Response::MODEL_PROJECT_LIST);
});
@@ -378,7 +362,6 @@ App::get('/v1/projects/:projectId')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'get',
description: '/docs/references/projects/get.md',
auth: [AuthType::ADMIN],
@@ -411,7 +394,6 @@ App::patch('/v1/projects/:projectId')
->label('audits.resource', 'project/{request.projectId}')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'update',
description: '/docs/references/projects/update.md',
auth: [AuthType::ADMIN],
@@ -465,7 +447,6 @@ App::patch('/v1/projects/:projectId/team')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'updateTeam',
description: '/docs/references/projects/update-team.md',
auth: [AuthType::ADMIN],
@@ -540,7 +521,6 @@ App::patch('/v1/projects/:projectId/service')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'updateServiceStatus',
description: '/docs/references/projects/update-service-status.md',
auth: [AuthType::ADMIN],
@@ -578,7 +558,6 @@ App::patch('/v1/projects/:projectId/service/all')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'updateServiceStatusAll',
description: '/docs/references/projects/update-service-status-all.md',
auth: [AuthType::ADMIN],
@@ -619,7 +598,6 @@ App::patch('/v1/projects/:projectId/api')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'updateApiStatus',
description: '/docs/references/projects/update-api-status.md',
auth: [AuthType::ADMIN],
@@ -657,7 +635,6 @@ App::patch('/v1/projects/:projectId/api/all')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'updateApiStatusAll',
description: '/docs/references/projects/update-api-status-all.md',
auth: [AuthType::ADMIN],
@@ -698,7 +675,6 @@ App::patch('/v1/projects/:projectId/oauth2')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateOAuth2',
description: '/docs/references/projects/update-oauth2.md',
auth: [AuthType::ADMIN],
@@ -749,7 +725,6 @@ App::patch('/v1/projects/:projectId/auth/session-alerts')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateSessionAlerts',
description: '/docs/references/projects/update-session-alerts.md',
auth: [AuthType::ADMIN],
@@ -787,7 +762,6 @@ App::patch('/v1/projects/:projectId/auth/memberships-privacy')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateMembershipsPrivacy',
description: '/docs/references/projects/update-memberships-privacy.md',
auth: [AuthType::ADMIN],
@@ -829,7 +803,6 @@ App::patch('/v1/projects/:projectId/auth/limit')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthLimit',
description: '/docs/references/projects/update-auth-limit.md',
auth: [AuthType::ADMIN],
@@ -867,7 +840,6 @@ App::patch('/v1/projects/:projectId/auth/duration')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthDuration',
description: '/docs/references/projects/update-auth-duration.md',
auth: [AuthType::ADMIN],
@@ -905,7 +877,6 @@ App::patch('/v1/projects/:projectId/auth/:method')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthStatus',
description: '/docs/references/projects/update-auth-status.md',
auth: [AuthType::ADMIN],
@@ -946,7 +917,6 @@ App::patch('/v1/projects/:projectId/auth/password-history')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthPasswordHistory',
description: '/docs/references/projects/update-auth-password-history.md',
auth: [AuthType::ADMIN],
@@ -984,7 +954,6 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthPasswordDictionary',
description: '/docs/references/projects/update-auth-password-dictionary.md',
auth: [AuthType::ADMIN],
@@ -1017,12 +986,11 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
});
App::patch('/v1/projects/:projectId/auth/personal-data')
->desc('Update personal data check')
->desc('Enable or disable checking user passwords for similarity with their personal data.')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updatePersonalDataCheck',
description: '/docs/references/projects/update-personal-data-check.md',
auth: [AuthType::ADMIN],
@@ -1060,7 +1028,6 @@ App::patch('/v1/projects/:projectId/auth/max-sessions')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthSessionsLimit',
description: '/docs/references/projects/update-auth-sessions-limit.md',
auth: [AuthType::ADMIN],
@@ -1098,7 +1065,6 @@ App::patch('/v1/projects/:projectId/auth/mock-numbers')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateMockNumbers',
description: '/docs/references/projects/update-mock-numbers.md',
auth: [AuthType::ADMIN],
@@ -1146,7 +1112,6 @@ App::delete('/v1/projects/:projectId')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'delete',
description: '/docs/references/projects/delete.md',
auth: [AuthType::ADMIN],
@@ -1190,7 +1155,6 @@ App::post('/v1/projects/:projectId/webhooks')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'createWebhook',
description: '/docs/references/projects/create-webhook.md',
auth: [AuthType::ADMIN],
@@ -1255,7 +1219,6 @@ App::get('/v1/projects/:projectId/webhooks')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'listWebhooks',
description: '/docs/references/projects/list-webhooks.md',
auth: [AuthType::ADMIN],
@@ -1294,7 +1257,6 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'getWebhook',
description: '/docs/references/projects/get-webhook.md',
auth: [AuthType::ADMIN],
@@ -1335,7 +1297,6 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'updateWebhook',
description: '/docs/references/projects/update-webhook.md',
auth: [AuthType::ADMIN],
@@ -1401,7 +1362,6 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'updateWebhookSignature',
description: '/docs/references/projects/update-webhook-signature.md',
auth: [AuthType::ADMIN],
@@ -1447,7 +1407,6 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'deleteWebhook',
description: '/docs/references/projects/delete-webhook.md',
auth: [AuthType::ADMIN],
@@ -1495,7 +1454,6 @@ App::post('/v1/projects/:projectId/keys')
->label('scope', 'keys.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'keys',
name: 'createKey',
description: '/docs/references/projects/create-key.md',
auth: [AuthType::ADMIN],
@@ -1552,7 +1510,6 @@ App::get('/v1/projects/:projectId/keys')
->label('scope', 'keys.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'keys',
name: 'listKeys',
description: '/docs/references/projects/list-keys.md',
auth: [AuthType::ADMIN],
@@ -1591,7 +1548,6 @@ App::get('/v1/projects/:projectId/keys/:keyId')
->label('scope', 'keys.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'keys',
name: 'getKey',
description: '/docs/references/projects/get-key.md',
auth: [AuthType::ADMIN],
@@ -1632,7 +1588,6 @@ App::put('/v1/projects/:projectId/keys/:keyId')
->label('scope', 'keys.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'keys',
name: 'updateKey',
description: '/docs/references/projects/update-key.md',
auth: [AuthType::ADMIN],
@@ -1685,7 +1640,6 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
->label('scope', 'keys.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'keys',
name: 'deleteKey',
description: '/docs/references/projects/delete-key.md',
auth: [AuthType::ADMIN],
@@ -1733,7 +1687,6 @@ App::post('/v1/projects/:projectId/jwts')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'createJWT',
description: '/docs/references/projects/create-jwt.md',
auth: [AuthType::ADMIN],
@@ -1777,7 +1730,6 @@ App::post('/v1/projects/:projectId/platforms')
->label('scope', 'platforms.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'platforms',
name: 'createPlatform',
description: '/docs/references/projects/create-platform.md',
auth: [AuthType::ADMIN],
@@ -1834,7 +1786,6 @@ App::get('/v1/projects/:projectId/platforms')
->label('scope', 'platforms.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'platforms',
name: 'listPlatforms',
description: '/docs/references/projects/list-platforms.md',
auth: [AuthType::ADMIN],
@@ -1873,7 +1824,6 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
->label('scope', 'platforms.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'platforms',
name: 'getPlatform',
description: '/docs/references/projects/get-platform.md',
auth: [AuthType::ADMIN],
@@ -1914,7 +1864,6 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
->label('scope', 'platforms.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'platforms',
name: 'updatePlatform',
description: '/docs/references/projects/update-platform.md',
auth: [AuthType::ADMIN],
@@ -1970,7 +1919,6 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
->label('scope', 'platforms.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'platforms',
name: 'deletePlatform',
description: '/docs/references/projects/delete-platform.md',
auth: [AuthType::ADMIN],
@@ -2018,7 +1966,6 @@ App::patch('/v1/projects/:projectId/smtp')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'updateSmtp',
description: '/docs/references/projects/update-smtp.md',
auth: [AuthType::ADMIN],
@@ -2115,7 +2062,6 @@ App::post('/v1/projects/:projectId/smtp/tests')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'createSmtpTest',
description: '/docs/references/projects/create-smtp-test.md',
auth: [AuthType::ADMIN],
@@ -2182,7 +2128,6 @@ App::get('/v1/projects/:projectId/templates/sms/:type/:locale')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'getSmsTemplate',
description: '/docs/references/projects/get-sms-template.md',
auth: [AuthType::ADMIN],
@@ -2230,7 +2175,6 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'getEmailTemplate',
description: '/docs/references/projects/get-email-template.md',
auth: [AuthType::ADMIN],
@@ -2289,7 +2233,6 @@ App::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'updateSmsTemplate',
description: '/docs/references/projects/update-sms-template.md',
auth: [AuthType::ADMIN],
@@ -2336,7 +2279,6 @@ App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'updateEmailTemplate',
description: '/docs/references/projects/update-email-template.md',
auth: [AuthType::ADMIN],
@@ -2393,7 +2335,6 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'deleteSmsTemplate',
description: '/docs/references/projects/delete-sms-template.md',
auth: [AuthType::ADMIN],
@@ -2439,12 +2380,11 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
});
App::delete('/v1/projects/:projectId/templates/email/:type/:locale')
->desc('Delete custom email template')
->desc('Reset custom email template')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'deleteEmailTemplate',
description: '/docs/references/projects/delete-email-template.md',
auth: [AuthType::ADMIN],
+3 -19
View File
@@ -35,7 +35,6 @@ App::post('/v1/proxy/rules')
->label('audits.resource', 'rule/{response.$id}')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
name: 'createRule',
description: '/docs/references/proxy/create-rule.md',
auth: [AuthType::ADMIN],
@@ -56,25 +55,14 @@ App::post('/v1/proxy/rules')
->inject('dbForPlatform')
->inject('dbForProject')
->action(function (string $domain, string $resourceType, string $resourceId, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject) {
$mainDomain = System::getEnv('_APP_DOMAIN', '');
if ($domain === $mainDomain) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your main domain to specific resource. Please use subdomain or a different domain.');
}
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS');
$denyListDomains = System::getEnv('_APP_CUSTOM_DOMAIN_DENY_LIST');
if (!empty($denyListDomains)) {
$functionsDomain .= ',' . $denyListDomains;
}
$deniedDomains = array_map('trim', explode(',', $functionsDomain));
foreach ($deniedDomains as $deniedDomain) {
if (str_ends_with($domain, $deniedDomain)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your functions domain or its subdomain to a specific resource. Please use a different domain.');
}
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
if ($functionsDomain != '' && str_ends_with($domain, $functionsDomain)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your functions domain or it\'s subdomain to specific resource. Please use different domain.');
}
if ($domain === 'localhost' || $domain === APP_HOSTNAME_INTERNAL) {
@@ -185,7 +173,6 @@ App::get('/v1/proxy/rules')
->label('scope', 'rules.read')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
name: 'listRules',
description: '/docs/references/proxy/list-rules.md',
auth: [AuthType::ADMIN],
@@ -260,7 +247,6 @@ App::get('/v1/proxy/rules/:ruleId')
->label('scope', 'rules.read')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
name: 'getRule',
description: '/docs/references/proxy/get-rule.md',
auth: [AuthType::ADMIN],
@@ -298,7 +284,6 @@ App::delete('/v1/proxy/rules/:ruleId')
->label('audits.resource', 'rule/{request.ruleId}')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
name: 'deleteRule',
description: '/docs/references/proxy/delete-rule.md',
auth: [AuthType::ADMIN],
@@ -343,7 +328,6 @@ App::patch('/v1/proxy/rules/:ruleId/verification')
->label('audits.resource', 'rule/{response.$id}')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
name: 'updateRuleVerification',
description: '/docs/references/proxy/update-rule-verification.md',
auth: [AuthType::ADMIN],
+9 -29
View File
@@ -24,7 +24,6 @@ use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\NotFound as NotFoundException;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -63,7 +62,6 @@ App::post('/v1/storage/buckets')
->label('audits.resource', 'bucket/{response.$id}')
->label('sdk', new Method(
namespace: 'storage',
group: 'buckets',
name: 'createBucket',
description: '/docs/references/storage/create-bucket.md',
auth: [AuthType::KEY],
@@ -166,7 +164,6 @@ App::get('/v1/storage/buckets')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'buckets',
name: 'listBuckets',
description: '/docs/references/storage/list-buckets.md',
auth: [AuthType::KEY],
@@ -219,15 +216,10 @@ App::get('/v1/storage/buckets')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$buckets = $dbForProject->find('buckets', $queries);
$total = $dbForProject->count('buckets', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'buckets' => $buckets,
'total' => $total,
'buckets' => $dbForProject->find('buckets', $queries),
'total' => $dbForProject->count('buckets', $filterQueries, APP_LIMIT_COUNT),
]), Response::MODEL_BUCKET_LIST);
});
@@ -238,7 +230,6 @@ App::get('/v1/storage/buckets/:bucketId')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'buckets',
name: 'getBucket',
description: '/docs/references/storage/get-bucket.md',
auth: [AuthType::KEY],
@@ -273,7 +264,6 @@ App::put('/v1/storage/buckets/:bucketId')
->label('audits.resource', 'bucket/{response.$id}')
->label('sdk', new Method(
namespace: 'storage',
group: 'buckets',
name: 'updateBucket',
description: '/docs/references/storage/update-bucket.md',
auth: [AuthType::KEY],
@@ -344,7 +334,6 @@ App::delete('/v1/storage/buckets/:bucketId')
->label('audits.resource', 'bucket/{request.bucketId}')
->label('sdk', new Method(
namespace: 'storage',
group: 'buckets',
name: 'deleteBucket',
description: '/docs/references/storage/delete-bucket.md',
auth: [AuthType::KEY],
@@ -398,18 +387,17 @@ App::post('/v1/storage/buckets/:bucketId/files')
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'createFile',
description: '/docs/references/storage/create-file.md',
type: MethodType::UPLOAD,
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
requestType: 'multipart/form-data',
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_FILE,
)
],
type: MethodType::UPLOAD,
requestType: ContentType::MULTIPART
]
))
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new CustomId(), 'File ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
@@ -768,7 +756,6 @@ App::get('/v1/storage/buckets/:bucketId/files')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'listFiles',
description: '/docs/references/storage/list-files.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -850,8 +837,6 @@ App::get('/v1/storage/buckets/:bucketId/files')
}
} catch (NotFoundException) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
@@ -868,7 +853,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'getFile',
description: '/docs/references/storage/get-file.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -925,7 +909,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
->label('cache.resource', 'file/{request.fileId}')
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'getFilePreview',
description: '/docs/references/storage/get-file-preview.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1108,7 +1091,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'getFileDownload',
description: '/docs/references/storage/get-file-download.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1257,7 +1239,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'getFileView',
description: '/docs/references/storage/get-file-view.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1415,6 +1396,9 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
->groups(['api', 'storage'])
->label('scope', 'public')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', '*/*')
->label('sdk.methodType', 'location')
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File ID.')
->param('jwt', '', new Text(2048, 0), 'JSON Web Token to validate', true)
@@ -1575,7 +1559,6 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'updateFile',
description: '/docs/references/storage/update-file.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1690,7 +1673,6 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'deleteFile',
description: '/docs/references/storage/delete-file.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1790,7 +1772,6 @@ App::get('/v1/storage/usage')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: null,
name: 'getUsage',
description: '/docs/references/storage/get-usage.md',
auth: [AuthType::ADMIN],
@@ -1877,7 +1858,6 @@ App::get('/v1/storage/:bucketId/usage')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: null,
name: 'getBucketUsage',
description: '/docs/references/storage/get-bucket-usage.md',
auth: [AuthType::ADMIN],
+18 -52
View File
@@ -33,7 +33,6 @@ use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Authorization as AuthorizationException;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -63,7 +62,6 @@ App::post('/v1/teams')
->label('audits.resource', 'team/{response.$id}')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'create',
description: '/docs/references/teams/create-team.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -154,7 +152,6 @@ App::get('/v1/teams')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'list',
description: '/docs/references/teams/list-teams.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -207,12 +204,9 @@ App::get('/v1/teams')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$results = $dbForProject->find('teams', $queries);
$total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$results = $dbForProject->find('teams', $queries);
$total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT);
$response->dynamic(new Document([
'teams' => $results,
@@ -226,7 +220,6 @@ App::get('/v1/teams/:teamId')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'get',
description: '/docs/references/teams/get-team.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -257,7 +250,6 @@ App::get('/v1/teams/:teamId/prefs')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'getPrefs',
description: '/docs/references/teams/get-team-prefs.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -293,7 +285,6 @@ App::put('/v1/teams/:teamId')
->label('audits.resource', 'team/{response.$id}')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'updateName',
description: '/docs/references/teams/update-team-name.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -322,7 +313,9 @@ App::put('/v1/teams/:teamId')
->setAttribute('name', $name)
->setAttribute('search', implode(' ', [$teamId, $name]));
$team = $dbForProject->updateDocument('teams', $team->getId(), $team);
$team = $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $team) {
return $dbForProject->updateDocument('teams', $team->getId(), $team);
});
$queueForEvents->setParam('teamId', $team->getId());
@@ -339,7 +332,6 @@ App::put('/v1/teams/:teamId/prefs')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'updatePrefs',
description: '/docs/references/teams/update-team-prefs.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -379,7 +371,6 @@ App::delete('/v1/teams/:teamId')
->label('audits.resource', 'team/{request.teamId}')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'delete',
description: '/docs/references/teams/delete-team.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -438,7 +429,6 @@ App::post('/v1/teams/:teamId/memberships')
->label('audits.userId', '{request.userId}')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'createMembership',
description: '/docs/references/teams/create-team-membership.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -806,7 +796,6 @@ App::get('/v1/teams/:teamId/memberships')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'listMemberships',
description: '/docs/references/teams/list-team-members.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -870,20 +859,17 @@ App::get('/v1/teams/:teamId/memberships')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$memberships = $dbForProject->find(
collection: 'memberships',
queries: $queries,
);
$total = $dbForProject->count(
collection: 'memberships',
queries: $filterQueries,
max: APP_LIMIT_COUNT
);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$memberships = $dbForProject->find(
collection: 'memberships',
queries: $queries,
);
$total = $dbForProject->count(
collection: 'memberships',
queries: $filterQueries,
max: APP_LIMIT_COUNT
);
$memberships = array_filter($memberships, fn (Document $membership) => !empty($membership->getAttribute('userId')));
@@ -948,7 +934,6 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'getMembership',
description: '/docs/references/teams/get-team-member.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1035,7 +1020,6 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->label('audits.resource', 'team/{request.teamId}')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'updateMembership',
description: '/docs/references/teams/update-team-membership.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1050,6 +1034,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->param('membershipId', '', new UID(), 'Membership ID.')
->param('roles', [], function (Document $project) {
if ($project->getId() === 'console') {
;
$roles = array_keys(Config::getParam('roles', []));
array_filter($roles, function ($role) {
return !in_array($role, [Auth::USER_ROLE_APPS, Auth::USER_ROLE_GUESTS, Auth::USER_ROLE_USERS]);
@@ -1061,10 +1046,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->inject('request')
->inject('response')
->inject('user')
->inject('project')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents) {
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -1085,21 +1069,6 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
$isAppUser = Auth::isAppUser(Authorization::getRoles());
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
if ($project->getId() === 'console') {
// Quick check: fetch up to 2 owners to determine if only one exists
$ownersCount = $dbForProject->count(
collection: 'memberships',
queries: [Query::contains('roles', ['owner'])],
max: 2
);
// Prevent role change if there's only one owner left,
// the requester is that owner, and the new `$roles` no longer include 'owner'!
if ($ownersCount === 1 && $isOwner && !\in_array('owner', $roles)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'There must be at least one owner in the organization.');
}
}
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to modify roles');
}
@@ -1139,7 +1108,6 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->label('audits.userId', '{request.userId}')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'updateMembershipStatus',
description: '/docs/references/teams/update-team-membership-status.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -1297,7 +1265,6 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
->label('audits.resource', 'team/{request.teamId}')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'deleteMembership',
description: '/docs/references/teams/delete-team-membership.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1368,7 +1335,6 @@ App::get('/v1/teams/:teamId/logs')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'logs',
name: 'listLogs',
description: '/docs/references/teams/get-team-logs.md',
auth: [AuthType::ADMIN],
+10 -73
View File
@@ -9,8 +9,6 @@ use Appwrite\Auth\Validator\PasswordDictionary;
use Appwrite\Auth\Validator\PasswordHistory;
use Appwrite\Auth\Validator\PersonalData;
use Appwrite\Auth\Validator\Phone;
use Appwrite\Deletes\Identities as DeleteIdentities;
use Appwrite\Deletes\Targets as DeleteTargets;
use Appwrite\Detector\Detector;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
@@ -35,7 +33,6 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -194,7 +191,6 @@ App::post('/v1/users')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'create',
description: '/docs/references/users/create-user.md',
auth: [AuthType::KEY],
@@ -229,7 +225,6 @@ App::post('/v1/users/bcrypt')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createBcryptUser',
description: '/docs/references/users/create-bcrypt-user.md',
auth: [AuthType::KEY],
@@ -264,7 +259,6 @@ App::post('/v1/users/md5')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createMD5User',
description: '/docs/references/users/create-md5-user.md',
auth: [AuthType::KEY],
@@ -299,7 +293,6 @@ App::post('/v1/users/argon2')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createArgon2User',
description: '/docs/references/users/create-argon2-user.md',
auth: [AuthType::KEY],
@@ -334,7 +327,6 @@ App::post('/v1/users/sha')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createSHAUser',
description: '/docs/references/users/create-sha-user.md',
auth: [AuthType::KEY],
@@ -376,7 +368,6 @@ App::post('/v1/users/phpass')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createPHPassUser',
description: '/docs/references/users/create-phpass-user.md',
auth: [AuthType::KEY],
@@ -411,7 +402,6 @@ App::post('/v1/users/scrypt')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createScryptUser',
description: '/docs/references/users/create-scrypt-user.md',
auth: [AuthType::KEY],
@@ -459,7 +449,6 @@ App::post('/v1/users/scrypt-modified')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createScryptModifiedUser',
description: '/docs/references/users/create-scrypt-modified-user.md',
auth: [AuthType::KEY],
@@ -498,7 +487,6 @@ App::post('/v1/users/:userId/targets')
->label('scope', 'targets.write')
->label('sdk', new Method(
namespace: 'users',
group: 'targets',
name: 'createTarget',
description: '/docs/references/users/create-target.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -590,7 +578,6 @@ App::get('/v1/users')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'list',
description: '/docs/references/users/list-users.md',
auth: [AuthType::KEY],
@@ -643,15 +630,10 @@ App::get('/v1/users')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$users = $dbForProject->find('users', $queries);
$total = $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'users' => $users,
'total' => $total,
'users' => $dbForProject->find('users', $queries),
'total' => $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT),
]), Response::MODEL_USER_LIST);
});
@@ -661,7 +643,6 @@ App::get('/v1/users/:userId')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'get',
description: '/docs/references/users/get-user.md',
auth: [AuthType::KEY],
@@ -692,7 +673,6 @@ App::get('/v1/users/:userId/prefs')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'getPrefs',
description: '/docs/references/users/get-user-prefs.md',
auth: [AuthType::KEY],
@@ -725,7 +705,6 @@ App::get('/v1/users/:userId/targets/:targetId')
->label('scope', 'targets.read')
->label('sdk', new Method(
namespace: 'users',
group: 'targets',
name: 'getTarget',
description: '/docs/references/users/get-user-target.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -763,7 +742,6 @@ App::get('/v1/users/:userId/sessions')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'listSessions',
description: '/docs/references/users/list-user-sessions.md',
auth: [AuthType::KEY],
@@ -810,7 +788,6 @@ App::get('/v1/users/:userId/memberships')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'memberships',
name: 'listMemberships',
description: '/docs/references/users/list-user-memberships.md',
auth: [AuthType::KEY],
@@ -855,7 +832,6 @@ App::get('/v1/users/:userId/logs')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'logs',
name: 'listLogs',
description: '/docs/references/users/list-user-logs.md',
auth: [AuthType::KEY],
@@ -952,7 +928,6 @@ App::get('/v1/users/:userId/targets')
->label('scope', 'targets.read')
->label('sdk', new Method(
namespace: 'users',
group: 'targets',
name: 'listTargets',
description: '/docs/references/users/list-user-targets.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -1005,15 +980,10 @@ App::get('/v1/users/:userId/targets')
$cursor->setValue($cursorDocument);
}
try {
$targets = $dbForProject->find('targets', $queries);
$total = $dbForProject->count('targets', $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'targets' => $targets,
'total' => $total,
'targets' => $dbForProject->find('targets', $queries),
'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_TARGET_LIST);
});
@@ -1023,7 +993,6 @@ App::get('/v1/users/identities')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'identities',
name: 'listIdentities',
description: '/docs/references/users/list-identities.md',
auth: [AuthType::KEY],
@@ -1076,15 +1045,10 @@ App::get('/v1/users/identities')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$identities = $dbForProject->find('identities', $queries);
$total = $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'identities' => $identities,
'total' => $total,
'identities' => $dbForProject->find('identities', $queries),
'total' => $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT),
]), Response::MODEL_IDENTITY_LIST);
});
@@ -1098,7 +1062,6 @@ App::patch('/v1/users/:userId/status')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateStatus',
description: '/docs/references/users/update-user-status.md',
auth: [AuthType::KEY],
@@ -1139,7 +1102,6 @@ App::put('/v1/users/:userId/labels')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateLabels',
description: '/docs/references/users/update-user-labels.md',
auth: [AuthType::KEY],
@@ -1182,7 +1144,6 @@ App::patch('/v1/users/:userId/verification/phone')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updatePhoneVerification',
description: '/docs/references/users/update-user-phone-verification.md',
auth: [AuthType::KEY],
@@ -1224,7 +1185,6 @@ App::patch('/v1/users/:userId/name')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateName',
description: '/docs/references/users/update-user-name.md',
auth: [AuthType::KEY],
@@ -1267,7 +1227,6 @@ App::patch('/v1/users/:userId/password')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updatePassword',
description: '/docs/references/users/update-user-password.md',
auth: [AuthType::KEY],
@@ -1350,7 +1309,6 @@ App::patch('/v1/users/:userId/email')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateEmail',
description: '/docs/references/users/update-user-email.md',
auth: [AuthType::KEY],
@@ -1450,7 +1408,6 @@ App::patch('/v1/users/:userId/phone')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updatePhone',
description: '/docs/references/users/update-user-phone.md',
auth: [AuthType::KEY],
@@ -1540,7 +1497,6 @@ App::patch('/v1/users/:userId/verification')
->label('audits.userId', '{request.userId}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateEmailVerification',
description: '/docs/references/users/update-user-email-verification.md',
auth: [AuthType::KEY],
@@ -1578,7 +1534,6 @@ App::patch('/v1/users/:userId/prefs')
->label('scope', 'users.write')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updatePrefs',
description: '/docs/references/users/update-user-prefs.md',
auth: [AuthType::KEY],
@@ -1619,7 +1574,6 @@ App::patch('/v1/users/:userId/targets/:targetId')
->label('scope', 'targets.write')
->label('sdk', new Method(
namespace: 'users',
group: 'targets',
name: 'updateTarget',
description: '/docs/references/users/update-target.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -1724,7 +1678,6 @@ App::patch('/v1/users/:userId/mfa')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateMfa',
description: '/docs/references/users/update-user-mfa.md',
auth: [AuthType::KEY],
@@ -1764,7 +1717,6 @@ App::get('/v1/users/:userId/mfa/factors')
->label('usage.metric', 'users.{scope}.requests.read')
->label('sdk', new Method(
namespace: 'users',
group: 'mfa',
name: 'listMfaFactors',
description: '/docs/references/users/list-mfa-factors.md',
auth: [AuthType::KEY],
@@ -1803,7 +1755,6 @@ App::get('/v1/users/:userId/mfa/recovery-codes')
->label('usage.metric', 'users.{scope}.requests.read')
->label('sdk', new Method(
namespace: 'users',
group: 'mfa',
name: 'getMfaRecoveryCodes',
description: '/docs/references/users/get-mfa-recovery-codes.md',
auth: [AuthType::KEY],
@@ -1848,7 +1799,6 @@ App::patch('/v1/users/:userId/mfa/recovery-codes')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk', new Method(
namespace: 'users',
group: 'mfa',
name: 'createMfaRecoveryCodes',
description: '/docs/references/users/create-mfa-recovery-codes.md',
auth: [AuthType::KEY],
@@ -1890,7 +1840,7 @@ App::patch('/v1/users/:userId/mfa/recovery-codes')
});
App::put('/v1/users/:userId/mfa/recovery-codes')
->desc('Update MFA recovery codes (regenerate)')
->desc('Regenerate MFA recovery codes')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.mfa.recovery-codes')
->label('scope', 'users.write')
@@ -1900,7 +1850,6 @@ App::put('/v1/users/:userId/mfa/recovery-codes')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk', new Method(
namespace: 'users',
group: 'mfa',
name: 'updateMfaRecoveryCodes',
description: '/docs/references/users/update-mfa-recovery-codes.md',
auth: [AuthType::KEY],
@@ -1951,7 +1900,6 @@ App::delete('/v1/users/:userId/mfa/authenticators/:type')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk', new Method(
namespace: 'users',
group: 'mfa',
name: 'deleteMfaAuthenticator',
description: '/docs/references/users/delete-mfa-authenticator.md',
auth: [AuthType::KEY],
@@ -1999,7 +1947,6 @@ App::post('/v1/users/:userId/sessions')
->label('usage.metric', 'sessions.{scope}.requests.create')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'createSession',
description: '/docs/references/users/create-session.md',
auth: [AuthType::KEY],
@@ -2084,7 +2031,6 @@ App::post('/v1/users/:userId/tokens')
->label('audits.resource', 'user/{request.userId}')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'createToken',
description: '/docs/references/users/create-token.md',
auth: [AuthType::KEY],
@@ -2147,7 +2093,6 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
->label('audits.resource', 'user/{request.userId}')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'deleteSession',
description: '/docs/references/users/delete-user-session.md',
auth: [AuthType::KEY],
@@ -2198,7 +2143,6 @@ App::delete('/v1/users/:userId/sessions')
->label('audits.resource', 'user/{user.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'deleteSessions',
description: '/docs/references/users/delete-user-sessions.md',
auth: [AuthType::KEY],
@@ -2248,7 +2192,6 @@ App::delete('/v1/users/:userId')
->label('audits.resource', 'user/{request.userId}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'delete',
description: '/docs/references/users/delete.md',
auth: [AuthType::KEY],
@@ -2277,8 +2220,6 @@ App::delete('/v1/users/:userId')
$clone = clone $user;
$dbForProject->deleteDocument('users', $userId);
DeleteIdentities::delete($dbForProject, Query::equal('userInternalId', [$user->getInternalId()]));
DeleteTargets::delete($dbForProject, Query::equal('userInternalId', [$user->getInternalId()]));
$queueForDeletes
->setType(DELETE_TYPE_DOCUMENT)
@@ -2300,7 +2241,6 @@ App::delete('/v1/users/:userId/targets/:targetId')
->label('scope', 'targets.write')
->label('sdk', new Method(
namespace: 'users',
group: 'targets',
name: 'deleteTarget',
description: '/docs/references/users/delete-target.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -2358,7 +2298,6 @@ App::delete('/v1/users/identities/:identityId')
->label('audits.resource', 'identity/{request.$identityId}')
->label('sdk', new Method(
namespace: 'users',
group: 'identities',
name: 'deleteIdentity',
description: '/docs/references/users/delete-identity.md',
auth: [AuthType::KEY],
@@ -2398,7 +2337,6 @@ App::post('/v1/users/:userId/jwts')
->label('scope', 'users.write')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'createJWT',
description: '/docs/references/users/create-user-jwt.md',
auth: [AuthType::KEY],
@@ -2454,7 +2392,6 @@ App::get('/v1/users/usage')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: null,
name: 'getUsage',
description: '/docs/references/users/get-usage.md',
auth: [AuthType::ADMIN],
+7 -22
View File
@@ -19,7 +19,6 @@ use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -270,13 +269,12 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
};
App::get('/v1/vcs/github/authorize')
->desc('Create GitHub app installation')
->desc('Install GitHub app')
->groups(['api', 'vcs'])
->label('scope', 'vcs.read')
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('sdk', new Method(
namespace: 'vcs',
group: 'installations',
name: 'createGitHubInstallation',
description: '/docs/references/vcs/create-github-installation.md',
auth: [AuthType::ADMIN],
@@ -320,7 +318,7 @@ App::get('/v1/vcs/github/authorize')
});
App::get('/v1/vcs/github/callback')
->desc('Get installation and authorization from GitHub app')
->desc('Capture installation and authorization from GitHub app')
->groups(['api', 'vcs'])
->label('scope', 'public')
->label('error', __DIR__ . '/../../views/general/error.phtml')
@@ -458,7 +456,6 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'getRepositoryContents',
description: '/docs/references/vcs/get-repository-contents.md',
auth: [AuthType::ADMIN],
@@ -520,12 +517,11 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
});
App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection')
->desc('Create runtime settings detection')
->desc('Detect runtime settings from source code')
->groups(['api', 'vcs'])
->label('scope', 'vcs.write')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'createRepositoryDetection',
description: '/docs/references/vcs/create-repository-detection.md',
auth: [AuthType::ADMIN],
@@ -603,7 +599,6 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'listRepositories',
description: '/docs/references/vcs/list-repositories.md',
auth: [AuthType::ADMIN],
@@ -704,7 +699,6 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories')
->label('scope', 'vcs.write')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'createRepository',
description: '/docs/references/vcs/create-repository.md',
auth: [AuthType::ADMIN],
@@ -817,7 +811,6 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'getRepository',
description: '/docs/references/vcs/get-repository.md',
auth: [AuthType::ADMIN],
@@ -872,7 +865,6 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'listRepositoryBranches',
description: '/docs/references/vcs/list-repository-branches.md',
auth: [AuthType::ADMIN],
@@ -1066,7 +1058,6 @@ App::get('/v1/vcs/installations')
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'installations',
name: 'listInstallations',
description: '/docs/references/vcs/list-installations.md',
auth: [AuthType::ADMIN],
@@ -1122,12 +1113,9 @@ App::get('/v1/vcs/installations')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$results = $dbForPlatform->find('installations', $queries);
$total = $dbForPlatform->count('installations', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$results = $dbForPlatform->find('installations', $queries);
$total = $dbForPlatform->count('installations', $filterQueries, APP_LIMIT_COUNT);
$response->dynamic(new Document([
'installations' => $results,
@@ -1141,7 +1129,6 @@ App::get('/v1/vcs/installations/:installationId')
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'installations',
name: 'getInstallation',
description: '/docs/references/vcs/get-installation.md',
auth: [AuthType::ADMIN],
@@ -1176,7 +1163,6 @@ App::delete('/v1/vcs/installations/:installationId')
->label('scope', 'vcs.write')
->label('sdk', new Method(
namespace: 'vcs',
group: 'installations',
name: 'deleteInstallation',
description: '/docs/references/vcs/delete-installation.md',
auth: [AuthType::ADMIN],
@@ -1212,12 +1198,11 @@ App::delete('/v1/vcs/installations/:installationId')
});
App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositoryId')
->desc('Update external deployment (authorize)')
->desc('Authorize external deployment')
->groups(['api', 'vcs'])
->label('scope', 'vcs.write')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'updateExternalDeployments',
description: '/docs/references/vcs/update-external-deployments.md',
auth: [AuthType::ADMIN],
+15 -27
View File
@@ -50,7 +50,7 @@ Config::setParam('domainVerification', false);
Config::setParam('cookieDomain', 'localhost');
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname)
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname)
{
$utopia->getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml');
@@ -72,19 +72,11 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
}
if ($route->isEmpty()) {
$appDomainFunctionsFallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', '');
$appDomainFunctions = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
if (!empty($appDomainFunctionsFallback) && \str_ends_with($host, $appDomainFunctionsFallback)) {
$appDomainFunctions = $appDomainFunctionsFallback;
}
if ($host === $appDomainFunctions) {
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.');
}
if (\str_ends_with($host, $appDomainFunctions)) {
if (\str_ends_with($host, System::getEnv('_APP_DOMAIN_FUNCTIONS', ''))) {
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain is not connected to any Appwrite resource yet. Please configure custom domain or function domain to allow this request.');
}
@@ -133,7 +125,6 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
if (empty($method)) {
$utopia->getRoute()?->label('sdk', new Method(
namespace: 'functions',
group: 'executions',
name: 'createExecution',
description: '/docs/references/functions/create-execution.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -144,6 +135,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
)
],
contentType: ContentType::MULTIPART,
requestType: 'application/json',
));
} else {
/** @var Method $method */
@@ -347,6 +339,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
]);
/** Execute function */
$executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST'));
try {
$version = $function->getAttribute('version', 'v2');
$command = $runtime['startCommand'];
@@ -510,10 +503,9 @@ App::init()
->inject('queueForEvents')
->inject('queueForCertificates')
->inject('queueForFunctions')
->inject('executor')
->inject('isResourceBlocked')
->inject('previewHostname')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, Executor $executor, callable $isResourceBlocked, string $previewHostname) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, callable $isResourceBlocked, string $previewHostname) {
/*
* Appwrite Router
*/
@@ -521,7 +513,7 @@ App::init()
$mainDomain = System::getEnv('_APP_DOMAIN', '');
// Only run Router when external domain
if ($host !== $mainDomain || !empty($previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) {
return;
}
}
@@ -750,12 +742,11 @@ App::options()
->inject('queueForEvents')
->inject('queueForStatsUsage')
->inject('queueForFunctions')
->inject('executor')
->inject('geodb')
->inject('isResourceBlocked')
->inject('previewHostname')
->inject('project')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, Document $project) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname, Document $project) {
/*
* Appwrite Router
*/
@@ -763,7 +754,7 @@ App::options()
$mainDomain = System::getEnv('_APP_DOMAIN', '');
// Only run Router when external domain
if ($host !== $mainDomain || !empty($previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) {
return;
}
}
@@ -880,18 +871,17 @@ App::error()
if (!empty($providerConfig) && $error->getCode() >= 400 && $error->getCode() < 500) {
// Register error logger
try {
$loggingProvider = new DSN($providerConfig);
$loggingProvider = new DSN($providerConfig ?? '');
$providerName = $loggingProvider->getScheme();
if (!empty($providerName) && $providerName === 'sentry') {
$key = $loggingProvider->getPassword();
$projectId = $loggingProvider->getUser() ?? '';
$host = 'https://' . $loggingProvider->getHost();
$sampleRate = $loggingProvider->getParam('sample', 0.01);
$adapter = new Sentry($projectId, $key, $host);
$logger = new Logger($adapter);
$logger->setSample($sampleRate);
$logger->setSample(0.01);
$publish = true;
} else {
throw new \Exception('Invalid experimental logging provider');
@@ -1071,11 +1061,10 @@ App::get('/robots.txt')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->inject('queueForFunctions')
->inject('executor')
->inject('geodb')
->inject('isResourceBlocked')
->inject('previewHostname')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname) {
$host = $request->getHostname() ?? '';
$mainDomain = System::getEnv('_APP_DOMAIN', '');
@@ -1083,7 +1072,7 @@ App::get('/robots.txt')
$template = new View(__DIR__ . '/../views/general/robots.phtml');
$response->text($template->render(false));
} else {
router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname);
router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname);
}
});
@@ -1100,11 +1089,10 @@ App::get('/humans.txt')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->inject('queueForFunctions')
->inject('executor')
->inject('geodb')
->inject('isResourceBlocked')
->inject('previewHostname')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname) {
$host = $request->getHostname() ?? '';
$mainDomain = System::getEnv('_APP_DOMAIN', '');
@@ -1112,7 +1100,7 @@ App::get('/humans.txt')
$template = new View(__DIR__ . '/../views/general/humans.phtml');
$response->text($template->render(false));
} else {
router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname);
router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname);
}
});
+30 -56
View File
@@ -92,20 +92,8 @@ $eventDatabaseListener = function (Document $project, Document $document, Respon
$usageDatabaseListener = function (string $event, Document $document, StatsUsage $queueForStatsUsage) {
$value = 1;
switch ($event) {
case Database::EVENT_DOCUMENT_DELETE:
$value = -1;
break;
case Database::EVENT_DOCUMENTS_DELETE:
$value = -1 * $document->getAttribute('modified', 0);
break;
case Database::EVENT_DOCUMENTS_CREATE:
$value = $document->getAttribute('modified', 0);
break;
case Database::EVENT_DOCUMENTS_UPSERT:
$value = $document->getAttribute('created', 0);
break;
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$value = -1;
}
switch (true) {
@@ -250,36 +238,34 @@ App::init()
subject: 'keys'
);
if (!$dbKey) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($dbKey) {
$accessedAt = $dbKey->getAttribute('accessedAt', '');
$accessedAt = $dbKey->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
$dbKey->setAttribute('accessedAt', DateTime::now());
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
$sdkValidator = new WhiteList($servers, true);
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
if ($sdkValidator->isValid($sdk)) {
$sdks = $dbKey->getAttribute('sdks', []);
if (!in_array($sdk, $sdks)) {
$sdks[] = $sdk;
$dbKey->setAttribute('sdks', $sdks);
/** Update access time as well */
$dbKey->setAttribute('accessedAt', Datetime::now());
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
$dbKey->setAttribute('accessedAt', DateTime::now());
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
}
$queueForAudits->setUser($user);
$sdkValidator = new WhiteList($servers, true);
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
if ($sdkValidator->isValid($sdk)) {
$sdks = $dbKey->getAttribute('sdks', []);
if (!in_array($sdk, $sdks)) {
$sdks[] = $sdk;
$dbKey->setAttribute('sdks', $sdks);
/** Update access time as well */
$dbKey->setAttribute('accessedAt', Datetime::now());
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
}
$queueForAudits->setUser($user);
}
}
} // Admin User Authentication
elseif (($project->getId() === 'console' && !$team->isEmpty() && !$user->isEmpty()) || ($project->getId() !== 'console' && !$user->isEmpty() && $mode === APP_MODE_ADMIN)) {
@@ -340,8 +326,6 @@ App::init()
*/
$method = $route->getLabel('sdk', false);
// Take the first method if there's more than one,
// namespace can not differ between methods on the same route
if (\is_array($method)) {
$method = $method[0];
}
@@ -406,8 +390,7 @@ App::init()
->inject('timelimit')
->inject('mode')
->inject('apiKey')
->inject('plan')
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, string $mode, ?Key $apiKey, array $plan) use ($usageDatabaseListener, $eventDatabaseListener) {
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, string $mode, ?Key $apiKey) use ($usageDatabaseListener, $eventDatabaseListener) {
$route = $utopia->getRoute();
@@ -525,9 +508,6 @@ App::init()
$dbForProject
->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_DOCUMENTS_CREATE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_DOCUMENTS_DELETE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_DOCUMENTS_UPSERT, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_DOCUMENT_CREATE, 'create-trigger-events', fn ($event, $document) => $eventDatabaseListener(
$project,
$document,
@@ -540,10 +520,6 @@ App::init()
$useCache = $route->getLabel('cache', false);
if ($useCache) {
$route = $utopia->match($request);
$isImageTransformation = $route->getPath() === '/v1/storage/buckets/:bucketId/files/:fileId/preview';
$isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && !Auth::isPrivilegedUser(Authorization::getRoles());
$key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$cache = new Cache(
@@ -553,10 +529,10 @@ App::init()
$data = $cache->load($key, $timestamp);
if (!empty($data) && !$cacheLog->isEmpty()) {
$parts = explode('/', $cacheLog->getAttribute('resourceType', ''));
$parts = explode('/', $cacheLog->getAttribute('resourceType'));
$type = $parts[0] ?? null;
if ($type === 'bucket' && (!$isImageTransformation || !$isDisabled)) {
if ($type === 'bucket') {
$bucketId = $parts[1] ?? null;
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
@@ -597,10 +573,8 @@ App::init()
$response
->addHeader('Cache-Control', sprintf('private, max-age=%d', $timestamp))
->addHeader('X-Appwrite-Cache', 'hit')
->setContentType($cacheLog->getAttribute('mimeType'));
if (!$isImageTransformation || !$isDisabled) {
$response->send($data);
}
->setContentType($cacheLog->getAttribute('mimeType'))
->send($data);
} else {
$response
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
+84 -90
View File
@@ -43,6 +43,29 @@ $http = new Server(
$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
$totalWorkers = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intval(System::getEnv('_APP_WORKER_PER_CORE', 6));
$http
->set([
'worker_num' => $totalWorkers,
'dispatch_func' => 'dispatch',
'open_http2_protocol' => true,
'http_compression' => false,
'package_max_length' => $payloadSize,
'buffer_output_size' => $payloadSize,
'task_worker_num' => 1, // required for the task to fetch domains background
]);
$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) {
Console::success('Worker ' . ++$workerId . ' started successfully');
});
$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...');
});
/**
* Assigns HTTP requests to worker threads by analyzing its payload/content.
*
@@ -60,105 +83,76 @@ $totalWorkers = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intva
*/
function dispatch(Server $server, int $fd, int $type, $data = null): int
{
$resolveWorkerId = function (Server $server, $data = null) {
global $totalWorkers, $domains;
global $totalWorkers, $domains;
// If data is not set we can send request to any worker
// first we try to pick idle worker, if not we randomly pick a worker
if ($data === null) {
for ($i = 0; $i < $totalWorkers; $i++) {
if ($server->getWorkerStatus($i) === SWOOLE_WORKER_IDLE) {
return $i;
}
}
return rand(0, $totalWorkers - 1);
}
$riskyWorkersPercent = intval(System::getEnv('_APP_RISKY_WORKERS_PERCENT', 80)) / 100; // Decimal form 0 to 1
// Each worker has numeric ID, starting from 0 and incrementing
// From 0 to riskyWorkers, we consider safe workers
// From riskyWorkers to totalWorkers, we consider risky workers
$riskyWorkers = (int)floor($totalWorkers * $riskyWorkersPercent); // Absolute amount of risky workers
$domain = '';
// max up to 3 as first line has request details and second line has host
$lines = explode("\n", $data, 3);
$request = $lines[0];
if (count($lines) > 1) {
$domain = trim(explode('Host: ', $lines[1])[1]);
}
// Sync executions are considered risky
$risky = false;
if (str_starts_with($request, 'POST') && str_contains($request, '/executions')) {
$risky = true;
} elseif (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS'))) {
$risky = true;
} elseif ($domains->get(md5($domain), 'value') === 1) {
// executions request coming from custom domain
$risky = true;
}
if ($risky) {
// If risky request, only consider risky workers
for ($j = $riskyWorkers; $j < $totalWorkers; $j++) {
/** Reference https://openswoole.com/docs/modules/swoole-server-getWorkerStatus#description */
if ($server->getWorkerStatus($j) === SWOOLE_WORKER_IDLE) {
// If idle worker found, give to him
return $j;
}
}
// If no idle workers, give to random risky worker
$worker = rand($riskyWorkers, $totalWorkers - 1);
Console::warning("swoole_dispatch: Risky branch: did not find a idle worker, picking random worker {$worker}");
return $worker;
}
// If safe request, give to any idle worker
// Its fine to pick risky worker here, because it's idle. Idle is never actually risky
// If data is not set we can send request to any worker
// first we try to pick idle worker, if not we randomly pick a worker
if ($data === null) {
for ($i = 0; $i < $totalWorkers; $i++) {
if ($server->getWorkerStatus($i) === SWOOLE_WORKER_IDLE) {
return $i;
}
}
return rand(0, $totalWorkers - 1);
}
// If no idle worker found, give to random safe worker
// We avoid risky workers here, as it could be in work - not idle. Thats exactly when they are risky.
$worker = rand(0, $riskyWorkers - 1);
Console::warning("swoole_dispatch: Non-risky branch: did not find a idle worker, picking random worker {$worker}");
$riskyWorkersPercent = intval(System::getEnv('_APP_RISKY_WORKERS_PERCENT', 80)) / 100; // Decimal form 0 to 1
// Each worker has numeric ID, starting from 0 and incrementing
// From 0 to riskyWorkers, we consider safe workers
// From riskyWorkers to totalWorkers, we consider risky workers
$riskyWorkers = (int) floor($totalWorkers * $riskyWorkersPercent); // Absolute amount of risky workers
$domain = '';
// max up to 3 as first line has request details and second line has host
$lines = explode("\n", $data, 3);
$request = $lines[0];
if (count($lines) > 1) {
$domain = trim(explode('Host: ', $lines[1])[1]);
}
// Sync executions are considered risky
$risky = false;
if (str_starts_with($request, 'POST') && str_contains($request, '/executions')) {
$risky = true;
} elseif (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS'))) {
$risky = true;
} elseif ($domains->get(md5($domain), 'value') === 1) {
// executions request coming from custom domain
$risky = true;
}
if ($risky) {
// If risky request, only consider risky workers
for ($j = $riskyWorkers; $j < $totalWorkers; $j++) {
/** Reference https://openswoole.com/docs/modules/swoole-server-getWorkerStatus#description */
if ($server->getWorkerStatus($j) === SWOOLE_WORKER_IDLE) {
// If idle worker found, give to him
return $j;
}
}
// If no idle workers, give to random risky worker
$worker = rand($riskyWorkers, $totalWorkers - 1);
Console::warning("swoole_dispatch: Risky branch: did not find a idle worker, picking random worker {$worker}");
return $worker;
};
$workerId = $resolveWorkerId($server, $data);
$server->bind($fd, $workerId);
return $workerId;
}
// If safe request, give to any idle worker
// Its fine to pick risky worker here, because it's idle. Idle is never actually risky
for ($i = 0; $i < $totalWorkers; $i++) {
if ($server->getWorkerStatus($i) === SWOOLE_WORKER_IDLE) {
return $i;
}
}
// If no idle worker found, give to random safe worker
// We avoid risky workers here, as it could be in work - not idle. Thats exactly when they are risky.
$worker = rand(0, $riskyWorkers - 1);
Console::warning("swoole_dispatch: Non-risky branch: did not find a idle worker, picking random worker {$worker}");
return $worker;
}
$http
->set([
Constant::OPTION_WORKER_NUM => $totalWorkers,
Constant::OPTION_DISPATCH_FUNC => dispatch(...),
Constant::OPTION_DISPATCH_MODE => SWOOLE_DISPATCH_UIDMOD,
Constant::OPTION_HTTP_COMPRESSION => false,
Constant::OPTION_PACKAGE_MAX_LENGTH => $payloadSize,
Constant::OPTION_OUTPUT_BUFFER_SIZE => $payloadSize,
Constant::OPTION_TASK_WORKER_NUM => 1, // required for the task to fetch domains background
]);
$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) {
Console::success('Worker ' . ++$workerId . ' started successfully');
});
$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';
function createDatabase(App $app, string $resourceKey, string $dbName, array $collections, mixed $pools, callable $extraSetup = null): void
@@ -496,7 +490,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
});
// Fetch domains every `DOMAIN_SYNC_TIMER` seconds and update in the memory
$http->on(Constant::EVENT_TASK, function () use ($register, $domains) {
$http->on('Task', function () use ($register, $domains) {
$lastSyncUpdate = null;
$pools = $register->get('pools');
App::setResource('pools', fn () => $pools);
-1
View File
@@ -26,7 +26,6 @@ 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_LIMIT_DATABASE_BATCH = 100; // Default maximum batch size for database operations
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
+5 -6
View File
@@ -15,7 +15,6 @@ use Utopia\Config\Config;
use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Adapter\MySQL;
use Utopia\Database\Adapter\SQL;
use Utopia\Database\PDO;
use Utopia\Domains\Validator\PublicDomain;
use Utopia\DSN\DSN;
use Utopia\Logger\Adapter\AppSignal;
@@ -217,11 +216,11 @@ $register->set('pools', function () {
'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array(
\PDO::ATTR_TIMEOUT => 3, // Seconds
\PDO::ATTR_PERSISTENT => false,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => true,
\PDO::ATTR_STRINGIFY_FETCHES => true
PDO::ATTR_TIMEOUT => 3, // Seconds
PDO::ATTR_PERSISTENT => false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => true,
PDO::ATTR_STRINGIFY_FETCHES => true
));
});
},
+7 -28
View File
@@ -21,7 +21,6 @@ use Appwrite\Extend\Exception;
use Appwrite\GraphQL\Schema;
use Appwrite\Network\Validator\Origin;
use Appwrite\Utopia\Request;
use Executor\Executor;
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
use Utopia\App;
use Utopia\Cache\Adapter\Sharding;
@@ -39,7 +38,6 @@ use Utopia\Logger\Log;
use Utopia\Pools\Group;
use Utopia\Queue\Publisher;
use Utopia\Storage\Device;
use Utopia\Storage\Device\AWS;
use Utopia\Storage\Device\Backblaze;
use Utopia\Storage\Device\DOSpaces;
use Utopia\Storage\Device\Linode;
@@ -48,8 +46,6 @@ use Utopia\Storage\Device\S3;
use Utopia\Storage\Device\Wasabi;
use Utopia\Storage\Storage;
use Utopia\System\System;
use Utopia\Telemetry\Adapter as Telemetry;
use Utopia\Telemetry\Adapter\None as NoTelemetry;
use Utopia\Validator\Hostname;
use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub;
@@ -466,9 +462,7 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
};
}, ['pools', 'cache']);
App::setResource('telemetry', fn () => new NoTelemetry());
App::setResource('cache', function (Group $pools, Telemetry $telemetry) {
App::setResource('cache', function (Group $pools) {
$list = Config::getParam('pools-cache', []);
$adapters = [];
@@ -476,15 +470,12 @@ App::setResource('cache', function (Group $pools, Telemetry $telemetry) {
$adapters[] = $pools
->get($value)
->pop()
->getResource();
->getResource()
;
}
$cache = new Cache(new Sharding($adapters));
$cache->setTelemetry($telemetry);
return $cache;
}, ['pools', 'telemetry']);
return new Cache(new Sharding($adapters));
}, ['pools']);
App::setResource('redis', function () {
$host = System::getEnv('_APP_REDIS_HOST', 'localhost');
@@ -549,12 +540,7 @@ function getDevice(string $root, string $connection = ''): Device
switch ($device) {
case Storage::DEVICE_S3:
if (!empty($url)) {
return new S3($root, $accessKey, $accessSecret, $url, $region, $acl);
} else {
return new AWS($root, $accessKey, $accessSecret, $bucket, $region, $acl);
}
// no break
return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl, $url);
case STORAGE::DEVICE_DO_SPACES:
$device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl);
$device->setHttpVersion(S3::HTTP_VERSION_1_1);
@@ -581,12 +567,7 @@ function getDevice(string $root, string $connection = ''): Device
$s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', '');
$s3Acl = 'private';
$s3EndpointUrl = System::getEnv('_APP_STORAGE_S3_ENDPOINT', '');
if (!empty($s3EndpointUrl)) {
return new S3($root, $s3AccessKey, $s3SecretKey, $s3EndpointUrl, $s3Region, $s3Acl);
} else {
return new AWS($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
}
// no break
return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl, $s3EndpointUrl);
case Storage::DEVICE_DO_SPACES:
$doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', '');
$doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', '');
@@ -841,5 +822,3 @@ App::setResource('apiKey', function (Request $request, Document $project): ?Key
return Key::decode($project, $key);
}, ['request', 'project']);
App::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
+29 -2
View File
@@ -144,7 +144,6 @@ $image = $this->getParam('image', '');
- _APP_LOGGING_CONFIG
- _APP_MAINTENANCE_INTERVAL
- _APP_MAINTENANCE_DELAY
- _APP_MAINTENANCE_START_TIME
- _APP_MAINTENANCE_RETENTION_EXECUTION
- _APP_MAINTENANCE_RETENTION_CACHE
- _APP_MAINTENANCE_RETENTION_ABUSE
@@ -169,7 +168,7 @@ $image = $this->getParam('image', '');
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: <?php echo $organization; ?>/console:5.2.58
image: <?php echo $organization; ?>/console:5.2.53
restart: unless-stopped
networks:
- appwrite
@@ -745,6 +744,34 @@ $image = $this->getParam('image', '');
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
appwrite-worker-stats-usage-dump:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-stats-usage-dump
<<: *x-logging
container_name: appwrite-worker-stats-usage-dump
restart: unless-stopped
networks:
- appwrite
depends_on:
- redis
- mariadb
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
appwrite-task-scheduler-functions:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: schedule-functions
+5 -3
View File
@@ -15,9 +15,9 @@ use Appwrite\Event\Messaging;
use Appwrite\Event\Migration;
use Appwrite\Event\Realtime;
use Appwrite\Event\StatsUsage;
use Appwrite\Event\StatsUsageDump;
use Appwrite\Event\Webhook;
use Appwrite\Platform\Appwrite;
use Executor\Executor;
use Swoole\Runtime;
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
use Utopia\Cache\Adapter\Sharding;
@@ -278,6 +278,10 @@ Server::setResource('queueForStatsUsage', function (Publisher $publisher) {
return new StatsUsage($publisher);
}, ['publisher']);
Server::setResource('queueForStatsUsageDump', function (Publisher $publisher) {
return new StatsUsageDump($publisher);
}, ['publisher']);
Server::setResource('queueForDatabase', function (Publisher $publisher) {
return new EventDatabase($publisher);
}, ['publisher']);
@@ -409,8 +413,6 @@ Server::setResource('logError', function (Registry $register, Document $project)
};
}, ['register', 'project']);
Server::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
$pools = $register->get('pools');
$platform = new Appwrite();
$args = $platform->getEnv('argv');
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
php /usr/src/code/app/worker.php stats-usage-dump $@
+8 -8
View File
@@ -30,7 +30,7 @@
}
},
"require": {
"php": ">=8.3.0",
"php": ">=8.0.0",
"ext-curl": "*",
"ext-imagick": "*",
"ext-mbstring": "*",
@@ -48,10 +48,10 @@
"utopia-php/abuse": "0.52.*",
"utopia-php/analytics": "0.10.*",
"utopia-php/audit": "0.55.*",
"utopia-php/cache": "0.13.*",
"utopia-php/cache": "0.12.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.69.*",
"utopia-php/database": "dev-bind-params as 0.64.999",
"utopia-php/domains": "0.5.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",
@@ -59,8 +59,8 @@
"utopia-php/image": "0.8.*",
"utopia-php/locale": "0.4.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.17.*",
"utopia-php/migration": "0.9.*",
"utopia-php/messaging": "0.16.*",
"utopia-php/migration": "0.8.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "0.8.*",
@@ -72,7 +72,7 @@
"utopia-php/system": "0.9.*",
"utopia-php/telemetry": "0.1.*",
"utopia-php/vcs": "0.9.*",
"utopia-php/websocket": "0.3.*",
"utopia-php/websocket": "0.1.*",
"matomo/device-detector": "6.1.*",
"dragonmantank/cron-expression": "3.3.2",
"phpmailer/phpmailer": "6.9.1",
@@ -99,8 +99,8 @@
"php": "8.3"
},
"allow-plugins": {
"php-http/discovery": true,
"tbachert/spi": true
"php-http/discovery": false,
"tbachert/spi": false
}
}
}
Generated
+177 -169
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "2c14e20244a06f508dd67cda717aefeb",
"content-hash": "f31906555e33bc99128fb59dc655ba0b",
"packages": [
{
"name": "adhocore/jwt",
@@ -1109,16 +1109,16 @@
},
{
"name": "open-telemetry/api",
"version": "1.3.0",
"version": "1.2.3",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/api.git",
"reference": "4e3bb38e069876fb73c2ce85c89583bf2b28cd86"
"reference": "199d7ddda88f5f5619fa73463f1a5a7149ccd1f1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/api/zipball/4e3bb38e069876fb73c2ce85c89583bf2b28cd86",
"reference": "4e3bb38e069876fb73c2ce85c89583bf2b28cd86",
"url": "https://api.github.com/repos/opentelemetry-php/api/zipball/199d7ddda88f5f5619fa73463f1a5a7149ccd1f1",
"reference": "199d7ddda88f5f5619fa73463f1a5a7149ccd1f1",
"shasum": ""
},
"require": {
@@ -1175,20 +1175,20 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-05-07T12:32:21+00:00"
"time": "2025-03-05T21:42:54+00:00"
},
{
"name": "open-telemetry/context",
"version": "1.2.1",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/context.git",
"reference": "1eb2b837ee9362db064a6b65d5ecce15a9f9f020"
"reference": "0cba875ea1953435f78aec7f1d75afa87bdbf7f3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/context/zipball/1eb2b837ee9362db064a6b65d5ecce15a9f9f020",
"reference": "1eb2b837ee9362db064a6b65d5ecce15a9f9f020",
"url": "https://api.github.com/repos/opentelemetry-php/context/zipball/0cba875ea1953435f78aec7f1d75afa87bdbf7f3",
"reference": "0cba875ea1953435f78aec7f1d75afa87bdbf7f3",
"shasum": ""
},
"require": {
@@ -1234,20 +1234,20 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-05-07T23:36:50+00:00"
"time": "2024-08-21T00:29:20+00:00"
},
{
"name": "open-telemetry/exporter-otlp",
"version": "1.3.0",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/exporter-otlp.git",
"reference": "19adf03d2b0f91f9e9b1c7f93db6c755c737cf6c"
"reference": "b7580440b7481a98da97aceabeb46e1b276c8747"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/19adf03d2b0f91f9e9b1c7f93db6c755c737cf6c",
"reference": "19adf03d2b0f91f9e9b1c7f93db6c755c737cf6c",
"url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/b7580440b7481a98da97aceabeb46e1b276c8747",
"reference": "b7580440b7481a98da97aceabeb46e1b276c8747",
"shasum": ""
},
"require": {
@@ -1298,7 +1298,7 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-05-12T00:36:35+00:00"
"time": "2025-03-06T23:21:56+00:00"
},
{
"name": "open-telemetry/gen-otlp-protobuf",
@@ -1365,16 +1365,16 @@
},
{
"name": "open-telemetry/sdk",
"version": "1.4.0",
"version": "1.2.2",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/sdk.git",
"reference": "939d3a28395c249a763676458140dad44b3a8011"
"reference": "37eec0fe47ddd627911f318f29b6cd48196be0c0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/939d3a28395c249a763676458140dad44b3a8011",
"reference": "939d3a28395c249a763676458140dad44b3a8011",
"url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/37eec0fe47ddd627911f318f29b6cd48196be0c0",
"reference": "37eec0fe47ddd627911f318f29b6cd48196be0c0",
"shasum": ""
},
"require": {
@@ -1451,20 +1451,20 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-05-07T12:32:21+00:00"
"time": "2025-01-29T21:40:28+00:00"
},
{
"name": "open-telemetry/sem-conv",
"version": "1.32.0",
"version": "1.30.0",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/sem-conv.git",
"reference": "16585cc0dbc3032a318e274043454679430d2ebf"
"reference": "4178c9f390da8e4dbca9b181a9d1efd50cf7ee0a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/16585cc0dbc3032a318e274043454679430d2ebf",
"reference": "16585cc0dbc3032a318e274043454679430d2ebf",
"url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/4178c9f390da8e4dbca9b181a9d1efd50cf7ee0a",
"reference": "4178c9f390da8e4dbca9b181a9d1efd50cf7ee0a",
"shasum": ""
},
"require": {
@@ -1508,7 +1508,7 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-05-05T03:58:53+00:00"
"time": "2025-02-06T00:21:48+00:00"
},
{
"name": "paragonie/constant_time_encoding",
@@ -2726,20 +2726,19 @@
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.32.0",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
"shasum": ""
},
"require": {
"ext-iconv": "*",
"php": ">=7.2"
},
"provide": {
@@ -2787,7 +2786,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
},
"funding": [
{
@@ -2803,11 +2802,11 @@
"type": "tidelift"
}
],
"time": "2024-12-23T08:48:59+00:00"
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-php82",
"version": "v1.32.0",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php82.git",
@@ -2863,7 +2862,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php82/tree/v1.32.0"
"source": "https://github.com/symfony/polyfill-php82/tree/v1.31.0"
},
"funding": [
{
@@ -2966,16 +2965,16 @@
},
{
"name": "tbachert/spi",
"version": "v1.0.3",
"version": "v1.0.2",
"source": {
"type": "git",
"url": "https://github.com/Nevay/spi.git",
"reference": "506a79c98e1a51522e76ee921ccb6c62d52faf3a"
"reference": "2ddfaf815dafb45791a61b08170de8d583c16062"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Nevay/spi/zipball/506a79c98e1a51522e76ee921ccb6c62d52faf3a",
"reference": "506a79c98e1a51522e76ee921ccb6c62d52faf3a",
"url": "https://api.github.com/repos/Nevay/spi/zipball/2ddfaf815dafb45791a61b08170de8d583c16062",
"reference": "2ddfaf815dafb45791a61b08170de8d583c16062",
"shasum": ""
},
"require": {
@@ -3012,9 +3011,9 @@
],
"support": {
"issues": "https://github.com/Nevay/spi/issues",
"source": "https://github.com/Nevay/spi/tree/v1.0.3"
"source": "https://github.com/Nevay/spi/tree/v1.0.2"
},
"time": "2025-04-02T19:38:14+00:00"
"time": "2024-10-04T16:36:12+00:00"
},
{
"name": "thecodingmachine/safe",
@@ -3301,16 +3300,16 @@
},
{
"name": "utopia-php/cache",
"version": "0.13.1",
"version": "0.12.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/cache.git",
"reference": "97220cb3b3822b166ee016d1646e2ae2815dc540"
"reference": "646038f1d470b759c129348be8fc14da3c00bbd9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/cache/zipball/97220cb3b3822b166ee016d1646e2ae2815dc540",
"reference": "97220cb3b3822b166ee016d1646e2ae2815dc540",
"url": "https://api.github.com/repos/utopia-php/cache/zipball/646038f1d470b759c129348be8fc14da3c00bbd9",
"reference": "646038f1d470b759c129348be8fc14da3c00bbd9",
"shasum": ""
},
"require": {
@@ -3318,7 +3317,6 @@
"ext-memcached": "*",
"ext-redis": "*",
"php": ">=8.0",
"utopia-php/pools": "0.8.*",
"utopia-php/telemetry": "0.1.*"
},
"require-dev": {
@@ -3347,22 +3345,22 @@
],
"support": {
"issues": "https://github.com/utopia-php/cache/issues",
"source": "https://github.com/utopia-php/cache/tree/0.13.1"
"source": "https://github.com/utopia-php/cache/tree/0.12.0"
},
"time": "2025-05-09T14:43:52+00:00"
"time": "2025-02-25T09:09:21+00:00"
},
{
"name": "utopia-php/cli",
"version": "0.15.2",
"version": "0.15.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/cli.git",
"reference": "da00ff6b8b29a826a1794002ae43442cdf3a0f5f"
"reference": "d69bbe51a6a94dc4e5bcdd542b5938038b985a65"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/cli/zipball/da00ff6b8b29a826a1794002ae43442cdf3a0f5f",
"reference": "da00ff6b8b29a826a1794002ae43442cdf3a0f5f",
"url": "https://api.github.com/repos/utopia-php/cli/zipball/d69bbe51a6a94dc4e5bcdd542b5938038b985a65",
"reference": "d69bbe51a6a94dc4e5bcdd542b5938038b985a65",
"shasum": ""
},
"require": {
@@ -3396,9 +3394,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/cli/issues",
"source": "https://github.com/utopia-php/cli/tree/0.15.2"
"source": "https://github.com/utopia-php/cli/tree/0.15.1"
},
"time": "2025-04-15T10:08:48+00:00"
"time": "2024-10-04T13:55:36+00:00"
},
{
"name": "utopia-php/compression",
@@ -3499,23 +3497,23 @@
},
{
"name": "utopia-php/database",
"version": "0.69.1",
"version": "dev-bind-params",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "cc6538e05e25d930244ab938c966d32db0922e83"
"reference": "9fcaa37c092ae89680590114f3a4d6029eb02bb2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/cc6538e05e25d930244ab938c966d32db0922e83",
"reference": "cc6538e05e25d930244ab938c966d32db0922e83",
"url": "https://api.github.com/repos/utopia-php/database/zipball/9fcaa37c092ae89680590114f3a4d6029eb02bb2",
"reference": "9fcaa37c092ae89680590114f3a4d6029eb02bb2",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"ext-pdo": "*",
"php": ">=8.1",
"utopia-php/cache": "0.13.*",
"utopia-php/cache": "0.12.*",
"utopia-php/framework": "0.33.*",
"utopia-php/pools": "0.8.*"
},
@@ -3549,9 +3547,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.69.1"
"source": "https://github.com/utopia-php/database/tree/bind-params"
},
"time": "2025-05-13T12:00:31+00:00"
"time": "2025-03-30T16:53:08+00:00"
},
{
"name": "utopia-php/domains",
@@ -3662,16 +3660,16 @@
},
{
"name": "utopia-php/fetch",
"version": "0.4.2",
"version": "0.4.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/fetch.git",
"reference": "83986d1be75a2fae4e684107fe70dd78a8e19b77"
"reference": "46e791ff6a95864517750b9df6bbf4a17e3c9c4e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/fetch/zipball/83986d1be75a2fae4e684107fe70dd78a8e19b77",
"reference": "83986d1be75a2fae4e684107fe70dd78a8e19b77",
"url": "https://api.github.com/repos/utopia-php/fetch/zipball/46e791ff6a95864517750b9df6bbf4a17e3c9c4e",
"reference": "46e791ff6a95864517750b9df6bbf4a17e3c9c4e",
"shasum": ""
},
"require": {
@@ -3695,9 +3693,9 @@
"description": "A simple library that provides an interface for making HTTP Requests.",
"support": {
"issues": "https://github.com/utopia-php/fetch/issues",
"source": "https://github.com/utopia-php/fetch/tree/0.4.2"
"source": "https://github.com/utopia-php/fetch/tree/0.4.0"
},
"time": "2025-04-25T13:48:02+00:00"
"time": "2025-03-11T21:06:56+00:00"
},
{
"name": "utopia-php/framework",
@@ -3748,16 +3746,16 @@
},
{
"name": "utopia-php/image",
"version": "0.8.2",
"version": "0.8.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/image.git",
"reference": "6c736965177f9a9e71311e22b80cfa88511768e9"
"reference": "dcae5b1c6deb3ff6865f4e68f012b3709c289bca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/image/zipball/6c736965177f9a9e71311e22b80cfa88511768e9",
"reference": "6c736965177f9a9e71311e22b80cfa88511768e9",
"url": "https://api.github.com/repos/utopia-php/image/zipball/dcae5b1c6deb3ff6865f4e68f012b3709c289bca",
"reference": "dcae5b1c6deb3ff6865f4e68f012b3709c289bca",
"shasum": ""
},
"require": {
@@ -3791,9 +3789,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/image/issues",
"source": "https://github.com/utopia-php/image/tree/0.8.2"
"source": "https://github.com/utopia-php/image/tree/0.8.0"
},
"time": "2025-04-08T11:31:45+00:00"
"time": "2025-02-20T11:49:03+00:00"
},
{
"name": "utopia-php/locale",
@@ -3902,16 +3900,16 @@
},
{
"name": "utopia-php/messaging",
"version": "0.17.0",
"version": "0.16.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/messaging.git",
"reference": "c51915d0e030db3a3add37f1561751d18b2d9a85"
"reference": "5f3083697102b1821d6624938186761b1e09c54e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/c51915d0e030db3a3add37f1561751d18b2d9a85",
"reference": "c51915d0e030db3a3add37f1561751d18b2d9a85",
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/5f3083697102b1821d6624938186761b1e09c54e",
"reference": "5f3083697102b1821d6624938186761b1e09c54e",
"shasum": ""
},
"require": {
@@ -3947,22 +3945,22 @@
],
"support": {
"issues": "https://github.com/utopia-php/messaging/issues",
"source": "https://github.com/utopia-php/messaging/tree/0.17.0"
"source": "https://github.com/utopia-php/messaging/tree/0.16.0"
},
"time": "2025-05-12T16:14:08+00:00"
"time": "2025-02-18T08:27:00+00:00"
},
{
"name": "utopia-php/migration",
"version": "0.9.3",
"version": "0.8.4",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "e518d39eb550fde36bc5cf06c9bd7b2faf5dbedd"
"reference": "845fd04ccf5e0edb03c184b864e0596080a432b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/e518d39eb550fde36bc5cf06c9bd7b2faf5dbedd",
"reference": "e518d39eb550fde36bc5cf06c9bd7b2faf5dbedd",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/845fd04ccf5e0edb03c184b864e0596080a432b8",
"reference": "845fd04ccf5e0edb03c184b864e0596080a432b8",
"shasum": ""
},
"require": {
@@ -4003,9 +4001,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/0.9.3"
"source": "https://github.com/utopia-php/migration/tree/0.8.4"
},
"time": "2025-05-01T05:41:26+00:00"
"time": "2025-03-28T02:08:22+00:00"
},
{
"name": "utopia-php/orchestration",
@@ -4109,16 +4107,16 @@
},
{
"name": "utopia-php/pools",
"version": "0.8.2",
"version": "0.8.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/pools.git",
"reference": "05c67aba42eb68ac65489cc1e7fc5db83db2dd4d"
"reference": "60733929dc328e7ea47e800579c8bbf0d49df5ba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/pools/zipball/05c67aba42eb68ac65489cc1e7fc5db83db2dd4d",
"reference": "05c67aba42eb68ac65489cc1e7fc5db83db2dd4d",
"url": "https://api.github.com/repos/utopia-php/pools/zipball/60733929dc328e7ea47e800579c8bbf0d49df5ba",
"reference": "60733929dc328e7ea47e800579c8bbf0d49df5ba",
"shasum": ""
},
"require": {
@@ -4155,9 +4153,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/pools/issues",
"source": "https://github.com/utopia-php/pools/tree/0.8.2"
"source": "https://github.com/utopia-php/pools/tree/0.8.0"
},
"time": "2025-04-17T02:04:54+00:00"
"time": "2025-03-19T10:22:03+00:00"
},
{
"name": "utopia-php/preloader",
@@ -4545,28 +4543,28 @@
},
{
"name": "utopia-php/vcs",
"version": "0.9.5",
"version": "0.9.4",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/vcs.git",
"reference": "055956545ca7ab4e8688df5de1df3e2833859793"
"reference": "1a8d280b176acc99ea8d9e7364b8767cbb206b4a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/055956545ca7ab4e8688df5de1df3e2833859793",
"reference": "055956545ca7ab4e8688df5de1df3e2833859793",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/1a8d280b176acc99ea8d9e7364b8767cbb206b4a",
"reference": "1a8d280b176acc99ea8d9e7364b8767cbb206b4a",
"shasum": ""
},
"require": {
"adhocore/jwt": "^1.1",
"php": ">=8.0",
"utopia-php/cache": "0.13.*",
"utopia-php/cache": "0.12.*",
"utopia-php/framework": "0.*.*",
"utopia-php/system": "0.9.*"
},
"require-dev": {
"laravel/pint": "1.*.*",
"phpstan/phpstan": "1.*.*",
"laravel/pint": "1.2.*",
"phpstan/phpstan": "1.8.*",
"phpunit/phpunit": "^9.4"
},
"type": "library",
@@ -4589,34 +4587,33 @@
],
"support": {
"issues": "https://github.com/utopia-php/vcs/issues",
"source": "https://github.com/utopia-php/vcs/tree/0.9.5"
"source": "https://github.com/utopia-php/vcs/tree/0.9.4"
},
"time": "2025-04-17T04:38:49+00:00"
"time": "2025-03-13T10:09:45+00:00"
},
{
"name": "utopia-php/websocket",
"version": "0.3.1",
"version": "0.1.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/websocket.git",
"reference": "77004ba9f66a0ab6eb840a85b2af332fca8f6bd9"
"reference": "51fcb86171400d8aa40d76c54593481fd273dab5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/websocket/zipball/77004ba9f66a0ab6eb840a85b2af332fca8f6bd9",
"reference": "77004ba9f66a0ab6eb840a85b2af332fca8f6bd9",
"url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5",
"reference": "51fcb86171400d8aa40d76c54593481fd273dab5",
"shasum": ""
},
"require": {
"php": ">=8.0"
},
"require-dev": {
"laravel/pint": "^1.15",
"phpstan/phpstan": "^1.12",
"phpunit/phpunit": "^9.5.5",
"swoole/ide-helper": "5.1.2",
"swoole/ide-helper": "4.6.6",
"textalk/websocket": "1.5.2",
"workerman/workerman": "4.1.*"
"vimeo/psalm": "^4.8.1",
"workerman/workerman": "^4.0"
},
"type": "library",
"autoload": {
@@ -4628,6 +4625,16 @@
"license": [
"MIT"
],
"authors": [
{
"name": "Eldad Fux",
"email": "eldad@appwrite.io"
},
{
"name": "Torsten Dittmann",
"email": "torsten@appwrite.io"
}
],
"description": "A simple abstraction for WebSocket servers.",
"keywords": [
"framework",
@@ -4638,9 +4645,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/websocket/issues",
"source": "https://github.com/utopia-php/websocket/tree/0.3.1"
"source": "https://github.com/utopia-php/websocket/tree/0.1.0"
},
"time": "2025-05-09T12:57:42+00:00"
"time": "2021-12-20T10:50:09+00:00"
},
{
"name": "webmozart/assert",
@@ -4769,16 +4776,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "0.40.16",
"version": "0.40.11",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "f1f506da74033f0cb5a11e3dffcfd1ee8daf237d"
"reference": "0ec5f4a60c15e33e208bc3444ba6148b1d0f0027"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/f1f506da74033f0cb5a11e3dffcfd1ee8daf237d",
"reference": "f1f506da74033f0cb5a11e3dffcfd1ee8daf237d",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/0ec5f4a60c15e33e208bc3444ba6148b1d0f0027",
"reference": "0ec5f4a60c15e33e208bc3444ba6148b1d0f0027",
"shasum": ""
},
"require": {
@@ -4814,9 +4821,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": {
"issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/0.40.16"
"source": "https://github.com/appwrite/sdk-generator/tree/0.40.11"
},
"time": "2025-05-09T12:06:09+00:00"
"time": "2025-03-26T10:53:16+00:00"
},
{
"name": "doctrine/annotations",
@@ -5043,16 +5050,16 @@
},
{
"name": "laravel/pint",
"version": "v1.22.1",
"version": "v1.21.2",
"source": {
"type": "git",
"url": "https://github.com/laravel/pint.git",
"reference": "941d1927c5ca420c22710e98420287169c7bcaf7"
"reference": "370772e7d9e9da087678a0edf2b11b6960e40558"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/pint/zipball/941d1927c5ca420c22710e98420287169c7bcaf7",
"reference": "941d1927c5ca420c22710e98420287169c7bcaf7",
"url": "https://api.github.com/repos/laravel/pint/zipball/370772e7d9e9da087678a0edf2b11b6960e40558",
"reference": "370772e7d9e9da087678a0edf2b11b6960e40558",
"shasum": ""
},
"require": {
@@ -5063,12 +5070,12 @@
"php": "^8.2.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.75.0",
"illuminate/view": "^11.44.7",
"larastan/larastan": "^3.4.0",
"friendsofphp/php-cs-fixer": "^3.72.0",
"illuminate/view": "^11.44.2",
"larastan/larastan": "^3.2.0",
"laravel-zero/framework": "^11.36.1",
"mockery/mockery": "^1.6.12",
"nunomaduro/termwind": "^2.3.1",
"nunomaduro/termwind": "^2.3",
"pestphp/pest": "^2.36.0"
},
"bin": [
@@ -5105,7 +5112,7 @@
"issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint"
},
"time": "2025-05-08T08:38:12+00:00"
"time": "2025-03-14T22:31:42+00:00"
},
{
"name": "matthiasmullie/minify",
@@ -5233,16 +5240,16 @@
},
{
"name": "myclabs/deep-copy",
"version": "1.13.1",
"version": "1.13.0",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c"
"reference": "024473a478be9df5fdaca2c793f2232fe788e414"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c",
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414",
"reference": "024473a478be9df5fdaca2c793f2232fe788e414",
"shasum": ""
},
"require": {
@@ -5281,7 +5288,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.1"
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.0"
},
"funding": [
{
@@ -5289,7 +5296,7 @@
"type": "tidelift"
}
],
"time": "2025-04-29T12:36:36+00:00"
"time": "2025-02-12T12:17:51+00:00"
},
{
"name": "nikic/php-parser",
@@ -5937,16 +5944,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.6.23",
"version": "9.6.22",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95"
"reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/43d2cb18d0675c38bd44982a5d1d88f6d53d8d95",
"reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f80235cb4d3caa59ae09be3adf1ded27521d1a9c",
"reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c",
"shasum": ""
},
"require": {
@@ -5957,7 +5964,7 @@
"ext-mbstring": "*",
"ext-xml": "*",
"ext-xmlwriter": "*",
"myclabs/deep-copy": "^1.13.1",
"myclabs/deep-copy": "^1.12.1",
"phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1",
"php": ">=7.3",
@@ -6020,7 +6027,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.23"
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.22"
},
"funding": [
{
@@ -6031,20 +6038,12 @@
"url": "https://github.com/sebastianbergmann",
"type": "github"
},
{
"url": "https://liberapay.com/sebastianbergmann",
"type": "liberapay"
},
{
"url": "https://thanks.dev/u/gh/sebastianbergmann",
"type": "thanks_dev"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
"type": "tidelift"
}
],
"time": "2025-05-02T06:40:34+00:00"
"time": "2024-12-05T13:48:26+00:00"
},
{
"name": "psr/cache",
@@ -7156,16 +7155,16 @@
},
{
"name": "symfony/console",
"version": "v7.2.6",
"version": "v7.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "0e2e3f38c192e93e622e41ec37f4ca70cfedf218"
"reference": "e51498ea18570c062e7df29d05a7003585b19b88"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/0e2e3f38c192e93e622e41ec37f4ca70cfedf218",
"reference": "0e2e3f38c192e93e622e41ec37f4ca70cfedf218",
"url": "https://api.github.com/repos/symfony/console/zipball/e51498ea18570c062e7df29d05a7003585b19b88",
"reference": "e51498ea18570c062e7df29d05a7003585b19b88",
"shasum": ""
},
"require": {
@@ -7229,7 +7228,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.2.6"
"source": "https://github.com/symfony/console/tree/v7.2.5"
},
"funding": [
{
@@ -7245,7 +7244,7 @@
"type": "tidelift"
}
],
"time": "2025-04-07T19:09:28+00:00"
"time": "2025-03-12T08:11:12+00:00"
},
{
"name": "symfony/filesystem",
@@ -7446,7 +7445,7 @@
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.32.0",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
@@ -7505,7 +7504,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
},
"funding": [
{
@@ -7525,7 +7524,7 @@
},
{
"name": "symfony/polyfill-intl-grapheme",
"version": "v1.32.0",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
@@ -7583,7 +7582,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0"
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0"
},
"funding": [
{
@@ -7603,7 +7602,7 @@
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.32.0",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
@@ -7664,7 +7663,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0"
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0"
},
"funding": [
{
@@ -7684,7 +7683,7 @@
},
{
"name": "symfony/polyfill-php81",
"version": "v1.32.0",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php81.git",
@@ -7740,7 +7739,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php81/tree/v1.32.0"
"source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0"
},
"funding": [
{
@@ -7821,16 +7820,16 @@
},
{
"name": "symfony/string",
"version": "v7.2.6",
"version": "v7.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "a214fe7d62bd4df2a76447c67c6b26e1d5e74931"
"reference": "446e0d146f991dde3e73f45f2c97a9faad773c82"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/a214fe7d62bd4df2a76447c67c6b26e1d5e74931",
"reference": "a214fe7d62bd4df2a76447c67c6b26e1d5e74931",
"url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82",
"reference": "446e0d146f991dde3e73f45f2c97a9faad773c82",
"shasum": ""
},
"require": {
@@ -7888,7 +7887,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v7.2.6"
"source": "https://github.com/symfony/string/tree/v7.2.0"
},
"funding": [
{
@@ -7904,7 +7903,7 @@
"type": "tidelift"
}
],
"time": "2025-04-20T20:18:16+00:00"
"time": "2024-11-13T13:31:26+00:00"
},
{
"name": "textalk/websocket",
@@ -8134,13 +8133,22 @@
"time": "2024-03-07T20:33:40+00:00"
}
],
"aliases": [],
"aliases": [
{
"package": "utopia-php/database",
"version": "dev-bind-params",
"alias": "0.64.999",
"alias_normalized": "0.64.999.0"
}
],
"minimum-stability": "stable",
"stability-flags": {},
"stability-flags": {
"utopia-php/database": 20
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=8.3.0",
"php": ">=8.0.0",
"ext-curl": "*",
"ext-imagick": "*",
"ext-mbstring": "*",
+35 -8
View File
@@ -198,14 +198,11 @@ services:
- _APP_DATABASE_SHARED_TABLES_V1
- _APP_DATABASE_SHARED_NAMESPACE
- _APP_FUNCTIONS_CREATION_ABUSE_LIMIT
- _APP_CUSTOM_DOMAIN_DENY_LIST
extra_hosts:
- "host.docker.internal:host-gateway"
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: appwrite/console:5.2.58
image: appwrite/console:5.2.53
restart: unless-stopped
networks:
- appwrite
@@ -492,8 +489,6 @@ services:
- _APP_STORAGE_WASABI_REGION
- _APP_STORAGE_WASABI_BUCKET
- _APP_DATABASE_SHARED_TABLES
extra_hosts:
- "host.docker.internal:host-gateway"
appwrite-worker-certificates:
entrypoint: worker-certificates
@@ -732,7 +727,7 @@ services:
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
- _APP_MAINTENANCE_RETENTION_SCHEDULES
- _APP_MAINTENANCE_START_TIME
- _APP_MAINTENANCE_DELAY
- _APP_DATABASE_SHARED_TABLES
appwrite-task-stats-resources:
@@ -828,6 +823,38 @@ services:
- _APP_USAGE_AGGREGATION_INTERVAL
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-stats-usage-dump:
entrypoint: worker-stats-usage-dump
<<: *x-logging
container_name: appwrite-worker-stats-usage-dump
image: appwrite-dev
networks:
- appwrite
volumes:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- redis
- mariadb
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
- _APP_DATABASE_SHARED_TABLES
- _APP_STATS_USAGE_DUAL_WRITING_DBS
appwrite-task-scheduler-functions:
entrypoint: schedule-functions
<<: *x-logging
@@ -1104,4 +1131,4 @@ volumes:
appwrite-certificates:
appwrite-functions:
appwrite-builds:
appwrite-config:
appwrite-config:
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Databases databases = new Databases(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Databases databases = new Databases(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Databases databases = new Databases(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Databases databases = new Databases(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Databases databases = new Databases(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Functions functions = new Functions(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Functions functions = new Functions(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Functions functions = new Functions(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);

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