Merge remote-tracking branch 'origin/0.14.x' into fix-bucket-size-update

This commit is contained in:
Matej Bačo
2022-05-10 10:28:32 +00:00
288 changed files with 3236 additions and 30278 deletions
+3 -1
View File
@@ -45,6 +45,7 @@ _APP_SMTP_SECURE=
_APP_SMTP_USERNAME=
_APP_SMTP_PASSWORD=
_APP_STORAGE_LIMIT=30000000
_APP_STORAGE_PREVIEW_LIMIT=20000000
_APP_FUNCTIONS_SIZE_LIMIT=30000000
_APP_FUNCTIONS_TIMEOUT=900
_APP_FUNCTIONS_BUILD_TIMEOUT=900
@@ -53,8 +54,9 @@ _APP_FUNCTIONS_CPUS=0
_APP_FUNCTIONS_MEMORY=0
_APP_FUNCTIONS_MEMORY_SWAP=0
_APP_FUNCTIONS_INACTIVE_THRESHOLD=60
_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes
OPEN_RUNTIMES_NETWORK=appwrite_runtimes
_APP_EXECUTOR_SECRET=your-secret-key
_APP_EXECUTOR_HOST=http://appwrite-executor/v1
_APP_MAINTENANCE_INTERVAL=86400
_APP_MAINTENANCE_RETENTION_EXECUTION=1209600
_APP_MAINTENANCE_RETENTION_ABUSE=86400
+3
View File
@@ -1,3 +1,6 @@
# Unreleased Version
- Renamed `providers` to `authProviders` in project collection **Breaking Change**
# Version 0.13.4
## Features
+6 -5
View File
@@ -12,7 +12,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \
--no-plugins --no-scripts --prefer-dist \
`if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi`
FROM node:16.13.2-alpine3.15 as node
FROM node:16.14.2-alpine3.15 as node
WORKDIR /usr/local/src/
@@ -24,14 +24,14 @@ COPY public /usr/local/src/public
RUN npm ci
RUN npm run build
FROM php:8.0.14-cli-alpine3.15 as compile
FROM php:8.0.18-cli-alpine3.15 as compile
ARG DEBUG=false
ENV DEBUG=$DEBUG
ENV PHP_REDIS_VERSION=5.3.7 \
PHP_MONGODB_VERSION=1.9.1 \
PHP_SWOOLE_VERSION=v4.8.7 \
PHP_MONGODB_VERSION=1.13.0 \
PHP_SWOOLE_VERSION=v4.8.9 \
PHP_IMAGICK_VERSION=3.7.0 \
PHP_YAML_VERSION=2.2.2 \
PHP_MAXMINDDB_VERSION=v1.11.0
@@ -123,7 +123,7 @@ RUN \
./configure && \
make && make install
FROM php:8.0.14-cli-alpine3.15 as final
FROM php:8.0.18-cli-alpine3.15 as final
LABEL maintainer="team@appwrite.io"
@@ -185,6 +185,7 @@ ENV _APP_SERVER=swoole \
_APP_FUNCTIONS_MEMORY=128 \
_APP_FUNCTIONS_MEMORY_SWAP=128 \
_APP_EXECUTOR_SECRET=a-random-secret \
_APP_EXECUTOR_HOST=http://appwrite-executor/v1 \
_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes \
_APP_SETUP=self-hosted \
_APP_VERSION=$VERSION \
+2 -2
View File
@@ -20,7 +20,7 @@
Appwrite是一个基于Docker的端到端开发者平台,其容器化的微服务库可应用于网页端,移动端,以及后端。Appwrite 通过视觉化界面极简了从零编写 API 的繁琐过程,在保证软件安全的前提下为开发者创造了一个高效的开发环境。
Appwrite 可以提供给开发者用户验证,外部授权,用户数据读写检索,文件储存, 图像处理,云函数计算,[等多种服务](https://appwrite.io/docs).
Appwrite 可以提供给开发者用户验证,外部授权,用户数据读写检索,文件储存图像处理,云函数计算,[等多种服务](https://appwrite.io/docs).
![Appwrite](public/images/github.png)
@@ -168,4 +168,4 @@ Appwrite API 界面层利用后台缓存和任务委派来提供极速的响应
## 版权说明
版权详情,访问 [BSD 3-Clause License](./LICENSE)。
版权详情,访问 [BSD 3-Clause License](./LICENSE)。
+1 -1
View File
@@ -19,7 +19,7 @@
English | [简体中文](README-CN.md)
[**Appwrite 0.13 has been released! Learn what's new!**](https://dev.to/appwrite/its-here-announcing-the-release-of-appwrite-012-5c8b)
[**Appwrite 0.13 has been released! Learn what's new!**](https://dev.to/appwrite/announcing-appwrite-013-with-major-upgrades-to-storage-and-functions-3hpf)
Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps packaged as a set of Docker<nobr> microservices. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster.
+101 -11
View File
@@ -512,7 +512,7 @@ $collections = [
'filters' => ['json'],
],
[
'$id' => 'providers',
'$id' => 'authProviders',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
@@ -1054,9 +1054,9 @@ $collections = [
'size' => 16384,
'signed' => true,
'required' => false,
'default' => [],
'array' => true,
'filters' => ['json'],
'default' => null,
'array' => false,
'filters' => ['subQuerySessions'],
],
[
'$id' => 'tokens',
@@ -1065,9 +1065,9 @@ $collections = [
'size' => 16384,
'signed' => true,
'required' => false,
'default' => [],
'array' => true,
'filters' => ['json'],
'default' => null,
'array' => false,
'filters' => ['subQueryTokens'],
],
[
'$id' => 'memberships',
@@ -1077,8 +1077,8 @@ $collections = [
'signed' => true,
'required' => false,
'default' => [],
'array' => true,
'filters' => ['json'],
'array' => false,
'filters' => ['subQueryMemberships'],
],
[
'$id' => 'search',
@@ -1128,6 +1128,89 @@ $collections = [
],
],
'tokens' => [
'$collection' => Database::METADATA,
'$id' => 'tokens',
'name' => 'Tokens',
'attributes' => [
[
'$id' => 'userId',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'type',
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'secret',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 512, // https://www.tutorialspoint.com/how-long-is-the-sha256-hash-in-mysql (512 for encryption)
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['encrypt'],
],
[
'$id' => 'expire',
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'userAgent',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'ip',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 45, // https://stackoverflow.com/a/166157/2299554
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
]
],
'indexes' => [
[
'$id' => '_key_user',
'type' => Database::INDEX_KEY,
'attributes' => ['userId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
],
],
'sessions' => [
'$collection' => Database::METADATA,
'$id' => 'sessions',
@@ -1395,6 +1478,13 @@ $collections = [
'lengths' => [100, 100],
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
],
[
'$id' => '_key_user',
'type' => Database::INDEX_KEY,
'attributes' => ['userId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
],
],
@@ -2121,7 +2211,7 @@ $collections = [
'filters' => [],
],
[
'$id' => 'stdout',
'$id' => 'response',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
@@ -2425,7 +2515,7 @@ $collections = [
'$id' => 'value',
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'size' => 8,
'signed' => false,
'required' => true,
'default' => null,
+12 -12
View File
@@ -63,7 +63,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '4.0.1',
'version' => '4.0.2',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@@ -81,7 +81,7 @@ return [
[
'key' => 'apple',
'name' => 'Apple',
'version' => '0.3.0',
'version' => '0.4.0',
'url' => 'https://github.com/appwrite/sdk-for-apple',
'package' => 'https://github.com/appwrite/sdk-for-apple',
'enabled' => true,
@@ -116,7 +116,7 @@ return [
[
'key' => 'android',
'name' => 'Android',
'version' => '0.4.0',
'version' => '0.5.0',
'url' => 'https://github.com/appwrite/sdk-for-android',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
'enabled' => true,
@@ -180,7 +180,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '0.15.0',
'version' => '0.16.0',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@@ -208,7 +208,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '5.0.0',
'version' => '5.1.0',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@@ -226,7 +226,7 @@ return [
[
'key' => 'deno',
'name' => 'Deno',
'version' => '3.0.0',
'version' => '3.1.0',
'url' => 'https://github.com/appwrite/sdk-for-deno',
'package' => 'https://deno.land/x/appwrite',
'enabled' => true,
@@ -244,7 +244,7 @@ return [
[
'key' => 'php',
'name' => 'PHP',
'version' => '4.0.0',
'version' => '4.1.0',
'url' => 'https://github.com/appwrite/sdk-for-php',
'package' => 'https://packagist.org/packages/appwrite/appwrite',
'enabled' => true,
@@ -262,7 +262,7 @@ return [
[
'key' => 'python',
'name' => 'Python',
'version' => '0.7.0',
'version' => '0.8.0',
'url' => 'https://github.com/appwrite/sdk-for-python',
'package' => 'https://pypi.org/project/appwrite/',
'enabled' => true,
@@ -280,7 +280,7 @@ return [
[
'key' => 'ruby',
'name' => 'Ruby',
'version' => '4.0.0',
'version' => '4.1.0',
'url' => 'https://github.com/appwrite/sdk-for-ruby',
'package' => 'https://rubygems.org/gems/appwrite',
'enabled' => true,
@@ -352,7 +352,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '4.0.1',
'version' => '4.0.2',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
@@ -370,7 +370,7 @@ return [
[
'key' => 'kotlin',
'name' => 'Kotlin',
'version' => '0.3.0',
'version' => '0.4.0',
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
'enabled' => true,
@@ -392,7 +392,7 @@ return [
[
'key' => 'swift',
'name' => 'Swift',
'version' => '0.3.0',
'version' => '0.4.0',
'url' => 'https://github.com/appwrite/sdk-for-swift',
'package' => 'https://github.com/appwrite/sdk-for-swift',
'enabled' => true,
+20
View File
@@ -21,6 +21,16 @@ return [ // Ordered by ABC.
'beta' => true,
'mock' => false,
],
'auth0' => [
'name' => 'Auth0',
'developers' => 'https://auth0.com/developers',
'icon' => 'icon-auth0',
'enabled' => true,
'sandbox' => false,
'form' => 'auth0.phtml',
'beta' => false,
'mock' => false,
],
'bitbucket' => [
'name' => 'BitBucket',
'developers' => 'https://developer.atlassian.com/bitbucket',
@@ -141,6 +151,16 @@ return [ // Ordered by ABC.
'beta' => false,
'mock' => false,
],
'okta' => [
'name' => 'Okta',
'developers' => 'https://developer.okta.com/',
'icon' => 'icon-okta',
'enabled' => true,
'sandbox' => false,
'form' => 'okta.phtml',
'beta' => false,
'mock' => false,
],
'paypal' => [
'name' => 'PayPal',
'developers' => 'https://developer.paypal.com/docs/api/overview/',
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+62 -44
View File
@@ -466,7 +466,7 @@ return [
'name' => '_APP_STORAGE_S3_REGION',
'description' => 'AWS S3 storage region. Required when storage adapter is set to S3. You can find your region info for your bucket from AWS console.',
'introduction' => '0.13.0',
'default' => 'us-eas-1',
'default' => 'us-east-1',
'required' => false,
'question' => '',
],
@@ -498,7 +498,7 @@ return [
'name' => '_APP_STORAGE_DO_SPACES_REGION',
'description' => 'DigitalOcean spaces region. Required when storage adapter is set to DOSpaces. You can find your region info for your space from DigitalOcean console.',
'introduction' => '0.13.0',
'default' => 'us-eas-1',
'default' => 'us-east-1',
'required' => false,
'question' => '',
],
@@ -597,9 +597,18 @@ return [
'question' => '',
'filter' => ''
],
[
'name' => '_APP_EXECUTOR_HOST',
'description' => 'The host used by Appwrite to communicate with the function executor!',
'introduction' => '0.13.0',
'default' => 'http://appwrite-executor/v1',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_EXECUTOR_RUNTIME_NETWORK',
'description' => 'The docker network used for communication between the executor and runtimes. Change this if you have altered the default network names.',
'description' => 'Deprecated with 0.14.0, use \'OPEN_RUNTIMES_NETWORK\' instead!',
'introduction' => '0.13.0',
'default' => 'appwrite_runtimes',
'required' => false,
@@ -651,48 +660,57 @@ return [
'question' => '',
'filter' => ''
],
],
[
'category' => 'Maintenance',
'description' => '',
'variables' => [
[
'name' => '_APP_MAINTENANCE_INTERVAL',
'description' => 'Interval value containing the number of seconds that the Appwrite maintenance process should wait before executing system cleanups and optimizations. The default value is 86400 seconds (1 day).',
'introduction' => '0.7.0',
'default' => '86400',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_MAINTENANCE_RETENTION_EXECUTION',
'description' => 'The maximum duration (in seconds) upto which to retain execution logs. The default value is 1209600 seconds (14 days).',
'introduction' => '0.7.0',
'default' => '1209600',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_MAINTENANCE_RETENTION_AUDIT',
'description' => 'IThe maximum duration (in seconds) upto which to retain audit logs. The default value is 1209600 seconds (14 days).',
'introduction' => '0.7.0',
'default' => '1209600',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_MAINTENANCE_RETENTION_ABUSE',
'description' => 'The maximum duration (in seconds) upto which to retain abuse logs. The default value is 86400 seconds (1 day).',
'introduction' => '0.7.0',
'default' => '86400',
'required' => false,
'question' => '',
'filter' => ''
]
[
'name' => 'OPEN_RUNTIMES_NETWORK',
'description' => 'The docker network used for communication between the executor and runtimes. Change this if you have altered the default network names.',
'introduction' => '0.13.0',
'default' => 'appwrite_runtimes',
'required' => false,
'question' => '',
'filter' => ''
],
],
],
[
'category' => 'Maintenance',
'description' => '',
'variables' => [
[
'name' => '_APP_MAINTENANCE_INTERVAL',
'description' => 'Interval value containing the number of seconds that the Appwrite maintenance process should wait before executing system cleanups and optimizations. The default value is 86400 seconds (1 day).',
'introduction' => '0.7.0',
'default' => '86400',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_MAINTENANCE_RETENTION_EXECUTION',
'description' => 'The maximum duration (in seconds) upto which to retain execution logs. The default value is 1209600 seconds (14 days).',
'introduction' => '0.7.0',
'default' => '1209600',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_MAINTENANCE_RETENTION_AUDIT',
'description' => 'IThe maximum duration (in seconds) upto which to retain audit logs. The default value is 1209600 seconds (14 days).',
'introduction' => '0.7.0',
'default' => '1209600',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_MAINTENANCE_RETENTION_ABUSE',
'description' => 'The maximum duration (in seconds) upto which to retain abuse logs. The default value is 86400 seconds (1 day).',
'introduction' => '0.7.0',
'default' => '86400',
'required' => false,
'question' => '',
'filter' => ''
]
],
],
];
+77 -93
View File
@@ -104,9 +104,9 @@ App::post('/v1/account')
'reset' => false,
'name' => $name,
'prefs' => new \stdClass(),
'sessions' => [],
'tokens' => [],
'memberships' => [],
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $name]),
'deleted' => false
])));
@@ -208,8 +208,7 @@ App::post('/v1/account/sessions')
->setAttribute('$write', ['user:' . $profile->getId()])
);
$profile->setAttribute('sessions', $session, Document::SET_TYPE_APPEND);
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile);
$dbForProject->deleteCachedDocument('users', $profile->getId());
$audits
->setParam('userId', $profile->getId())
@@ -274,8 +273,8 @@ App::get('/v1/account/sessions/oauth2/:provider')
$protocol = $request->getProtocol();
$callback = $protocol.'://'.$request->getHostname().'/v1/account/sessions/oauth2/callback/'.$provider.'/'.$project->getId();
$appId = $project->getAttribute('providers', [])[$provider.'Appid'] ?? '';
$appSecret = $project->getAttribute('providers', [])[$provider.'Secret'] ?? '{}';
$appId = $project->getAttribute('authProviders', [])[$provider.'Appid'] ?? '';
$appSecret = $project->getAttribute('authProviders', [])[$provider.'Secret'] ?? '{}';
if (!empty($appSecret) && isset($appSecret['version'])) {
$key = App::getEnv('_APP_OPENSSL_KEY_V' . $appSecret['version']);
@@ -396,8 +395,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
$validateURL = new URL();
$appId = $project->getAttribute('providers', [])[$provider.'Appid'] ?? '';
$appSecret = $project->getAttribute('providers', [])[$provider.'Secret'] ?? '{}';
$appId = $project->getAttribute('authProviders', [])[$provider.'Appid'] ?? '';
$appSecret = $project->getAttribute('authProviders', [])[$provider.'Secret'] ?? '{}';
if (!empty($appSecret) && isset($appSecret['version'])) {
$key = App::getEnv('_APP_OPENSSL_KEY_V' . $appSecret['version']);
@@ -458,13 +457,10 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$current = Auth::sessionVerify($sessions, Auth::$secret);
if ($current) { // Delete current session of new one.
foreach ($sessions as $key => $session) {/** @var Document $session */
if ($current === $session['$id']) {
unset($sessions[$key]);
$dbForProject->deleteDocument('sessions', $session->getId());
$dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', $sessions));
}
$currentDocument = $dbForProject->getDocument('sessions', $current);
if(!$currentDocument->isEmpty()) {
$dbForProject->deleteDocument('sessions', $currentDocument->getId());
$dbForProject->deleteCachedDocument('users', $user->getId());
}
}
@@ -505,9 +501,9 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'reset' => false,
'name' => $name,
'prefs' => new \stdClass(),
'sessions' => [],
'tokens' => [],
'memberships' => [],
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $name]),
'deleted' => false
])));
@@ -553,17 +549,18 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$user
->setAttribute('status', true)
->setAttribute('sessions', $session, Document::SET_TYPE_APPEND)
;
Authorization::setRole('user:' . $user->getId());
$dbForProject->updateDocument('users', $user->getId(), $user);
$session = $dbForProject->createDocument('sessions', $session
->setAttribute('$read', ['user:' . $user->getId()])
->setAttribute('$write', ['user:' . $user->getId()])
);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$dbForProject->deleteCachedDocument('users', $user->getId());
$audits
->setParam('userId', $user->getId())
@@ -675,13 +672,13 @@ App::post('/v1/account/sessions/magic-url')
'emailVerification' => false,
'status' => true,
'password' => null,
'passwordUpdate' => \time(),
'passwordUpdate' => 0,
'registration' => \time(),
'reset' => false,
'prefs' => new \stdClass(),
'sessions' => [],
'tokens' => [],
'memberships' => [],
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email]),
'deleted' => false
])));
@@ -706,13 +703,12 @@ App::post('/v1/account/sessions/magic-url')
Authorization::setRole('user:'.$user->getId());
$user->setAttribute('tokens', $token, Document::SET_TYPE_APPEND);
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$read', ['user:'.$user->getId()])
->setAttribute('$write', ['user:'.$user->getId()])
);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
if (false === $user) {
throw new Exception('Failed to save user to DB', 500, Exception::GENERAL_SERVER_ERROR);
}
$dbForProject->deleteCachedDocument('users', $user->getId());
if(empty($url)) {
$url = $request->getProtocol().'://'.$request->getHostname().'/auth/magic-url';
@@ -786,7 +782,7 @@ App::put('/v1/account/sessions/magic-url')
/** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Event\Event $audits */
$user = $dbForProject->getDocument('users', $userId);
$user = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId));
if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
@@ -825,22 +821,19 @@ App::put('/v1/account/sessions/magic-url')
->setAttribute('$write', ['user:' . $user->getId()])
);
$dbForProject->deleteCachedDocument('users', $user->getId());
$tokens = $user->getAttribute('tokens', []);
/**
* We act like we're updating and validating
* the recovery token but actually we don't need it anymore.
*/
foreach ($tokens as $key => $singleToken) {
if ($token === $singleToken->getId()) {
unset($tokens[$key]);
}
}
$dbForProject->deleteDocument('tokens', $token);
$dbForProject->deleteCachedDocument('users', $user->getId());
$user
->setAttribute('sessions', $session, Document::SET_TYPE_APPEND)
->setAttribute('tokens', $tokens);
->setAttribute('emailVerification', true);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
@@ -868,9 +861,7 @@ App::put('/v1/account/sessions/magic-url')
->setStatusCode(Response::STATUS_CODE_CREATED)
;
$countryName = (isset($countries[strtoupper($session->getAttribute('countryCode'))]))
? $countries[strtoupper($session->getAttribute('countryCode'))]
: $locale->getText('locale.country.unknown');
$countryName = $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
$session
->setAttribute('current', true)
@@ -946,14 +937,14 @@ App::post('/v1/account/sessions/anonymous')
'emailVerification' => false,
'status' => true,
'password' => null,
'passwordUpdate' => \time(),
'passwordUpdate' => 0,
'registration' => \time(),
'reset' => false,
'name' => null,
'prefs' => new \stdClass(),
'sessions' => [],
'tokens' => [],
'memberships' => [],
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => $userId,
'deleted' => false
])));
@@ -987,8 +978,7 @@ App::post('/v1/account/sessions/anonymous')
->setAttribute('$write', ['user:' . $user->getId()])
);
$user = $dbForProject->updateDocument('users', $user->getId(),
$user->setAttribute('sessions', $session, Document::SET_TYPE_APPEND));
$dbForProject->deleteCachedDocument('users', $user->getId());
$audits
->setParam('userId', $user->getId())
@@ -1013,9 +1003,7 @@ App::post('/v1/account/sessions/anonymous')
->setStatusCode(Response::STATUS_CODE_CREATED)
;
$countryName = (isset($countries[strtoupper($session->getAttribute('countryCode'))]))
? $countries[strtoupper($session->getAttribute('countryCode'))]
: $locale->getText('locale.country.unknown');
$countryName = $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
$session
->setAttribute('current', true)
@@ -1041,16 +1029,17 @@ App::post('/v1/account/jwt')
->label('abuse-key', 'url:{url},userId:{userId}')
->inject('response')
->inject('user')
->action(function ($response, $user) {
->inject('dbForProject')
->action(function ($response, $user, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
$sessions = $user->getAttribute('sessions', []);
$current = new Document();
foreach ($sessions as $session) {
/** @var Utopia\Database\Document $session */
foreach ($sessions as $session) { /** @var Utopia\Database\Document $session */
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$current = $session;
}
@@ -1280,15 +1269,13 @@ App::get('/v1/account/sessions/:sessionId')
$sessions = $user->getAttribute('sessions', []);
$sessionId = ($sessionId === 'current')
? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret)
: $sessionId;
? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret)
: $sessionId;
foreach ($sessions as $session) {/** @var Document $session */
if ($sessionId == $session->getId()) {
$countryName = (isset($countries[strtoupper($session->getAttribute('countryCode'))]))
? $countries[strtoupper($session->getAttribute('countryCode'))]
: $locale->getText('locale.country.unknown');
$countryName = $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
$session
->setAttribute('current', ($session->getAttribute('secret') == Auth::hash(Auth::$secret)))
@@ -1622,7 +1609,7 @@ App::delete('/v1/account/sessions/:sessionId')
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$session
->setAttribute('current', true)
->setAttribute('countryName', (isset($countries[strtoupper($session->getAttribute('countryCode'))])) ? $countries[strtoupper($session->getAttribute('countryCode'))] : $locale->getText('locale.country.unknown'))
->setAttribute('countryName', $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')))
;
if (!Config::getParam('domainVerification')) {
@@ -1636,8 +1623,8 @@ App::delete('/v1/account/sessions/:sessionId')
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
;
}
$dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', $sessions));
$dbForProject->deleteCachedDocument('users', $user->getId());
$events
->setParam('eventData', $response->output($session, Response::MODEL_SESSION))
@@ -1710,8 +1697,8 @@ App::patch('/v1/account/sessions/:sessionId')
$provider = $session->getAttribute('provider');
$refreshToken = $session->getAttribute('providerRefreshToken');
$appId = $project->getAttribute('providers', [])[$provider.'Appid'] ?? '';
$appSecret = $project->getAttribute('providers', [])[$provider.'Secret'] ?? '{}';
$appId = $project->getAttribute('authProviders', [])[$provider.'Appid'] ?? '';
$appSecret = $project->getAttribute('authProviders', [])[$provider.'Secret'] ?? '{}';
$className = 'Appwrite\\Auth\\OAuth2\\'.\ucfirst($provider);
@@ -1731,8 +1718,7 @@ App::patch('/v1/account/sessions/:sessionId')
$dbForProject->updateDocument('sessions', $sessionId, $session);
$user->setAttribute("sessions", $sessions);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$dbForProject->deleteCachedDocument('users', $user->getId());
$audits
->setParam('userId', $user->getId())
@@ -1806,7 +1792,7 @@ App::delete('/v1/account/sessions')
$session
->setAttribute('current', false)
->setAttribute('countryName', (isset($countries[strtoupper($session->getAttribute('countryCode'))])) ? $countries[strtoupper($session->getAttribute('countryCode'))] : $locale->getText('locale.country.unknown'))
->setAttribute('countryName', $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')))
;
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
@@ -1818,7 +1804,7 @@ App::delete('/v1/account/sessions')
}
}
$dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', []));
$dbForProject->deleteCachedDocument('users', $user->getId());
$numOfSessions = count($sessions);
@@ -1906,9 +1892,12 @@ App::post('/v1/account/recovery')
Authorization::setRole('user:' . $profile->getId());
$profile->setAttribute('tokens', $recovery, Document::SET_TYPE_APPEND);
$recovery = $dbForProject->createDocument('tokens', $recovery
->setAttribute('$read', ['user:'.$profile->getId()])
->setAttribute('$write', ['user:'.$profile->getId()])
);
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile);
$dbForProject->deleteCachedDocument('users', $profile->getId());
$url = Template::parseURL($url);
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $profile->getId(), 'secret' => $secret, 'expire' => $expire]);
@@ -2003,18 +1992,14 @@ App::put('/v1/account/recovery')
->setAttribute('emailVerification', true)
);
$recoveryDocument = $dbForProject->getDocument('tokens', $recovery);
/**
* We act like we're updating and validating
* the recovery token but actually we don't need it anymore.
*/
foreach ($tokens as $key => $token) {
if ($recovery === $token->getId()) {
$recovery = $token;
unset($tokens[$key]);
}
}
$dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('tokens', $tokens));
$dbForProject->deleteDocument('tokens', $recovery);
$dbForProject->deleteCachedDocument('users', $profile->getId());
$audits
->setParam('userId', $profile->getId())
@@ -2025,7 +2010,7 @@ App::put('/v1/account/recovery')
$usage
->setParam('users.update', 1)
;
$response->dynamic($recovery, Response::MODEL_TOKEN);
$response->dynamic($recoveryDocument, Response::MODEL_TOKEN);
});
App::post('/v1/account/verification')
@@ -2089,9 +2074,12 @@ App::post('/v1/account/verification')
Authorization::setRole('user:' . $user->getId());
$user->setAttribute('tokens', $verification, Document::SET_TYPE_APPEND);
$verification = $dbForProject->createDocument('tokens', $verification
->setAttribute('$read', ['user:'.$user->getId()])
->setAttribute('$write', ['user:'.$user->getId()])
);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$dbForProject->deleteCachedDocument('users', $user->getId());
$url = Template::parseURL($url);
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $user->getId(), 'secret' => $verificationSecret, 'expire' => $expire]);
@@ -2161,7 +2149,7 @@ App::put('/v1/account/verification')
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */
$profile = $dbForProject->getDocument('users', $userId);
$profile = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId));
if ($profile->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
@@ -2177,19 +2165,15 @@ App::put('/v1/account/verification')
Authorization::setRole('user:' . $profile->getId());
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true));
$verificationDocument = $dbForProject->getDocument('tokens', $verification);
/**
* We act like we're updating and validating
* the verification token but actually we don't need it anymore.
*/
foreach ($tokens as $key => $token) {
if ($token->getId() === $verification) {
$verification = $token;
unset($tokens[$key]);
}
}
$dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('tokens', $tokens));
$dbForProject->deleteDocument('tokens', $verification);
$dbForProject->deleteCachedDocument('users', $profile->getId());
$audits
->setParam('userId', $profile->getId())
@@ -2200,5 +2184,5 @@ App::put('/v1/account/verification')
$usage
->setParam('users.update', 1)
;
$response->dynamic($verification, Response::MODEL_TOKEN);
$response->dynamic($verificationDocument, Response::MODEL_TOKEN);
});
+27 -38
View File
@@ -1,5 +1,7 @@
<?php
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\URL;
use Appwrite\URL\URL as URLParse;
use Appwrite\Utopia\Response;
use chillerlan\QRCode\QRCode;
@@ -8,17 +10,15 @@ use Utopia\App;
use Utopia\Cache\Adapter\Filesystem;
use Utopia\Cache\Cache;
use Utopia\Config\Config;
use Appwrite\Extend\Exception;
use Utopia\Database\Document;
use Utopia\Image\Image;
use Utopia\Validator\Boolean;
use Utopia\Validator\HexColor;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Appwrite\Network\Validator\URL;
use Utopia\Validator\WhiteList;
$avatarCallback = function ($type, $code, $width, $height, $quality, $response) {
/** @var Appwrite\Utopia\Response $response */
$avatarCallback = function (string $type, string $code, int $width, int $height, int $quality, Response $response) {
$code = \strtolower($code);
$type = \strtolower($type);
@@ -38,7 +38,7 @@ $avatarCallback = function ($type, $code, $width, $height, $quality, $response)
$output = 'png';
$date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT'; // 45 days cache
$key = \md5('/v1/avatars/'.$type.'/:code-' . $code . $width . $height . $quality . $output);
$key = \md5('/v1/avatars/' . $type . '/:code-' . $code . $width . $height . $quality . $output);
$path = $set[$code];
$type = 'png';
@@ -56,8 +56,7 @@ $avatarCallback = function ($type, $code, $width, $height, $quality, $response)
->setContentType('image/png')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'hit')
->send($data)
;
->send($data);
}
$image = new Image(\file_get_contents($path));
@@ -95,7 +94,7 @@ App::get('/v1/avatars/credit-cards/:code')
->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->inject('response')
->action(fn($code, $width, $height, $quality, $response) => $avatarCallback('credit-cards', $code, $width, $height, $quality, $response));
->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('credit-cards', $code, $width, $height, $quality, $response));
App::get('/v1/avatars/browsers/:code')
->desc('Get Browser Icon')
@@ -113,7 +112,7 @@ App::get('/v1/avatars/browsers/:code')
->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->inject('response')
->action(fn($code, $width, $height, $quality, $response) => $avatarCallback('browsers', $code, $width, $height, $quality, $response));
->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('browsers', $code, $width, $height, $quality, $response));
App::get('/v1/avatars/flags/:code')
->desc('Get Country Flag')
@@ -131,7 +130,7 @@ App::get('/v1/avatars/flags/:code')
->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->inject('response')
->action(fn($code, $width, $height, $quality, $response) => $avatarCallback('flags', $code, $width, $height, $quality, $response));
->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('flags', $code, $width, $height, $quality, $response));
App::get('/v1/avatars/image')
->desc('Get Image from URL')
@@ -145,11 +144,10 @@ App::get('/v1/avatars/image')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE)
->param('url', '', new URL(['http', 'https']), 'Image URL which you want to crop.')
->param('width', 400, new Range(0, 2000), 'Resize preview image width, Pass an integer between 0 to 2000.', true)
->param('height', 400, new Range(0, 2000), 'Resize preview image height, Pass an integer between 0 to 2000.', true)
->param('width', 400, new Range(0, 2000), 'Resize preview image width, Pass an integer between 0 to 2000. Defaults to 400.', true)
->param('height', 400, new Range(0, 2000), 'Resize preview image height, Pass an integer between 0 to 2000. Defaults to 400.', true)
->inject('response')
->action(function ($url, $width, $height, $response) {
/** @var Appwrite\Utopia\Response $response */
->action(function (string $url, int $width, int $height, Response $response) {
$quality = 80;
$output = 'png';
@@ -164,8 +162,7 @@ App::get('/v1/avatars/image')
->setContentType('image/png')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'hit')
->send($data)
;
->send($data);
}
if (!\extension_loaded('imagick')) {
@@ -180,7 +177,7 @@ App::get('/v1/avatars/image')
try {
$image = new Image($fetch);
} catch (\Exception$exception) {
} catch (\Exception $exception) {
throw new Exception('Unable to parse image', 500, Exception::GENERAL_SERVER_ERROR);
}
@@ -196,8 +193,7 @@ App::get('/v1/avatars/image')
->setContentType('image/png')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'miss')
->send($data);
;
->send($data);;
unset($image);
});
@@ -215,8 +211,7 @@ App::get('/v1/avatars/favicon')
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE)
->param('url', '', new URL(['http', 'https']), 'Website URL which you want to fetch the favicon from.')
->inject('response')
->action(function ($url, $response) {
/** @var Appwrite\Utopia\Response $response */
->action(function (string $url, Response $response) {
$width = 56;
$height = 56;
@@ -233,8 +228,7 @@ App::get('/v1/avatars/favicon')
->setContentType('image/png')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'hit')
->send($data)
;
->send($data);
}
if (!\extension_loaded('imagick')) {
@@ -248,7 +242,8 @@ App::get('/v1/avatars/favicon')
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3,
CURLOPT_URL => $url,
CURLOPT_USERAGENT => \sprintf(APP_USERAGENT,
CURLOPT_USERAGENT => \sprintf(
APP_USERAGENT,
App::getEnv('_APP_VERSION', 'UNKNOWN'),
App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
),
@@ -326,8 +321,7 @@ App::get('/v1/avatars/favicon')
->setContentType('image/x-icon')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'miss')
->send($data)
;
->send($data);
}
$fetch = @\file_get_contents($outputHref, false);
@@ -367,12 +361,11 @@ App::get('/v1/avatars/qr')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE_PNG)
->param('text', '', new Text(512), 'Plain text to be converted to QR code image.')
->param('size', 400, new Range(0, 1000), 'QR code size. Pass an integer between 0 to 1000. Defaults to 400.', true)
->param('size', 400, new Range(1, 1000), 'QR code size. Pass an integer between 1 to 1000. Defaults to 400.', true)
->param('margin', 1, new Range(0, 10), 'Margin from edge. Pass an integer between 0 to 10. Defaults to 1.', true)
->param('download', false, new Boolean(true), 'Return resulting image with \'Content-Disposition: attachment \' headers for the browser to start downloading it. Pass 0 for no header, or 1 for otherwise. Default value is set to 0.', true)
->inject('response')
->action(function ($text, $size, $margin, $download, $response) {
/** @var Appwrite\Utopia\Response $response */
->action(function (string $text, int $size, int $margin, bool $download, Response $response) {
$download = ($download === '1' || $download === 'true' || $download === 1 || $download === true);
$options = new QROptions([
@@ -394,8 +387,7 @@ App::get('/v1/avatars/qr')
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->setContentType('image/png')
->send($image->output('png', 9))
;
->send($image->output('png', 9));
});
App::get('/v1/avatars/initials')
@@ -416,9 +408,7 @@ App::get('/v1/avatars/initials')
->param('background', '', new HexColor(), 'Changes background color. By default a random color will be picked and stay will persistent to the given name.', true)
->inject('response')
->inject('user')
->action(function ($name, $width, $height, $color, $background, $response, $user) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
->action(function (string $name, int $width, int $height, string $color, string $background, Response $response, Document $user) {
$themes = [
['color' => '#27005e', 'background' => '#e1d2f6'], // VIOLET
@@ -438,8 +428,8 @@ App::get('/v1/avatars/initials')
$name = (!empty($name)) ? $name : $user->getAttribute('name', $user->getAttribute('email', ''));
$words = \explode(' ', \strtoupper($name));
// if there is no space, try to split by `_` underscore
$words = (count($words) == 1 ) ? \explode('_', \strtoupper($name)) : $words;
$words = (count($words) == 1) ? \explode('_', \strtoupper($name)) : $words;
$initials = null;
$code = 0;
@@ -478,6 +468,5 @@ App::get('/v1/avatars/initials')
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->setContentType('image/png')
->send($image->getImageBlob())
;
->send($image->getImageBlob());
});
+10 -2
View File
@@ -19,7 +19,6 @@ use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Permissions;
use Utopia\Database\Validator\QueryValidator;
use Utopia\Database\Validator\Queries as QueriesValidator;
use Utopia\Database\Validator\Structure;
use Utopia\Database\Validator\UID;
use Utopia\Database\Exception\Authorization as AuthorizationException;
@@ -31,6 +30,8 @@ use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\IP;
use Appwrite\Network\Validator\URL;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries as QueriesValidator;
use Appwrite\Utopia\Database\Validator\OrderAttributes as OrderAttributesValidator;
use Appwrite\Utopia\Response;
use Appwrite\Detector\Detector;
use Appwrite\Event\Event;
@@ -150,7 +151,7 @@ App::post('/v1/database/collections')
->label('sdk.response.model', Response::MODEL_COLLECTION)
->param('collectionId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('name', '', new Text(128), 'Collection name. Max length: 128 chars.')
->param('permission', null, new WhiteList(['document', 'collection']), 'Permissions type model to use for reading documents in this collection. You can use collection-level permission set once on the collection using the `read` and `write` params, or you can set document-level permission where each document read and write params will decide who has access to read and write to each document individually. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->param('permission', null, new WhiteList(['document', 'collection']), 'Specifies the permissions model used in this collection, which accepts either \'collection\' or \'document\'. For \'collection\' level permission, the permissions specified in read and write params are applied to all documents in the collection. For \'document\' level permissions, read and write permissions are specified in each document. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->inject('response')
@@ -1742,6 +1743,13 @@ App::get('/v1/database/collections/:collectionId/documents')
}
}
if(!empty($orderAttributes)) {
$validator = new OrderAttributesValidator($collection->getAttribute('attributes', []), $collection->getAttribute('indexes', []), true);
if (!$validator->isValid($orderAttributes)) {
throw new Exception($validator->getDescription(), 400, Exception::GENERAL_QUERY_INVALID);
}
}
$cursorDocument = null;
if (!empty($cursor)) {
$cursorDocument = $collection->getAttribute('permission') === 'collection'
+3 -3
View File
@@ -880,7 +880,7 @@ App::post('/v1/functions/:functionId/executions')
'trigger' => 'http', // http / schedule / event
'status' => 'waiting', // waiting / processing / completed / failed
'statusCode' => 0,
'stdout' => '',
'response' => '',
'stderr' => '',
'time' => 0.0,
'search' => implode(' ', [$functionId, $executionId]),
@@ -938,7 +938,7 @@ App::post('/v1/functions/:functionId/executions')
]);
/** Execute function */
$executor = new Executor();
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
$executionResponse = [];
try {
$executionResponse = $executor->createExecution(
@@ -956,7 +956,7 @@ App::post('/v1/functions/:functionId/executions')
/** Update execution status */
$execution->setAttribute('status', $executionResponse['status']);
$execution->setAttribute('statusCode', $executionResponse['statusCode']);
$execution->setAttribute('stdout', $executionResponse['stdout']);
$execution->setAttribute('response', $executionResponse['response']);
$execution->setAttribute('stderr', $executionResponse['stderr']);
$execution->setAttribute('time', $executionResponse['time']);
} catch (\Throwable $th) {
+19 -52
View File
@@ -1,13 +1,15 @@
<?php
use Appwrite\Utopia\Response;
use Utopia\App;
use Appwrite\Extend\Exception;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage;
use Appwrite\ClamAV\Network;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Database\Document;
use Utopia\Registry\Registry;
use Utopia\Storage\Device;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage;
App::get('/v1/health')
->desc('Get HTTP')
@@ -21,8 +23,7 @@ App::get('/v1/health')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
->action(function (Response $response) {
$output = [
'status' => 'pass',
@@ -40,8 +41,7 @@ App::get('/v1/health/version')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_VERSION)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
->action(function (Response $response) {
$response->dynamic(new Document([ 'version' => APP_VERSION_STABLE ]), Response::MODEL_HEALTH_VERSION);
});
@@ -59,9 +59,7 @@ App::get('/v1/health/db')
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
->inject('response')
->inject('utopia')
->action(function ($response, $utopia) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\App $utopia */
->action(function (Response $response, App $utopia) {
$checkStart = \microtime(true);
@@ -99,10 +97,7 @@ App::get('/v1/health/cache')
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
->inject('response')
->inject('utopia')
->action(function ($response, $utopia) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\App $utopia */
/** @var Redis */
->action(function (Response $response, App $utopia) {
$checkStart = \microtime(true);
@@ -132,8 +127,7 @@ App::get('/v1/health/time')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_TIME)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
->action(function (Response $response) {
/*
* Code from: @see https://www.beliefmedia.com.au/query-ntp-time-server
@@ -190,8 +184,7 @@ App::get('/v1/health/queue/webhooks')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
->action(function (Response $response) {
$response->dynamic(new Document([ 'size' => Resque::size(Event::WEBHOOK_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
@@ -208,30 +201,11 @@ App::get('/v1/health/queue/logs')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
->action(function (Response $response) {
$response->dynamic(new Document([ 'size' => Resque::size(Event::AUDITS_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
App::get('/v1/health/queue/usage')
->desc('Get Usage Queue')
->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueUsage')
->label('sdk.description', '/docs/references/health/get-queue-usage.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
$response->dynamic(new Document([ 'size' => Resque::size(Event::USAGE_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
App::get('/v1/health/queue/certificates')
->desc('Get Certificates Queue')
->groups(['api', 'health'])
@@ -244,8 +218,7 @@ App::get('/v1/health/queue/certificates')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
->action(function (Response $response) {
$response->dynamic(new Document([ 'size' => Resque::size(Event::CERTIFICATES_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
@@ -262,8 +235,7 @@ App::get('/v1/health/queue/functions')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
->action(function (Response $response) {
$response->dynamic(new Document([ 'size' => Resque::size(Event::FUNCTIONS_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
@@ -280,8 +252,7 @@ App::get('/v1/health/storage/local')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
->action(function (Response $response) {
$checkStart = \microtime(true);
@@ -322,8 +293,7 @@ App::get('/v1/health/anti-virus')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_ANTIVIRUS)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
->action(function (Response $response) {
$output = [
'status' => '',
@@ -359,10 +329,7 @@ App::get('/v1/health/stats') // Currently only used internally
->inject('response')
->inject('register')
->inject('deviceFiles')
->action(function ($response, $register, $deviceFiles) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Registry\Registry $register */
/** @var Utopia\Storage\Device $deviceFiles */
->action(function (Response $response, Registry $register, Device $deviceFiles) {
$cache = $register->get('cache');
+26 -44
View File
@@ -1,9 +1,12 @@
<?php
use Utopia\Database\Document;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Request;
use MaxMind\Db\Reader;
use Utopia\App;
use Utopia\Config\Config;
use Utopia\Database\Document;
use Utopia\Locale\Locale;
App::get('/v1/locale')
->desc('Get User Locale')
@@ -20,12 +23,7 @@ App::get('/v1/locale')
->inject('response')
->inject('locale')
->inject('geodb')
->action(function ($request, $response, $locale, $geodb) {
/** @var Appwrite\Utopia\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
/** @var MaxMind\Db\Reader $geodb */
->action(function (Request $request, Response $response, Locale $locale, Reader $geodb) {
$eu = Config::getParam('locale-eu');
$currencies = Config::getParam('locale-currencies');
$output = [];
@@ -40,8 +38,8 @@ App::get('/v1/locale')
if ($record) {
$output['countryCode'] = $record['country']['iso_code'];
$output['country'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
$output['continent'] = $locale->getText('continents.'.strtolower($record['continent']['code']), $locale->getText('locale.country.unknown'));
$output['country'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
$output['continent'] = $locale->getText('continents.' . strtolower($record['continent']['code']), $locale->getText('locale.country.unknown'));
$output['continent'] = (isset($continents[$record['continent']['code']])) ? $continents[$record['continent']['code']] : $locale->getText('locale.country.unknown');
$output['continentCode'] = $record['continent']['code'];
$output['eu'] = (\in_array($record['country']['iso_code'], $eu)) ? true : false;
@@ -63,8 +61,8 @@ App::get('/v1/locale')
}
$response
->addHeader('Cache-Control', 'public, max-age='.$time)
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time).' GMT') // 45 days cache
->addHeader('Cache-Control', 'public, max-age=' . $time)
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time) . ' GMT') // 45 days cache
;
$response->dynamic(new Document($output), Response::MODEL_LOCALE);
});
@@ -82,16 +80,13 @@ App::get('/v1/locale/countries')
->label('sdk.response.model', Response::MODEL_COUNTRY_LIST)
->inject('response')
->inject('locale')
->action(function ($response, $locale) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
->action(function (Response $response, Locale $locale) {
$list = Config::getParam('locale-countries'); /* @var $list array */
$output = [];
foreach ($list as $value) {
$output[] = new Document([
'name' => $locale->getText('countries.'.strtolower($value)),
'name' => $locale->getText('countries.' . strtolower($value)),
'code' => $value,
]);
}
@@ -116,17 +111,14 @@ App::get('/v1/locale/countries/eu')
->label('sdk.response.model', Response::MODEL_COUNTRY_LIST)
->inject('response')
->inject('locale')
->action(function ($response, $locale) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
->action(function (Response $response, Locale $locale) {
$eu = Config::getParam('locale-eu');
$output = [];
foreach ($eu as $code) {
if ($locale->getText('countries.'.strtolower($code), false) !== false) {
if ($locale->getText('countries.' . strtolower($code), false) !== false) {
$output[] = new Document([
'name' => $locale->getText('countries.'.strtolower($code)),
'name' => $locale->getText('countries.' . strtolower($code)),
'code' => $code,
]);
}
@@ -152,21 +144,18 @@ App::get('/v1/locale/countries/phones')
->label('sdk.response.model', Response::MODEL_PHONE_LIST)
->inject('response')
->inject('locale')
->action(function ($response, $locale) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
->action(function (Response $response, Locale $locale) {
$list = Config::getParam('locale-phones'); /* @var $list array */
$output = [];
\asort($list);
foreach ($list as $code => $name) {
if ($locale->getText('countries.'.strtolower($code), false) !== false) {
if ($locale->getText('countries.' . strtolower($code), false) !== false) {
$output[] = new Document([
'code' => '+'.$list[$code],
'code' => '+' . $list[$code],
'countryCode' => $code,
'countryName' => $locale->getText('countries.'.strtolower($code)),
'countryName' => $locale->getText('countries.' . strtolower($code)),
]);
}
}
@@ -187,15 +176,12 @@ App::get('/v1/locale/continents')
->label('sdk.response.model', Response::MODEL_CONTINENT_LIST)
->inject('response')
->inject('locale')
->action(function ($response, $locale) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
->action(function (Response $response, Locale $locale) {
$list = Config::getParam('locale-continents');
$list = Config::getParam('locale-continents'); /* @var $list array */
foreach ($list as $key => $value) {
foreach ($list as $value) {
$output[] = new Document([
'name' => $locale->getText('continents.'.strtolower($value)),
'name' => $locale->getText('continents.' . strtolower($value)),
'code' => $value,
]);
}
@@ -219,12 +205,10 @@ App::get('/v1/locale/currencies')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_CURRENCY_LIST)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
->action(function (Response $response) {
$list = Config::getParam('locale-currencies');
$list = array_map(fn($node) => new Document($node), $list);
$list = array_map(fn ($node) => new Document($node), $list);
$response->dynamic(new Document(['currencies' => $list, 'total' => \count($list)]), Response::MODEL_CURRENCY_LIST);
});
@@ -242,12 +226,10 @@ App::get('/v1/locale/languages')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_LANGUAGE_LIST)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
->action(function (Response $response) {
$list = Config::getParam('locale-languages');
$list = array_map(fn ($node) => new Document($node), $list);
$response->dynamic(new Document(['languages' => $list, 'total' => \count($list)]), Response::MODEL_LANGUAGE_LIST);
});
});
+41 -101
View File
@@ -2,6 +2,7 @@
use Appwrite\Auth\Auth;
use Appwrite\Auth\Validator\Password;
use Appwrite\Event\Event;
use Appwrite\Network\Validator\CNAME;
use Appwrite\Network\Validator\Domain as DomainValidator;
use Appwrite\Network\Validator\Origin;
@@ -18,6 +19,7 @@ use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\UID;
use Utopia\Domains\Domain;
use Utopia\Registry\Registry;
use Appwrite\Extend\Exception;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean;
@@ -26,8 +28,7 @@ use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
App::init(function ($project) {
/** @var Utopia\Database\Document $project */
App::init(function (Document $project) {
if ($project->getId() !== 'console') {
throw new Exception('Access to this API is forbidden.', 401, Exception::GENERAL_ACCESS_FORBIDDEN);
@@ -59,10 +60,7 @@ App::post('/v1/projects')
->inject('response')
->inject('dbForConsole')
->inject('dbForProject')
->action(function ($projectId, $name, $teamId, $description, $logo, $url, $legalName, $legalCountry, $legalState, $legalCity, $legalAddress, $legalTaxId, $response, $dbForConsole, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
/** @var Utopia\Database\Database $dbForProject */
->action(function (string $projectId, string $name, string $teamId, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForConsole, Database $dbForProject) {
$team = $dbForConsole->getDocument('teams', $teamId);
@@ -77,6 +75,9 @@ App::post('/v1/projects')
}
$projectId = ($projectId == 'unique()') ? $dbForConsole->getId() : $projectId;
if($projectId === 'console') {
throw new Exception("'console' is a reserved project.", 400, Exception::PROJECT_RESERVED_PROJECT);
}
$project = $dbForConsole->createDocument('projects', new Document([
'$id' => $projectId == 'unique()' ? $dbForConsole->getId() : $projectId,
'$read' => ['team:' . $teamId],
@@ -95,7 +96,7 @@ App::post('/v1/projects')
'legalTaxId' => $legalTaxId,
'services' => new stdClass(),
'platforms' => null,
'providers' => [],
'authProviders' => [],
'webhooks' => null,
'keys' => null,
'domains' => null,
@@ -130,6 +131,8 @@ App::post('/v1/projects')
'signed' => $attribute['signed'],
'array' => $attribute['array'],
'filters' => $attribute['filters'],
'default' => $attribute['default'] ?? null,
'format' => $attribute['format'] ?? ''
]);
}
@@ -168,9 +171,7 @@ App::get('/v1/projects')
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForConsole')
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForConsole) {
if (!empty($cursor)) {
$cursorProject = $dbForConsole->getDocument('projects', $cursor);
@@ -208,9 +209,7 @@ App::get('/v1/projects/:projectId')
->param('projectId', '', new UID(), 'Project unique ID.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -237,11 +236,7 @@ App::get('/v1/projects/:projectId/usage')
->inject('dbForConsole')
->inject('dbForProject')
->inject('register')
->action(function ($projectId, $range, $response, $dbForConsole, $dbForProject, $register) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Registry\Registry $register */
->action(function (string $projectId, string $range, Response $response, Database $dbForConsole, Database $dbForProject, Registry $register) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -358,9 +353,7 @@ App::patch('/v1/projects/:projectId')
->param('legalTaxId', '', new Text(256), 'Project legal tax ID. Max length: 256 chars.', true)
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $name, $description, $logo, $url, $legalName, $legalCountry, $legalState, $legalCity, $legalAddress, $legalTaxId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $name, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -400,10 +393,7 @@ App::patch('/v1/projects/:projectId/service')
->param('status', null, new Boolean(), 'Service status.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $service, $status, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
/** @var Boolean $status */
->action(function (string $projectId, string $service, bool $status, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -435,9 +425,7 @@ App::patch('/v1/projects/:projectId/oauth2')
->param('secret', '', new text(512), 'Provider secret key. Max length: 512 chars.', true)
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $provider, $appId, $secret, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $provider, string $appId, string $secret, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -445,11 +433,11 @@ App::patch('/v1/projects/:projectId/oauth2')
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
}
$providers = $project->getAttribute('providers', []);
$providers = $project->getAttribute('authProviders', []);
$providers[$provider . 'Appid'] = $appId;
$providers[$provider . 'Secret'] = $secret;
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('providers', $providers));
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('authProviders', $providers));
$response->dynamic($project, Response::MODEL_PROJECT);
});
@@ -468,9 +456,7 @@ App::patch('/v1/projects/:projectId/auth/limit')
->param('limit', false, new Range(0, APP_LIMIT_USERS), 'Set the max number of users allowed in this project. Use 0 for unlimited.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $limit, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -503,9 +489,7 @@ App::patch('/v1/projects/:projectId/auth/:method')
->param('status', false, new Boolean(true), 'Set the status of this auth method.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $method, $status, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $method, bool $status, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
$auth = Config::getParam('auth')[$method] ?? [];
@@ -539,11 +523,7 @@ App::delete('/v1/projects/:projectId')
->inject('user')
->inject('dbForConsole')
->inject('deletes')
->action(function ($projectId, $password, $response, $user, $dbForConsole, $deletes) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForConsole */
/** @var Appwrite\Event\Event $deletes */
->action(function (string $projectId, string $password, Response $response, Document $user, Database $dbForConsole, Event $deletes) {
if (!Auth::passwordVerify($password, $user->getAttribute('password'))) { // Double check user password
throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS);
@@ -592,9 +572,7 @@ App::post('/v1/projects/:projectId/webhooks')
->param('httpPass', '', new Text(256), 'Webhook HTTP password. Max length: 256 chars.', true)
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $name, $events, $url, $security, $httpUser, $httpPass, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $name, array $events, string $url, bool $security, string $httpUser, string $httpPass, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -638,9 +616,7 @@ App::get('/v1/projects/:projectId/webhooks')
->param('projectId', '', new UID(), 'Project unique ID.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -672,9 +648,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
->param('webhookId', null, new UID(), 'Webhook unique ID.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $webhookId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -714,9 +688,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
->param('httpPass', '', new Text(256), 'Webhook HTTP password. Max length: 256 chars.', true)
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $webhookId, $name, $events, $url, $security, $httpUser, $httpPass, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $webhookId, string $name, array $events, string $url, bool $security, string $httpUser, string $httpPass, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -764,9 +736,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
->param('webhookId', null, new UID(), 'Webhook unique ID.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $webhookId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -807,9 +777,7 @@ App::post('/v1/projects/:projectId/keys')
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true)), 'Key scopes list.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $name, $scopes, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $name, array $scopes, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -848,9 +816,7 @@ App::get('/v1/projects/:projectId/keys')
->param('projectId', null, new UID(), 'Project unique ID.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -882,9 +848,7 @@ App::get('/v1/projects/:projectId/keys/:keyId')
->param('keyId', null, new UID(), 'Key unique ID.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $keyId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -920,9 +884,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true)), 'Key scopes list')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $keyId, $name, $scopes, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $keyId, string $name, array $scopes, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -964,9 +926,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
->param('keyId', null, new UID(), 'Key unique ID.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $keyId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -1010,9 +970,7 @@ App::post('/v1/projects/:projectId/platforms')
->param('hostname', '', new Text(256), 'Platform client hostname. Max length: 256 chars.', true)
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $type, $name, $key, $store, $hostname, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $type, string $name, string $key, string $store, string $hostname, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -1055,9 +1013,7 @@ App::get('/v1/projects/:projectId/platforms')
->param('projectId', '', new UID(), 'Project unique ID.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -1089,9 +1045,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
->param('platformId', null, new UID(), 'Platform unique ID.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $platformId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -1129,9 +1083,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
->param('hostname', '', new Text(256), 'Platform client URL. Max length: 256 chars.', true)
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $platformId, $name, $key, $store, $hostname, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $platformId, string $name, string $key, string $store, string $hostname, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -1176,9 +1128,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
->param('platformId', null, new UID(), 'Platform unique ID.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $platformId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -1218,9 +1168,7 @@ App::post('/v1/projects/:projectId/domains')
->param('domain', null, new DomainValidator(), 'Domain name.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $domain, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $domain, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -1279,9 +1227,7 @@ App::get('/v1/projects/:projectId/domains')
->param('projectId', '', new UID(), 'Project unique ID.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -1313,9 +1259,7 @@ App::get('/v1/projects/:projectId/domains/:domainId')
->param('domainId', null, new UID(), 'Domain unique ID.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $domainId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -1349,9 +1293,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
->param('domainId', null, new UID(), 'Domain unique ID.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $domainId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -1411,9 +1353,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId')
->inject('response')
->inject('dbForConsole')
->inject('deletes')
->action(function ($projectId, $domainId, $response, $dbForConsole, $deletes) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole, $deletes) {
$project = $dbForConsole->getDocument('projects', $projectId);
+22 -93
View File
@@ -2,8 +2,10 @@
use Appwrite\Auth\Auth;
use Appwrite\ClamAV\Network;
use Appwrite\Event\Event;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Stats\Stats;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Cache\Adapter\Filesystem;
@@ -21,6 +23,7 @@ use Utopia\Database\Validator\UID;
use Appwrite\Extend\Exception;
use Utopia\Image\Image;
use Utopia\Storage\Compression\Algorithms\GZIP;
use Utopia\Storage\Device;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage;
use Utopia\Storage\Validator\File;
@@ -34,6 +37,7 @@ use Utopia\Validator\Integer;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
use Utopia\Swoole\Request;
App::post('/v1/storage/buckets')
->desc('Create bucket')
@@ -61,11 +65,7 @@ App::post('/v1/storage/buckets')
->inject('dbForProject')
->inject('audits')
->inject('usage')
->action(function ($bucketId, $name, $permission, $read, $write, $enabled, $maximumFileSize, $allowedFileExtensions, $encryption, $antivirus, $response, $dbForProject, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */
->action(function (string $bucketId, string $name, string $permission, ?array $read, ?array $write, bool $enabled, int $maximumFileSize, array $allowedFileExtensions, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Event $audits, Stats $usage) {
$bucketId = $bucketId === 'unique()' ? $dbForProject->getId() : $bucketId;
try {
@@ -86,6 +86,8 @@ App::post('/v1/storage/buckets')
'signed' => $attribute['signed'],
'array' => $attribute['array'],
'filters' => $attribute['filters'],
'default' => $attribute['default'] ?? null,
'format' => $attribute['format'] ?? ''
]);
}
@@ -155,12 +157,9 @@ App::get('/v1/storage/buckets')
->inject('response')
->inject('dbForProject')
->inject('usage')
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */
->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject, Stats $usage) {
$queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, $search)] : [];
$queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, [$search])] : [];
if (!empty($cursor)) {
$cursorBucket = $dbForProject->getDocument('buckets', $cursor);
@@ -193,10 +192,7 @@ App::get('/v1/storage/buckets/:bucketId')
->inject('response')
->inject('dbForProject')
->inject('usage')
->action(function ($bucketId, $response, $dbForProject, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */
->action(function (string $bucketId, Response $response, Database $dbForProject, Stats $usage) {
$bucket = $dbForProject->getDocument('buckets', $bucketId);
@@ -235,11 +231,7 @@ App::put('/v1/storage/buckets/:bucketId')
->inject('dbForProject')
->inject('audits')
->inject('usage')
->action(function ($bucketId, $name, $permission, $read, $write, $enabled, $maximumFileSize, $allowedFileExtensions, $encryption, $antivirus, $response, $dbForProject, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */
->action(function (string $bucketId, string $name, string $permission, ?array $read, ?array $write, bool $enabled, ?int $maximumFileSize, array $allowedFileExtensions, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Event $audits, Stats $usage) {
$bucket = $dbForProject->getDocument('buckets', $bucketId);
@@ -296,13 +288,7 @@ App::delete('/v1/storage/buckets/:bucketId')
->inject('deletes')
->inject('events')
->inject('usage')
->action(function ($bucketId, $response, $dbForProject, $audits, $deletes, $events, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $deletes */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Stats\Stats $usage */
->action(function (string $bucketId, Response $response, Database $dbForProject, Event $audits, Event $deletes, Event $events, Stats $usage) {
$bucket = $dbForProject->getDocument('buckets', $bucketId);
@@ -364,17 +350,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
->inject('mode')
->inject('deviceFiles')
->inject('deviceLocal')
->action(function ($bucketId, $fileId, $file, $read, $write, $request, $response, $dbForProject, $user, $audits, $usage, $events, $mode, $deviceFiles, $deviceLocal) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $user */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Stats\Stats $usage */
/** @var Utopia\Storage\Device $deviceFiles */
/** @var Utopia\Storage\Device $deviceLocal */
/** @var string $mode */
->action(function (string $bucketId, string $fileId, array $file, ?array $read, ?array $write, Request $request, Response $response, Database $dbForProject, Document $user, Event $audits, Stats $usage, Event $events, string $mode, Device $deviceFiles, Device $deviceLocal) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
@@ -709,11 +685,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
->inject('dbForProject')
->inject('usage')
->inject('mode')
->action(function ($bucketId, $search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject, $usage, $mode) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */
/** @var string $mode */
->action(function (string $bucketId, string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject, Stats $usage, string $mode) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
@@ -789,11 +761,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
->inject('dbForProject')
->inject('usage')
->inject('mode')
->action(function ($bucketId, $fileId, $response, $dbForProject, $usage, $mode) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */
/** @var string $mode */
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Stats $usage, string $mode) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
@@ -861,15 +829,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
->inject('mode')
->inject('deviceFiles')
->inject('deviceLocal')
->action(function ($bucketId, $fileId, $width, $height, $gravity, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $request, $response, $project, $dbForProject, $usage, $mode, $deviceFiles, $deviceLocal) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */
/** @var Utopia\Storage\Device $deviceFiles */
/** @var Utopia\Storage\Device $deviceLocal */
/** @var string $mode */
->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Request $request, Response $response, Document $project, Database $dbForProject, Stats $usage, string $mode, Device $deviceFiles, Device $deviceLocal) {
if (!\extension_loaded('imagick')) {
throw new Exception('Imagick extension is missing', 500, Exception::GENERAL_SERVER_ERROR);
@@ -1039,13 +999,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
->inject('usage')
->inject('mode')
->inject('deviceFiles')
->action(function ($bucketId, $fileId, $request, $response, $dbForProject, $usage, $mode, $deviceFiles) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */
/** @var Utopia\Storage\Device $deviceFiles */
/** @var string $mode */
->action(function (string $bucketId, string $fileId, Request $request, Response $response, Database $dbForProject, Stats $usage, string $mode, Device $deviceFiles) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
@@ -1182,13 +1136,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
->inject('usage')
->inject('mode')
->inject('deviceFiles')
->action(function ($bucketId, $fileId, $response, $request, $dbForProject, $usage, $mode, $deviceFiles) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Appwrite\Stats\Stats $usage */
/** @var Utopia\Storage\Device $deviceFiles */
/** @var string $mode */
->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, Stats $usage, string $mode, Device $deviceFiles) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
@@ -1342,14 +1290,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
->inject('usage')
->inject('mode')
->inject('events')
->action(function ($bucketId, $fileId, $read, $write, $response, $dbForProject, $user, $audits, $usage, $mode, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $user */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
/** @var string $mode */
->action(function (string $bucketId, string $fileId, ?array $read, ?array $write,Response $response, Database $dbForProject, Document $user, Event $audits, Stats $usage, string $mode, Event $events) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$read = (is_null($read) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $read ?? []; // By default set read permissions for user
@@ -1442,15 +1383,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
->inject('mode')
->inject('deviceFiles')
->inject('project')
->action(function ($bucketId, $fileId, $response, $dbForProject, $events, $audits, $usage, $mode, $deviceFiles, $project) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Utopia\Storage\Device $deviceFiles */
/** @var string $mode */
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $events, Event $audits, Stats $usage, string $mode, Device $deviceFiles, Document $project) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
@@ -1537,9 +1470,7 @@ App::get('/v1/storage/usage')
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
->inject('response')
->inject('dbForProject')
->action(function ($range, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
->action(function (string $range, Response $response, Database $dbForProject) {
$usage = [];
if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') {
@@ -1649,9 +1580,7 @@ App::get('/v1/storage/:bucketId/usage')
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
->inject('response')
->inject('dbForProject')
->action(function ($bucketId, $range, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
->action(function (string $bucketId, string $range, Response $response, Database $dbForProject) {
$bucket = $dbForProject->getDocument('buckets', $bucketId);
+135 -86
View File
@@ -2,13 +2,17 @@
use Appwrite\Auth\Auth;
use Appwrite\Detector\Detector;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\Host;
use Appwrite\Template\Template;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use MaxMind\Db\Reader;
use Utopia\App;
use Appwrite\Extend\Exception;
use Utopia\Audit\Audit;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
@@ -18,6 +22,7 @@ use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Locale\Locale;
use Utopia\Validator\Text;
use Utopia\Validator\Range;
use Utopia\Validator\ArrayList;
@@ -42,11 +47,8 @@ App::post('/v1/teams')
->inject('user')
->inject('dbForProject')
->inject('events')
->action(function ($teamId, $name, $roles, $response, $user, $dbForProject, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $events */
->inject('audits')
->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $events, Event $audits) {
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAppUser = Auth::isAppUser(Authorization::getRoles());
@@ -79,16 +81,19 @@ App::post('/v1/teams')
]);
$membership = $dbForProject->createDocument('memberships', $membership);
// Attach user to team
$user->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$dbForProject->deleteCachedDocument('users', $user->getId());
}
if (!empty($user->getId())) {
$events->setParam('userId', $user->getId());
}
$audits
->setParam('event', 'teams.create')
->setParam('resource', 'team/'.$teamId)
->setParam('data', $team->getArrayCopy())
;
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($team, Response::MODEL_TEAM);
});
@@ -112,9 +117,7 @@ App::get('/v1/teams')
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForProject')
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject) {
if (!empty($cursor)) {
$cursorTeam = $dbForProject->getDocument('teams', $cursor);
@@ -153,9 +156,7 @@ App::get('/v1/teams/:teamId')
->param('teamId', '', new UID(), 'Team ID.')
->inject('response')
->inject('dbForProject')
->action(function ($teamId, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
->action(function (string $teamId, Response $response, Database $dbForProject) {
$team = $dbForProject->getDocument('teams', $teamId);
@@ -182,9 +183,8 @@ App::put('/v1/teams/:teamId')
->param('name', null, new Text(128), 'New team name. Max length: 128 chars.')
->inject('response')
->inject('dbForProject')
->action(function ($teamId, $name, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
->inject('audits')
->action(function (string $teamId, string $name, Response $response, Database $dbForProject, Event $audits) {
$team = $dbForProject->getDocument('teams', $teamId);
@@ -197,6 +197,12 @@ App::put('/v1/teams/:teamId')
->setAttribute('search', implode(' ', [$teamId, $name]))
);
$audits
->setParam('event', 'teams.update')
->setParam('resource', 'team/'.$teamId)
->setParam('data', $team->getArrayCopy())
;
$response->dynamic($team, Response::MODEL_TEAM);
});
@@ -216,11 +222,8 @@ App::delete('/v1/teams/:teamId')
->inject('dbForProject')
->inject('events')
->inject('deletes')
->action(function ($teamId, $response, $dbForProject, $events, $deletes) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $deletes */
->inject('audits')
->action(function (string $teamId, Response $response, Database $dbForProject, Event $events, Event $deletes, Event $audits) {
$team = $dbForProject->getDocument('teams', $teamId);
@@ -252,6 +255,12 @@ App::delete('/v1/teams/:teamId')
->setParam('eventData', $response->output($team, Response::MODEL_TEAM))
;
$audits
->setParam('event', 'teams.delete')
->setParam('resource', 'team/'.$teamId)
->setParam('data', $team->getArrayCopy())
;
$response->noContent();
});
@@ -281,13 +290,7 @@ App::post('/v1/teams/:teamId/memberships')
->inject('locale')
->inject('audits')
->inject('mails')
->action(function ($teamId, $email, $roles, $url, $name, $response, $project, $user, $dbForProject, $locale, $audits, $mails) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $mails */
->action(function (string $teamId, string $email, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $audits, Event $mails) {
if(empty(App::getEnv('_APP_SMTP_HOST'))) {
throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED);
@@ -338,10 +341,11 @@ App::post('/v1/teams/:teamId/memberships')
'reset' => false,
'name' => $name,
'prefs' => new \stdClass(),
'sessions' => [],
'tokens' => [],
'memberships' => [],
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $name]),
'deleted' => false
])));
} catch (Duplicate $th) {
throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS);
@@ -380,10 +384,7 @@ App::post('/v1/teams/:teamId/memberships')
$team->setAttribute('total', $team->getAttribute('total', 0) + 1);
$team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team));
// Attach user to team
$invitee->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND);
$invitee = Authorization::skip(fn() => $dbForProject->updateDocument('users', $invitee->getId(), $invitee));
$dbForProject->deleteCachedDocument('users', $invitee->getId());
} else {
try {
$membership = $dbForProject->createDocument('memberships', $membership);
@@ -445,9 +446,7 @@ App::get('/v1/teams/:teamId/memberships')
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForProject')
->action(function ($teamId, $search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
->action(function (string $teamId, string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject) {
$team = $dbForProject->getDocument('teams', $teamId);
@@ -519,9 +518,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
->param('membershipId', '', new UID(), 'Membership ID.')
->inject('response')
->inject('dbForProject')
->action(function ($teamId, $membershipId, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject) {
$team = $dbForProject->getDocument('teams', $teamId);
@@ -565,12 +562,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->inject('user')
->inject('dbForProject')
->inject('audits')
->action(function ($teamId, $membershipId, $roles, $request, $response, $user, $dbForProject, $audits) {
/** @var Appwrite\Utopia\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $audits) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -604,13 +596,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
/**
* Replace membership on profile
*/
$memberships = array_filter($profile->getAttribute('memberships'), fn (Document $m) => $m->getId() !== $membership->getId());
$profile
->setAttribute('memberships', $memberships)
->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND);
Authorization::skip(fn () => $dbForProject->updateDocument('users', $profile->getId(), $profile));
$dbForProject->deleteCachedDocument('users', $profile->getId());
$audits
->setParam('userId', $user->getId())
@@ -647,13 +634,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->inject('dbForProject')
->inject('geodb')
->inject('audits')
->action(function ($teamId, $membershipId, $userId, $secret, $request, $response, $user, $dbForProject, $geodb, $audits) {
/** @var Appwrite\Utopia\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Event\Event $audits */
->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Reader $geodb, Event $audits) {
$protocol = $request->getProtocol();
@@ -700,7 +681,6 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
$user
->setAttribute('emailVerification', true)
->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND)
;
// Log user in
@@ -728,12 +708,13 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->setAttribute('$write', ['user:'.$user->getId()])
);
$user->setAttribute('sessions', $session, Document::SET_TYPE_APPEND);
$dbForProject->deleteCachedDocument('users', $user->getId());
Authorization::setRole('user:'.$userId);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership);
$dbForProject->deleteCachedDocument('users', $user->getId());
$team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team->setAttribute('total', $team->getAttribute('total', 0) + 1)));
@@ -777,11 +758,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
->inject('dbForProject')
->inject('audits')
->inject('events')
->action(function ($teamId, $membershipId, $response, $dbForProject, $audits, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $events */
->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $audits, Event $events) {
$membership = $dbForProject->getDocument('memberships', $membershipId);
@@ -813,20 +790,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
throw new Exception('Failed to remove membership from DB', 500, Exception::GENERAL_SERVER_ERROR);
}
$memberships = $user->getAttribute('memberships', []);
foreach ($memberships as $key => $child) {
/** @var Document $child */
if ($membershipId == $child->getId()) {
unset($memberships[$key]);
break;
}
}
$user->setAttribute('memberships', $memberships);
Authorization::skip(fn() => $dbForProject->updateDocument('users', $user->getId(), $user));
$dbForProject->deleteCachedDocument('users', $user->getId());
if ($membership->getAttribute('confirm')) { // Count only confirmed members
$team->setAttribute('total', \max($team->getAttribute('total', 0) - 1, 0));
@@ -845,3 +809,88 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
$response->noContent();
});
App::get('/v1/teams/:teamId/logs')
->desc('List Team Logs')
->groups(['api', 'teams'])
->label('scope', 'teams.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'teams')
->label('sdk.method', 'listLogs')
->label('sdk.description', '/docs/references/teams/get-team-logs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_LOG_LIST)
->param('teamId', null, new UID(), 'Team ID.')
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this value to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->inject('response')
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->action(function ($teamId, $limit, $offset, $response, $dbForProject, $locale, $geodb) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */
/** @var MaxMind\Db\Reader $geodb */
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
}
$audit = new Audit($dbForProject);
$resource = 'team/'.$team->getId();
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$output = [];
foreach ($logs as $i => &$log) {
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
$detector = new Detector($log['userAgent']);
$detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
$os = $detector->getOS();
$client = $detector->getClient();
$device = $detector->getDevice();
$output[$i] = new Document([
'event' => $log['event'],
'userId' => $log['userId'],
'userEmail' => $log['data']['userEmail'] ?? null,
'userName' => $log['data']['userName'] ?? null,
'mode' => $log['data']['mode'] ?? null,
'ip' => $log['ip'],
'time' => $log['time'],
'osCode' => $os['osCode'],
'osName' => $os['osName'],
'osVersion' => $os['osVersion'],
'clientType' => $client['clientType'],
'clientCode' => $client['clientCode'],
'clientName' => $client['clientName'],
'clientVersion' => $client['clientVersion'],
'clientEngine' => $client['clientEngine'],
'clientEngineVersion' => $client['clientEngineVersion'],
'deviceName' => $device['deviceName'],
'deviceBrand' => $device['deviceBrand'],
'deviceModel' => $device['deviceModel']
]);
$record = $geodb->get($log['ip']);
if ($record) {
$output[$i]['countryCode'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
} else {
$output[$i]['countryCode'] = '--';
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
}
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
+28 -23
View File
@@ -63,9 +63,9 @@ App::post('/v1/users')
'reset' => false,
'name' => $name,
'prefs' => new \stdClass(),
'sessions' => [],
'tokens' => [],
'memberships' => [],
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $name]),
'deleted' => false
]));
@@ -453,7 +453,12 @@ App::patch('/v1/users/:userId/name')
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('name', $name));
$user
->setAttribute('name', $name)
->setAttribute('search', \implode(' ', [$user->getId(), $user->getAttribute('email'), $name]));
;
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$audits
->setParam('userId', $user->getId())
@@ -542,8 +547,13 @@ App::patch('/v1/users/:userId/email')
$email = \strtolower($email);
$user
->setAttribute('email', $email)
->setAttribute('search', \implode(' ', [$user->getId(), $email, $user->getAttribute('name')]))
;
try {
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('email', $email));
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
} catch(Duplicate $th) {
throw new Exception('Email already exists', 409, Exception::USER_EMAIL_ALREADY_EXISTS);
}
@@ -622,25 +632,20 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
}
$sessions = $user->getAttribute('sessions', []);
$session = $dbForProject->getDocument('sessions', $sessionId);
foreach ($sessions as $key => $session) { /** @var Document $session */
if ($sessionId == $session->getId()) {
unset($sessions[$key]);
$dbForProject->deleteDocument('sessions', $session->getId());
$user->setAttribute('sessions', $sessions);
$events
->setParam('eventData', $response->output($user, Response::MODEL_USER))
;
$dbForProject->updateDocument('users', $user->getId(), $user);
}
if($session->isEmpty()) {
throw new Exception('User not found', 404, Exception::USER_SESSION_NOT_FOUND);
}
$dbForProject->deleteDocument('sessions', $session->getId());
$dbForProject->deleteCachedDocument('users', $user->getId());
$events
->setParam('eventData', $response->output($user, Response::MODEL_USER))
;
$usage
->setParam('users.update', 1)
->setParam('users.sessions.delete', 1)
@@ -683,7 +688,7 @@ App::delete('/v1/users/:userId/sessions')
$dbForProject->deleteDocument('sessions', $session->getId());
}
$dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', []));
$dbForProject->deleteCachedDocument('users', $user->getId());
$events
->setParam('eventData', $response->output($user, Response::MODEL_USER))
@@ -739,7 +744,7 @@ App::delete('/v1/users/:userId')
->setAttribute("email", null)
->setAttribute("password", null)
->setAttribute("deleted", true)
->setAttribute("tokens", [])
->setAttribute("tokens", null)
->setAttribute("search", null)
;
+33 -21
View File
@@ -12,6 +12,7 @@ use Appwrite\Extend\Exception;
use Utopia\Config\Config;
use Utopia\Domains\Domain;
use Appwrite\Auth\Auth;
use Appwrite\Event\Event;
use Appwrite\Network\Validator\Origin;
use Appwrite\Utopia\Response\Filters\V11 as ResponseV11;
use Appwrite\Utopia\Response\Filters\V12 as ResponseV12;
@@ -73,33 +74,44 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
} else {
Authorization::disable();
$domainDocument = $dbForConsole->findOne('domains', [
new Query('domain', QUERY::TYPE_EQUAL, [$domain->get()])
]);
$mainDomain = null;
if(!empty(App::getEnv('_APP_DOMAIN', ''))) {
$mainDomain = App::getEnv('_APP_DOMAIN', '');
} else {
$domainDocument = $dbForConsole->findOne('domains', [], 0, ['_id'], ['ASC']);
$mainDomain = $domainDocument ? $domainDocument->getAttribute('domain') : $domain->get();
}
if (!$domainDocument) {
$domainDocument = new Document([
'domain' => $domain->get(),
'tld' => $domain->getSuffix(),
'registerable' => $domain->getRegisterable(),
'verification' => false,
'certificateId' => null,
if($mainDomain !== $domain->get()) {
Console::warning($domain->get() . ' is not a main domain. Skipping SSL certificate generation.');
} else {
$domainDocument = $dbForConsole->findOne('domains', [
new Query('domain', QUERY::TYPE_EQUAL, [$domain->get()])
]);
$domainDocument = $dbForConsole->createDocument('domains', $domainDocument);
Console::info('Issuing a TLS certificate for the master domain (' . $domain->get() . ') in a few seconds...');
Resque::enqueue('v1-certificates', 'CertificatesV1', [
'document' => $domainDocument,
'domain' => $domain->get(),
'validateTarget' => false,
'validateCNAME' => false,
]);
if (!$domainDocument) {
$domainDocument = new Document([
'domain' => $domain->get(),
'tld' => $domain->getSuffix(),
'registerable' => $domain->getRegisterable(),
'verification' => false,
'certificateId' => null,
]);
$domainDocument = $dbForConsole->createDocument('domains', $domainDocument);
Console::info('Issuing a TLS certificate for the main domain (' . $domain->get() . ') in a few seconds...');
Resque::enqueue(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME, [
'document' => $domainDocument,
'domain' => $domain->get(),
'validateTarget' => false,
'validateCNAME' => false,
]);
}
}
$domains[$domain->get()] = true;
Authorization::reset(); // ensure authorization is re-enabled
}
Config::setParam('domains', $domains);
-90
View File
@@ -1,90 +0,0 @@
CREATE DATABASE IF NOT EXISTS `appwrite` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
USE `appwrite`;
CREATE TABLE IF NOT EXISTS `template.abuse.abuse` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`_key` varchar(255) NOT NULL,
`_time` int(11) NOT NULL,
`_count` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `unique1` (`_key`,`_time`),
KEY `index1` (`_key`,`_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `template.audit.audit` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userId` varchar(45) NOT NULL,
`event` varchar(45) NOT NULL,
`resource` varchar(45) DEFAULT NULL,
`userAgent` text NOT NULL,
`ip` varchar(45) NOT NULL,
`location` varchar(45) DEFAULT NULL,
`time` datetime NOT NULL,
`data` longtext DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`),
KEY `index_1` (`userId`),
KEY `index_2` (`event`),
KEY `index_3` (`resource`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `template.database.documents` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Unique ID for each node',
`uid` varchar(45) DEFAULT NULL,
`status` int(11) NOT NULL DEFAULT 0,
`createdAt` datetime DEFAULT NULL,
`updatedAt` datetime DEFAULT NULL,
`signature` varchar(32) NOT NULL,
`revision` varchar(45) NOT NULL,
`permissions` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`),
UNIQUE KEY `index2` (`uid`),
KEY `index3` (`signature`,`uid`,`revision`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `template.database.properties` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
`documentUid` varchar(45) NOT NULL COMMENT 'Unique UID foreign key',
`documentRevision` varchar(45) NOT NULL,
`key` varchar(32) NOT NULL COMMENT 'Property key name',
`value` text NOT NULL COMMENT 'Value of property',
`primitive` varchar(32) NOT NULL COMMENT 'Primitive type of property value',
`array` tinyint(4) NOT NULL DEFAULT 0,
`order` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `index1` (`documentUid`),
KEY `index2` (`key`,`value`(5)),
FULLTEXT KEY `index3` (`value`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `template.database.relationships` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`revision` varchar(45) NOT NULL,
`start` varchar(45) NOT NULL COMMENT 'Unique UID foreign key',
`end` varchar(45) NOT NULL COMMENT 'Unique UID foreign key',
`key` varchar(256) NOT NULL,
`path` int(11) NOT NULL DEFAULT 0,
`array` tinyint(4) NOT NULL DEFAULT 0,
`order` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `relationships_start_nodes_id_idx` (`start`),
KEY `relationships_end_nodes_id_idx` (`end`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `template.database.unique` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`key` varchar(128) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index1` (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/* Default App */
CREATE TABLE IF NOT EXISTS `app_console.database.documents` LIKE `template.database.documents`;
CREATE TABLE IF NOT EXISTS `app_console.database.properties` LIKE `template.database.properties`;
CREATE TABLE IF NOT EXISTS `app_console.database.relationships` LIKE `template.database.relationships`;
CREATE TABLE IF NOT EXISTS `app_console.database.unique` LIKE `template.database.unique`;
CREATE TABLE IF NOT EXISTS `app_console.audit.audit` LIKE `template.audit.audit`;
CREATE TABLE IF NOT EXISTS `app_console.abuse.abuse` LIKE `template.abuse.abuse`;
+5 -9
View File
@@ -141,7 +141,6 @@ App::post('/v1/runtimes')
->param('vars', [], new Assoc(), 'Environment Variables required for the build')
->param('commands', [], new ArrayList(new Text(0)), 'Commands required to build the container')
->param('runtime', '', new Text(128), 'Runtime for the cloud function')
->param('network', '', new Text(128), 'Network to attach the container to')
->param('baseImage', '', new Text(128), 'Base image name of the runtime')
->param('entrypoint', '', new Text(256), 'Entrypoint of the code file', true)
->param('remove', false, new Boolean(), 'Remove a runtime after execution')
@@ -149,8 +148,7 @@ App::post('/v1/runtimes')
->inject('orchestrationPool')
->inject('activeRuntimes')
->inject('response')
->action(function (string $runtimeId, string $source, string $destination, array $vars, array $commands, string $runtime, string $network, string $baseImage, string $entrypoint, bool $remove, string $workdir, $orchestrationPool, $activeRuntimes, Response $response) {
->action(function (string $runtimeId, string $source, string $destination, array $vars, array $commands, string $runtime, string $baseImage, string $entrypoint, bool $remove, string $workdir, $orchestrationPool, $activeRuntimes, Response $response) {
if ($activeRuntimes->exists($runtimeId)) {
throw new Exception('Runtime already exists.', 409);
}
@@ -235,9 +233,7 @@ App::post('/v1/runtimes')
throw new Exception('Failed to create build container', 500);
}
if (!empty($network)) {
$orchestration->networkConnect($runtimeId, $network);
}
$orchestration->networkConnect($runtimeId, App::getEnv('OPEN_RUNTIMES_NETWORK', 'appwrite_runtimes'));
/**
* Execute any commands if they were provided
@@ -283,7 +279,7 @@ App::post('/v1/runtimes')
$endTime = \time();
$container = array_merge($container, [
'status' => 'ready',
'stdout' => \utf8_encode($stdout),
'response' => \utf8_encode($stdout),
'stderr' => \utf8_encode($stderr),
'startTime' => $startTime,
'endTime' => $endTime,
@@ -412,7 +408,7 @@ App::post('/v1/execution')
->desc('Create an execution')
->param('runtimeId', '', new Text(64), 'The runtimeID to execute')
->param('vars', [], new Assoc(), 'Environment variables required for the build')
->param('data', '{}', new Text(8192), 'Data to be forwarded to the function, this is user specified.', true)
->param('data', '', new Text(8192), 'Data to be forwarded to the function, this is user specified.', true)
->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Function maximum execution time in seconds.')
->inject('activeRuntimes')
->inject('response')
@@ -516,7 +512,7 @@ App::post('/v1/execution')
$execution = [
'status' => $functionStatus,
'statusCode' => $statusCode,
'stdout' => \utf8_encode(\mb_substr($stdout, -16384)),
'response' => \utf8_encode(\mb_substr($stdout, -16384)),
'stderr' => \utf8_encode(\mb_substr($stderr, -16384)),
'time' => $executionTime,
];
+4
View File
@@ -132,6 +132,8 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
'signed' => $attribute['signed'],
'array' => $attribute['array'],
'filters' => $attribute['filters'],
'default' => $attribute['default'] ?? null,
'format' => $attribute['format'] ?? ''
]);
}
@@ -187,6 +189,8 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
'signed' => $attribute['signed'],
'array' => $attribute['array'],
'filters' => $attribute['filters'],
'default' => $attribute['default'] ?? null,
'format' => $attribute['format'] ?? ''
]);
}
+38 -1
View File
@@ -181,7 +181,7 @@ if(!empty($user) || !empty($pass)) {
*/
Database::addFilter('casting',
function($value) {
return json_encode(['value' => $value]);
return json_encode(['value' => $value], JSON_PRESERVE_ZERO_FRACTION);
},
function($value) {
if (is_null($value)) {
@@ -301,6 +301,43 @@ Database::addFilter('subQueryWebhooks',
}
);
Database::addFilter('subQuerySessions',
function($value) {
return null;
},
function($value, Document $document, Database $database) {
$sessions = Authorization::skip(fn () => $database->find('sessions', [
new Query('userId', Query::TYPE_EQUAL, [$document->getId()])
], $database->getIndexLimit(), 0, []));
return $sessions;
}
);
Database::addFilter('subQueryTokens',
function($value) {
return null;
},
function($value, Document $document, Database $database) {
return Authorization::skip(fn() => $database
->find('tokens', [
new Query('userId', Query::TYPE_EQUAL, [$document->getId()])
], $database->getIndexLimit(), 0, []));
}
);
Database::addFilter('subQueryMemberships',
function($value) {
return null;
},
function($value, Document $document, Database $database) {
return Authorization::skip(fn() => $database
->find('memberships', [
new Query('userId', Query::TYPE_EQUAL, [$document->getId()])
], $database->getIndexLimit(), 0, []));
}
);
Database::addFilter('encrypt',
function($value) {
$key = App::getEnv('_APP_OPENSSL_KEY_V1');
-22
View File
@@ -163,28 +163,6 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
* Save current connections to the Database every 5 seconds.
*/
Timer::tick(5000, function () use ($register, $stats, &$statsDocument, $logError) {
/** @var Document $statsDocument */
foreach ($stats as $projectId => $value) {
$connections = $stats->get($projectId, 'connections') ?? 0;
$messages = $stats->get($projectId, 'messages' ?? 0);
$usage = new Event('v1-usage', 'UsageV1');
$usage
->setParam('projectId', $projectId)
->setParam('realtimeConnections', $connections)
->setParam('realtimeMessages', $messages)
->setParam('networkRequestSize', 0)
->setParam('networkResponseSize', 0);
$stats->set($projectId, [
'messages' => 0,
'connections' => 0
]);
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
$usage->trigger();
}
}
$payload = [];
foreach ($stats as $projectId => $value) {
$payload[$projectId] = $stats->get($projectId, 'connectionsTotal');
+48 -10
View File
@@ -418,7 +418,7 @@ $cli
// Get total storage
$dbForProject->setNamespace('_' . $projectId);
$storageTotal = $dbForProject->sum('deployments', 'size');
$deploymentsTotal = $dbForProject->sum('deployments', 'size');
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
$id = \md5($time . '_30m_storage.deployments.total'); //Construct unique id for each metric using time, period and metric
@@ -431,14 +431,14 @@ $cli
'period' => '30m',
'time' => $time,
'metric' => 'storage.deployments.total',
'value' => $storageTotal,
'value' => $deploymentsTotal,
'type' => 1,
]));
} else {
$dbForProject->updateDocument(
'stats',
$document->getId(),
$document->setAttribute('value', $storageTotal)
$document->setAttribute('value', $deploymentsTotal)
);
}
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
@@ -450,14 +450,14 @@ $cli
'period' => '1d',
'time' => $time,
'metric' => 'storage.deployments.total',
'value' => $storageTotal,
'value' => $deploymentsTotal,
'type' => 1,
]));
} else {
$dbForProject->updateDocument(
'stats',
$document->getId(),
$document->setAttribute('value', $storageTotal)
$document->setAttribute('value', $deploymentsTotal)
);
}
} catch(\Exception $e) {
@@ -565,13 +565,13 @@ $cli
foreach ($parents as $parent) {
foreach ($subCollections as $subCollection => $subOptions) { // Sub collection counts, like database.collections.collectionId.documents.count
$dbForProject->setNamespace("_{$projectId}");
$count = $dbForProject->count(($subOptions['collectionPrefix'] ?? '') . $parent->getId());
$count = $dbForProject->count(($subOptions['collectionPrefix'] ?? '') . $parent->getInternalId());
$subCollectionCounts[$subCollection] = ($subCollectionCounts[$subCollection] ?? 0) + $count; // Project level counts for sub collections like database.documents.count
$dbForProject->setNamespace("_{$projectId}");
$metric = empty($metricPrefix) ? "{$collection}.{$parent->getId()}.{$subCollection}.count" : "{$metricPrefix}.{$collection}.{$parent->getId()}.{$subCollection}.count";
$metric = empty($metricPrefix) ? "{$collection}.{$parent->getId()}.{$subCollection}.count" : "{$metricPrefix}.{$collection}.{$parent->getInternalId()}.{$subCollection}.count";
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
@@ -619,13 +619,13 @@ $cli
}
$dbForProject->setNamespace("_{$projectId}");
$total = (int) $dbForProject->sum(($subOptions['collectionPrefix'] ?? '') . $parent->getId(), $total['field']);
$total = (int) $dbForProject->sum(($subOptions['collectionPrefix'] ?? '') . $parent->getInternalId(), $total['field']);
$subCollectionTotals[$subCollection] = ($ssubCollectionTotals[$subCollection] ?? 0) + $total; // Project level sum for sub collections like storage.total
$dbForProject->setNamespace("_{$projectId}");
$metric = empty($metricPrefix) ? "{$collection}.{$parent->getId()}.{$subCollection}.total" : "{$metricPrefix}.{$collection}.{$parent->getId()}.{$subCollection}.total";
$metric = empty($metricPrefix) ? "{$collection}.{$parent->getId()}.{$subCollection}.total" : "{$metricPrefix}.{$collection}.{$parent->getInternalId()}.{$subCollection}.total";
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
@@ -714,7 +714,7 @@ $cli
}
/**
* Inserting project level sums for sub collections like storage.total
* Inserting project level sums for sub collections like storage.files.total
*/
foreach ($subCollectionTotals as $subCollection => $count) {
$dbForProject->setNamespace("_{$projectId}");
@@ -754,6 +754,44 @@ $cli
$dbForProject->updateDocument('stats', $document->getId(),
$document->setAttribute('value', $count));
}
// aggregate storage.total = storage.files.total + storage.deployments.total
if($metricPrefix === 'storage' && $subCollection === 'files') {
$metric = 'storage.total';
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '30m',
'metric' => $metric,
'value' => $count + $deploymentsTotal,
'type' => 1,
]));
} else {
$dbForProject->updateDocument('stats', $document->getId(),
$document->setAttribute('value', $count + $deploymentsTotal));
}
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '1d',
'metric' => $metric,
'value' => $count + $deploymentsTotal,
'type' => 1,
]));
} else {
$dbForProject->updateDocument('stats', $document->getId(),
$document->setAttribute('value', $count + $deploymentsTotal));
}
}
}
} catch (\Exception$e) {
Console::warning("Failed to save database counters data for project {$collection}: {$e->getMessage()}");
+12 -12
View File
@@ -621,7 +621,7 @@ $logs = $this->getParam('logs', null);
data-failure-param-alert-text="Failed to create attribute"
data-failure-param-alert-classname="error"
@reset="array = required = false"
x-data="{ array: false, required: false }">
x-data="{ array: false, required: false, size: null }">
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
<input type="hidden" name="collectionId" data-ls-bind="{{router.params.id}}" />
@@ -631,7 +631,7 @@ $logs = $this->getParam('logs', null);
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Allowed Characters A-Z, a-z, 0-9, and non-leading underscore, hyphen and dot</div>
<label for="string-length">Size</label>
<input id="string-length" name="size" type="number" class="margin-bottom" autocomplete="off" required value="255" data-cast-to="integer" />
<input id="string-length" name="size" type="number" class="margin-bottom" autocomplete="off" required value="255" data-cast-to="integer" x-model="size" />
<div class="margin-bottom">
<input x-model="required" name="required" class="button switch" type="checkbox" /> &nbsp; Required <span class="tooltip" data-tooltip="Mark whether this is a required attribute"><i class="icon-info-circled"></i></span>
@@ -643,7 +643,7 @@ $logs = $this->getParam('logs', null);
<label for="xdefault">Default Value</label>
<template x-if="!(array || required)">
<input name="xdefault" type="text" class="margin-bottom-large" autocomplete="off">
<input name="xdefault" type="text" class="margin-bottom-large" autocomplete="off" :maxlength="size">
</template>
<template x-if="(array || required)">
<input name="xdefault" type="text" class="margin-bottom-large" autocomplete="off" disabled value="">
@@ -677,7 +677,7 @@ $logs = $this->getParam('logs', null);
data-failure-param-alert-text="Failed to create attribute"
data-failure-param-alert-classname="error"
@reset="array = required = false"
x-data="{ array: false, required: false }">
x-data="{ array: false, required: false, min: null, max: null }">
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
<input type="hidden" name="collectionId" data-ls-bind="{{router.params.id}}" />
@@ -697,18 +697,18 @@ $logs = $this->getParam('logs', null);
<div class="row responsive thin">
<div class="col span-6 margin-bottom-small">
<label for="integer-min">Min</label>
<input id="integer-min" type="number" step="1" class="full-width" name="min" autocomplete="off" data-cast-to="integer" />
<input id="integer-min" type="number" step="1" class="full-width" name="min" autocomplete="off" data-cast-to="integer" x-model="min" />
</div>
<div class="col span-6 margin-bottom-small">
<label for="integer-max">Max</label>
<input id="integer-max" type="number" step="1" class="full-width" name="max" autocomplete="off" data-cast-to="integer" />
<input id="integer-max" type="number" step="1" class="full-width" name="max" autocomplete="off" data-cast-to="integer" x-model="max" />
</div>
</div>
<label for="integer-default">Default Value</label>
<template x-if="!(array || required)">
<input name="xdefault" type="number" class="margin-bottom-large" autocomplete="off" data-cast-to="integer">
<input name="xdefault" type="number" class="margin-bottom-large" autocomplete="off" data-cast-to="integer" :min="min" :max="max">
</template>
<template x-if="(array || required)">
<input name="xdefault" type="number" class="margin-bottom-large" autocomplete="off" data-cast-to="integer" disabled>
@@ -742,7 +742,7 @@ $logs = $this->getParam('logs', null);
data-failure-param-alert-text="Failed to create attribute"
data-failure-param-alert-classname="error"
@reset="array = required = false"
x-data="{ array: false, required: false }">
x-data="{ array: false, required: false, min: null, max: null }">
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
<input type="hidden" name="collectionId" data-ls-bind="{{router.params.id}}" />
@@ -762,18 +762,18 @@ $logs = $this->getParam('logs', null);
<div class="row responsive thin">
<div class="col span-6 margin-bottom-small">
<label for="float-min">Min</label>
<input id="float-min" type="number" class="full-width" name="min" step="any" autocomplete="off" data-cast-to="float" />
<input id="float-min" type="number" class="full-width" name="min" step="any" autocomplete="off" data-cast-to="float" x-model="min" />
</div>
<div class="col span-6 margin-bottom-small">
<label for="float-max">Max</label>
<input id="float-max" type="number" class="full-width" name="max" step="any" autocomplete="off" data-cast-to="float" />
<input id="float-max" type="number" class="full-width" name="max" step="any" autocomplete="off" data-cast-to="float" x-model="max" />
</div>
</div>
<label for="float-default">Default Value</label>
<template x-if="!(array || required)">
<input name="xdefault" type="number" step="any" class="margin-bottom-large" autocomplete="off" data-cast-to="float">
<input name="xdefault" type="number" step="any" class="margin-bottom-large" autocomplete="off" data-cast-to="float" :min="min" :max="max">
</template>
<template x-if="(array || required)">
<input name="xdefault" type="number" step="any" class="margin-bottom-large" autocomplete="off" data-cast-to="float" disabled>
@@ -1040,7 +1040,7 @@ $logs = $this->getParam('logs', null);
<div data-forms-clone="" data-label="Add Element" data-target="elements-section" data-first="1">
<div class="row responsive thin margin-bottom-tiny">
<div class="col span-11 margin-bottom-small">
<input type="text" class="full-width" name="elements" required autocomplete="off" maxlength="128" />
<input data-cast-to="array" type="text" class="full-width" name="elements" required autocomplete="off" maxlength="128" />
</div>
<div class="col span-1 margin-bottom-small">
<button type="button" data-remove class="dark danger small round pull-end" style="margin-top: 10px;"><i class="icon-cancel"></i></button>
+27 -8
View File
@@ -55,17 +55,26 @@ $logs = $this->getParam('logs', null);
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Database Document"
data-service="{{|documentAction}}"
data-name="project-document"
data-scope="sdk"
data-event="submit"
data-success="trigger,redirect"
data-success-param-trigger-events="database.updateDocument"
data-success-param-redirect-url="/console/database/document?id={{serviceData.$id}}&collection={{project-collection.$id}}&project={{router.params.project}}"
data-failure="alert"
data-failure-param-alert-text="Failed to update document"
data-failure-param-alert-classname="error">
data-failure-param-alert-classname="error"
<?php if($new): ?>
data-analytics-label="Create Database Document"
data-success="trigger,redirect"
data-success-param-trigger-events="database.createDocument"
data-success-param-redirect-url="/console/database/collection?id={{project-collection.$id}}&project={{router.params.project}}"
data-failure-param-alert-text="Failed to create document"
<?php else: ?>
data-analytics-label="Update Database Document"
data-success="trigger,alert"
data-success-param-trigger-events="database.updateDocument"
data-success-param-alert-text="Your document was updated"
data-failure-param-alert-text="Failed to update document"
<?php endif; ?>
>
<input type="hidden" name="collectionId" data-ls-bind="{{project-collection.$id}}" />
<?php if(!$new): ?><input type="hidden" name="documentId" data-ls-bind="{{project-document.$id}}" /><?php endif; ?>
@@ -104,6 +113,8 @@ $logs = $this->getParam('logs', null);
:placeholder="attr.default"
:name="attr.key"
:required="attr.required"
:min="attr.min"
:max="attr.max"
x-model="doc[attr.key]"
data-cast-to="integer" />
</template>
@@ -114,6 +125,8 @@ $logs = $this->getParam('logs', null);
:placeholder="attr.default"
:name="attr.key"
:required="attr.required"
:min="attr.min"
:max="attr.max"
x-model="doc[attr.key]"
data-cast-to="float" />
</template>
@@ -131,6 +144,7 @@ $logs = $this->getParam('logs', null);
:placeholder="attr.default"
:name="attr.key"
:required="attr.required"
:maxlength="attr.size"
x-model="doc[attr.key]"
data-cast-to="string"></textarea>
</template>
@@ -197,6 +211,8 @@ $logs = $this->getParam('logs', null);
:placeholder="attr.default"
:name="attr.key"
:required="attr.required"
:min="attr.min"
:max="attr.max"
x-model="doc[attr.key][index]"
data-cast-to="integer" />
</template>
@@ -207,6 +223,8 @@ $logs = $this->getParam('logs', null);
:placeholder="attr.default"
:name="attr.key"
:required="attr.required"
:min="attr.min"
:max="attr.max"
x-model="doc[attr.key][index]"
data-cast-to="float" />
</template>
@@ -226,6 +244,7 @@ $logs = $this->getParam('logs', null);
:placeholder="attr.default"
:name="attr.key"
:required="attr.required"
:maxlength="attr.size"
x-model="doc[attr.key][index]"
data-cast-to="string"></textarea>
</template>
@@ -267,9 +286,9 @@ $logs = $this->getParam('logs', null);
:required="attr.required"
:name="attr.key"
data-cast-to="string">
<template x-for="element in attr.elements">
<option :disabled="attr.required" selected label=" "></option>
<option :disabled="attr.required" selected label=" "></option>
<template x-for="element in attr.elements">
<option
:value="element"
x-text="element"
+12
View File
@@ -0,0 +1,12 @@
<?php
$provider = $this->getParam('provider', '');
?>
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid">Client ID<span class="tooltip" data-tooltip="Provided by Auth0"><i class="icon-info-circled"></i></span></label>
<input name="appId" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid" type="text" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Appid}}" placeholder="Client ID" />
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>ClientSecret">Client Secret <span class="tooltip" data-tooltip="Provided in the Application you created in Auth0"><i class="icon-info-circled"></i></span></label>
<input name="clientSecret" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>ClientSecret" type="password" autocomplete="off" placeholder="Client Secret" />
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Domain">Auth0 Domain<span class="tooltip" data-tooltip="Your Auth0 Domain (without 'https://')"><i class="icon-info-circled"></i></span></label>
<input name="auth0Domain" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Domain" type="text" autocomplete="off" placeholder="YOUR_DOMAIN" />
<?php /*Hidden input for the final secret. Gets filled with a JSON via JS. */ ?>
<input name="secret" data-forms-oauth-custom="<?php echo $this->escape(ucfirst($provider)); ?>" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret" type="hidden" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Secret}}" />
+14
View File
@@ -0,0 +1,14 @@
<?php
$provider = $this->getParam('provider', '');
?>
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid">Client ID<span class="tooltip" data-tooltip="Provided by Okta"><i class="icon-info-circled"></i></span></label>
<input name="appId" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid" type="text" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Appid}}" placeholder="Client ID" />
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>ClientSecret">Client Secret <span class="tooltip" data-tooltip="Provided in the Application you created in Okta"><i class="icon-info-circled"></i></span></label>
<input name="clientSecret" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>ClientSecret" type="password" autocomplete="off" placeholder="Client Secret" />
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Domain">Okta Domain<span class="tooltip" data-tooltip="Your Okta Domain (without 'https://')"><i class="icon-info-circled"></i></span></label>
<input name="oktaDomain" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Domain" type="text" autocomplete="off" placeholder="dev-1337.okta.com" />
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>AuthorizationServerId">Authorization Server ID<span class="tooltip" data-tooltip="Authorization Server ID for custom authorization servers"><i class="icon-info-circled"></i></span></label>
<input name="authorizationServerId" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>AuthorizationServerId" type="text" autocomplete="off" placeholder="default" />
<?php /*Hidden input for the final secret. Gets filled with a JSON via JS. */ ?>
<input name="secret" data-forms-oauth-custom="<?php echo $this->escape(ucfirst($provider)); ?>" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret" type="hidden" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Secret}}" />
+46
View File
@@ -233,7 +233,53 @@
</div>
</div>
</li>
<li data-state="/console/users/teams/team/audit?id={{router.params.id}}&project={{router.params.project}}">
<h2>Activity</h2>
<div
data-service="teams.listLogs"
data-name="logs"
data-param-team-id="{{router.params.id}}"
data-event="load,logs-load">
<div class="box margin-top margin-bottom" data-ls-if="{{logs.logs.length}} === 0" style="display: none" class="margin-top-xxl margin-bottom-xxl text-align-center">
<h3 class="text-bold margin-bottom-no">No logs available.</h3>
</div>
<div class="box" data-ls-if="{{logs.logs.length}} !== 0" style="display: none">
<table class="vertical small">
<thead>
<tr>
<th width="140">Date</th>
<th width="175">Event</th>
<th>Client</th>
<th width="90">Location</th>
<th width="90">IP</th>
</tr>
</thead>
<tbody data-ls-loop="logs.logs" data-ls-as="log">
<tr>
<td data-title="Date: "><span data-ls-bind="{{log.time|dateTime}}"></span></td>
<td data-title="Event: "><span data-ls-bind="{{log.event}}"></span></td>
<td data-title="Client: ">
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{log.clientCode|lowercase}} !== 'cli'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{log.clientCode|lowercase}}?width=80&height=80&project={{env.PROJECT}},title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{log.clientCode|lowercase}} === 'cli'" data-ls-attrs="src=/images/clients/terminal.png?buster=<?php echo APP_CACHE_BUSTER; ?>,title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
<span data-ls-if="({{log.clientName}})" data-ls-bind="{{log.clientName}} {{log.clientVersion}} on {{log.model}} {{log.osName}} {{log.osVersion}}"></span>
<div data-ls-if="(!{{log.clientName}})" class="text-align-center text-fade">Unknown</div>
</td>
<td data-title="Location: ">
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{log.countryCode}} !== '--'" data-ls-attrs="src={{env.API}}/avatars/flags/{{log.countryCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs inline margin-end-small" />
<span data-ls-bind="{{log.countryName}}"></span>
</td>
<td data-title="IP: "><span data-ls-bind="{{log.ip}}"></span></td>
</tr>
</tbody>
</table>
</div>
</div>
</li>
</ul>
</div>
</div>
+46
View File
@@ -242,6 +242,52 @@
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-json" class="link text-size-small">View as JSON</button></li>
</ul>
<div data-ls-if="{{user.emailVerification}} === false" style="display: none">
<form name="users.updateVerification" class="margin-bottom"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Verification Status"
data-service="users.updateVerification"
data-scope="sdk"
data-event="submit"
data-success="trigger,alert"
data-success-param-alert-text="User verification status was updated successfully"
data-success-param-trigger-events="users.update"
data-failure="alert"
data-failure-param-alert-text="Failed to update user verification status"
data-failure-param-alert-classname="error"
>
<input type="hidden" disabled name="userId" data-ls-bind="{{user.$id}}">
<input type="hidden" disabled name="emailVerification" value="true" data-cast-to="boolean">
<button type="submit" class="dark fill">Verify Account</button>
</form>
</div>
<div data-ls-if="{{user.emailVerification}} === true" style="display: none">
<form name="users.updateVerification" class="margin-bottom"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Verification Status"
data-service="users.updateVerification"
data-scope="sdk"
data-event="submit"
data-success="trigger,alert"
data-success-param-alert-text="User verification status was updated successfully"
data-success-param-trigger-events="users.update"
data-failure="alert"
data-failure-param-alert-text="Failed to update user verification status"
data-failure-param-alert-classname="error"
>
<input type="hidden" disabled name="userId" data-ls-bind="{{user.$id}}">
<input type="hidden" disabled name="emailVerification" value="false" data-cast-to="boolean">
<button type="submit" class="dark fill">Unverify Account</button>
</form>
</div>
<div data-ls-if="{{user.status}} === true" style="display: none">
<form name="users.updateStatus" class="margin-bottom"
data-analytics
+32 -2
View File
@@ -1,3 +1,10 @@
x-logging: &x-logging
logging:
driver: 'json-file'
options:
max-file: 5
max-size: 10m
<?php
$httpPort = $this->getParam('httpPort', '');
@@ -9,7 +16,7 @@ $image = $this->getParam('image', '');
services:
traefik:
image: traefik:2.5
image: traefik:2.7
container_name: appwrite-traefik
command:
- --providers.file.directory=/storage/config
@@ -36,6 +43,7 @@ services:
appwrite:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
container_name: appwrite
<<: *x-logging
restart: unless-stopped
networks:
- appwrite
@@ -98,6 +106,7 @@ services:
- _APP_INFLUXDB_HOST
- _APP_INFLUXDB_PORT
- _APP_STORAGE_LIMIT
- _APP_STORAGE_PREVIEW_LIMIT
- _APP_STORAGE_ANTIVIRUS
- _APP_STORAGE_ANTIVIRUS_HOST
- _APP_STORAGE_ANTIVIRUS_PORT
@@ -119,6 +128,7 @@ services:
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_FUNCTIONS_RUNTIMES
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_STATSD_HOST
@@ -132,6 +142,7 @@ services:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: realtime
container_name: appwrite-realtime
<<: *x-logging
restart: unless-stopped
labels:
- "traefik.enable=true"
@@ -172,6 +183,7 @@ services:
appwrite-worker-audits:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-audits
<<: *x-logging
container_name: appwrite-worker-audits
restart: unless-stopped
networks:
@@ -197,6 +209,7 @@ services:
appwrite-worker-webhooks:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-webhooks
<<: *x-logging
container_name: appwrite-worker-webhooks
restart: unless-stopped
networks:
@@ -218,6 +231,7 @@ services:
appwrite-worker-deletes:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-deletes
<<: *x-logging
container_name: appwrite-worker-deletes
restart: unless-stopped
networks:
@@ -255,10 +269,12 @@ services:
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
appwrite-worker-database:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-database
<<: *x-logging
container_name: appwrite-worker-database
restart: unless-stopped
networks:
@@ -284,6 +300,7 @@ services:
appwrite-worker-builds:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-builds
<<: *x-logging
container_name: appwrite-worker-builds
restart: unless-stopped
networks:
@@ -295,6 +312,7 @@ services:
- _APP_ENV
- _APP_OPENSSL_KEY_V1
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
@@ -310,6 +328,7 @@ services:
appwrite-worker-certificates:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-certificates
<<: *x-logging
container_name: appwrite-worker-certificates
restart: unless-stopped
networks:
@@ -340,6 +359,7 @@ services:
appwrite-worker-functions:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-functions
<<: *x-logging
container_name: appwrite-worker-functions
restart: unless-stopped
networks:
@@ -362,6 +382,7 @@ services:
- _APP_DB_PASS
- _APP_FUNCTIONS_TIMEOUT
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_USAGE_STATS
- DOCKERHUB_PULL_USERNAME
- DOCKERHUB_PULL_PASSWORD
@@ -369,6 +390,7 @@ services:
appwrite-executor:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: executor
<<: *x-logging
container_name: appwrite-executor
restart: unless-stopped
stop_signal: SIGINT
@@ -396,7 +418,7 @@ services:
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_FUNCTIONS_INACTIVE_THRESHOLD
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_RUNTIME_NETWORK
- OPEN_RUNTIMES_NETWORK
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_STORAGE_DEVICE
@@ -414,6 +436,7 @@ services:
appwrite-worker-mails:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-mails
<<: *x-logging
container_name: appwrite-worker-mails
restart: unless-stopped
networks:
@@ -440,6 +463,7 @@ services:
appwrite-maintenance:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: maintenance
<<: *x-logging
container_name: appwrite-maintenance
restart: unless-stopped
networks:
@@ -462,6 +486,7 @@ services:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: usage
container_name: appwrite-usage
<<: *x-logging
restart: unless-stopped
networks:
- appwrite
@@ -488,6 +513,7 @@ services:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: schedule
container_name: appwrite-schedule
<<: *x-logging
restart: unless-stopped
networks:
- appwrite
@@ -503,6 +529,7 @@ services:
mariadb:
image: mariadb:10.7 # fix issues when upgrading using: mysql_upgrade -u root -p
container_name: appwrite-mariadb
<<: *x-logging
restart: unless-stopped
networks:
- appwrite
@@ -518,6 +545,7 @@ services:
redis:
image: redis:6.2-alpine
container_name: appwrite-redis
<<: *x-logging
restart: unless-stopped
networks:
- appwrite
@@ -536,6 +564,7 @@ services:
influxdb:
image: appwrite/influxdb:1.5.0
container_name: appwrite-influxdb
<<: *x-logging
restart: unless-stopped
networks:
- appwrite
@@ -545,6 +574,7 @@ services:
telegraf:
image: appwrite/telegraf:1.4.0
container_name: appwrite-telegraf
<<: *x-logging
restart: unless-stopped
networks:
- appwrite
+1 -1
View File
@@ -33,7 +33,7 @@ class BuildsV1 extends Worker
}
public function init(): void {
$this->executor = new Executor();
$this->executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
}
public function run(): void
+5
View File
@@ -71,6 +71,11 @@ class DatabaseV1 extends Worker
$dbForConsole = $this->getConsoleDB();
$dbForProject = $this->getProjectDB($projectId);
/**
* Fetch attribute from the database, since with Resque float values are loosing informations.
*/
$attribute = $dbForProject->getDocument('attributes', $attribute->getId());
$event = 'database.attributes.update';
$collectionId = $collection->getId();
$key = $attribute->getAttribute('key', '');
+10 -6
View File
@@ -19,6 +19,7 @@ use Utopia\Audit\Audit;
require_once __DIR__ . '/../init.php';
Authorization::disable();
Authorization::setDefaultStatus(false);
Console::title('Deletes V1 Worker');
Console::success(APP_NAME . ' deletes worker v1 has started' . "\n");
@@ -207,15 +208,13 @@ class DeletesV1 extends Worker
*/
$userId = $document->getId();
$user = $this->getProjectDB($projectId)->getDocument('users', $userId);
// Delete all sessions of this user from the sessions table and update the sessions field of the user record
$this->deleteByGroup('sessions', [
new Query('userId', Query::TYPE_EQUAL, [$userId])
], $this->getProjectDB($projectId));
$user->setAttribute('sessions', []);
$updated = $this->getProjectDB($projectId)->updateDocument('users', $userId, $user);
$this->getProjectDB($projectId)->deleteCachedDocument('users', $userId);
// Delete Memberships and decrement team membership counts
$this->deleteByGroup('memberships', [
@@ -232,6 +231,11 @@ class DeletesV1 extends Worker
}
}
});
// Delete tokens
$this->deleteByGroup('tokens', [
new Query('userId', Query::TYPE_EQUAL, [$userId])
], $this->getProjectDB($projectId));
}
/**
@@ -368,7 +372,7 @@ class DeletesV1 extends Worker
* Request executor to delete all deployment containers
*/
Console::info("Requesting executor to delete all deployment containers for function " . $functionId);
$executor = new Executor();
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
foreach ($deploymentIds as $deploymentId) {
try {
$executor->deleteRuntime($projectId, $deploymentId);
@@ -420,7 +424,7 @@ class DeletesV1 extends Worker
*/
Console::info("Requesting executor to delete deployment container for deployment " . $deploymentId);
try {
$executor = new Executor();
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
$executor->deleteRuntime($projectId, $deploymentId);
} catch (Throwable $th) {
Console::error($th->getMessage());
+3 -3
View File
@@ -37,7 +37,7 @@ class FunctionsV1 extends Worker
public function init(): void
{
$this->executor = new Executor();
$this->executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
}
public function run(): void
@@ -254,7 +254,7 @@ class FunctionsV1 extends Worker
'trigger' => $trigger,
'status' => 'waiting',
'statusCode' => 0,
'stdout' => '',
'response' => '',
'stderr' => '',
'time' => 0.0,
'search' => implode(' ', [$functionId, $executionId]),
@@ -303,7 +303,7 @@ class FunctionsV1 extends Worker
/** Update execution status */
$execution->setAttribute('status', $executionResponse['status']);
$execution->setAttribute('statusCode', $executionResponse['statusCode']);
$execution->setAttribute('stdout', $executionResponse['stdout']);
$execution->setAttribute('response', $executionResponse['response']);
$execution->setAttribute('stderr', $executionResponse['stderr']);
$execution->setAttribute('time', $executionResponse['time']);
} catch (\Throwable $th) {
+6 -6
View File
@@ -42,7 +42,7 @@
"utopia-php/abuse": "0.7.*",
"utopia-php/analytics": "0.2.*",
"utopia-php/audit": "0.8.*",
"utopia-php/cache": "0.4.*",
"utopia-php/cache": "0.6.*",
"utopia-php/cli": "0.12.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.15.*",
@@ -56,7 +56,7 @@
"utopia-php/image": "0.5.*",
"utopia-php/orchestration": "0.4.*",
"resque/php-resque": "1.3.6",
"matomo/device-detector": "5.0.4",
"matomo/device-detector": "6.0.0",
"dragonmantank/cron-expression": "3.3.1",
"influxdb/influxdb-php": "1.15.2",
"phpmailer/phpmailer": "6.6.0",
@@ -71,10 +71,10 @@
}
],
"require-dev": {
"appwrite/sdk-generator": "0.18.1",
"phpunit/phpunit": "9.5.10",
"swoole/ide-helper": "4.8.5",
"textalk/websocket": "1.5.5",
"appwrite/sdk-generator": "0.18.3",
"phpunit/phpunit": "9.5.20",
"swoole/ide-helper": "4.8.9",
"textalk/websocket": "1.5.7",
"vimeo/psalm": "4.13.1"
},
"provide": {
Generated
+68 -67
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": "3ef20ad6919a1844f207e06719ad8929",
"content-hash": "78c5402d7bf745469d0063a9bc955df0",
"packages": [
{
"name": "adhocore/jwt",
@@ -925,16 +925,16 @@
},
{
"name": "matomo/device-detector",
"version": "5.0.4",
"version": "6.0.0",
"source": {
"type": "git",
"url": "https://github.com/matomo-org/device-detector.git",
"reference": "99ea1953fc7f23f785e593ce1499a00586645530"
"reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/99ea1953fc7f23f785e593ce1499a00586645530",
"reference": "99ea1953fc7f23f785e593ce1499a00586645530",
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/7fc2af3af62bd69e6e3404d561e371a83c112be9",
"reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9",
"shasum": ""
},
"require": {
@@ -990,7 +990,7 @@
"source": "https://github.com/matomo-org/matomo",
"wiki": "https://dev.matomo.org/"
},
"time": "2022-02-18T19:51:56+00:00"
"time": "2022-04-11T09:58:17+00:00"
},
{
"name": "mongodb/mongodb",
@@ -1976,22 +1976,22 @@
},
{
"name": "utopia-php/cache",
"version": "0.4.2",
"version": "0.6.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/cache.git",
"reference": "7334c5b182c6ccf66b21ca61808d7c6c899e2bcb"
"reference": "8ea1353a4bbab617e23c865a7c97b60d8074aee3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/cache/zipball/7334c5b182c6ccf66b21ca61808d7c6c899e2bcb",
"reference": "7334c5b182c6ccf66b21ca61808d7c6c899e2bcb",
"url": "https://api.github.com/repos/utopia-php/cache/zipball/8ea1353a4bbab617e23c865a7c97b60d8074aee3",
"reference": "8ea1353a4bbab617e23c865a7c97b60d8074aee3",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-redis": "*",
"php": ">=7.4"
"php": ">=8.0"
},
"require-dev": {
"phpunit/phpunit": "^9.3",
@@ -2023,9 +2023,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/cache/issues",
"source": "https://github.com/utopia-php/cache/tree/0.4.2"
"source": "https://github.com/utopia-php/cache/tree/0.6.0"
},
"time": "2021-11-25T16:41:35+00:00"
"time": "2022-04-04T12:30:05+00:00"
},
{
"name": "utopia-php/cli",
@@ -2133,16 +2133,16 @@
},
{
"name": "utopia-php/database",
"version": "0.15.4",
"version": "0.15.5",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "166365d9c39c4d70b1267af4562f4327e8930f79"
"reference": "6507b58ef3e22703b9df68d3dbd5e822d5bb023f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/166365d9c39c4d70b1267af4562f4327e8930f79",
"reference": "166365d9c39c4d70b1267af4562f4327e8930f79",
"url": "https://api.github.com/repos/utopia-php/database/zipball/6507b58ef3e22703b9df68d3dbd5e822d5bb023f",
"reference": "6507b58ef3e22703b9df68d3dbd5e822d5bb023f",
"shasum": ""
},
"require": {
@@ -2151,7 +2151,7 @@
"ext-redis": "*",
"mongodb/mongodb": "1.8.0",
"php": ">=8.0",
"utopia-php/cache": "0.4.*",
"utopia-php/cache": "0.6.*",
"utopia-php/framework": "0.*.*"
},
"require-dev": {
@@ -2190,9 +2190,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.15.4"
"source": "https://github.com/utopia-php/database/tree/0.15.5"
},
"time": "2022-03-15T17:20:14+00:00"
"time": "2022-04-04T13:42:00+00:00"
},
{
"name": "utopia-php/domains",
@@ -3075,16 +3075,16 @@
},
{
"name": "appwrite/sdk-generator",
"version": "0.18.1",
"version": "0.18.3",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "1d5293dd65e014f2067f2bfc9c0c45e5b0de56aa"
"reference": "96e2c9b0f350d4fa43ddff6334ef44908716c944"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/1d5293dd65e014f2067f2bfc9c0c45e5b0de56aa",
"reference": "1d5293dd65e014f2067f2bfc9c0c45e5b0de56aa",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/96e2c9b0f350d4fa43ddff6334ef44908716c944",
"reference": "96e2c9b0f350d4fa43ddff6334ef44908716c944",
"shasum": ""
},
"require": {
@@ -3096,6 +3096,7 @@
"twig/twig": "^3.3"
},
"require-dev": {
"brianium/paratest": "^6.4",
"phpunit/phpunit": "^9.5.13"
},
"type": "library",
@@ -3118,9 +3119,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.18.1"
"source": "https://github.com/appwrite/sdk-generator/tree/0.18.3"
},
"time": "2022-03-03T09:45:38+00:00"
"time": "2022-04-13T04:00:59+00:00"
},
{
"name": "composer/pcre",
@@ -3550,16 +3551,16 @@
},
{
"name": "matthiasmullie/minify",
"version": "1.3.68",
"version": "1.3.67",
"source": {
"type": "git",
"url": "https://github.com/matthiasmullie/minify.git",
"reference": "c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297"
"reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matthiasmullie/minify/zipball/c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297",
"reference": "c00fb02f71b2ef0a5f53fe18c5a8b9aa30f48297",
"url": "https://api.github.com/repos/matthiasmullie/minify/zipball/acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9",
"reference": "acaee1b7ca3cd67a39d7f98673cacd7e4739a8d9",
"shasum": ""
},
"require": {
@@ -3608,7 +3609,7 @@
],
"support": {
"issues": "https://github.com/matthiasmullie/minify/issues",
"source": "https://github.com/matthiasmullie/minify/tree/1.3.68"
"source": "https://github.com/matthiasmullie/minify/tree/1.3.67"
},
"funding": [
{
@@ -3616,7 +3617,7 @@
"type": "github"
}
],
"time": "2022-04-19T08:28:56+00:00"
"time": "2022-03-24T08:54:59+00:00"
},
{
"name": "matthiasmullie/path-converter",
@@ -4548,16 +4549,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.5.10",
"version": "9.5.20",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a"
"reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a",
"reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba",
"reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba",
"shasum": ""
},
"require": {
@@ -4573,7 +4574,7 @@
"phar-io/version": "^3.0.2",
"php": ">=7.3",
"phpspec/prophecy": "^1.12.1",
"phpunit/php-code-coverage": "^9.2.7",
"phpunit/php-code-coverage": "^9.2.13",
"phpunit/php-file-iterator": "^3.0.5",
"phpunit/php-invoker": "^3.1.1",
"phpunit/php-text-template": "^2.0.3",
@@ -4587,7 +4588,7 @@
"sebastian/global-state": "^5.0.1",
"sebastian/object-enumerator": "^4.0.3",
"sebastian/resource-operations": "^3.0.3",
"sebastian/type": "^2.3.4",
"sebastian/type": "^3.0",
"sebastian/version": "^3.0.2"
},
"require-dev": {
@@ -4635,11 +4636,11 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10"
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20"
},
"funding": [
{
"url": "https://phpunit.de/donate.html",
"url": "https://phpunit.de/sponsors.html",
"type": "custom"
},
{
@@ -4647,7 +4648,7 @@
"type": "github"
}
],
"time": "2021-09-25T07:38:51+00:00"
"time": "2022-04-01T12:37:26+00:00"
},
{
"name": "psr/container",
@@ -5559,28 +5560,28 @@
},
{
"name": "sebastian/type",
"version": "2.3.4",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/type.git",
"reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914"
"reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914",
"reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914",
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b233b84bc4465aff7b57cf1c4bc75c86d00d6dad",
"reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad",
"shasum": ""
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
"phpunit/phpunit": "^9.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
"dev-master": "3.0-dev"
}
},
"autoload": {
@@ -5603,7 +5604,7 @@
"homepage": "https://github.com/sebastianbergmann/type",
"support": {
"issues": "https://github.com/sebastianbergmann/type/issues",
"source": "https://github.com/sebastianbergmann/type/tree/2.3.4"
"source": "https://github.com/sebastianbergmann/type/tree/3.0.0"
},
"funding": [
{
@@ -5611,7 +5612,7 @@
"type": "github"
}
],
"time": "2021-06-15T12:49:02+00:00"
"time": "2022-03-15T09:54:48+00:00"
},
{
"name": "sebastian/version",
@@ -5668,16 +5669,16 @@
},
{
"name": "swoole/ide-helper",
"version": "4.8.5",
"version": "4.8.9",
"source": {
"type": "git",
"url": "https://github.com/swoole/ide-helper.git",
"reference": "d03c707d4dc803228e93b4884c72949c4d28e8b8"
"reference": "8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/swoole/ide-helper/zipball/d03c707d4dc803228e93b4884c72949c4d28e8b8",
"reference": "d03c707d4dc803228e93b4884c72949c4d28e8b8",
"url": "https://api.github.com/repos/swoole/ide-helper/zipball/8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe",
"reference": "8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe",
"shasum": ""
},
"type": "library",
@@ -5694,7 +5695,7 @@
"description": "IDE help files for Swoole.",
"support": {
"issues": "https://github.com/swoole/ide-helper/issues",
"source": "https://github.com/swoole/ide-helper/tree/4.8.5"
"source": "https://github.com/swoole/ide-helper/tree/4.8.9"
},
"funding": [
{
@@ -5706,20 +5707,20 @@
"type": "github"
}
],
"time": "2021-12-24T22:44:20+00:00"
"time": "2022-04-18T20:38:04+00:00"
},
{
"name": "symfony/console",
"version": "v6.0.8",
"version": "v6.0.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "0d00aa289215353aa8746a31d101f8e60826285c"
"reference": "70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/0d00aa289215353aa8746a31d101f8e60826285c",
"reference": "0d00aa289215353aa8746a31d101f8e60826285c",
"url": "https://api.github.com/repos/symfony/console/zipball/70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e",
"reference": "70dcf7b2ca2ea08ad6ebcc475f104a024fb5632e",
"shasum": ""
},
"require": {
@@ -5785,7 +5786,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v6.0.8"
"source": "https://github.com/symfony/console/tree/v6.0.7"
},
"funding": [
{
@@ -5801,7 +5802,7 @@
"type": "tidelift"
}
],
"time": "2022-04-20T15:01:42+00:00"
"time": "2022-03-31T17:18:25+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
@@ -6220,16 +6221,16 @@
},
{
"name": "textalk/websocket",
"version": "1.5.5",
"version": "1.5.7",
"source": {
"type": "git",
"url": "https://github.com/Textalk/websocket-php.git",
"reference": "846542f82658132cd36acb7a7e8ce0f03960c295"
"reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Textalk/websocket-php/zipball/846542f82658132cd36acb7a7e8ce0f03960c295",
"reference": "846542f82658132cd36acb7a7e8ce0f03960c295",
"url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46",
"reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46",
"shasum": ""
},
"require": {
@@ -6263,9 +6264,9 @@
"description": "WebSocket client and server",
"support": {
"issues": "https://github.com/Textalk/websocket-php/issues",
"source": "https://github.com/Textalk/websocket-php/tree/1.5.5"
"source": "https://github.com/Textalk/websocket-php/tree/1.5.7"
},
"time": "2021-08-07T10:21:40+00:00"
"time": "2022-03-29T09:46:59+00:00"
},
{
"name": "theseer/tokenizer",
+35 -2
View File
@@ -3,11 +3,19 @@
# Avoid using this file in your production environment.
# We're exposing here sensitive ports and mounting code volumes for rapid development and debugging of the server stack.
x-logging: &x-logging
logging:
driver: 'json-file'
options:
max-file: '5'
max-size: '10m'
version: '3'
services:
traefik:
image: traefik:2.5
image: traefik:2.7
<<: *x-logging
container_name: appwrite-traefik
command:
- --log.level=DEBUG
@@ -37,6 +45,7 @@ services:
appwrite:
container_name: appwrite
<<: *x-logging
build:
context: .
args:
@@ -141,6 +150,7 @@ services:
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_FUNCTIONS_RUNTIMES
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_STATSD_HOST
@@ -152,6 +162,7 @@ services:
appwrite-realtime:
entrypoint: realtime
<<: *x-logging
container_name: appwrite-realtime
build:
context: .
@@ -200,6 +211,7 @@ services:
appwrite-worker-audits:
entrypoint: worker-audits
<<: *x-logging
container_name: appwrite-worker-audits
build:
context: .
@@ -228,6 +240,7 @@ services:
appwrite-worker-webhooks:
entrypoint: worker-webhooks
<<: *x-logging
container_name: appwrite-worker-webhooks
build:
context: .
@@ -253,6 +266,7 @@ services:
appwrite-worker-deletes:
entrypoint: worker-deletes
<<: *x-logging
container_name: appwrite-worker-deletes
build:
context: .
@@ -293,9 +307,11 @@ services:
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
appwrite-worker-database:
entrypoint: worker-database
<<: *x-logging
container_name: appwrite-worker-database
build:
context: .
@@ -325,6 +341,7 @@ services:
appwrite-worker-builds:
entrypoint: worker-builds
<<: *x-logging
container_name: appwrite-worker-builds
build:
context: .
@@ -340,6 +357,7 @@ services:
- _APP_ENV
- _APP_OPENSSL_KEY_V1
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
@@ -354,6 +372,7 @@ services:
appwrite-worker-certificates:
entrypoint: worker-certificates
<<: *x-logging
container_name: appwrite-worker-certificates
build:
context: .
@@ -386,6 +405,7 @@ services:
appwrite-worker-functions:
entrypoint: worker-functions
<<: *x-logging
container_name: appwrite-worker-functions
build:
context: .
@@ -412,12 +432,14 @@ services:
- _APP_DB_PASS
- _APP_FUNCTIONS_TIMEOUT
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_USAGE_STATS
- DOCKERHUB_PULL_USERNAME
- DOCKERHUB_PULL_PASSWORD
appwrite-executor:
container_name: appwrite-executor
<<: *x-logging
entrypoint: executor
stop_signal: SIGINT
build:
@@ -454,7 +476,7 @@ services:
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_FUNCTIONS_INACTIVE_THRESHOLD
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_RUNTIME_NETWORK
- OPEN_RUNTIMES_NETWORK
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_STORAGE_DEVICE
@@ -471,6 +493,7 @@ services:
appwrite-worker-mails:
entrypoint: worker-mails
<<: *x-logging
container_name: appwrite-worker-mails
build:
context: .
@@ -502,6 +525,7 @@ services:
appwrite-maintenance:
entrypoint: maintenance
<<: *x-logging
container_name: appwrite-maintenance
build:
context: .
@@ -526,6 +550,7 @@ services:
appwrite-usage:
entrypoint: usage
<<: *x-logging
container_name: appwrite-usage
build:
context: .
@@ -558,6 +583,7 @@ services:
appwrite-schedule:
entrypoint: schedule
<<: *x-logging
container_name: appwrite-schedule
build:
context: .
@@ -578,6 +604,7 @@ services:
mariadb:
image: mariadb:10.7 # fix issues when upgrading using: mysql_upgrade -u root -p
container_name: appwrite-mariadb
<<: *x-logging
networks:
- appwrite
volumes:
@@ -606,6 +633,7 @@ services:
redis:
image: redis:6.2-alpine
<<: *x-logging
container_name: appwrite-redis
ports:
- "6379:6379"
@@ -625,6 +653,7 @@ services:
influxdb:
image: appwrite/influxdb:1.5.0
container_name: appwrite-influxdb
<<: *x-logging
networks:
- appwrite
volumes:
@@ -633,6 +662,7 @@ services:
telegraf:
image: appwrite/telegraf:1.4.0
container_name: appwrite-telegraf
<<: *x-logging
networks:
- appwrite
environment:
@@ -655,6 +685,7 @@ services:
maildev: # used mainly for dev tests
image: appwrite/mailcatcher:1.0.0
container_name: appwrite-mailcatcher
<<: *x-logging
ports:
- '9503:1080'
networks:
@@ -663,6 +694,7 @@ services:
request-catcher: # used mainly for dev tests
image: appwrite/requestcatcher:1.0.0
container_name: appwrite-requestcatcher
<<: *x-logging
ports:
- '9504:5000'
networks:
@@ -671,6 +703,7 @@ services:
adminer:
image: adminer
container_name: appwrite-adminer
<<: *x-logging
restart: always
ports:
- 9506:8080
@@ -1,17 +1,11 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.createAnonymousSession() { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let session):
print(String(describing: session)
}
}
let session = try await account.createAnonymousSession()
print(String(describing: session)
}
@@ -1,17 +1,11 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.createJWT() { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let jwt):
print(String(describing: jwt)
}
}
let jwt = try await account.createJWT()
print(String(describing: jwt)
}
@@ -1,20 +1,14 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.createMagicURLSession(
let token = try await account.createMagicURLSession(
userId: "[USER_ID]",
email: "email@example.com"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let token):
print(String(describing: token)
}
}
)
print(String(describing: token)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.createOAuth2Session(
let success = try await account.createOAuth2Session(
provider: "amazon"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let success):
print(String(describing: success)
}
}
)
print(String(describing: success)
}
@@ -1,20 +1,14 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.createRecovery(
let token = try await account.createRecovery(
email: "email@example.com",
url: "https://example.com"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let token):
print(String(describing: token)
}
}
)
print(String(describing: token)
}
@@ -1,20 +1,14 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.createSession(
let session = try await account.createSession(
email: "email@example.com",
password: "password"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let session):
print(String(describing: session)
}
}
)
print(String(describing: session)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.createVerification(
let token = try await account.createVerification(
url: "https://example.com"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let token):
print(String(describing: token)
}
}
)
print(String(describing: token)
}
@@ -1,21 +1,15 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.create(
let user = try await account.create(
userId: "[USER_ID]",
email: "email@example.com",
password: "password"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let user):
print(String(describing: user)
}
}
)
print(String(describing: user)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.deleteSession(
let result = try await account.deleteSession(
sessionId: "[SESSION_ID]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let ):
print(String(describing: )
}
}
)
print(String(describing: result)
}
@@ -1,17 +1,11 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.deleteSessions() { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let ):
print(String(describing: )
}
}
let result = try await account.deleteSessions()
print(String(describing: result)
}
@@ -1,17 +1,11 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.delete() { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let ):
print(String(describing: )
}
}
let result = try await account.delete()
print(String(describing: result)
}
@@ -1,17 +1,11 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.getLogs() { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let logList):
print(String(describing: logList)
}
}
let logList = try await account.getLogs()
print(String(describing: logList)
}
@@ -1,17 +1,11 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.getPrefs() { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let preferences):
print(String(describing: preferences)
}
}
let preferences = try await account.getPrefs()
print(String(describing: preferences)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.getSession(
let session = try await account.getSession(
sessionId: "[SESSION_ID]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let session):
print(String(describing: session)
}
}
)
print(String(describing: session)
}
@@ -1,17 +1,11 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.getSessions() { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let sessionList):
print(String(describing: sessionList)
}
}
let sessionList = try await account.getSessions()
print(String(describing: sessionList)
}
@@ -1,17 +1,11 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.get() { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let user):
print(String(describing: user)
}
}
let user = try await account.get()
print(String(describing: user)
}
@@ -1,20 +1,14 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.updateEmail(
let user = try await account.updateEmail(
email: "email@example.com",
password: "password"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let user):
print(String(describing: user)
}
}
)
print(String(describing: user)
}
@@ -1,20 +1,14 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.updateMagicURLSession(
let session = try await account.updateMagicURLSession(
userId: "[USER_ID]",
secret: "[SECRET]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let session):
print(String(describing: session)
}
}
)
print(String(describing: session)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.updateName(
let user = try await account.updateName(
name: "[NAME]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let user):
print(String(describing: user)
}
}
)
print(String(describing: user)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.updatePassword(
let user = try await account.updatePassword(
password: "password"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let user):
print(String(describing: user)
}
}
)
print(String(describing: user)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.updatePrefs(
let user = try await account.updatePrefs(
prefs:
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let user):
print(String(describing: user)
}
}
)
print(String(describing: user)
}
@@ -1,22 +1,16 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.updateRecovery(
let token = try await account.updateRecovery(
userId: "[USER_ID]",
secret: "[SECRET]",
password: "password",
passwordAgain: "password"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let token):
print(String(describing: token)
}
}
)
print(String(describing: token)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.updateSession(
let session = try await account.updateSession(
sessionId: "[SESSION_ID]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let session):
print(String(describing: session)
}
}
)
print(String(describing: session)
}
@@ -1,20 +1,14 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let account = Account(client)
account.updateVerification(
let token = try await account.updateVerification(
userId: "[USER_ID]",
secret: "[SECRET]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let token):
print(String(describing: token)
}
}
)
print(String(describing: token)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let avatars = Avatars(client)
avatars.getBrowser(
let byteBuffer = try await avatars.getBrowser(
code: "aa"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let byteBuffer):
print(String(describing: byteBuffer)
}
}
)
print(String(describing: byteBuffer)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let avatars = Avatars(client)
avatars.getCreditCard(
let byteBuffer = try await avatars.getCreditCard(
code: "amex"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let byteBuffer):
print(String(describing: byteBuffer)
}
}
)
print(String(describing: byteBuffer)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let avatars = Avatars(client)
avatars.getFavicon(
let byteBuffer = try await avatars.getFavicon(
url: "https://example.com"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let byteBuffer):
print(String(describing: byteBuffer)
}
}
)
print(String(describing: byteBuffer)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let avatars = Avatars(client)
avatars.getFlag(
let byteBuffer = try await avatars.getFlag(
code: "af"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let byteBuffer):
print(String(describing: byteBuffer)
}
}
)
print(String(describing: byteBuffer)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let avatars = Avatars(client)
avatars.getImage(
let byteBuffer = try await avatars.getImage(
url: "https://example.com"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let byteBuffer):
print(String(describing: byteBuffer)
}
}
)
print(String(describing: byteBuffer)
}
@@ -1,17 +1,11 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let avatars = Avatars(client)
avatars.getInitials() { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let byteBuffer):
print(String(describing: byteBuffer)
}
}
let byteBuffer = try await avatars.getInitials()
print(String(describing: byteBuffer)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let avatars = Avatars(client)
avatars.getQR(
let byteBuffer = try await avatars.getQR(
text: "[TEXT]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let byteBuffer):
print(String(describing: byteBuffer)
}
}
)
print(String(describing: byteBuffer)
}
@@ -1,21 +1,15 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let database = Database(client)
database.createDocument(
let document = try await database.createDocument(
collectionId: "[COLLECTION_ID]",
documentId: "[DOCUMENT_ID]",
data:
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let document):
print(String(describing: document)
}
}
)
print(String(describing: document)
}
@@ -1,20 +1,14 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let database = Database(client)
database.deleteDocument(
let result = try await database.deleteDocument(
collectionId: "[COLLECTION_ID]",
documentId: "[DOCUMENT_ID]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let ):
print(String(describing: )
}
}
)
print(String(describing: result)
}
@@ -1,20 +1,14 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let database = Database(client)
database.getDocument(
let document = try await database.getDocument(
collectionId: "[COLLECTION_ID]",
documentId: "[DOCUMENT_ID]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let document):
print(String(describing: document)
}
}
)
print(String(describing: document)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let database = Database(client)
database.listDocuments(
let documentList = try await database.listDocuments(
collectionId: "[COLLECTION_ID]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let documentList):
print(String(describing: documentList)
}
}
)
print(String(describing: documentList)
}
@@ -1,21 +1,15 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let database = Database(client)
database.updateDocument(
let document = try await database.updateDocument(
collectionId: "[COLLECTION_ID]",
documentId: "[DOCUMENT_ID]",
data:
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let document):
print(String(describing: document)
}
}
)
print(String(describing: document)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let functions = Functions(client)
functions.createExecution(
let execution = try await functions.createExecution(
functionId: "[FUNCTION_ID]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let execution):
print(String(describing: execution)
}
}
)
print(String(describing: execution)
}
@@ -1,20 +1,14 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let functions = Functions(client)
functions.getExecution(
let execution = try await functions.getExecution(
functionId: "[FUNCTION_ID]",
executionId: "[EXECUTION_ID]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let execution):
print(String(describing: execution)
}
}
)
print(String(describing: execution)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let functions = Functions(client)
functions.listExecutions(
let executionList = try await functions.listExecutions(
functionId: "[FUNCTION_ID]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let executionList):
print(String(describing: executionList)
}
}
)
print(String(describing: executionList)
}
@@ -1,21 +1,15 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let functions = Functions(client)
functions.retryBuild(
let result = try await functions.retryBuild(
functionId: "[FUNCTION_ID]",
deploymentId: "[DEPLOYMENT_ID]",
buildId: "[BUILD_ID]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let ):
print(String(describing: )
}
}
)
print(String(describing: result)
}
@@ -1,17 +1,11 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let locale = Locale(client)
locale.getContinents() { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let continentList):
print(String(describing: continentList)
}
}
let continentList = try await locale.getContinents()
print(String(describing: continentList)
}
@@ -1,17 +1,11 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let locale = Locale(client)
locale.getCountriesEU() { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let countryList):
print(String(describing: countryList)
}
}
let countryList = try await locale.getCountriesEU()
print(String(describing: countryList)
}
@@ -1,17 +1,11 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let locale = Locale(client)
locale.getCountriesPhones() { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let phoneList):
print(String(describing: phoneList)
}
}
let phoneList = try await locale.getCountriesPhones()
print(String(describing: phoneList)
}
@@ -1,17 +1,11 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let locale = Locale(client)
locale.getCountries() { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let countryList):
print(String(describing: countryList)
}
}
let countryList = try await locale.getCountries()
print(String(describing: countryList)
}
@@ -1,17 +1,11 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let locale = Locale(client)
locale.getCurrencies() { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let currencyList):
print(String(describing: currencyList)
}
}
let currencyList = try await locale.getCurrencies()
print(String(describing: currencyList)
}
@@ -1,17 +1,11 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let locale = Locale(client)
locale.getLanguages() { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let languageList):
print(String(describing: languageList)
}
}
let languageList = try await locale.getLanguages()
print(String(describing: languageList)
}
@@ -1,17 +1,11 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let locale = Locale(client)
locale.get() { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let locale):
print(String(describing: locale)
}
}
let locale = try await locale.get()
print(String(describing: locale)
}
@@ -1,21 +1,15 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let storage = Storage(client)
storage.createFile(
let file = try await storage.createFile(
bucketId: "[BUCKET_ID]",
fileId: "[FILE_ID]",
file: File(name: "image.jpg", buffer: yourByteBuffer)
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let file):
print(String(describing: file)
}
}
)
print(String(describing: file)
}
@@ -1,20 +1,14 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let storage = Storage(client)
storage.deleteFile(
let result = try await storage.deleteFile(
bucketId: "[BUCKET_ID]",
fileId: "[FILE_ID]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let ):
print(String(describing: )
}
}
)
print(String(describing: result)
}
@@ -1,20 +1,14 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let storage = Storage(client)
storage.getFileDownload(
let byteBuffer = try await storage.getFileDownload(
bucketId: "[BUCKET_ID]",
fileId: "[FILE_ID]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let byteBuffer):
print(String(describing: byteBuffer)
}
}
)
print(String(describing: byteBuffer)
}
@@ -1,20 +1,14 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let storage = Storage(client)
storage.getFilePreview(
let byteBuffer = try await storage.getFilePreview(
bucketId: "[BUCKET_ID]",
fileId: "[FILE_ID]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let byteBuffer):
print(String(describing: byteBuffer)
}
}
)
print(String(describing: byteBuffer)
}
@@ -1,20 +1,14 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let storage = Storage(client)
storage.getFileView(
let byteBuffer = try await storage.getFileView(
bucketId: "[BUCKET_ID]",
fileId: "[FILE_ID]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let byteBuffer):
print(String(describing: byteBuffer)
}
}
)
print(String(describing: byteBuffer)
}
@@ -1,20 +1,14 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let storage = Storage(client)
storage.getFile(
let file = try await storage.getFile(
bucketId: "[BUCKET_ID]",
fileId: "[FILE_ID]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let file):
print(String(describing: file)
}
}
)
print(String(describing: file)
}
@@ -1,19 +1,13 @@
import Appwrite
func main() {
func main() async throws {
let client = Client()
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
let storage = Storage(client)
storage.listFiles(
let fileList = try await storage.listFiles(
bucketId: "[BUCKET_ID]"
) { result in
switch result {
case .failure(let error):
print(error.message)
case .success(let fileList):
print(String(describing: fileList)
}
}
)
print(String(describing: fileList)
}

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