Merge branch 'feat-storage-buckets' into feat-large-file
This commit is contained in:
commit
879bff2bb1
97 changed files with 3242 additions and 2284 deletions
|
@ -4,7 +4,7 @@ We would ❤️ for you to contribute to Appwrite and help make it better! We wa
|
|||
|
||||
## How to Start?
|
||||
|
||||
If you are worried or don’t know where to start, check out our next section explaining what kind of help we could use and where can you get involved. You can reach out with questions to [Eldad Fux (@eldadfux)](https://twitter.com/eldadfux) or [@appwrite_io](https://twitter.com/appwrite_io) on Twitter, and anyone from the [Appwrite team on Discord](https://discord.gg/GSeTUeA). You can also submit an issue, and a maintainer can guide you!
|
||||
If you are worried or don’t know where to start, check out our next section explaining what kind of help we could use and where can you get involved. You can reach out with questions to [Eldad Fux (@eldadfux)](https://twitter.com/eldadfux) or [@appwrite](https://twitter.com/appwrite) on Twitter, and anyone from the [Appwrite team on Discord](https://discord.gg/GSeTUeA). You can also submit an issue, and a maintainer can guide you!
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
|
@ -406,7 +406,7 @@ Pull requests are great, but there are many other areas where you can help Appwr
|
|||
|
||||
### Blogging & Speaking
|
||||
|
||||
Blogging, speaking about, or creating tutorials about one of Appwrite’s many features. Mention [@appwrite_io](https://twitter.com/appwrite_io) on Twitter and/or [email team@appwrite.io](mailto:team@appwrite.io) so we can give pointers and tips and help you spread the word by promoting your content on the different Appwrite communication channels. Please add your blog posts and videos of talks to our [Awesome Appwrite](https://github.com/appwrite/awesome-appwrite) repo on GitHub.
|
||||
Blogging, speaking about, or creating tutorials about one of Appwrite’s many features. Mention [@appwrite](https://twitter.com/appwrite) on Twitter and/or [email team@appwrite.io](mailto:team@appwrite.io) so we can give pointers and tips and help you spread the word by promoting your content on the different Appwrite communication channels. Please add your blog posts and videos of talks to our [Awesome Appwrite](https://github.com/appwrite/awesome-appwrite) repo on GitHub.
|
||||
|
||||
### Presenting at Meetups
|
||||
|
||||
|
|
|
@ -31,10 +31,10 @@ ENV DEBUG=$DEBUG
|
|||
|
||||
ENV PHP_REDIS_VERSION=5.3.4 \
|
||||
PHP_MONGODB_VERSION=1.9.1 \
|
||||
PHP_SWOOLE_VERSION=v4.8.0 \
|
||||
PHP_SWOOLE_VERSION=v4.8.3 \
|
||||
PHP_IMAGICK_VERSION=3.5.1 \
|
||||
PHP_YAML_VERSION=2.2.1 \
|
||||
PHP_MAXMINDDB_VERSION=v1.10.1
|
||||
PHP_YAML_VERSION=2.2.2 \
|
||||
PHP_MAXMINDDB_VERSION=v1.11.0
|
||||
|
||||
RUN \
|
||||
apk add --no-cache --virtual .deps \
|
||||
|
|
10
README.md
10
README.md
|
@ -8,13 +8,13 @@
|
|||
<br />
|
||||
</p>
|
||||
|
||||
[![Hacktoberfest](https://img.shields.io/static/v1?label=hacktoberfest&message=friendly&color=90a88b&style=flat-square)](https://hacktoberfest.appwrite.io)
|
||||
[![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord)
|
||||
<!-- [![Hacktoberfest](https://img.shields.io/static/v1?label=hacktoberfest&message=friendly&color=90a88b&style=flat-square)](https://hacktoberfest.appwrite.io) -->
|
||||
[![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord?r=Github)
|
||||
[![Docker Pulls](https://img.shields.io/docker/pulls/appwrite/appwrite?color=f02e65&style=flat-square)](https://hub.docker.com/r/appwrite/appwrite)
|
||||
[![Build Status](https://img.shields.io/travis/com/appwrite/appwrite?style=flat-square)](https://travis-ci.com/appwrite/appwrite)
|
||||
[![Twitter Account](https://img.shields.io/twitter/follow/appwrite_io?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite_io)
|
||||
[![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite)
|
||||
[![Translate](https://img.shields.io/badge/translate-f02e65?style=flat-square)](docs/tutorials/add-translations.md)
|
||||
<!-- [![Swag Store](https://img.shields.io/badge/swag%20store-f02e65?style=flat-square)](https://store.appwrite.io) -->
|
||||
[![Swag Store](https://img.shields.io/badge/swag%20store-f02e65?style=flat-square)](https://store.appwrite.io)
|
||||
|
||||
[**Appwrite 0.11 has been released! Learn what's new!**](https://dev.to/appwrite/building-apps-just-got-swifter-announcing-appwrite-v011-4g62)
|
||||
|
||||
|
@ -155,7 +155,7 @@ For security issues, kindly email us at [security@appwrite.io](mailto:security@a
|
|||
|
||||
## Follow Us
|
||||
|
||||
Join our growing community around the world! See our official [Blog](https://medium.com/appwrite-io). Follow us on [Twitter](https://twitter.com/appwrite_io), [Facebook Page](https://www.facebook.com/appwrite.io), [Facebook Group](https://www.facebook.com/groups/appwrite.developers/) , [Dev Community](https://dev.to/appwrite) or join our live [Discord server](https://discord.gg/GSeTUeA) for more help, ideas, and discussions.
|
||||
Join our growing community around the world! See our official [Blog](https://medium.com/appwrite-io). Follow us on [Twitter](https://twitter.com/appwrite), [Facebook Page](https://www.facebook.com/appwrite.io), [Facebook Group](https://www.facebook.com/groups/appwrite.developers/) , [Dev Community](https://dev.to/appwrite) or join our live [Discord server](https://discord.gg/GSeTUeA) for more help, ideas, and discussions.
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
@ -43,6 +43,17 @@ $collections = [
|
|||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'enabled',
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'signed' => true,
|
||||
'size' => 0,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => 'permission',
|
||||
'type' => Database::VAR_STRING,
|
||||
|
@ -500,7 +511,7 @@ $collections = [
|
|||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['json'],
|
||||
'filters' => ['json', 'encrypt'],
|
||||
],
|
||||
[
|
||||
'$id' => 'platforms',
|
||||
|
@ -810,12 +821,12 @@ $collections = [
|
|||
'$id' => 'secret',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 256, // var_dump of \bin2hex(\random_bytes(128)) => string(256)
|
||||
'size' => 512, // var_dump of \bin2hex(\random_bytes(128)) => string(256) doubling for encryption
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
'filters' => ['encrypt'],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
|
@ -882,12 +893,12 @@ $collections = [
|
|||
'$id' => 'httpPass',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'size' => Database::LENGTH_KEY, // TODO will the length suffice after encryption?
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
'filters' => ['encrypt'],
|
||||
],
|
||||
[
|
||||
'$id' => 'security',
|
||||
|
@ -1155,18 +1166,18 @@ $collections = [
|
|||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
'filters' => ['encrypt'],
|
||||
],
|
||||
[
|
||||
'$id' => 'secret',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 64, // https://www.tutorialspoint.com/how-long-is-the-sha256-hash-in-mysql
|
||||
'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' => [],
|
||||
'filters' => ['encrypt'],
|
||||
],
|
||||
[
|
||||
'$id' => 'expire',
|
||||
|
@ -1497,7 +1508,7 @@ $collections = [
|
|||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
'filters' => ['encrypt'],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
|
|
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
|
@ -317,7 +317,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
|||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->param('projectId', '', new Text(1024), 'Project unique ID.')
|
||||
->param('projectId', '', new Text(1024), 'Project ID.')
|
||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
|
||||
->param('code', '', new Text(1024), 'OAuth2 code.')
|
||||
->param('state', '', new Text(2048), 'Login state params.', true)
|
||||
|
@ -344,7 +344,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
|||
->label('scope', 'public')
|
||||
->label('origin', '*')
|
||||
->label('docs', false)
|
||||
->param('projectId', '', new Text(1024), 'Project unique ID.')
|
||||
->param('projectId', '', new Text(1024), 'Project ID.')
|
||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
|
||||
->param('code', '', new Text(1024), 'OAuth2 code.')
|
||||
->param('state', '', new Text(2048), 'Login state params.', true)
|
||||
|
@ -648,8 +648,9 @@ App::post('/v1/account/sessions/magic-url')
|
|||
throw new Exception('SMTP Disabled', 503);
|
||||
}
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
|
||||
$isAppUser = Auth::isAppUser(Authorization::$roles);
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$user = $dbForInternal->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]);
|
||||
|
||||
|
@ -668,8 +669,7 @@ App::post('/v1/account/sessions/magic-url')
|
|||
|
||||
$userId = $userId == 'unique()' ? $dbForInternal->getId() : $userId;
|
||||
|
||||
$user = Authorization::skip(function () use ($dbForInternal, $userId, $email) {
|
||||
return $dbForInternal->createDocument('users', new Document([
|
||||
$user = Authorization::skip(fn () => $dbForInternal->createDocument('users', new Document([
|
||||
'$id' => $userId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['user:' . $userId],
|
||||
|
@ -686,8 +686,7 @@ App::post('/v1/account/sessions/magic-url')
|
|||
'memberships' => [],
|
||||
'search' => implode(' ', [$userId, $email]),
|
||||
'deleted' => false
|
||||
]));
|
||||
});
|
||||
])));
|
||||
|
||||
$mails->setParam('event', 'users.create');
|
||||
$audits->setParam('event', 'users.create');
|
||||
|
@ -771,7 +770,7 @@ App::put('/v1/account/sessions/magic-url')
|
|||
->label('sdk.response.model', Response::MODEL_SESSION)
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},userId:{param-userId}')
|
||||
->param('userId', '', new CustomId(), 'User unique ID.')
|
||||
->param('userId', '', new CustomId(), 'User ID.')
|
||||
->param('secret', '', new Text(256), 'Valid verification token.')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
|
@ -1185,8 +1184,8 @@ App::get('/v1/account/logs')
|
|||
->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('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. Use this value to manage pagination. 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 param to manage pagination.', true)
|
||||
->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('user')
|
||||
->inject('locale')
|
||||
|
@ -1271,7 +1270,7 @@ App::get('/v1/account/sessions/:sessionId')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_SESSION)
|
||||
->param('sessionId', null, new UID(), 'Session unique ID. Use the string \'current\' to get the current device session.')
|
||||
->param('sessionId', null, new UID(), 'Session ID. Use the string \'current\' to get the current device session.')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('locale')
|
||||
|
@ -1367,8 +1366,8 @@ App::patch('/v1/account/password')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->param('password', '', new Password(), 'User password. Must be at least 8 chars.')
|
||||
->param('oldPassword', '', new Password(), 'Old user password. Must be at least 8 chars.', true)
|
||||
->param('password', '', new Password(), 'New user password. Must be at least 8 chars.')
|
||||
->param('oldPassword', '', new Password(), 'Current user password. Must be at least 8 chars.', true)
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('dbForInternal')
|
||||
|
@ -1585,7 +1584,7 @@ App::delete('/v1/account/sessions/:sessionId')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->label('abuse-limit', 100)
|
||||
->param('sessionId', null, new UID(), 'Session unique ID. Use the string \'current\' to delete the current device session.')
|
||||
->param('sessionId', null, new UID(), 'Session ID. Use the string \'current\' to delete the current device session.')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
|
@ -1780,8 +1779,9 @@ App::post('/v1/account/recovery')
|
|||
throw new Exception('SMTP Disabled', 503);
|
||||
}
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
|
||||
$isAppUser = Auth::isAppUser(Authorization::$roles);
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$email = \strtolower($email);
|
||||
$profile = $dbForInternal->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
|
||||
|
@ -1867,10 +1867,10 @@ App::put('/v1/account/recovery')
|
|||
->label('sdk.response.model', Response::MODEL_TOKEN)
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},userId:{param-userId}')
|
||||
->param('userId', '', new UID(), 'User account UID address.')
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('secret', '', new Text(256), 'Valid reset token.')
|
||||
->param('password', '', new Password(), 'User password. Must be at least 8 chars.')
|
||||
->param('passwordAgain', '', new Password(), 'New password again. Must be at least 8 chars.')
|
||||
->param('password', '', new Password(), 'New user password. Must be at least 8 chars.')
|
||||
->param('passwordAgain', '', new Password(), 'Repeat new user password. Must be at least 8 chars.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('audits')
|
||||
|
@ -1972,8 +1972,9 @@ App::post('/v1/account/verification')
|
|||
throw new Exception('SMTP Disabled', 503);
|
||||
}
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
|
||||
$isAppUser = Auth::isAppUser(Authorization::$roles);
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$verificationSecret = Auth::tokenGenerator();
|
||||
|
||||
|
@ -2049,7 +2050,7 @@ App::put('/v1/account/verification')
|
|||
->label('sdk.response.model', Response::MODEL_TOKEN)
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},userId:{param-userId}')
|
||||
->param('userId', '', new UID(), 'User unique ID.')
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('secret', '', new Text(256), 'Valid verification token.')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
|
|
|
@ -95,9 +95,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(function ($code, $width, $height, $quality, $response) use ($avatarCallback) {
|
||||
return $avatarCallback('credit-cards', $code, $width, $height, $quality, $response);
|
||||
});
|
||||
->action(fn($code, $width, $height, $quality, $response) => $avatarCallback('credit-cards', $code, $width, $height, $quality, $response));
|
||||
|
||||
App::get('/v1/avatars/browsers/:code')
|
||||
->desc('Get Browser Icon')
|
||||
|
@ -115,9 +113,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(function ($code, $width, $height, $quality, $response) use ($avatarCallback) {
|
||||
return $avatarCallback('browsers', $code, $width, $height, $quality, $response);
|
||||
});
|
||||
->action(fn($code, $width, $height, $quality, $response) => $avatarCallback('browsers', $code, $width, $height, $quality, $response));
|
||||
|
||||
App::get('/v1/avatars/flags/:code')
|
||||
->desc('Get Country Flag')
|
||||
|
@ -135,9 +131,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(function ($code, $width, $height, $quality, $response) use ($avatarCallback) {
|
||||
return $avatarCallback('flags', $code, $width, $height, $quality, $response);
|
||||
});
|
||||
->action(fn($code, $width, $height, $quality, $response) => $avatarCallback('flags', $code, $width, $height, $quality, $response));
|
||||
|
||||
App::get('/v1/avatars/image')
|
||||
->desc('Get Image from URL')
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -39,11 +39,11 @@ App::post('/v1/functions')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_FUNCTION)
|
||||
->param('functionId', '', 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('functionId', '', new CustomId(), 'Function 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), 'Function name. Max length: 128 chars.')
|
||||
->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
|
||||
->param('runtime', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Execution runtime.')
|
||||
->param('vars', [], new Assoc(), 'Key-value JSON object.', true)
|
||||
->param('vars', [], new Assoc(), 'Key-value JSON object that will be passed to the function as environment variables.', true)
|
||||
->param('events', [], new ArrayList(new WhiteList(array_keys(Config::getParam('events')), true)), 'Events list.', true)
|
||||
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
|
||||
->param('timeout', 15, new Range(1, 900), 'Function maximum execution time in seconds.', true)
|
||||
|
@ -88,9 +88,9 @@ App::get('/v1/functions')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_FUNCTION_LIST)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
|
||||
->param('cursor', '', new UID(), 'ID of the function used as the starting point for the query, excluding the function itself. Should be used for efficient pagination when working with large sets of data.', true)
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of functions 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)
|
||||
->param('cursor', '', new UID(), 'ID of the function used as the starting point for the query, excluding the function itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
|
@ -119,6 +119,34 @@ App::get('/v1/functions')
|
|||
]), Response::MODEL_FUNCTION_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/functions/runtimes')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('List the currently active function runtimes.')
|
||||
->label('scope', 'functions.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'listRuntimes')
|
||||
->label('sdk.description', '/docs/references/functions/list-runtimes.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_RUNTIME_LIST)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
|
||||
$runtimes = Config::getParam('runtimes');
|
||||
|
||||
$runtimes = array_map(function ($key) use ($runtimes) {
|
||||
$runtimes[$key]['$id'] = $key;
|
||||
return $runtimes[$key];
|
||||
}, array_keys($runtimes));
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'sum' => count($runtimes),
|
||||
'runtimes' => $runtimes
|
||||
]), Response::MODEL_RUNTIME_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/functions/:functionId')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Get Function')
|
||||
|
@ -130,7 +158,7 @@ App::get('/v1/functions/:functionId')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_FUNCTION)
|
||||
->param('functionId', '', new UID(), 'Function unique ID.')
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->action(function ($functionId, $response, $dbForInternal) {
|
||||
|
@ -156,7 +184,7 @@ App::get('/v1/functions/:functionId/usage')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USAGE_FUNCTIONS)
|
||||
->param('functionId', '', new UID(), 'Function unique ID.')
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d']), 'Date range.', true)
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
|
@ -262,13 +290,13 @@ App::put('/v1/functions/:functionId')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_FUNCTION)
|
||||
->param('functionId', '', new UID(), 'Function unique ID.')
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
|
||||
->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->param('vars', [], new Assoc(), 'Key-value JSON object.', true)
|
||||
->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
|
||||
->param('vars', [], new Assoc(), 'Key-value JSON object that will be passed to the function as environment variables.', true)
|
||||
->param('events', [], new ArrayList(new WhiteList(array_keys(Config::getParam('events')), true)), 'Events list.', true)
|
||||
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
|
||||
->param('timeout', 15, new Range(1, 900), 'Function maximum execution time in seconds.', true)
|
||||
->param('timeout', 15, new Range(1, 900), 'Maximum execution time in seconds.', true)
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('project')
|
||||
|
@ -324,8 +352,8 @@ App::patch('/v1/functions/:functionId/tag')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_FUNCTION)
|
||||
->param('functionId', '', new UID(), 'Function unique ID.')
|
||||
->param('tag', '', new UID(), 'Tag unique ID.')
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('tag', '', new UID(), 'Tag ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('project')
|
||||
|
@ -378,7 +406,7 @@ App::delete('/v1/functions/:functionId')
|
|||
->label('sdk.description', '/docs/references/functions/delete-function.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->param('functionId', '', new UID(), 'Function unique ID.')
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('deletes')
|
||||
|
@ -419,7 +447,7 @@ App::post('/v1/functions/:functionId/tags')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_TAG)
|
||||
->param('functionId', '', new UID(), 'Function unique ID.')
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('command', '', new Text('1028'), 'Code execution command.')
|
||||
->param('code', [], new File(), 'Gzip file with your code package. When used with the Appwrite CLI, pass the path to your code directory, and the CLI will automatically package your code. Use a path that is within the current directory.', false)
|
||||
->inject('request')
|
||||
|
@ -565,11 +593,11 @@ App::get('/v1/functions/:functionId/tags')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_TAG_LIST)
|
||||
->param('functionId', '', new UID(), 'Function unique ID.')
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
|
||||
->param('cursor', '', new UID(), 'ID of the tag used as the starting point for the query, excluding the tag itself. Should be used for efficient pagination when working with large sets of data.', true)
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of tags 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)
|
||||
->param('cursor', '', new UID(), 'ID of the tag used as the starting point for the query, excluding the tag itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
|
@ -620,8 +648,8 @@ App::get('/v1/functions/:functionId/tags/:tagId')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_TAG)
|
||||
->param('functionId', '', new UID(), 'Function unique ID.')
|
||||
->param('tagId', '', new UID(), 'Tag unique ID.')
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('tagId', '', new UID(), 'Tag ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->action(function ($functionId, $tagId, $response, $dbForInternal) {
|
||||
|
@ -658,8 +686,8 @@ App::delete('/v1/functions/:functionId/tags/:tagId')
|
|||
->label('sdk.description', '/docs/references/functions/delete-tag.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->param('functionId', '', new UID(), 'Function unique ID.')
|
||||
->param('tagId', '', new UID(), 'Tag unique ID.')
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('tagId', '', new UID(), 'Tag ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('usage')
|
||||
|
@ -719,7 +747,7 @@ App::post('/v1/functions/:functionId/executions')
|
|||
->label('sdk.response.model', Response::MODEL_EXECUTION)
|
||||
->label('abuse-limit', 60)
|
||||
->label('abuse-time', 60)
|
||||
->param('functionId', '', new UID(), 'Function unique ID.')
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('data', '', new Text(8192), 'String of custom data to send to function.', true)
|
||||
// ->param('async', 1, new Range(0, 1), 'Execute code asynchronously. Pass 1 for true, 0 for false. Default value is 1.', true)
|
||||
->inject('response')
|
||||
|
@ -827,11 +855,11 @@ App::get('/v1/functions/:functionId/executions')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_EXECUTION_LIST)
|
||||
->param('functionId', '', new UID(), 'Function unique ID.')
|
||||
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of executions 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)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->param('cursor', '', new UID(), 'ID of the execution used as the starting point for the query, excluding the execution itself. Should be used for efficient pagination when working with large sets of data.', true)
|
||||
->param('cursor', '', new UID(), 'ID of the execution used as the starting point for the query, excluding the execution itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
|
@ -839,9 +867,7 @@ App::get('/v1/functions/:functionId/executions')
|
|||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForInternal */
|
||||
|
||||
$function = Authorization::skip(function() use ($dbForInternal, $functionId) {
|
||||
return $dbForInternal->getDocument('functions', $functionId);
|
||||
});
|
||||
$function = Authorization::skip(fn() => $dbForInternal->getDocument('functions', $functionId));
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
|
@ -883,8 +909,8 @@ App::get('/v1/functions/:functionId/executions/:executionId')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_EXECUTION)
|
||||
->param('functionId', '', new UID(), 'Function unique ID.')
|
||||
->param('executionId', '', new UID(), 'Execution unique ID.')
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('executionId', '', new UID(), 'Execution ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->action(function ($functionId, $executionId, $response, $dbForInternal) {
|
||||
|
|
|
@ -162,8 +162,8 @@ App::get('/v1/projects')
|
|||
->label('sdk.response.model', Response::MODEL_PROJECT_LIST)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
|
||||
->param('cursor', '', new UID(), 'ID of the project used as the starting point for the query, excluding the project itself. Should be used for efficient pagination when working with large sets of data.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursor', '', new UID(), 'ID of the project used as the starting point for the query, excluding the project itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Adapter\Filesystem;
|
||||
use Appwrite\ClamAV\Network;
|
||||
|
@ -524,8 +525,8 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', 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('file', [], new File(), 'Binary file.', false)
|
||||
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default only the current user is granted with write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default only the current user is granted with write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
|
@ -560,6 +561,25 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
}
|
||||
}
|
||||
|
||||
$read = (is_null($read) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $read ?? []; // By default set read permissions for user
|
||||
$write = (is_null($write) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $write ?? [];
|
||||
|
||||
// Users can only add their roles to files, API keys and Admin users can add any
|
||||
$roles = Authorization::getRoles();
|
||||
|
||||
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
|
||||
foreach ($read as $role) {
|
||||
if (!Authorization::isRole($role)) {
|
||||
throw new Exception('Read permissions must be one of: ('.\implode(', ', $roles).')', 400);
|
||||
}
|
||||
}
|
||||
foreach ($write as $role) {
|
||||
if (!Authorization::isRole($role)) {
|
||||
throw new Exception('Write permissions must be one of: ('.\implode(', ', $roles).')', 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$file = $request->getFiles('file');
|
||||
|
||||
/**
|
||||
|
@ -832,9 +852,9 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
|||
->label('sdk.response.model', Response::MODEL_FILE_LIST)
|
||||
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
|
||||
->param('cursor', '', new UID(), 'ID of the file used as the starting point for the query, excluding the file itself. Should be used for efficient pagination when working with large sets of data.', true)
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of files 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 param to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursor', '', new UID(), 'ID of the file used as the starting point for the query, excluding the file itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
|
@ -922,7 +942,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_FILE)
|
||||
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new UID(), 'File unique ID.')
|
||||
->param('fileId', '', new UID(), 'File ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('dbForExternal')
|
||||
|
@ -981,7 +1001,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE)
|
||||
->label('sdk.methodType', 'location')
|
||||
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new UID(), 'File unique ID')
|
||||
->param('fileId', '', new UID(), 'File ID')
|
||||
->param('width', 0, new Range(0, 4000), 'Resize preview image width, Pass an integer between 0 to 4000.', true)
|
||||
->param('height', 0, new Range(0, 4000), 'Resize preview image height, Pass an integer between 0 to 4000.', true)
|
||||
->param('gravity', Image::GRAVITY_CENTER, new WhiteList(Image::getGravityTypes()), 'Image crop gravity. Can be one of ' . implode(",", Image::getGravityTypes()), true)
|
||||
|
@ -1167,8 +1187,9 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', '*/*')
|
||||
->label('sdk.methodType', 'location')
|
||||
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new UID(), 'File unique ID.')
|
||||
->inject('request')
|
||||
->param('bucketId', null, new UID(), 'Storage bucket ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new UID(), 'File ID.')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
|
@ -1315,7 +1336,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
|||
->label('sdk.response.type', '*/*')
|
||||
->label('sdk.methodType', 'location')
|
||||
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new UID(), 'File unique ID.')
|
||||
->param('fileId', '', new UID(), 'File ID.')
|
||||
->inject('response')
|
||||
->inject('request')
|
||||
->inject('dbForInternal')
|
||||
|
@ -1475,22 +1496,42 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
->label('sdk.response.model', Response::MODEL_FILE)
|
||||
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new UID(), 'File unique ID.')
|
||||
->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](/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](/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.', true)
|
||||
->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.', true)
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('dbForExternal')
|
||||
->inject('user')
|
||||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('mode')
|
||||
->action(function ($bucketId, $fileId, $read, $write, $response, $dbForInternal, $dbForExternal, $audits, $usage, $mode) {
|
||||
->action(function ($bucketId, $fileId, $read, $write, $response, $dbForInternal, $dbForExternal, $user, $audits, $usage, $mode) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForInternal */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Appwrite\Event\Event $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var string $mode */
|
||||
|
||||
$bucket = $dbForInternal->getDocument('buckets', $bucketId);
|
||||
$read = (is_null($read) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $read ?? []; // By default set read permissions for user
|
||||
$write = (is_null($write) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $write ?? [];
|
||||
|
||||
// Users can only add their roles to files, API keys and Admin users can add any
|
||||
$roles = Authorization::getRoles();
|
||||
|
||||
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
|
||||
foreach ($read as $role) {
|
||||
if (!Authorization::isRole($role)) {
|
||||
throw new Exception('Read permissions must be one of: ('.\implode(', ', $roles).')', 400);
|
||||
}
|
||||
}
|
||||
foreach ($write as $role) {
|
||||
if (!Authorization::isRole($role)) {
|
||||
throw new Exception('Write permissions must be one of: ('.\implode(', ', $roles).')', 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN )) {
|
||||
|
@ -1557,7 +1598,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new UID(), 'File unique ID.')
|
||||
->param('fileId', '', new UID(), 'File ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('dbForExternal')
|
||||
|
@ -1757,7 +1798,7 @@ App::get('/v1/storage/:bucketId/usage')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USAGE_BUCKETS)
|
||||
->param('bucketId', '', new UID(), 'Bucket unique ID.')
|
||||
->param('bucketId', '', new UID(), 'Bucket ID.')
|
||||
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
|
|
|
@ -34,7 +34,7 @@ App::post('/v1/teams')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_TEAM)
|
||||
->param('teamId', '', 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('teamId', '', new CustomId(), 'Team 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', null, new Text(128), 'Team name. Max length: 128 chars.')
|
||||
->param('roles', ['owner'], new ArrayList(new Key()), 'Array of strings. Use this param to set the roles in the team for the user who created it. The default role is **owner**. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Max length for each role is 32 chars.', true)
|
||||
->inject('response')
|
||||
|
@ -49,8 +49,8 @@ App::post('/v1/teams')
|
|||
|
||||
Authorization::disable();
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
|
||||
$isAppUser = Auth::isAppUser(Authorization::$roles);
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
|
||||
$teamId = $teamId == 'unique()' ? $dbForInternal->getId() : $teamId;
|
||||
$team = $dbForInternal->createDocument('teams', new Document([
|
||||
|
@ -105,9 +105,9 @@ App::get('/v1/teams')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_TEAM_LIST)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
|
||||
->param('cursor', '', new UID(), 'ID of the team used as the starting point for the query, excluding the team itself. Should be used for efficient pagination when working with large sets of data.', true)
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of teams 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 param to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursor', '', new UID(), 'ID of the team used as the starting point for the query, excluding the team itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
|
@ -150,7 +150,7 @@ App::get('/v1/teams/:teamId')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_TEAM)
|
||||
->param('teamId', '', new UID(), 'Team unique ID.')
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->action(function ($teamId, $response, $dbForInternal) {
|
||||
|
@ -178,8 +178,8 @@ App::put('/v1/teams/:teamId')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_TEAM)
|
||||
->param('teamId', '', new UID(), 'Team unique ID.')
|
||||
->param('name', null, new Text(128), 'Team name. Max length: 128 chars.')
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('name', null, new Text(128), 'New team name. Max length: 128 chars.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->action(function ($teamId, $name, $response, $dbForInternal) {
|
||||
|
@ -211,7 +211,7 @@ App::delete('/v1/teams/:teamId')
|
|||
->label('sdk.description', '/docs/references/teams/delete-team.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->param('teamId', '', new UID(), 'Team unique ID.')
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('events')
|
||||
|
@ -269,11 +269,11 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_MEMBERSHIP)
|
||||
->label('abuse-limit', 10)
|
||||
->param('teamId', '', new UID(), 'Team unique ID.')
|
||||
->param('email', '', new Email(), 'New team member email.')
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('email', '', new Email(), 'Email of the new team member.')
|
||||
->param('roles', [], new ArrayList(new Key()), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Max length for each role is 32 chars.')
|
||||
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) // TODO add our own built-in confirm page
|
||||
->param('name', '', new Text(128), 'New team member name. Max length: 128 chars.', true)
|
||||
->param('name', '', new Text(128), 'Name of the new team member. Max length: 128 chars.', true)
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('user')
|
||||
|
@ -293,8 +293,8 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
throw new Exception('SMTP Disabled', 503);
|
||||
}
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
|
||||
$isAppUser = Auth::isAppUser(Authorization::$roles);
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
|
||||
$email = \strtolower($email);
|
||||
$name = (empty($name)) ? $email : $name;
|
||||
|
@ -441,11 +441,11 @@ App::get('/v1/teams/:teamId/memberships')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_MEMBERSHIP_LIST)
|
||||
->param('teamId', '', new UID(), 'Team unique ID.')
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
|
||||
->param('cursor', '', new UID(), 'ID of the membership used as the starting point for the query, excluding the membership itself. Should be used for efficient pagination when working with large sets of data.', true)
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of memberships 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)
|
||||
->param('cursor', '', new UID(), 'ID of the membership used as the starting point for the query, excluding the membership itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
|
@ -470,20 +470,22 @@ App::get('/v1/teams/:teamId/memberships')
|
|||
|
||||
$memberships = $dbForInternal->find('memberships', [new Query('teamId', Query::TYPE_EQUAL, [$teamId])], $limit, $offset, [], [$orderType], $cursorMembership ?? null, $cursorDirection);
|
||||
$sum = $dbForInternal->count('memberships', [new Query('teamId', Query::TYPE_EQUAL, [$teamId])], APP_LIMIT_COUNT);
|
||||
$users = [];
|
||||
|
||||
foreach ($memberships as $membership) {
|
||||
if (empty($membership->getAttribute('userId', null))) {
|
||||
continue;
|
||||
}
|
||||
$memberships = array_filter($memberships, fn(Document $membership) => !empty($membership->getAttribute('userId')));
|
||||
|
||||
$temp = $dbForInternal->getDocument('users', $membership->getAttribute('userId', null))->getArrayCopy(['email', 'name']);
|
||||
$memberships = array_map(function($membership) use ($dbForInternal) {
|
||||
$user = $dbForInternal->getDocument('users', $membership->getAttribute('userId'));
|
||||
|
||||
$users[] = new Document(\array_merge($temp, $membership->getArrayCopy()));
|
||||
}
|
||||
$membership
|
||||
->setAttribute('name', $user->getAttribute('name'))
|
||||
->setAttribute('email', $user->getAttribute('email'))
|
||||
;
|
||||
|
||||
return $membership;
|
||||
}, $memberships);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'memberships' => $users,
|
||||
'memberships' => $memberships,
|
||||
'sum' => $sum,
|
||||
]), Response::MODEL_MEMBERSHIP_LIST);
|
||||
});
|
||||
|
@ -499,8 +501,8 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_MEMBERSHIP_LIST)
|
||||
->param('teamId', '', new UID(), 'Team unique ID.')
|
||||
->param('membershipId', '', new UID(), 'membership unique ID.')
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('membershipId', '', new UID(), 'Membership ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->action(function ($teamId, $membershipId, $response, $dbForInternal) {
|
||||
|
@ -515,13 +517,18 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
|
|||
|
||||
$membership = $dbForInternal->getDocument('memberships', $membershipId);
|
||||
|
||||
if($membership->isEmpty() || empty($membership->getAttribute('userId', null))) {
|
||||
if($membership->isEmpty() || empty($membership->getAttribute('userId'))) {
|
||||
throw new Exception('Membership not found', 404);
|
||||
}
|
||||
|
||||
$temp = $dbForInternal->getDocument('users', $membership->getAttribute('userId', null))->getArrayCopy(['email', 'name']);
|
||||
$user = $dbForInternal->getDocument('users', $membership->getAttribute('userId'));
|
||||
|
||||
$response->dynamic(new Document(\array_merge($temp, $membership->getArrayCopy())), Response::MODEL_MEMBERSHIP );
|
||||
$membership
|
||||
->setAttribute('name', $user->getAttribute('name'))
|
||||
->setAttribute('email', $user->getAttribute('email'))
|
||||
;
|
||||
|
||||
$response->dynamic($membership, Response::MODEL_MEMBERSHIP );
|
||||
});
|
||||
|
||||
App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
|
@ -536,9 +543,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_MEMBERSHIP)
|
||||
->param('teamId', '', new UID(), 'Team unique ID.')
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('membershipId', '', new UID(), 'Membership ID.')
|
||||
->param('roles', [], new ArrayList(new Key()), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Max length for each role is 32 chars.')
|
||||
->param('roles', [], new ArrayList(new Key()), 'An array of strings. Use this param to set the user\'s roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Max length for each role is 32 chars.')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
|
@ -566,8 +573,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
|||
throw new Exception('User not found', 404);
|
||||
}
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
|
||||
$isAppUser = Auth::isAppUser(Authorization::$roles);
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
$isOwner = Authorization::isRole('team:'.$team->getId().'/owner');;
|
||||
|
||||
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
|
||||
|
@ -601,9 +608,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_MEMBERSHIP)
|
||||
->param('teamId', '', new UID(), 'Team unique ID.')
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('membershipId', '', new UID(), 'Membership ID.')
|
||||
->param('userId', '', new UID(), 'User unique ID.')
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('secret', '', new Text(256), 'Secret key.')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
|
@ -631,11 +638,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
|||
throw new Exception('Team IDs don\'t match', 404);
|
||||
}
|
||||
|
||||
Authorization::disable();
|
||||
|
||||
$team = $dbForInternal->getDocument('teams', $teamId);
|
||||
|
||||
Authorization::reset();
|
||||
$team = Authorization::skip(fn() => $dbForInternal->getDocument('teams', $teamId));
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
|
@ -739,7 +742,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
|||
->label('sdk.description', '/docs/references/teams/delete-team-membership.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->param('teamId', '', new UID(), 'Team unique ID.')
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('membershipId', '', new UID(), 'Membership ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
|
|
|
@ -15,7 +15,7 @@ use Utopia\Audit\Audit;
|
|||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use DeviceDetector\DeviceDetector;
|
||||
use Appwrite\Detector\Detector;
|
||||
use Appwrite\Database\Validator\CustomId;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
|
@ -34,7 +34,7 @@ App::post('/v1/users')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->param('userId', '', 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('userId', '', new CustomId(), 'User 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('email', '', new Email(), 'User email.')
|
||||
->param('password', '', new Password(), 'User password. Must be at least 8 chars.')
|
||||
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
|
||||
|
@ -93,9 +93,9 @@ App::get('/v1/users')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER_LIST)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
|
||||
->param('cursor', '', new UID(), 'ID of the user used as the starting point for the query, excluding the user itself. Should be used for efficient pagination when working with large sets of data.', true)
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of users 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 param to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursor', '', new UID(), 'ID of the user used as the starting point for the query, excluding the user itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
|
@ -143,7 +143,7 @@ App::get('/v1/users/:userId')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->param('userId', '', new UID(), 'User unique ID.')
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('usage')
|
||||
|
@ -175,7 +175,7 @@ App::get('/v1/users/:userId/prefs')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_PREFERENCES)
|
||||
->param('userId', '', new UID(), 'User unique ID.')
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('usage')
|
||||
|
@ -209,7 +209,7 @@ App::get('/v1/users/:userId/sessions')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_SESSION_LIST)
|
||||
->param('userId', '', new UID(), 'User unique ID.')
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('locale')
|
||||
|
@ -258,9 +258,9 @@ App::get('/v1/users/:userId/logs')
|
|||
->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('userId', '', new UID(), 'User unique ID.')
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. Use this value to manage pagination. 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 param to manage pagination.', true)
|
||||
->param('userId', '', new UID(), 'User 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('dbForInternal')
|
||||
->inject('locale')
|
||||
|
@ -306,42 +306,29 @@ App::get('/v1/users/:userId/logs')
|
|||
foreach ($logs as $i => &$log) {
|
||||
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
|
||||
|
||||
$dd = new DeviceDetector($log['userAgent']);
|
||||
$detector = new Detector($log['userAgent']);
|
||||
$detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
|
||||
|
||||
$dd->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
|
||||
|
||||
$dd->parse();
|
||||
|
||||
$os = $dd->getOs();
|
||||
$osCode = (isset($os['short_name'])) ? $os['short_name'] : '';
|
||||
$osName = (isset($os['name'])) ? $os['name'] : '';
|
||||
$osVersion = (isset($os['version'])) ? $os['version'] : '';
|
||||
|
||||
$client = $dd->getClient();
|
||||
$clientType = (isset($client['type'])) ? $client['type'] : '';
|
||||
$clientCode = (isset($client['short_name'])) ? $client['short_name'] : '';
|
||||
$clientName = (isset($client['name'])) ? $client['name'] : '';
|
||||
$clientVersion = (isset($client['version'])) ? $client['version'] : '';
|
||||
$clientEngine = (isset($client['engine'])) ? $client['engine'] : '';
|
||||
$clientEngineVersion = (isset($client['engine_version'])) ? $client['engine_version'] : '';
|
||||
$os = $detector->getOS();
|
||||
$client = $detector->getClient();
|
||||
$device = $detector->getDevice();
|
||||
|
||||
$output[$i] = new Document([
|
||||
'event' => $log['event'],
|
||||
'ip' => $log['ip'],
|
||||
'time' => $log['time'],
|
||||
|
||||
'osCode' => $osCode,
|
||||
'osName' => $osName,
|
||||
'osVersion' => $osVersion,
|
||||
'clientType' => $clientType,
|
||||
'clientCode' => $clientCode,
|
||||
'clientName' => $clientName,
|
||||
'clientVersion' => $clientVersion,
|
||||
'clientEngine' => $clientEngine,
|
||||
'clientEngineVersion' => $clientEngineVersion,
|
||||
'deviceName' => $dd->getDeviceName(),
|
||||
'deviceBrand' => $dd->getBrandName(),
|
||||
'deviceModel' => $dd->getModel(),
|
||||
'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']);
|
||||
|
@ -377,8 +364,8 @@ App::patch('/v1/users/:userId/status')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->param('userId', '', new UID(), 'User unique ID.')
|
||||
->param('status', null, new Boolean(true), 'User Status. To activate the user pass `true` and to block the user pass `false`')
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('status', null, new Boolean(true), 'User Status. To activate the user pass `true` and to block the user pass `false`.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('usage')
|
||||
|
@ -413,8 +400,8 @@ App::patch('/v1/users/:userId/verification')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->param('userId', '', new UID(), 'User unique ID.')
|
||||
->param('emailVerification', false, new Boolean(), 'User Email Verification Status.')
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('emailVerification', false, new Boolean(), 'User email verification status.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('usage')
|
||||
|
@ -449,7 +436,7 @@ App::patch('/v1/users/:userId/name')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->param('userId', '', new UID(), 'User unique ID.')
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('name', '', new Text(128), 'User name. Max length: 128 chars.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
|
@ -488,8 +475,8 @@ App::patch('/v1/users/:userId/password')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->param('userId', '', new UID(), 'User unique ID.')
|
||||
->param('password', '', new Password(), 'New user password. Must be between 6 to 32 chars.')
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('password', '', new Password(), 'New user password. Must be at least 8 chars.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('audits')
|
||||
|
@ -531,7 +518,7 @@ App::patch('/v1/users/:userId/email')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->param('userId', '', new UID(), 'User unique ID.')
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
|
@ -581,7 +568,7 @@ App::patch('/v1/users/:userId/prefs')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_PREFERENCES)
|
||||
->param('userId', '', new UID(), 'User unique ID.')
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
|
@ -616,8 +603,8 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
|
|||
->label('sdk.description', '/docs/references/users/delete-user-session.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->param('userId', '', new UID(), 'User unique ID.')
|
||||
->param('sessionId', null, new UID(), 'User unique session ID.')
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('sessionId', null, new UID(), 'Session ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('events')
|
||||
|
@ -672,7 +659,7 @@ App::delete('/v1/users/:userId/sessions')
|
|||
->label('sdk.description', '/docs/references/users/delete-user-sessions.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->param('userId', '', new UID(), 'User unique ID.')
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('events')
|
||||
|
@ -695,7 +682,7 @@ App::delete('/v1/users/:userId/sessions')
|
|||
$dbForInternal->deleteDocument('sessions', $session->getId());
|
||||
}
|
||||
|
||||
$dbForInternal->updateDocument('users', $user->getId(), $user->getAttribute('sessions', []));
|
||||
$dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('sessions', []));
|
||||
|
||||
$events
|
||||
->setParam('eventData', $response->output($user, Response::MODEL_USER))
|
||||
|
@ -719,7 +706,7 @@ App::delete('/v1/users/:userId')
|
|||
->label('sdk.description', '/docs/references/users/delete.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->param('userId', '', function () {return new UID();}, 'User unique ID.')
|
||||
->param('userId', '', function () {return new UID();}, 'User ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('events')
|
||||
|
|
|
@ -5,7 +5,7 @@ require_once __DIR__.'/../init.php';
|
|||
use Utopia\App;
|
||||
use Utopia\Swoole\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\View;
|
||||
use Appwrite\Utopia\View;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Domains\Domain;
|
||||
|
@ -254,7 +254,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
|
|||
if(!empty($service)) {
|
||||
if(array_key_exists($service, $project->getAttribute('services',[]))
|
||||
&& !$project->getAttribute('services',[])[$service]
|
||||
&& !Auth::isPrivilegedUser(Authorization::$roles)) {
|
||||
&& !Auth::isPrivilegedUser(Authorization::getRoles())) {
|
||||
throw new Exception('Service is disabled', 503);
|
||||
}
|
||||
}
|
||||
|
@ -298,7 +298,7 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project) {
|
|||
/** @var Utopia\App $utopia */
|
||||
/** @var Utopia\Swoole\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
|
||||
if ($error instanceof PDOException) {
|
||||
|
|
|
@ -64,8 +64,9 @@ App::init(function ($utopia, $request, $response, $project, $user, $events, $aud
|
|||
;
|
||||
}
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
|
||||
$isAppUser = Auth::isAppUser(Authorization::$roles);
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
if (($abuse->check() // Route is rate-limited
|
||||
&& App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') // Abuse is not disabled
|
||||
|
@ -128,8 +129,8 @@ App::init(function ($utopia, $request, $project) {
|
|||
|
||||
$route = $utopia->match($request);
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
|
||||
$isAppUser = Auth::isAppUser(Authorization::$roles);
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
|
||||
if($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs
|
||||
return;
|
||||
|
@ -206,10 +207,17 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
|
|||
|
||||
if ($project->getId() !== 'console') {
|
||||
$payload = new Document($response->getPayload());
|
||||
$target = Realtime::fromPayload($events->getParam('event'), $payload);
|
||||
$collection = new Document($events->getParam('collection') ?? []);
|
||||
|
||||
$target = Realtime::fromPayload(
|
||||
event: $events->getParam('event'),
|
||||
payload: $payload,
|
||||
project: $project,
|
||||
collection: $collection
|
||||
);
|
||||
|
||||
Realtime::send(
|
||||
$project->getId(),
|
||||
$target['projectId'] ?? $project->getId(),
|
||||
$response->getPayload(),
|
||||
$events->getParam('event'),
|
||||
$target['channels'],
|
||||
|
|
|
@ -7,7 +7,7 @@ App::init(function ($utopia, $request, $response, $layout) {
|
|||
/** @var Utopia\App $utopia */
|
||||
/** @var Utopia\Swoole\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
/* AJAX check */
|
||||
if (!empty($request->getQuery('version', ''))) {
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Utopia\View;
|
||||
use Utopia\App;
|
||||
use Utopia\View;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Domains\Domain;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Storage\Storage;
|
||||
|
||||
App::init(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$layout
|
||||
->setParam('description', 'Appwrite Console allows you to easily manage, monitor, and control your entire backend API and tools.')
|
||||
|
@ -18,7 +18,7 @@ App::init(function ($layout) {
|
|||
|
||||
App::shutdown(function ($response, $layout) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$header = new View(__DIR__.'/../../views/console/comps/header.phtml');
|
||||
$footer = new View(__DIR__.'/../../views/console/comps/footer.phtml');
|
||||
|
@ -43,7 +43,7 @@ App::get('/error/:code')
|
|||
->param('code', null, new \Utopia\Validator\Numeric(), 'Valid status code number', false)
|
||||
->inject('layout')
|
||||
->action(function ($code, $layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/error.phtml');
|
||||
|
||||
|
@ -62,7 +62,7 @@ App::get('/console')
|
|||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/index.phtml');
|
||||
|
||||
|
@ -81,7 +81,7 @@ App::get('/console/account')
|
|||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/account/index.phtml');
|
||||
|
||||
|
@ -102,7 +102,7 @@ App::get('/console/notifications')
|
|||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/v1/console/notifications/index.phtml');
|
||||
|
||||
|
@ -117,7 +117,7 @@ App::get('/console/home')
|
|||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/home/index.phtml');
|
||||
$page
|
||||
|
@ -133,7 +133,7 @@ App::get('/console/settings')
|
|||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', ''));
|
||||
|
||||
|
@ -157,7 +157,7 @@ App::get('/console/webhooks')
|
|||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/webhooks/index.phtml');
|
||||
|
||||
|
@ -176,7 +176,7 @@ App::get('/console/keys')
|
|||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$scopes = array_keys(Config::getParam('scopes'));
|
||||
$page = new View(__DIR__.'/../../views/console/keys/index.phtml');
|
||||
|
@ -194,7 +194,7 @@ App::get('/console/database')
|
|||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/database/index.phtml');
|
||||
|
||||
|
@ -212,7 +212,7 @@ App::get('/console/database/collection')
|
|||
->inject('layout')
|
||||
->action(function ($id, $response, $layout) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$logs = new View(__DIR__.'/../../views/console/comps/logs.phtml');
|
||||
|
||||
|
@ -247,7 +247,7 @@ App::get('/console/database/document')
|
|||
->param('collection', '', new UID(), 'Collection unique ID.')
|
||||
->inject('layout')
|
||||
->action(function ($collection, $layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$logs = new View(__DIR__.'/../../views/console/comps/logs.phtml');
|
||||
|
||||
|
@ -280,7 +280,7 @@ App::get('/console/database/document/new')
|
|||
->param('collection', '', new UID(), 'Collection unique ID.')
|
||||
->inject('layout')
|
||||
->action(function ($collection, $layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/database/document.phtml');
|
||||
|
||||
|
@ -301,7 +301,7 @@ App::get('/console/storage')
|
|||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
$page = new View(__DIR__.'/../../views/console/storage/index.phtml');
|
||||
|
||||
$page
|
||||
|
@ -351,7 +351,7 @@ App::get('/console/users')
|
|||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/users/index.phtml');
|
||||
|
||||
|
@ -372,7 +372,7 @@ App::get('/console/users/user')
|
|||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/users/user.phtml');
|
||||
|
||||
|
@ -387,7 +387,7 @@ App::get('/console/users/teams/team')
|
|||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/users/team.phtml');
|
||||
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
use Appwrite\Specification\Format\OpenAPI3;
|
||||
use Appwrite\Specification\Format\Swagger2;
|
||||
use Appwrite\Specification\Specification;
|
||||
use Appwrite\Utopia\View;
|
||||
use Utopia\App;
|
||||
use Utopia\View;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
App::init(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$header = new View(__DIR__.'/../../views/home/comps/header.phtml');
|
||||
$footer = new View(__DIR__.'/../../views/home/comps/footer.phtml');
|
||||
|
@ -32,7 +32,7 @@ App::init(function ($layout) {
|
|||
|
||||
App::shutdown(function ($response, $layout) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$response->html($layout->render());
|
||||
}, ['response', 'layout'], 'home');
|
||||
|
@ -76,7 +76,7 @@ App::get('/auth/signin')
|
|||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/signin.phtml');
|
||||
|
||||
|
@ -95,7 +95,7 @@ App::get('/auth/signup')
|
|||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
$page = new View(__DIR__.'/../../views/home/auth/signup.phtml');
|
||||
|
||||
$page
|
||||
|
@ -113,7 +113,7 @@ App::get('/auth/recovery')
|
|||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/recovery.phtml');
|
||||
|
||||
|
@ -132,7 +132,7 @@ App::get('/auth/confirm')
|
|||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/confirm.phtml');
|
||||
|
||||
|
@ -147,7 +147,7 @@ App::get('/auth/join')
|
|||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/join.phtml');
|
||||
|
||||
|
@ -162,7 +162,7 @@ App::get('/auth/recovery/reset')
|
|||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/recovery/reset.phtml');
|
||||
|
||||
|
@ -177,7 +177,7 @@ App::get('/auth/oauth2/success')
|
|||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/oauth2.phtml');
|
||||
|
||||
|
@ -195,7 +195,7 @@ App::get('/auth/magic-url')
|
|||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/magicURL.phtml');
|
||||
|
||||
|
@ -213,7 +213,7 @@ App::get('/auth/oauth2/failure')
|
|||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/oauth2.phtml');
|
||||
|
||||
|
@ -232,7 +232,7 @@ App::get('/error/:code')
|
|||
->param('code', null, new \Utopia\Validator\Numeric(), 'Valid status code number', false)
|
||||
->inject('layout')
|
||||
->action(function ($code, $layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/error.phtml');
|
||||
|
||||
|
|
22
app/http.php
22
app/http.php
|
@ -73,17 +73,8 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
}
|
||||
} while ($attempts < $max);
|
||||
|
||||
App::setResource('db', function () use (&$db) {
|
||||
return $db;
|
||||
});
|
||||
|
||||
App::setResource('cache', function () use (&$redis) {
|
||||
return $redis;
|
||||
});
|
||||
|
||||
App::setResource('app', function() use (&$app) {
|
||||
return $app;
|
||||
});
|
||||
App::setResource('db', fn() => $db);
|
||||
App::setResource('cache', fn() => $redis);
|
||||
|
||||
$dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */
|
||||
|
||||
|
@ -170,13 +161,8 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
|||
$db = $register->get('dbPool')->get();
|
||||
$redis = $register->get('redisPool')->get();
|
||||
|
||||
App::setResource('db', function () use (&$db) {
|
||||
return $db;
|
||||
});
|
||||
|
||||
App::setResource('cache', function () use (&$redis) {
|
||||
return $redis;
|
||||
});
|
||||
App::setResource('db', fn() => $db);
|
||||
App::setResource('cache', fn() => $redis);
|
||||
|
||||
try {
|
||||
Authorization::cleanRoles();
|
||||
|
|
67
app/init.php
67
app/init.php
|
@ -21,17 +21,14 @@ use Appwrite\Extend\PDO;
|
|||
use Ahc\Jwt\JWT;
|
||||
use Ahc\Jwt\JWTException;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Database\Database as DatabaseOld;
|
||||
use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
|
||||
use Appwrite\Database\Adapter\Redis as RedisAdapter;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\IP;
|
||||
use Appwrite\Network\Validator\URL;
|
||||
use Appwrite\OpenSSL\OpenSSL;
|
||||
use Appwrite\Stats\Stats;
|
||||
use Appwrite\Utopia\View;
|
||||
use Utopia\App;
|
||||
use Utopia\View;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Registry\Registry;
|
||||
|
@ -80,8 +77,8 @@ const APP_STORAGE_CACHE = '/storage/cache';
|
|||
const APP_STORAGE_CERTIFICATES = '/storage/certificates';
|
||||
const APP_STORAGE_CONFIG = '/storage/config';
|
||||
const APP_STORAGE_READ_BUFFER = 20 * (1024 * 1024); //20MB other names `APP_STORAGE_MEMORY_LIMIT`, `APP_STORAGE_MEMORY_BUFFER`, `APP_STORAGE_READ_LIMIT`, `APP_STORAGE_BUFFER_LIMIT`
|
||||
const APP_SOCIAL_TWITTER = 'https://twitter.com/appwrite_io';
|
||||
const APP_SOCIAL_TWITTER_HANDLE = 'appwrite_io';
|
||||
const APP_SOCIAL_TWITTER = 'https://twitter.com/appwrite';
|
||||
const APP_SOCIAL_TWITTER_HANDLE = 'appwrite';
|
||||
const APP_SOCIAL_FACEBOOK = 'https://www.facebook.com/appwrite.io';
|
||||
const APP_SOCIAL_LINKEDIN = 'https://www.linkedin.com/company/appwrite';
|
||||
const APP_SOCIAL_INSTAGRAM = 'https://www.instagram.com/appwrite.io';
|
||||
|
@ -90,7 +87,7 @@ const APP_SOCIAL_DISCORD = 'https://appwrite.io/discord';
|
|||
const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244';
|
||||
const APP_SOCIAL_DEV = 'https://dev.to/appwrite';
|
||||
const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
|
||||
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite';
|
||||
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1';
|
||||
// Database Worker Types
|
||||
const DATABASE_TYPE_CREATE_ATTRIBUTE = 'createAttribute';
|
||||
const DATABASE_TYPE_CREATE_INDEX = 'createIndex';
|
||||
|
@ -163,43 +160,6 @@ if(!empty($user) || !empty($pass)) {
|
|||
Resque::setBackend(App::getEnv('_APP_REDIS_HOST', '').':'.App::getEnv('_APP_REDIS_PORT', ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Old DB Filters
|
||||
*/
|
||||
DatabaseOld::addFilter('json',
|
||||
function($value) {
|
||||
if(!is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
return json_encode($value);
|
||||
},
|
||||
function($value) {
|
||||
return json_decode($value, true);
|
||||
}
|
||||
);
|
||||
|
||||
DatabaseOld::addFilter('encrypt',
|
||||
function($value) {
|
||||
$key = App::getEnv('_APP_OPENSSL_KEY_V1');
|
||||
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
|
||||
$tag = null;
|
||||
|
||||
return json_encode([
|
||||
'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
|
||||
'method' => OpenSSL::CIPHER_AES_128_GCM,
|
||||
'iv' => bin2hex($iv),
|
||||
'tag' => bin2hex($tag),
|
||||
'version' => '1',
|
||||
]);
|
||||
},
|
||||
function($value) {
|
||||
$value = json_decode($value, true);
|
||||
$key = App::getEnv('_APP_OPENSSL_KEY_V'.$value['version']);
|
||||
|
||||
return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag']));
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* New DB Filters
|
||||
*/
|
||||
|
@ -333,12 +293,15 @@ Database::addFilter('encrypt',
|
|||
return json_encode([
|
||||
'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
|
||||
'method' => OpenSSL::CIPHER_AES_128_GCM,
|
||||
'iv' => bin2hex($iv),
|
||||
'tag' => bin2hex($tag),
|
||||
'iv' => \bin2hex($iv),
|
||||
'tag' => \bin2hex($tag ?? ''),
|
||||
'version' => '1',
|
||||
]);
|
||||
},
|
||||
function($value) {
|
||||
if(is_null($value)) {
|
||||
return null;
|
||||
}
|
||||
$value = json_decode($value, true);
|
||||
$key = App::getEnv('_APP_OPENSSL_KEY_V'.$value['version']);
|
||||
|
||||
|
@ -355,7 +318,7 @@ Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function() {
|
|||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function($attribute) {
|
||||
$elements = $attribute['formatOptions']['elements'];
|
||||
return new WhiteList($elements);
|
||||
return new WhiteList($elements, true);
|
||||
}, Database::VAR_STRING);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function() {
|
||||
|
@ -587,9 +550,7 @@ Locale::setLanguageFromJSON('zh-tw', __DIR__.'/config/locale/translations/zh-tw.
|
|||
|
||||
// Runtime Execution
|
||||
|
||||
App::setResource('register', function() use ($register) {
|
||||
return $register;
|
||||
});
|
||||
App::setResource('register', fn() => $register);
|
||||
|
||||
App::setResource('layout', function($locale) {
|
||||
$layout = new View(__DIR__.'/views/layouts/default.phtml');
|
||||
|
@ -841,6 +802,12 @@ App::setResource('dbForConsole', function($db, $cache) {
|
|||
|
||||
App::setResource('mode', function($request) {
|
||||
/** @var Utopia\Swoole\Request $request */
|
||||
|
||||
/**
|
||||
* Defines the mode for the request:
|
||||
* - 'default' => Requests for Client and Server Side
|
||||
* - 'admin' => Request from the Console on non-console projects
|
||||
*/
|
||||
return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT));
|
||||
}, ['request']);
|
||||
|
||||
|
|
|
@ -87,9 +87,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
|||
'timestamp' => time(),
|
||||
'value' => '{}'
|
||||
]);
|
||||
$statsDocument = Authorization::skip(function () use ($database, $document) {
|
||||
return $database->createDocument('realtime', $document);
|
||||
});
|
||||
$statsDocument = Authorization::skip(fn() => $database->createDocument('realtime', $document));
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
|
@ -106,12 +104,8 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
|||
Timer::tick(5000, function () use ($register, $stats, $containerId, &$statsDocument) {
|
||||
/** @var Document $statsDocument */
|
||||
foreach ($stats as $projectId => $value) {
|
||||
if (empty($value['connections']) && empty($value['messages'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$connections = $stats->get($projectId, 'connections');
|
||||
$messages = $stats->get($projectId, 'messages');
|
||||
$connections = $stats->get($projectId, 'connections') ?? 0;
|
||||
$messages = $stats->get($projectId, 'messages' ?? 0);
|
||||
|
||||
$usage = new Event('v1-usage', 'UsageV1');
|
||||
$usage
|
||||
|
@ -132,10 +126,8 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
|||
}
|
||||
$payload = [];
|
||||
foreach ($stats as $projectId => $value) {
|
||||
if (!empty($value['connectionsTotal'])) {
|
||||
$payload[$projectId] = $stats->get($projectId, 'connectionsTotal');
|
||||
}
|
||||
}
|
||||
if (empty($payload) || empty($statsDocument)) {
|
||||
return;
|
||||
}
|
||||
|
@ -147,9 +139,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
|||
->setAttribute('timestamp', time())
|
||||
->setAttribute('value', json_encode($payload));
|
||||
|
||||
Authorization::skip(function () use ($database, $statsDocument) {
|
||||
$database->updateDocument('realtime', $statsDocument->getId(), $statsDocument);
|
||||
});
|
||||
Authorization::skip(fn() => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument));
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
|
@ -177,11 +167,9 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
|||
|
||||
$payload = [];
|
||||
|
||||
$list = Authorization::skip(function () use ($database) {
|
||||
return $database->find('realtime', [
|
||||
$list = Authorization::skip(fn() => $database->find('realtime', [
|
||||
new Query('timestamp', Query::TYPE_GREATER, [(time() - 15)])
|
||||
]);
|
||||
});
|
||||
]));
|
||||
|
||||
/**
|
||||
* Aggregate stats across containers.
|
||||
|
@ -335,21 +323,10 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
|||
|
||||
Console::info("Connection open (user: {$connection})");
|
||||
|
||||
App::setResource('db', function () use (&$db) {
|
||||
return $db;
|
||||
});
|
||||
|
||||
App::setResource('cache', function () use (&$redis) {
|
||||
return $redis;
|
||||
});
|
||||
|
||||
App::setResource('request', function () use ($request) {
|
||||
return $request;
|
||||
});
|
||||
|
||||
App::setResource('response', function () use ($response) {
|
||||
return $response;
|
||||
});
|
||||
App::setResource('db', fn() => $db);
|
||||
App::setResource('cache', fn() => $redis);
|
||||
App::setResource('request', fn() => $request);
|
||||
App::setResource('response', fn() => $response);
|
||||
|
||||
try {
|
||||
/** @var \Utopia\Database\Document $user */
|
||||
|
|
|
@ -5,10 +5,10 @@ global $cli;
|
|||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Docker\Compose;
|
||||
use Appwrite\Docker\Env;
|
||||
use Appwrite\Utopia\View;
|
||||
use Utopia\Analytics\GoogleAnalytics;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\View;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
$cli
|
||||
|
|
|
@ -180,7 +180,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|||
->setShareText('Appwrite is a backend as a service for building web or mobile apps')
|
||||
->setShareURL('http://appwrite.io')
|
||||
->setShareTags('JS,javascript,reactjs,angular,ios,android,serverless')
|
||||
->setShareVia('appwrite_io')
|
||||
->setShareVia('appwrite')
|
||||
->setWarning($warning)
|
||||
->setReadme($readme)
|
||||
->setGettingStarted($gettingStarted)
|
||||
|
|
|
@ -221,7 +221,7 @@ $logs = $this->getParam('logs', null);
|
|||
|
||||
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
|
||||
<input type="hidden" name="collectionId" data-ls-bind="{{project-collection.$id}}" />
|
||||
<input type="hidden" name="attributeId" data-ls-bind="{{attribute.key}}" />
|
||||
<input type="hidden" name="key" data-ls-bind="{{attribute.key}}" />
|
||||
|
||||
<button class="danger small">Delete</button>
|
||||
</form>
|
||||
|
@ -275,7 +275,7 @@ $logs = $this->getParam('logs', null);
|
|||
<tr>
|
||||
<th width="30"></th>
|
||||
<th width="80"></th>
|
||||
<th width="130">Index ID</th>
|
||||
<th width="130">Index Key</th>
|
||||
<th width="100">Type</th>
|
||||
<th width="180">Attributes</th>
|
||||
<th></th>
|
||||
|
@ -295,7 +295,7 @@ $logs = $this->getParam('logs', null);
|
|||
<span data-ls-if="{{index.status}} == 'deleting'" class="text-size-small text-danger">deleting </span>
|
||||
</td>
|
||||
|
||||
<td data-title="Index ID: ">
|
||||
<td data-title="Index Key: ">
|
||||
<span class="text-size-small" data-ls-bind="{{index.key}}"></span><span class="text-size-small" data-ls-if="{{index.size}}" data-ls-bind="({{index.size}})"></span>
|
||||
</td>
|
||||
|
||||
|
@ -334,7 +334,7 @@ $logs = $this->getParam('logs', null);
|
|||
|
||||
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
|
||||
<input type="hidden" name="collectionId" data-ls-bind="{{project-collection.$id}}" />
|
||||
<input type="hidden" name="indexId" data-ls-bind="{{index.key}}" />
|
||||
<input type="hidden" name="key" data-ls-bind="{{index.key}}" />
|
||||
|
||||
<button class="danger small">Delete</button>
|
||||
</form>
|
||||
|
@ -347,7 +347,7 @@ $logs = $this->getParam('logs', null);
|
|||
<button class="new-index">Add Index</button>
|
||||
</li>
|
||||
<li data-state="/console/database/collection/activity?id={{router.params.id}}&project={{router.params.project}}">
|
||||
<h2>Activity <span class="badge" data-ls-bind="{{logs.sum}}"></span></h2>
|
||||
<h2>Activity</h2>
|
||||
|
||||
<?php echo $logs->render(); ?>
|
||||
</li>
|
||||
|
@ -456,6 +456,9 @@ $logs = $this->getParam('logs', null);
|
|||
<label for="collection-name">Name</label>
|
||||
<input name="name" id="collection-name" type="text" autocomplete="off" data-ls-bind="{{project-collection.name}}" data-forms-text-direction required placeholder="Collection Name" maxlength="128" />
|
||||
|
||||
<div class="margin-bottom">
|
||||
<input name="enabled" type="hidden" data-forms-switch data-cast-to="boolean" data-ls-bind="{{project-collection.enabled}}" /> Enabled <span class="tooltip" data-tooltip="Mark whether collection is enabled"><i class="icon-info-circled"></i></span>
|
||||
</div>
|
||||
|
||||
<label class="margin-bottom-small">Permissions</label>
|
||||
|
||||
|
@ -467,8 +470,8 @@ $logs = $this->getParam('logs', null);
|
|||
<div class="col span-1"><input name="permission" value="document" type="radio" class="margin-top-no" data-ls-bind="{{project-collection.permission}}" /></div>
|
||||
<div class="col span-11">
|
||||
<b>Document Level</b>
|
||||
<p class="text-fade margin-top-tiny">With Document Level permissions, you have granular access control over every document, and users will only be able to access documents for which they have explicit permissions.</p>
|
||||
<p class="text-fade margin-top-tiny">Document permissions are required in this permission model.</p>
|
||||
<p class="text-fade margin-top-tiny">With Document Level permissions, you have granular access control over every document. Users will only be able to access documents for which they have explicit permissions.</p>
|
||||
<p class="text-fade margin-top-tiny">In this permission level, document permissions take precedence and collection permissions are ignored.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -476,8 +479,8 @@ $logs = $this->getParam('logs', null);
|
|||
<div class="col span-1"><input name="permission" value="collection" type="radio" class="margin-top-tiny" data-ls-bind="{{project-collection.permission}}" /></div>
|
||||
<div class="col span-11">
|
||||
<b>Collection Level</b>
|
||||
<p class="text-fade margin-top-tiny">With Collection Level permissions, you assign permissions once for every document in the collection - users with read access to the collection can see all documents.</p>
|
||||
<p class="text-fade margin-top-tiny">Collection permissions are required in this permission model, and document permissions are optional.</p>
|
||||
<p class="text-fade margin-top-tiny">With Collection Level permissions, you assign permissions only once in the collection.</p>
|
||||
<p class="text-fade margin-top-tiny">In this permission level, permissions assigned to collection takes the precedence and documents permissions are ignored.</p>
|
||||
<div data-ls-if="{{project-collection.permission}} === 'collection'">
|
||||
<label for="collection-read">Read Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</span></label>
|
||||
<input type="hidden" id="collection-read" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{project-collection.$read}}" placeholder="User ID, Team ID or Role" />
|
||||
|
@ -560,8 +563,8 @@ $logs = $this->getParam('logs', null);
|
|||
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
|
||||
<input type="hidden" name="collectionId" data-ls-bind="{{router.params.id}}" />
|
||||
|
||||
<label for="string-attributeId">Attribute ID</label>
|
||||
<input type="text" class="full-width" name="attributeId" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<label for="string-key">Attribute ID</label>
|
||||
<input id="string-key" type="text" class="full-width" name="key" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<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</div>
|
||||
|
||||
<label for="string-length">Size</label>
|
||||
|
@ -589,14 +592,6 @@ $logs = $this->getParam('logs', null);
|
|||
(() => {
|
||||
const form = document.getElementById("add-string-attribute");
|
||||
const fields = {
|
||||
required: {
|
||||
array: ["disable", "uncheck"],
|
||||
required: ["enable"]
|
||||
},
|
||||
array: {
|
||||
required: ["disable", "uncheck"],
|
||||
array: ["enable"]
|
||||
},
|
||||
xdefault: {
|
||||
oneOf: {
|
||||
if: ["array", "required"],
|
||||
|
@ -645,8 +640,8 @@ $logs = $this->getParam('logs', null);
|
|||
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
|
||||
<input type="hidden" name="collectionId" data-ls-bind="{{router.params.id}}" />
|
||||
|
||||
<label for="integer-attributeId">Attribute ID</label>
|
||||
<input id="integer-attributeId" type="text" class="full-width" name="attributeId" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<label for="integer-key">Attribute ID</label>
|
||||
<input id="integer-key" type="text" class="full-width" name="key" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<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</div>
|
||||
|
||||
<div class="margin-bottom">
|
||||
|
@ -680,14 +675,6 @@ $logs = $this->getParam('logs', null);
|
|||
(() => {
|
||||
const form = document.getElementById("add-integer-attribute");
|
||||
const fields = {
|
||||
required: {
|
||||
array: ["disable", "uncheck"],
|
||||
required: ["enable"]
|
||||
},
|
||||
array: {
|
||||
required: ["disable", "uncheck"],
|
||||
array: ["enable"]
|
||||
},
|
||||
xdefault: {
|
||||
oneOf: {
|
||||
if: ["array", "required"],
|
||||
|
@ -735,8 +722,8 @@ $logs = $this->getParam('logs', null);
|
|||
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
|
||||
<input type="hidden" name="collectionId" data-ls-bind="{{router.params.id}}" />
|
||||
|
||||
<label for="float-attributeId">Attribute ID</label>
|
||||
<input id="float-attributeId" type="text" class="full-width" name="attributeId" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<label for="float-key">Attribute ID</label>
|
||||
<input id="float-key" type="text" class="full-width" name="key" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<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</div>
|
||||
|
||||
<div class="margin-bottom">
|
||||
|
@ -769,14 +756,6 @@ $logs = $this->getParam('logs', null);
|
|||
(() => {
|
||||
const form = document.getElementById("add-float-attribute");
|
||||
const fields = {
|
||||
required: {
|
||||
array: ["disable", "uncheck"],
|
||||
required: ["enable"]
|
||||
},
|
||||
array: {
|
||||
required: ["disable", "uncheck"],
|
||||
array: ["enable"]
|
||||
},
|
||||
xdefault: {
|
||||
oneOf: {
|
||||
if: ["array", "required"],
|
||||
|
@ -824,8 +803,8 @@ $logs = $this->getParam('logs', null);
|
|||
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
|
||||
<input type="hidden" name="collectionId" data-ls-bind="{{router.params.id}}" />
|
||||
|
||||
<label for="email-attributeId">Attribute ID</label>
|
||||
<input id="email-attributeId" type="text" class="full-width" name="attributeId" required autocomplete="off" maxlength="128" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<label for="email-key">Attribute ID</label>
|
||||
<input id="email-key" type="text" class="full-width" name="key" required autocomplete="off" maxlength="128" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<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</div>
|
||||
|
||||
<div class="margin-bottom">
|
||||
|
@ -847,14 +826,6 @@ $logs = $this->getParam('logs', null);
|
|||
(() => {
|
||||
const form = document.getElementById("add-email-attribute");
|
||||
const fields = {
|
||||
required: {
|
||||
array: ["disable", "uncheck"],
|
||||
required: ["enable"]
|
||||
},
|
||||
array: {
|
||||
required: ["disable", "uncheck"],
|
||||
array: ["enable"]
|
||||
},
|
||||
xdefault: {
|
||||
oneOf: {
|
||||
if: ["array", "required"],
|
||||
|
@ -902,8 +873,8 @@ $logs = $this->getParam('logs', null);
|
|||
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
|
||||
<input type="hidden" name="collectionId" data-ls-bind="{{router.params.id}}" />
|
||||
|
||||
<label for="boolean-attributeId">Attribute ID</label>
|
||||
<input id="boolean-attributeId" type="text" class="full-width" name="attributeId" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<label for="boolean-key">Attribute ID</label>
|
||||
<input id="boolean-key" type="text" class="full-width" name="key" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<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</div>
|
||||
|
||||
<div class="margin-bottom">
|
||||
|
@ -927,14 +898,6 @@ $logs = $this->getParam('logs', null);
|
|||
(() => {
|
||||
const form = document.getElementById("add-boolean-attribute");
|
||||
const fields = {
|
||||
required: {
|
||||
array: ["disable", "uncheck"],
|
||||
required: ["enable"]
|
||||
},
|
||||
array: {
|
||||
required: ["disable", "uncheck"],
|
||||
array: ["enable"]
|
||||
},
|
||||
xdefault: {
|
||||
oneOf: {
|
||||
if: ["array", "required"],
|
||||
|
@ -983,8 +946,8 @@ $logs = $this->getParam('logs', null);
|
|||
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
|
||||
<input type="hidden" name="collectionId" data-ls-bind="{{router.params.id}}" />
|
||||
|
||||
<label for="ip-attributeId">Attribute ID</label>
|
||||
<input id="ip-attributeId" type="text" class="full-width" name="attributeId" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<label for="ip-key">Attribute ID</label>
|
||||
<input id="ip-key" type="text" class="full-width" name="key" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<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</div>
|
||||
|
||||
<div class="margin-bottom">
|
||||
|
@ -1006,14 +969,6 @@ $logs = $this->getParam('logs', null);
|
|||
(() => {
|
||||
const form = document.getElementById("add-ip-attribute");
|
||||
const fields = {
|
||||
required: {
|
||||
array: ["disable", "uncheck"],
|
||||
required: ["enable"]
|
||||
},
|
||||
array: {
|
||||
required: ["disable", "uncheck"],
|
||||
array: ["enable"]
|
||||
},
|
||||
xdefault: {
|
||||
oneOf: {
|
||||
if: ["array", "required"],
|
||||
|
@ -1061,8 +1016,8 @@ $logs = $this->getParam('logs', null);
|
|||
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
|
||||
<input type="hidden" name="collectionId" data-ls-bind="{{router.params.id}}" />
|
||||
|
||||
<label for="url-attributeId">Attribute ID</label>
|
||||
<input id="url-attributeId" type="text" class="full-width" name="attributeId" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<label for="url-key">Attribute ID</label>
|
||||
<input id="url-key" type="text" class="full-width" name="key" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<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</div>
|
||||
|
||||
<div class="margin-bottom">
|
||||
|
@ -1074,7 +1029,7 @@ $logs = $this->getParam('logs', null);
|
|||
</div>
|
||||
|
||||
<label for="string-default">Default Value</label>
|
||||
<input id="string-default" name="xdefault" type="url" pattern="https://.*" title="Valid URL address" class="margin-bottom-large" autocomplete="off">
|
||||
<input id="string-default" name="xdefault" type="url" title="Valid URL address" class="margin-bottom-large" autocomplete="off">
|
||||
|
||||
<footer>
|
||||
<button type="submit">Create</button> <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
|
||||
|
@ -1084,14 +1039,6 @@ $logs = $this->getParam('logs', null);
|
|||
(() => {
|
||||
const form = document.getElementById("add-url-attribute");
|
||||
const fields = {
|
||||
required: {
|
||||
array: ["disable", "uncheck"],
|
||||
required: ["enable"]
|
||||
},
|
||||
array: {
|
||||
required: ["disable", "uncheck"],
|
||||
array: ["enable"]
|
||||
},
|
||||
xdefault: {
|
||||
oneOf: {
|
||||
if: ["array", "required"],
|
||||
|
@ -1139,8 +1086,8 @@ $logs = $this->getParam('logs', null);
|
|||
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
|
||||
<input type="hidden" name="collectionId" data-ls-bind="{{router.params.id}}" />
|
||||
|
||||
<label for="enum-attributeId">Attribute ID</label>
|
||||
<input id="enum-attributeId" type="text" class="full-width" name="attributeId" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<label for="enum-key">Attribute ID</label>
|
||||
<input id="enum-key" type="text" class="full-width" name="key" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<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</div>
|
||||
|
||||
<label>Elements</label>
|
||||
|
@ -1179,14 +1126,6 @@ $logs = $this->getParam('logs', null);
|
|||
(() => {
|
||||
const form = document.getElementById("add-enum-attribute");
|
||||
const fields = {
|
||||
required: {
|
||||
array: ["disable", "uncheck"],
|
||||
required: ["enable"]
|
||||
},
|
||||
array: {
|
||||
required: ["disable", "uncheck"],
|
||||
array: ["enable"]
|
||||
},
|
||||
xdefault: {
|
||||
oneOf: {
|
||||
if: ["array", "required"],
|
||||
|
@ -1244,8 +1183,8 @@ $logs = $this->getParam('logs', null);
|
|||
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
|
||||
<input type="hidden" name="collectionId" data-ls-bind="{{router.params.id}}" />
|
||||
|
||||
<label for="index-indexId">Index ID</label>
|
||||
<input id="index-indexId" type="text" class="full-width" name="indexId" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<label for="index-key">Index Key</label>
|
||||
<input id="index-key" type="text" class="full-width" name="key" required autocomplete="off" maxlength="36" pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<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</div>
|
||||
|
||||
<label for="index-type">Type</label>
|
||||
|
|
|
@ -280,15 +280,6 @@ $logs = $this->getParam('logs', null);
|
|||
<button type="button" class="margin-end margin-bottom-small reverse" @click="doc = addAttribute(doc, attr.key)"> Add Attribute </button>
|
||||
</div>
|
||||
</template>
|
||||
<script type="text/javascript">
|
||||
function addAttribute(doc, key) {
|
||||
if (!Array.isArray(doc[key])) {
|
||||
doc[key] = [];
|
||||
}
|
||||
doc[key].push(null);
|
||||
return doc;
|
||||
}
|
||||
</script>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Utopia\View;
|
||||
use Appwrite\Utopia\View;
|
||||
|
||||
$collection = $this->getParam('collection', null);
|
||||
$collections = $this->getParam('collections', []);
|
||||
|
|
|
@ -444,7 +444,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
<div class="zone xl"
|
||||
data-service="teams.getMemberships"
|
||||
data-scope="console"
|
||||
data-event="load,teams.createMembership,teams.deleteMembership"
|
||||
data-event="load,teams.createMembership,teams.deleteMembership,teams.createMembership.resent"
|
||||
data-name="members"
|
||||
data-param-team-id="{{console-project.teamId}}"
|
||||
data-success="trigger"
|
||||
|
|
|
@ -323,7 +323,9 @@
|
|||
<button class="danger">Logout</button>
|
||||
</form>
|
||||
<div class="pull-start margin-end avatar-container">
|
||||
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{session.clientCode|lowercase}}?width=120&height=120&project={{env.PROJECT}},title={{session.clientName}},alt={{session.clientName}}" class="avatar" loading="lazy" width="60" height="60" />
|
||||
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{session.clientCode|lowercase}} !== 'cli'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{session.clientCode|lowercase}}?width=120&height=120&project={{env.PROJECT}},title={{session.clientName}},alt={{session.clientName}}" class="avatar" loading="lazy" width="60" height="60" />
|
||||
|
||||
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{session.clientCode|lowercase}} === 'cli'" data-ls-attrs="src=/images/clients/terminal.png?buster=<?php echo APP_CACHE_BUSTER; ?>,title={{session.clientName}},alt={{session.clientName}}" class="avatar" loading="lazy" width="60" height="60" />
|
||||
|
||||
<div data-ls-if="{{session.provider}} !== 'email'" class="corner">
|
||||
<img data-ls-attrs="src=/images/users/{{session.provider}}.png?buster=<?php echo APP_CACHE_BUSTER; ?>,title={{session.provider}},alt={{session.provider}}" class="avatar xs" loading="lazy" width="30" height="30" />
|
||||
|
@ -390,7 +392,10 @@
|
|||
<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}})" 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={{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>
|
||||
|
|
|
@ -53,12 +53,17 @@ if(!empty($platforms)) {
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5" />
|
||||
<meta name="theme-color" content="#f02e65">
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="<?php echo APP_NAME; ?>">
|
||||
<meta property="og:title" content="<?php echo $this->escape($this->getParam('title', '')); ?>" />
|
||||
<meta property="og:description" content="<?php echo $this->escape($this->getParam('description', '')); ?>" />
|
||||
<?php if (!empty($canonical)): ?>
|
||||
<meta property="og:url" content="<?php echo $this->escape($canonical); ?>" />
|
||||
<?php endif; ?>
|
||||
<meta property="og:image" content="<?php echo $this->escape($endpoint); ?><?php echo $this->escape($image); ?>?v=<?php echo APP_CACHE_BUSTER; ?>" />
|
||||
<meta name="twitter:site" content="@<?php echo APP_SOCIAL_TWITTER_HANDLE; ?>">
|
||||
<meta name="twitter:title" content="<?php echo $this->escape($this->getParam('title', '')); ?>">
|
||||
<meta name="twitter:image:src" content="<?php echo $this->escape($endpoint); ?><?php echo $this->escape($image); ?>?v=<?php echo APP_CACHE_BUSTER; ?>">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<script>
|
||||
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Appwrite\Resque\Worker;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Document;
|
||||
|
@ -67,9 +68,11 @@ class DatabaseV1 extends Worker
|
|||
*/
|
||||
protected function createAttribute(Document $collection, Document $attribute, string $projectId): void
|
||||
{
|
||||
$dbForConsole = $this->getConsoleDB();
|
||||
$dbForInternal = $this->getInternalDB($projectId);
|
||||
$dbForExternal = $this->getExternalDB($projectId);
|
||||
|
||||
$event = 'database.attributes.update';
|
||||
$collectionId = $collection->getId();
|
||||
$key = $attribute->getAttribute('key', '');
|
||||
$type = $attribute->getAttribute('type', '');
|
||||
|
@ -81,6 +84,7 @@ class DatabaseV1 extends Worker
|
|||
$format = $attribute->getAttribute('format', '');
|
||||
$formatOptions = $attribute->getAttribute('formatOptions', []);
|
||||
$filters = $attribute->getAttribute('filters', []);
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
try {
|
||||
if(!$dbForExternal->createAttribute($collectionId, $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) {
|
||||
|
@ -90,6 +94,20 @@ class DatabaseV1 extends Worker
|
|||
} catch (\Throwable $th) {
|
||||
Console::error($th->getMessage());
|
||||
$dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed'));
|
||||
} finally {
|
||||
$target = Realtime::fromPayload($event, $attribute, $project);
|
||||
|
||||
Realtime::send(
|
||||
projectId: 'console',
|
||||
payload: $attribute->getArrayCopy(),
|
||||
event: $event,
|
||||
channels: $target['channels'],
|
||||
roles: $target['roles'],
|
||||
options: [
|
||||
'projectId' => $projectId,
|
||||
'collectionId' => $collection->getId()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$dbForInternal->deleteCachedDocument('collections', $collectionId);
|
||||
|
@ -102,11 +120,15 @@ class DatabaseV1 extends Worker
|
|||
*/
|
||||
protected function deleteAttribute(Document $collection, Document $attribute, string $projectId): void
|
||||
{
|
||||
$dbForConsole = $this->getConsoleDB();
|
||||
$dbForInternal = $this->getInternalDB($projectId);
|
||||
$dbForExternal = $this->getExternalDB($projectId);
|
||||
|
||||
$event = 'database.attributes.delete';
|
||||
$collectionId = $collection->getId();
|
||||
$key = $attribute->getAttribute('key', '');
|
||||
$status = $attribute->getAttribute('status', '');
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
// possible states at this point:
|
||||
// - available: should not land in queue; controller flips these to 'deleting'
|
||||
|
@ -122,6 +144,20 @@ class DatabaseV1 extends Worker
|
|||
} catch (\Throwable $th) {
|
||||
Console::error($th->getMessage());
|
||||
$dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'stuck'));
|
||||
} finally {
|
||||
$target = Realtime::fromPayload($event, $attribute, $project);
|
||||
|
||||
Realtime::send(
|
||||
projectId: 'console',
|
||||
payload: $attribute->getArrayCopy(),
|
||||
event: $event,
|
||||
channels: $target['channels'],
|
||||
roles: $target['roles'],
|
||||
options: [
|
||||
'projectId' => $projectId,
|
||||
'collectionId' => $collection->getId()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// The underlying database removes/rebuilds indexes when attribute is removed
|
||||
|
@ -185,15 +221,18 @@ class DatabaseV1 extends Worker
|
|||
*/
|
||||
protected function createIndex(Document $collection, Document $index, string $projectId): void
|
||||
{
|
||||
$dbForConsole = $this->getConsoleDB();
|
||||
$dbForInternal = $this->getInternalDB($projectId);
|
||||
$dbForExternal = $this->getExternalDB($projectId);
|
||||
|
||||
$event = 'database.indexes.update';
|
||||
$collectionId = $collection->getId();
|
||||
$key = $index->getAttribute('key', '');
|
||||
$type = $index->getAttribute('type', '');
|
||||
$attributes = $index->getAttribute('attributes', []);
|
||||
$lengths = $index->getAttribute('lengths', []);
|
||||
$orders = $index->getAttribute('orders', []);
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
try {
|
||||
if(!$dbForExternal->createIndex($collectionId, $key, $type, $attributes, $lengths, $orders)) {
|
||||
|
@ -203,6 +242,20 @@ class DatabaseV1 extends Worker
|
|||
} catch (\Throwable $th) {
|
||||
Console::error($th->getMessage());
|
||||
$dbForInternal->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'failed'));
|
||||
} finally {
|
||||
$target = Realtime::fromPayload($event, $index, $project);
|
||||
|
||||
Realtime::send(
|
||||
projectId: 'console',
|
||||
payload: $index->getArrayCopy(),
|
||||
event: $event,
|
||||
channels: $target['channels'],
|
||||
roles: $target['roles'],
|
||||
options: [
|
||||
'projectId' => $projectId,
|
||||
'collectionId' => $collection->getId()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$dbForInternal->deleteCachedDocument('collections', $collectionId);
|
||||
|
@ -215,12 +268,15 @@ class DatabaseV1 extends Worker
|
|||
*/
|
||||
protected function deleteIndex(Document $collection, Document $index, string $projectId): void
|
||||
{
|
||||
$dbForConsole = $this->getConsoleDB();
|
||||
$dbForInternal = $this->getInternalDB($projectId);
|
||||
$dbForExternal = $this->getExternalDB($projectId);
|
||||
|
||||
$collectionId = $collection->getId();
|
||||
$key = $index->getAttribute('key');
|
||||
$status = $index->getAttribute('status', '');
|
||||
$event = 'database.indexes.delete';
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
try {
|
||||
if($status !== 'failed' && !$dbForExternal->deleteIndex($collectionId, $key)) {
|
||||
|
@ -230,6 +286,20 @@ class DatabaseV1 extends Worker
|
|||
} catch (\Throwable $th) {
|
||||
Console::error($th->getMessage());
|
||||
$dbForInternal->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'stuck'));
|
||||
} finally {
|
||||
$target = Realtime::fromPayload($event, $index, $project);
|
||||
|
||||
Realtime::send(
|
||||
projectId: 'console',
|
||||
payload: $index->getArrayCopy(),
|
||||
event: $event,
|
||||
channels: $target['channels'],
|
||||
roles: $target['roles'],
|
||||
options: [
|
||||
'projectId' => $projectId,
|
||||
'collectionId' => $collection->getId()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$dbForInternal->deleteCachedDocument('collections', $collectionId);
|
||||
|
|
|
@ -106,6 +106,8 @@ class DeletesV1 extends Worker
|
|||
$dbForInternal = $this->getInternalDB($projectId);
|
||||
$dbForExternal = $this->getExternalDB($projectId);
|
||||
|
||||
$dbForExternal->deleteCollection($collectionId);
|
||||
|
||||
$this->deleteByGroup('attributes', [
|
||||
new Query('collectionId', Query::TYPE_EQUAL, [$collectionId])
|
||||
], $dbForInternal);
|
||||
|
@ -113,8 +115,6 @@ class DeletesV1 extends Worker
|
|||
$this->deleteByGroup('indexes', [
|
||||
new Query('collectionId', Query::TYPE_EQUAL, [$collectionId])
|
||||
], $dbForInternal);
|
||||
|
||||
$dbForExternal->deleteCollection($collectionId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -481,16 +481,14 @@ class FunctionsV1 extends Worker
|
|||
|
||||
Console::info('Function executed in ' . ($executionEnd - $executionStart) . ' seconds, status: ' . $functionStatus);
|
||||
|
||||
$execution = Authorization::skip(function() use ($database, $execution, $tag, $functionStatus, $exitCode, $stdout, $stderr, $executionTime) {
|
||||
return $database->updateDocument('executions', $execution->getId(), new Document(array_merge($execution->getArrayCopy(), [
|
||||
$execution = Authorization::skip(fn() => $database->updateDocument('executions', $execution->getId(), new Document(array_merge($execution->getArrayCopy(), [
|
||||
'tagId' => $tag->getId(),
|
||||
'status' => $functionStatus,
|
||||
'exitCode' => $exitCode,
|
||||
'stdout' => \utf8_encode(\mb_substr($stdout, -8000)), // log last 8000 chars output
|
||||
'stderr' => \utf8_encode(\mb_substr($stderr, -8000)), // log last 8000 chars output
|
||||
'time' => (float)$executionTime,
|
||||
])));
|
||||
});
|
||||
]))));
|
||||
|
||||
$executionModel = new Execution();
|
||||
$executionUpdate = new Event('v1-webhooks', 'WebhooksV1');
|
||||
|
|
|
@ -57,11 +57,11 @@
|
|||
"utopia-php/image": "0.5.*",
|
||||
|
||||
"resque/php-resque": "1.3.6",
|
||||
"matomo/device-detector": "4.3.1",
|
||||
"matomo/device-detector": "5.0.1",
|
||||
"dragonmantank/cron-expression": "3.1.0",
|
||||
"influxdb/influxdb-php": "1.15.2",
|
||||
"phpmailer/phpmailer": "6.5.1",
|
||||
"chillerlan/php-qrcode": "4.3.1",
|
||||
"phpmailer/phpmailer": "6.5.3",
|
||||
"chillerlan/php-qrcode": "4.3.2",
|
||||
"adhocore/jwt": "1.1.2",
|
||||
"slickdeals/statsd": "3.1.0"
|
||||
},
|
||||
|
@ -69,22 +69,14 @@
|
|||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/storage"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/framework"
|
||||
}
|
||||
],
|
||||
"require-dev": {
|
||||
"appwrite/sdk-generator": "0.16.2",
|
||||
"phpunit/phpunit": "9.5.6",
|
||||
"swoole/ide-helper": "4.6.7",
|
||||
"textalk/websocket": "1.5.2",
|
||||
"vimeo/psalm": "4.7.2"
|
||||
"phpunit/phpunit": "9.5.10",
|
||||
"swoole/ide-helper": "4.8.3",
|
||||
"textalk/websocket": "1.5.5",
|
||||
"vimeo/psalm": "4.13.1"
|
||||
},
|
||||
"provide": {
|
||||
"ext-phpiredis": "*"
|
||||
|
|
329
composer.lock
generated
329
composer.lock
generated
|
@ -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": "d49875c416f7ee3ef27bbc3d5de26fe9",
|
||||
"content-hash": "355acc3f36af9b5a6aa619d7226ee740",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
@ -170,16 +170,16 @@
|
|||
},
|
||||
{
|
||||
"name": "chillerlan/php-qrcode",
|
||||
"version": "4.3.1",
|
||||
"version": "4.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/chillerlan/php-qrcode.git",
|
||||
"reference": "be3beb936c21fe53a4e7e8f7f3582e9f02443666"
|
||||
"reference": "b625396e0752d79747a55205ae7e191eeb459dcd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/be3beb936c21fe53a4e7e8f7f3582e9f02443666",
|
||||
"reference": "be3beb936c21fe53a4e7e8f7f3582e9f02443666",
|
||||
"url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/b625396e0752d79747a55205ae7e191eeb459dcd",
|
||||
"reference": "b625396e0752d79747a55205ae7e191eeb459dcd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -188,7 +188,7 @@
|
|||
"php": "^7.4 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phan/phan": "^3.2.2",
|
||||
"phan/phan": "^5.3",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"setasign/fpdf": "^1.8.2"
|
||||
},
|
||||
|
@ -232,7 +232,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/chillerlan/php-qrcode/issues",
|
||||
"source": "https://github.com/chillerlan/php-qrcode/tree/4.3.1"
|
||||
"source": "https://github.com/chillerlan/php-qrcode/tree/4.3.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -244,7 +244,7 @@
|
|||
"type": "ko_fi"
|
||||
}
|
||||
],
|
||||
"time": "2021-01-05T21:21:28+00:00"
|
||||
"time": "2021-11-18T08:46:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "chillerlan/php-settings-container",
|
||||
|
@ -933,21 +933,21 @@
|
|||
},
|
||||
{
|
||||
"name": "matomo/device-detector",
|
||||
"version": "4.3.1",
|
||||
"version": "5.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/matomo-org/device-detector.git",
|
||||
"reference": "88e5419ee1448ccb9537e287dd09836ff9d2de3b"
|
||||
"reference": "ebd8a07e4b69088c0e34f29ec72dc162c34c9264"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/88e5419ee1448ccb9537e287dd09836ff9d2de3b",
|
||||
"reference": "88e5419ee1448ccb9537e287dd09836ff9d2de3b",
|
||||
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/ebd8a07e4b69088c0e34f29ec72dc162c34c9264",
|
||||
"reference": "ebd8a07e4b69088c0e34f29ec72dc162c34c9264",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"mustangostang/spyc": "*",
|
||||
"php": ">=7.2"
|
||||
"php": "^7.2|^8.0"
|
||||
},
|
||||
"replace": {
|
||||
"piwik/device-detector": "self.version"
|
||||
|
@ -998,7 +998,7 @@
|
|||
"source": "https://github.com/matomo-org/matomo",
|
||||
"wiki": "https://dev.matomo.org/"
|
||||
},
|
||||
"time": "2021-09-20T12:34:12+00:00"
|
||||
"time": "2021-12-07T11:40:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mongodb/mongodb",
|
||||
|
@ -1120,16 +1120,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpmailer/phpmailer",
|
||||
"version": "v6.5.1",
|
||||
"version": "v6.5.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPMailer/PHPMailer.git",
|
||||
"reference": "dd803df5ad7492e1b40637f7ebd258fee5ca7355"
|
||||
"reference": "baeb7cde6b60b1286912690ab0693c7789a31e71"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/dd803df5ad7492e1b40637f7ebd258fee5ca7355",
|
||||
"reference": "dd803df5ad7492e1b40637f7ebd258fee5ca7355",
|
||||
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/baeb7cde6b60b1286912690ab0693c7789a31e71",
|
||||
"reference": "baeb7cde6b60b1286912690ab0693c7789a31e71",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1186,7 +1186,7 @@
|
|||
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
|
||||
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.5.1"
|
||||
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.5.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1194,7 +1194,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-08-18T09:14:16+00:00"
|
||||
"time": "2021-11-25T16:34:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-client",
|
||||
|
@ -2138,18 +2138,24 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "0.12.0",
|
||||
"version": "0.12.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database",
|
||||
"reference": "102ee1d21fd55fc92dc7a07b60672a98ae49be26"
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "af512b7a00cc7c6e30fa03efbc5fd7e77a93e2df"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/af512b7a00cc7c6e30fa03efbc5fd7e77a93e2df",
|
||||
"reference": "af512b7a00cc7c6e30fa03efbc5fd7e77a93e2df",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mongodb": "*",
|
||||
"ext-pdo": "*",
|
||||
"ext-redis": "*",
|
||||
"mongodb/mongodb": "1.8.0",
|
||||
"php": ">=7.1",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/cache": "0.4.*",
|
||||
"utopia-php/framework": "0.*.*"
|
||||
},
|
||||
|
@ -2165,11 +2171,7 @@
|
|||
"Utopia\\Database\\": "src/Database"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Utopia\\Tests\\": "tests/Database"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
|
@ -2191,7 +2193,11 @@
|
|||
"upf",
|
||||
"utopia"
|
||||
],
|
||||
"time": "2021-11-24T14:53:22+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/0.12.1"
|
||||
},
|
||||
"time": "2021-12-13T14:57:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/domains",
|
||||
|
@ -2249,18 +2255,24 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/framework",
|
||||
"version": "0.19.1",
|
||||
"version": "0.19.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/framework",
|
||||
"reference": "cc7629b5f7a8f45912ec2e069b7f14e361e41c34"
|
||||
"url": "https://github.com/utopia-php/framework.git",
|
||||
"reference": "4c6c841d738cec458b73fec5aedd40fd43bd41a7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/framework/zipball/4c6c841d738cec458b73fec5aedd40fd43bd41a7",
|
||||
"reference": "4c6c841d738cec458b73fec5aedd40fd43bd41a7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.3.0"
|
||||
"php": ">=8.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.4",
|
||||
"vimeo/psalm": "4.0.1"
|
||||
"phpunit/phpunit": "^9.5.10",
|
||||
"vimeo/psalm": "4.13.1"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
|
@ -2268,6 +2280,7 @@
|
|||
"Utopia\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
|
@ -2283,7 +2296,11 @@
|
|||
"php",
|
||||
"upf"
|
||||
],
|
||||
"time": "2021-11-25T16:11:40+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/framework/issues",
|
||||
"source": "https://github.com/utopia-php/framework/tree/0.19.3"
|
||||
},
|
||||
"time": "2021-12-17T13:04:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/image",
|
||||
|
@ -2554,7 +2571,7 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/storage",
|
||||
"reference": "c47611b3a4a36c674c16e9720e86187452eb1cc2"
|
||||
"reference": "6d82e34bac0b46b55ea781e0499bd1dfcff94160"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
|
@ -2592,31 +2609,31 @@
|
|||
"upf",
|
||||
"utopia"
|
||||
],
|
||||
"time": "2021-12-09T07:34:55+00:00"
|
||||
"time": "2021-12-14T07:04:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/swoole",
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/swoole.git",
|
||||
"reference": "b564dacb13472845f06df158ae5382e8098dfd7a"
|
||||
"reference": "2b714eddf77cd5eda1889219c9656d7c0a63ce73"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/b564dacb13472845f06df158ae5382e8098dfd7a",
|
||||
"reference": "b564dacb13472845f06df158ae5382e8098dfd7a",
|
||||
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/2b714eddf77cd5eda1889219c9656d7c0a63ce73",
|
||||
"reference": "2b714eddf77cd5eda1889219c9656d7c0a63ce73",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-swoole": "*",
|
||||
"php": ">=7.4",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/framework": "0.*.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.3",
|
||||
"swoole/ide-helper": "4.5.5",
|
||||
"vimeo/psalm": "4.0.1"
|
||||
"swoole/ide-helper": "4.8.3",
|
||||
"vimeo/psalm": "4.15.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
|
@ -2646,9 +2663,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/swoole/issues",
|
||||
"source": "https://github.com/utopia-php/swoole/tree/0.3.1"
|
||||
"source": "https://github.com/utopia-php/swoole/tree/0.3.2"
|
||||
},
|
||||
"time": "2021-07-27T09:28:10+00:00"
|
||||
"time": "2021-12-13T15:37:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/system",
|
||||
|
@ -4470,16 +4487,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "9.5.6",
|
||||
"version": "9.5.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "fb9b8333f14e3dce976a60ef6a7e05c7c7ed8bfb"
|
||||
"reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fb9b8333f14e3dce976a60ef6a7e05c7c7ed8bfb",
|
||||
"reference": "fb9b8333f14e3dce976a60ef6a7e05c7c7ed8bfb",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a",
|
||||
"reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -4491,11 +4508,11 @@
|
|||
"ext-xml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"myclabs/deep-copy": "^1.10.1",
|
||||
"phar-io/manifest": "^2.0.1",
|
||||
"phar-io/manifest": "^2.0.3",
|
||||
"phar-io/version": "^3.0.2",
|
||||
"php": ">=7.3",
|
||||
"phpspec/prophecy": "^1.12.1",
|
||||
"phpunit/php-code-coverage": "^9.2.3",
|
||||
"phpunit/php-code-coverage": "^9.2.7",
|
||||
"phpunit/php-file-iterator": "^3.0.5",
|
||||
"phpunit/php-invoker": "^3.1.1",
|
||||
"phpunit/php-text-template": "^2.0.3",
|
||||
|
@ -4557,7 +4574,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.6"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -4569,7 +4586,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-06-23T05:14:38+00:00"
|
||||
"time": "2021-09-25T07:38:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
|
@ -5590,24 +5607,18 @@
|
|||
},
|
||||
{
|
||||
"name": "swoole/ide-helper",
|
||||
"version": "4.6.7",
|
||||
"version": "4.8.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/swoole/ide-helper.git",
|
||||
"reference": "0d1409b8274117addfe64d3ea412812a69807411"
|
||||
"reference": "3ac4971814273889933b871e03b2a6b340e58f79"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/swoole/ide-helper/zipball/0d1409b8274117addfe64d3ea412812a69807411",
|
||||
"reference": "0d1409b8274117addfe64d3ea412812a69807411",
|
||||
"url": "https://api.github.com/repos/swoole/ide-helper/zipball/3ac4971814273889933b871e03b2a6b340e58f79",
|
||||
"reference": "3ac4971814273889933b871e03b2a6b340e58f79",
|
||||
"shasum": ""
|
||||
},
|
||||
"require-dev": {
|
||||
"guzzlehttp/guzzle": "~6.5.0",
|
||||
"laminas/laminas-code": "~3.4.0",
|
||||
"squizlabs/php_codesniffer": "~3.5.0",
|
||||
"symfony/filesystem": "~4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
|
@ -5622,7 +5633,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.6.7"
|
||||
"source": "https://github.com/swoole/ide-helper/tree/4.8.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -5632,56 +5643,48 @@
|
|||
{
|
||||
"url": "https://github.com/swoole",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://opencollective.com/swoole-src",
|
||||
"type": "open_collective"
|
||||
}
|
||||
],
|
||||
"time": "2021-05-14T16:05:16+00:00"
|
||||
"time": "2021-12-01T08:11:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v5.4.0",
|
||||
"version": "v6.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "ec3661faca1d110d6c307e124b44f99ac54179e3"
|
||||
"reference": "fafd9802d386bf1c267e0249ddb7ceb14dcfdad4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/ec3661faca1d110d6c307e124b44f99ac54179e3",
|
||||
"reference": "ec3661faca1d110d6c307e124b44f99ac54179e3",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/fafd9802d386bf1c267e0249ddb7ceb14dcfdad4",
|
||||
"reference": "fafd9802d386bf1c267e0249ddb7ceb14dcfdad4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"symfony/deprecation-contracts": "^2.1|^3",
|
||||
"php": ">=8.0.2",
|
||||
"symfony/polyfill-mbstring": "~1.0",
|
||||
"symfony/polyfill-php73": "^1.8",
|
||||
"symfony/polyfill-php80": "^1.16",
|
||||
"symfony/service-contracts": "^1.1|^2|^3",
|
||||
"symfony/string": "^5.1|^6.0"
|
||||
"symfony/string": "^5.4|^6.0"
|
||||
},
|
||||
"conflict": {
|
||||
"psr/log": ">=3",
|
||||
"symfony/dependency-injection": "<4.4",
|
||||
"symfony/dotenv": "<5.1",
|
||||
"symfony/event-dispatcher": "<4.4",
|
||||
"symfony/lock": "<4.4",
|
||||
"symfony/process": "<4.4"
|
||||
"symfony/dependency-injection": "<5.4",
|
||||
"symfony/dotenv": "<5.4",
|
||||
"symfony/event-dispatcher": "<5.4",
|
||||
"symfony/lock": "<5.4",
|
||||
"symfony/process": "<5.4"
|
||||
},
|
||||
"provide": {
|
||||
"psr/log-implementation": "1.0|2.0"
|
||||
"psr/log-implementation": "1.0|2.0|3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/log": "^1|^2",
|
||||
"symfony/config": "^4.4|^5.0|^6.0",
|
||||
"symfony/dependency-injection": "^4.4|^5.0|^6.0",
|
||||
"symfony/event-dispatcher": "^4.4|^5.0|^6.0",
|
||||
"symfony/lock": "^4.4|^5.0|^6.0",
|
||||
"symfony/process": "^4.4|^5.0|^6.0",
|
||||
"symfony/var-dumper": "^4.4|^5.0|^6.0"
|
||||
"psr/log": "^1|^2|^3",
|
||||
"symfony/config": "^5.4|^6.0",
|
||||
"symfony/dependency-injection": "^5.4|^6.0",
|
||||
"symfony/event-dispatcher": "^5.4|^6.0",
|
||||
"symfony/lock": "^5.4|^6.0",
|
||||
"symfony/process": "^5.4|^6.0",
|
||||
"symfony/var-dumper": "^5.4|^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "For using the console logger",
|
||||
|
@ -5721,7 +5724,7 @@
|
|||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v5.4.0"
|
||||
"source": "https://github.com/symfony/console/tree/v6.0.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -5737,7 +5740,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-29T15:30:56+00:00"
|
||||
"time": "2021-12-09T12:47:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-grapheme",
|
||||
|
@ -5984,85 +5987,6 @@
|
|||
],
|
||||
"time": "2021-05-27T12:26:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php73",
|
||||
"version": "v1.23.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php73.git",
|
||||
"reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010",
|
||||
"reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php73\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php73/tree/v1.23.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-02-19T12:13:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
"version": "v3.0.0",
|
||||
|
@ -6147,16 +6071,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v6.0.0",
|
||||
"version": "v6.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "ba727797426af0f587f4800566300bdc0cda0777"
|
||||
"reference": "0cfed595758ec6e0a25591bdc8ca733c1896af32"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/ba727797426af0f587f4800566300bdc0cda0777",
|
||||
"reference": "ba727797426af0f587f4800566300bdc0cda0777",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/0cfed595758ec6e0a25591bdc8ca733c1896af32",
|
||||
"reference": "0cfed595758ec6e0a25591bdc8ca733c1896af32",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -6212,7 +6136,7 @@
|
|||
"utf8"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v6.0.0"
|
||||
"source": "https://github.com/symfony/string/tree/v6.0.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -6228,25 +6152,25 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-10-29T07:35:21+00:00"
|
||||
"time": "2021-12-08T15:13:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "textalk/websocket",
|
||||
"version": "1.5.2",
|
||||
"version": "1.5.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Textalk/websocket-php.git",
|
||||
"reference": "b93249453806a2dd46495de46d76fcbcb0d8dee8"
|
||||
"reference": "846542f82658132cd36acb7a7e8ce0f03960c295"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Textalk/websocket-php/zipball/b93249453806a2dd46495de46d76fcbcb0d8dee8",
|
||||
"reference": "b93249453806a2dd46495de46d76fcbcb0d8dee8",
|
||||
"url": "https://api.github.com/repos/Textalk/websocket-php/zipball/846542f82658132cd36acb7a7e8ce0f03960c295",
|
||||
"reference": "846542f82658132cd36acb7a7e8ce0f03960c295",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 | ^8.0",
|
||||
"psr/log": "^1.0"
|
||||
"psr/log": "^1 | ^2 | ^3"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.0",
|
||||
|
@ -6275,9 +6199,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.2"
|
||||
"source": "https://github.com/Textalk/websocket-php/tree/1.5.5"
|
||||
},
|
||||
"time": "2021-02-12T15:39:23+00:00"
|
||||
"time": "2021-08-07T10:21:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
|
@ -6410,16 +6334,16 @@
|
|||
},
|
||||
{
|
||||
"name": "vimeo/psalm",
|
||||
"version": "4.7.2",
|
||||
"version": "4.13.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vimeo/psalm.git",
|
||||
"reference": "83a0325c0a95c0ab531d6b90c877068b464377b5"
|
||||
"reference": "5cf660f63b548ccd4a56f62d916ee4d6028e01a3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/vimeo/psalm/zipball/83a0325c0a95c0ab531d6b90c877068b464377b5",
|
||||
"reference": "83a0325c0a95c0ab531d6b90c877068b464377b5",
|
||||
"url": "https://api.github.com/repos/vimeo/psalm/zipball/5cf660f63b548ccd4a56f62d916ee4d6028e01a3",
|
||||
"reference": "5cf660f63b548ccd4a56f62d916ee4d6028e01a3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -6429,6 +6353,7 @@
|
|||
"composer/semver": "^1.4 || ^2.0 || ^3.0",
|
||||
"composer/xdebug-handler": "^1.1 || ^2.0",
|
||||
"dnoegel/php-xdg-base-dir": "^0.1.1",
|
||||
"ext-ctype": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-json": "*",
|
||||
"ext-libxml": "*",
|
||||
|
@ -6438,11 +6363,11 @@
|
|||
"felixfbecker/advanced-json-rpc": "^3.0.3",
|
||||
"felixfbecker/language-server-protocol": "^1.5",
|
||||
"netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0",
|
||||
"nikic/php-parser": "^4.10.1",
|
||||
"nikic/php-parser": "^4.13",
|
||||
"openlss/lib-array2xml": "^1.0",
|
||||
"php": "^7.1|^8",
|
||||
"sebastian/diff": "^3.0 || ^4.0",
|
||||
"symfony/console": "^3.4.17 || ^4.1.6 || ^5.0",
|
||||
"symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0",
|
||||
"webmozart/path-util": "^2.3"
|
||||
},
|
||||
"provide": {
|
||||
|
@ -6457,15 +6382,15 @@
|
|||
"phpmyadmin/sql-parser": "5.1.0||dev-master",
|
||||
"phpspec/prophecy": ">=1.9.0",
|
||||
"phpunit/phpunit": "^9.0",
|
||||
"psalm/plugin-phpunit": "^0.13",
|
||||
"slevomat/coding-standard": "^6.3.11",
|
||||
"psalm/plugin-phpunit": "^0.16",
|
||||
"slevomat/coding-standard": "^7.0",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"symfony/process": "^4.3",
|
||||
"weirdan/phpunit-appveyor-reporter": "^1.0.0",
|
||||
"symfony/process": "^4.3 || ^5.0 || ^6.0",
|
||||
"weirdan/prophecy-shim": "^1.0 || ^2.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-igbinary": "^2.0.5"
|
||||
"ext-curl": "In order to send data to shepherd",
|
||||
"ext-igbinary": "^2.0.5 is required, used to serialize caching data"
|
||||
},
|
||||
"bin": [
|
||||
"psalm",
|
||||
|
@ -6509,9 +6434,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/vimeo/psalm/issues",
|
||||
"source": "https://github.com/vimeo/psalm/tree/4.7.2"
|
||||
"source": "https://github.com/vimeo/psalm/tree/4.13.1"
|
||||
},
|
||||
"time": "2021-05-01T20:56:25+00:00"
|
||||
"time": "2021-11-23T23:52:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "webmozart/path-util",
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
const sdk = new Appwrite();
|
||||
|
||||
sdk
|
||||
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
|
||||
.setProject('5df5acd0d48c2') // Your project ID
|
||||
;
|
||||
|
||||
let promise = sdk.functions.listRuntimes();
|
||||
|
||||
promise.then(function (response) {
|
||||
console.log(response); // Success
|
||||
}, function (error) {
|
||||
console.log(error); // Failure
|
||||
});
|
|
@ -5,7 +5,7 @@ sdk
|
|||
.setProject('5df5acd0d48c2') // Your project ID
|
||||
;
|
||||
|
||||
let promise = sdk.projects.delete('[PROJECT_ID]', '[PASSWORD]');
|
||||
let promise = sdk.projects.delete('[PROJECT_ID]', 'password');
|
||||
|
||||
promise.then(function (response) {
|
||||
console.log(response); // Success
|
||||
|
|
1
docs/references/functions/list-runtimes.md
Normal file
1
docs/references/functions/list-runtimes.md
Normal file
|
@ -0,0 +1 @@
|
|||
Get a list of all runtimes that are currently active in your project.
|
|
@ -1,5 +1,5 @@
|
|||
Use this endpoint to invite a new member to join your team. If initiated from Client SDK, an email with a link to join the team will be sent to the new member's email address if the member doesn't exist in the project it will be created automatically. If initiated from server side SDKs, new member will automatically be added to the team.
|
||||
Invite a new member to join your team. If initiated from the client SDK, an email with a link to join the team will be sent to the member's email address and an account will be created for them should they not be signed up already. If initiated from server-side SDKs, the new member will automatically be added to the team.
|
||||
|
||||
Use the 'URL' parameter to redirect the user from the invitation email back to your app. When the user is redirected, use the [Update Team Membership Status](/docs/client/teams#teamsUpdateMembershipStatus) endpoint to allow the user to accept the invitation to the team. While calling from side SDKs the redirect url can be empty string.
|
||||
Use the 'url' parameter to redirect the user from the invitation email back to your app. When the user is redirected, use the [Update Team Membership Status](/docs/client/teams#teamsUpdateMembershipStatus) endpoint to allow the user to accept the invitation to the team.
|
||||
|
||||
Please note that in order to avoid a [Redirect Attacks](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) the only valid redirect URL's are the once from domains you have set when added your platforms in the console interface.
|
||||
Please note that to avoid a [Redirect Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) the only valid redirect URL's are the once from domains you have set when adding your platforms in the console interface.
|
|
@ -1 +1 @@
|
|||
Create a new team. The user who creates the team will automatically be assigned as the owner of the team. The team owner can invite new members, who will be able add new owners and update or delete the team from your project.
|
||||
Create a new team. The user who creates the team will automatically be assigned as the owner of the team. Only the users with the owner role can invite new members, add new owners and delete or update the team.
|
|
@ -1 +1 @@
|
|||
Delete a team by its unique ID. Only team owners have write access for this resource.
|
||||
Delete a team using its ID. Only team members with the owner role can delete the team.
|
|
@ -1 +1 @@
|
|||
Get a team members by the team unique ID. All team members have read access for this list of resources.
|
||||
Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint.
|
|
@ -1 +1 @@
|
|||
Get a team by its unique ID. All team members have read access for this resource.
|
||||
Get a team by its ID. All team members have read access for this resource.
|
|
@ -1 +1,3 @@
|
|||
Get a list of all the current user teams. You can use the query params to filter your results. On admin mode, this endpoint will return a list of all of the project's teams. [Learn more about different API modes](/docs/admin).
|
||||
Get a list of all the teams in which the current user is a member. You can use the parameters to filter your results.
|
||||
|
||||
In admin mode, this endpoint returns a list of all the teams in the current project. [Learn more about different API modes](/docs/admin).
|
1
docs/references/teams/update-team-membership-roles.md
Normal file
1
docs/references/teams/update-team-membership-roles.md
Normal file
|
@ -0,0 +1 @@
|
|||
Modify the roles of a team member. Only team members with the owner role have access to this endpoint. Learn more about [roles and permissions](/docs/permissions).
|
|
@ -1 +1 @@
|
|||
Update a team by its unique ID. Only team owners have write access for this resource.
|
||||
Update a team using its ID. Only members with the owner role can update the team.
|
|
@ -46,6 +46,23 @@ For **Mac OS** add your app name and Bundle ID, You can find your Bundle Identif
|
|||
### Web
|
||||
Appwrite 0.7, and the Appwrite Flutter SDK 0.3.0 have added support for Flutter Web. To build web apps that integrate with Appwrite successfully, all you have to do is add a web platform on your Appwrite project's dashboard and list the domain your website will use to allow communication to the Appwrite API.
|
||||
|
||||
For web in order to capture the OAuth2 callback URL and send it to the application using JavaScript `postMessage()`, you need to create an html file inside `./web` folder of your Flutter project. For example `auth.html` with the following content.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<title>Authentication complete</title>
|
||||
<p>Authentication is complete. If this does not happen automatically, please
|
||||
close the window.
|
||||
<script>
|
||||
window.opener.postMessage({
|
||||
flutter-web-auth: window.location.href
|
||||
}, window.location.origin);
|
||||
window.close();
|
||||
</script>
|
||||
```
|
||||
|
||||
Redirection URL passed to the authentication service must be the same as the URL on which the application is running (schema, host, port if necessary) and the path must point to created HTML file, /auth.html in this case. The callbackUrlScheme parameter of the authenticate() method does not take into account, so it is possible to use a schema for native platforms in the code.
|
||||
|
||||
#### Flutter Web Cross-Domain Communication & Cookies
|
||||
While running Flutter Web, make sure your Appwrite server and your Flutter client are using the same top-level domain and the same protocol (HTTP or HTTPS) to communicate. When trying to communicate between different domains or protocols, you may receive HTTP status error 401 because some modern browsers block cross-site or insecure cookies for enhanced privacy. In production, Appwrite allows you set multiple [custom-domains](https://appwrite.io/docs/custom-domains) for each project.
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 495 KiB |
|
@ -199,7 +199,7 @@
|
|||
*
|
||||
* Use this endpoint to allow a new user to register a new account in your
|
||||
* project. After the user registration completes successfully, you can use
|
||||
* the [/account/verification](/docs/client/account#accountCreateVerification)
|
||||
* the [/account/verfication](/docs/client/account#accountCreateVerification)
|
||||
* route to start verifying the user email address. To allow the new user to
|
||||
* login to their new account, you need to create a new [account
|
||||
* session](/docs/client/account#accountCreateSession).
|
||||
|
@ -264,12 +264,14 @@
|
|||
* Update Account Email
|
||||
*
|
||||
* Update currently logged in user account email address. After changing user
|
||||
* address, user confirmation status is being reset and a new confirmation
|
||||
* mail is sent. For security measures, user password is required to complete
|
||||
* this request.
|
||||
* address, the user confirmation status will get reset. A new confirmation
|
||||
* email is not sent automatically however you can use the send confirmation
|
||||
* email endpoint again to send the confirmation email. For security measures,
|
||||
* user password is required to complete this request.
|
||||
* This endpoint can also be used to convert an anonymous account to a normal
|
||||
* one, by passing an email address and a new password.
|
||||
*
|
||||
*
|
||||
* @param {string} email
|
||||
* @param {string} password
|
||||
* @throws {AppwriteException}
|
||||
|
@ -1165,8 +1167,8 @@
|
|||
* @param {string} collectionId
|
||||
* @param {string} name
|
||||
* @param {string} permission
|
||||
* @param {string} read
|
||||
* @param {string} write
|
||||
* @param {string[]} read
|
||||
* @param {string[]} write
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
@ -1237,12 +1239,13 @@
|
|||
* @param {string} collectionId
|
||||
* @param {string} name
|
||||
* @param {string} permission
|
||||
* @param {string} read
|
||||
* @param {string} write
|
||||
* @param {string[]} read
|
||||
* @param {string[]} write
|
||||
* @param {boolean} enabled
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
updateCollection: (collectionId, name, permission, read, write) => __awaiter(this, void 0, void 0, function* () {
|
||||
updateCollection: (collectionId, name, permission, read, write, enabled) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
|
@ -1266,6 +1269,9 @@
|
|||
if (typeof write !== 'undefined') {
|
||||
payload['write'] = write;
|
||||
}
|
||||
if (typeof enabled !== 'undefined') {
|
||||
payload['enabled'] = enabled;
|
||||
}
|
||||
const uri = new URL(this.config.endpoint + path);
|
||||
return yield this.call('put', uri, {
|
||||
'content-type': 'application/json',
|
||||
|
@ -1318,27 +1324,27 @@
|
|||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
* @param {string} key
|
||||
* @param {boolean} required
|
||||
* @param {boolean} xdefault
|
||||
* @param {boolean} array
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createBooleanAttribute: (collectionId, attributeId, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
|
||||
createBooleanAttribute: (collectionId, key, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
if (typeof attributeId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "attributeId"');
|
||||
if (typeof key === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "key"');
|
||||
}
|
||||
if (typeof required === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "required"');
|
||||
}
|
||||
let path = '/database/collections/{collectionId}/attributes/boolean'.replace('{collectionId}', collectionId);
|
||||
let payload = {};
|
||||
if (typeof attributeId !== 'undefined') {
|
||||
payload['attributeId'] = attributeId;
|
||||
if (typeof key !== 'undefined') {
|
||||
payload['key'] = key;
|
||||
}
|
||||
if (typeof required !== 'undefined') {
|
||||
payload['required'] = required;
|
||||
|
@ -1361,27 +1367,27 @@
|
|||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
* @param {string} key
|
||||
* @param {boolean} required
|
||||
* @param {string} xdefault
|
||||
* @param {boolean} array
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createEmailAttribute: (collectionId, attributeId, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
|
||||
createEmailAttribute: (collectionId, key, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
if (typeof attributeId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "attributeId"');
|
||||
if (typeof key === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "key"');
|
||||
}
|
||||
if (typeof required === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "required"');
|
||||
}
|
||||
let path = '/database/collections/{collectionId}/attributes/email'.replace('{collectionId}', collectionId);
|
||||
let payload = {};
|
||||
if (typeof attributeId !== 'undefined') {
|
||||
payload['attributeId'] = attributeId;
|
||||
if (typeof key !== 'undefined') {
|
||||
payload['key'] = key;
|
||||
}
|
||||
if (typeof required !== 'undefined') {
|
||||
payload['required'] = required;
|
||||
|
@ -1402,7 +1408,7 @@
|
|||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
* @param {string} key
|
||||
* @param {string[]} elements
|
||||
* @param {boolean} required
|
||||
* @param {string} xdefault
|
||||
|
@ -1410,12 +1416,12 @@
|
|||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createEnumAttribute: (collectionId, attributeId, elements, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
|
||||
createEnumAttribute: (collectionId, key, elements, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
if (typeof attributeId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "attributeId"');
|
||||
if (typeof key === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "key"');
|
||||
}
|
||||
if (typeof elements === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "elements"');
|
||||
|
@ -1425,8 +1431,8 @@
|
|||
}
|
||||
let path = '/database/collections/{collectionId}/attributes/enum'.replace('{collectionId}', collectionId);
|
||||
let payload = {};
|
||||
if (typeof attributeId !== 'undefined') {
|
||||
payload['attributeId'] = attributeId;
|
||||
if (typeof key !== 'undefined') {
|
||||
payload['key'] = key;
|
||||
}
|
||||
if (typeof elements !== 'undefined') {
|
||||
payload['elements'] = elements;
|
||||
|
@ -1453,7 +1459,7 @@
|
|||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
* @param {string} key
|
||||
* @param {boolean} required
|
||||
* @param {string} min
|
||||
* @param {string} max
|
||||
|
@ -1462,20 +1468,20 @@
|
|||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createFloatAttribute: (collectionId, attributeId, required, min, max, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
|
||||
createFloatAttribute: (collectionId, key, required, min, max, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
if (typeof attributeId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "attributeId"');
|
||||
if (typeof key === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "key"');
|
||||
}
|
||||
if (typeof required === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "required"');
|
||||
}
|
||||
let path = '/database/collections/{collectionId}/attributes/float'.replace('{collectionId}', collectionId);
|
||||
let payload = {};
|
||||
if (typeof attributeId !== 'undefined') {
|
||||
payload['attributeId'] = attributeId;
|
||||
if (typeof key !== 'undefined') {
|
||||
payload['key'] = key;
|
||||
}
|
||||
if (typeof required !== 'undefined') {
|
||||
payload['required'] = required;
|
||||
|
@ -1505,7 +1511,7 @@
|
|||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
* @param {string} key
|
||||
* @param {boolean} required
|
||||
* @param {number} min
|
||||
* @param {number} max
|
||||
|
@ -1514,20 +1520,20 @@
|
|||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createIntegerAttribute: (collectionId, attributeId, required, min, max, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
|
||||
createIntegerAttribute: (collectionId, key, required, min, max, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
if (typeof attributeId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "attributeId"');
|
||||
if (typeof key === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "key"');
|
||||
}
|
||||
if (typeof required === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "required"');
|
||||
}
|
||||
let path = '/database/collections/{collectionId}/attributes/integer'.replace('{collectionId}', collectionId);
|
||||
let payload = {};
|
||||
if (typeof attributeId !== 'undefined') {
|
||||
payload['attributeId'] = attributeId;
|
||||
if (typeof key !== 'undefined') {
|
||||
payload['key'] = key;
|
||||
}
|
||||
if (typeof required !== 'undefined') {
|
||||
payload['required'] = required;
|
||||
|
@ -1556,27 +1562,27 @@
|
|||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
* @param {string} key
|
||||
* @param {boolean} required
|
||||
* @param {string} xdefault
|
||||
* @param {boolean} array
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createIpAttribute: (collectionId, attributeId, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
|
||||
createIpAttribute: (collectionId, key, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
if (typeof attributeId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "attributeId"');
|
||||
if (typeof key === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "key"');
|
||||
}
|
||||
if (typeof required === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "required"');
|
||||
}
|
||||
let path = '/database/collections/{collectionId}/attributes/ip'.replace('{collectionId}', collectionId);
|
||||
let payload = {};
|
||||
if (typeof attributeId !== 'undefined') {
|
||||
payload['attributeId'] = attributeId;
|
||||
if (typeof key !== 'undefined') {
|
||||
payload['key'] = key;
|
||||
}
|
||||
if (typeof required !== 'undefined') {
|
||||
payload['required'] = required;
|
||||
|
@ -1599,7 +1605,7 @@
|
|||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
* @param {string} key
|
||||
* @param {number} size
|
||||
* @param {boolean} required
|
||||
* @param {string} xdefault
|
||||
|
@ -1607,12 +1613,12 @@
|
|||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createStringAttribute: (collectionId, attributeId, size, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
|
||||
createStringAttribute: (collectionId, key, size, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
if (typeof attributeId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "attributeId"');
|
||||
if (typeof key === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "key"');
|
||||
}
|
||||
if (typeof size === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "size"');
|
||||
|
@ -1622,8 +1628,8 @@
|
|||
}
|
||||
let path = '/database/collections/{collectionId}/attributes/string'.replace('{collectionId}', collectionId);
|
||||
let payload = {};
|
||||
if (typeof attributeId !== 'undefined') {
|
||||
payload['attributeId'] = attributeId;
|
||||
if (typeof key !== 'undefined') {
|
||||
payload['key'] = key;
|
||||
}
|
||||
if (typeof size !== 'undefined') {
|
||||
payload['size'] = size;
|
||||
|
@ -1649,27 +1655,27 @@
|
|||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
* @param {string} key
|
||||
* @param {boolean} required
|
||||
* @param {string} xdefault
|
||||
* @param {boolean} array
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createUrlAttribute: (collectionId, attributeId, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
|
||||
createUrlAttribute: (collectionId, key, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
if (typeof attributeId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "attributeId"');
|
||||
if (typeof key === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "key"');
|
||||
}
|
||||
if (typeof required === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "required"');
|
||||
}
|
||||
let path = '/database/collections/{collectionId}/attributes/url'.replace('{collectionId}', collectionId);
|
||||
let payload = {};
|
||||
if (typeof attributeId !== 'undefined') {
|
||||
payload['attributeId'] = attributeId;
|
||||
if (typeof key !== 'undefined') {
|
||||
payload['key'] = key;
|
||||
}
|
||||
if (typeof required !== 'undefined') {
|
||||
payload['required'] = required;
|
||||
|
@ -1690,18 +1696,18 @@
|
|||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
* @param {string} key
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAttribute: (collectionId, attributeId) => __awaiter(this, void 0, void 0, function* () {
|
||||
getAttribute: (collectionId, key) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
if (typeof attributeId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "attributeId"');
|
||||
if (typeof key === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "key"');
|
||||
}
|
||||
let path = '/database/collections/{collectionId}/attributes/{attributeId}'.replace('{collectionId}', collectionId).replace('{attributeId}', attributeId);
|
||||
let path = '/database/collections/{collectionId}/attributes/{key}'.replace('{collectionId}', collectionId).replace('{key}', key);
|
||||
let payload = {};
|
||||
const uri = new URL(this.config.endpoint + path);
|
||||
return yield this.call('get', uri, {
|
||||
|
@ -1713,18 +1719,18 @@
|
|||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
* @param {string} key
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
deleteAttribute: (collectionId, attributeId) => __awaiter(this, void 0, void 0, function* () {
|
||||
deleteAttribute: (collectionId, key) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
if (typeof attributeId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "attributeId"');
|
||||
if (typeof key === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "key"');
|
||||
}
|
||||
let path = '/database/collections/{collectionId}/attributes/{attributeId}'.replace('{collectionId}', collectionId).replace('{attributeId}', attributeId);
|
||||
let path = '/database/collections/{collectionId}/attributes/{key}'.replace('{collectionId}', collectionId).replace('{key}', key);
|
||||
let payload = {};
|
||||
const uri = new URL(this.config.endpoint + path);
|
||||
return yield this.call('delete', uri, {
|
||||
|
@ -1793,8 +1799,8 @@
|
|||
* @param {string} collectionId
|
||||
* @param {string} documentId
|
||||
* @param {object} data
|
||||
* @param {string} read
|
||||
* @param {string} write
|
||||
* @param {string[]} read
|
||||
* @param {string[]} write
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
@ -1861,8 +1867,8 @@
|
|||
* @param {string} collectionId
|
||||
* @param {string} documentId
|
||||
* @param {object} data
|
||||
* @param {string} read
|
||||
* @param {string} write
|
||||
* @param {string[]} read
|
||||
* @param {string[]} write
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
@ -1974,19 +1980,19 @@
|
|||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} indexId
|
||||
* @param {string} key
|
||||
* @param {string} type
|
||||
* @param {string[]} attributes
|
||||
* @param {string[]} orders
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
createIndex: (collectionId, indexId, type, attributes, orders) => __awaiter(this, void 0, void 0, function* () {
|
||||
createIndex: (collectionId, key, type, attributes, orders) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
if (typeof indexId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "indexId"');
|
||||
if (typeof key === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "key"');
|
||||
}
|
||||
if (typeof type === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "type"');
|
||||
|
@ -1996,8 +2002,8 @@
|
|||
}
|
||||
let path = '/database/collections/{collectionId}/indexes'.replace('{collectionId}', collectionId);
|
||||
let payload = {};
|
||||
if (typeof indexId !== 'undefined') {
|
||||
payload['indexId'] = indexId;
|
||||
if (typeof key !== 'undefined') {
|
||||
payload['key'] = key;
|
||||
}
|
||||
if (typeof type !== 'undefined') {
|
||||
payload['type'] = type;
|
||||
|
@ -2018,18 +2024,18 @@
|
|||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} indexId
|
||||
* @param {string} key
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getIndex: (collectionId, indexId) => __awaiter(this, void 0, void 0, function* () {
|
||||
getIndex: (collectionId, key) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
if (typeof indexId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "indexId"');
|
||||
if (typeof key === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "key"');
|
||||
}
|
||||
let path = '/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}', collectionId).replace('{indexId}', indexId);
|
||||
let path = '/database/collections/{collectionId}/indexes/{key}'.replace('{collectionId}', collectionId).replace('{key}', key);
|
||||
let payload = {};
|
||||
const uri = new URL(this.config.endpoint + path);
|
||||
return yield this.call('get', uri, {
|
||||
|
@ -2041,18 +2047,18 @@
|
|||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} indexId
|
||||
* @param {string} key
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
deleteIndex: (collectionId, indexId) => __awaiter(this, void 0, void 0, function* () {
|
||||
deleteIndex: (collectionId, key) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
if (typeof indexId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "indexId"');
|
||||
if (typeof key === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "key"');
|
||||
}
|
||||
let path = '/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}', collectionId).replace('{indexId}', indexId);
|
||||
let path = '/database/collections/{collectionId}/indexes/{key}'.replace('{collectionId}', collectionId).replace('{key}', key);
|
||||
let payload = {};
|
||||
const uri = new URL(this.config.endpoint + path);
|
||||
return yield this.call('delete', uri, {
|
||||
|
@ -2234,6 +2240,22 @@
|
|||
'content-type': 'application/json',
|
||||
}, payload);
|
||||
}),
|
||||
/**
|
||||
* List the currently active function runtimes.
|
||||
*
|
||||
* Get a list of all runtimes that are currently active in your project.
|
||||
*
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
listRuntimes: () => __awaiter(this, void 0, void 0, function* () {
|
||||
let path = '/functions/runtimes';
|
||||
let payload = {};
|
||||
const uri = new URL(this.config.endpoint + path);
|
||||
return yield this.call('get', uri, {
|
||||
'content-type': 'application/json',
|
||||
}, payload);
|
||||
}),
|
||||
/**
|
||||
* Get Function
|
||||
*
|
||||
|
|
|
@ -57,10 +57,27 @@ window.addEventListener("load", async () => {
|
|||
const realtime = window.ls.container.get('realtime');
|
||||
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
let current = {};
|
||||
window.ls.container.get('console').subscribe('project', event => {
|
||||
for (let project in event.payload) {
|
||||
current[project] = event.payload[project] ?? 0;
|
||||
window.ls.container.get('console').subscribe(['project', 'console'], response => {
|
||||
switch (response.event) {
|
||||
case 'stats.connections':
|
||||
for (let project in response.payload) {
|
||||
current[project] = response.payload[project] ?? 0;
|
||||
}
|
||||
break;
|
||||
case 'database.attributes.create':
|
||||
case 'database.attributes.update':
|
||||
case 'database.attributes.delete':
|
||||
document.dispatchEvent(new CustomEvent('database.createAttribute'));
|
||||
|
||||
break;
|
||||
case 'database.indexes.create':
|
||||
case 'database.indexes.update':
|
||||
case 'database.indexes.delete':
|
||||
document.dispatchEvent(new CustomEvent('database.createIndex'));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
while (true) {
|
||||
|
@ -192,3 +209,18 @@ window.formValidation = (form, fields) => {
|
|||
}
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Method to add attribute for the UI on array attributes.
|
||||
*
|
||||
* Needs to be global - since client side routing will break it.
|
||||
* @param {*} doc
|
||||
* @param {*} key
|
||||
* @returns
|
||||
*/
|
||||
function addAttribute(doc, key) {
|
||||
if (!Array.isArray(doc[key])) {
|
||||
doc[key] = [];
|
||||
}
|
||||
doc[key].push(null);
|
||||
return doc;
|
||||
}
|
|
@ -38,6 +38,9 @@
|
|||
datasets: []
|
||||
},
|
||||
options: {
|
||||
animation: {
|
||||
duration: 0
|
||||
},
|
||||
responsive: true,
|
||||
hover: {
|
||||
mode: "nearest",
|
||||
|
|
|
@ -612,20 +612,6 @@
|
|||
"code": 61751,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "00d86f3e46c3c3d768e7246eb0eadd7f",
|
||||
"css": "discord",
|
||||
"code": 59463,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M435 432.9C411.3 432.9 392.5 453.8 392.5 479.2S411.7 525.4 435 525.4C458.8 525.4 477.5 504.6 477.5 479.2 477.9 453.8 458.8 432.9 435 432.9ZM587.1 432.9C563.3 432.9 544.6 453.8 544.6 479.2S563.8 525.4 587.1 525.4C610.8 525.4 629.6 504.6 629.6 479.2S610.8 432.9 587.1 432.9ZM789.6 83.3H231.3C184.2 83.3 145.8 121.7 145.8 169.2V732.5C145.8 780 184.2 818.3 231.3 818.3H703.8L681.7 741.3 735 790.8 785.4 837.5 875 916.7V169.2C875 121.7 836.7 83.3 789.6 83.3ZM628.8 627.5S613.8 609.6 601.3 593.8C655.8 578.3 676.7 544.2 676.7 544.2 659.6 555.4 643.3 563.3 628.8 568.8 607.9 577.5 587.9 583.3 568.3 586.7 528.3 594.2 491.7 592.1 460.4 586.3 436.7 581.7 416.3 575 399.2 568.3 389.6 564.6 379.2 560 368.8 554.2 367.5 553.3 366.3 552.9 365 552.1 364.2 551.7 363.8 551.3 363.3 550.8 355.8 546.7 351.7 543.8 351.7 543.8S371.7 577.1 424.6 592.9C412.1 608.8 396.7 627.5 396.7 627.5 304.6 624.6 269.6 564.2 269.6 564.2 269.6 430 329.6 321.3 329.6 321.3 389.6 276.3 446.7 277.5 446.7 277.5L450.8 282.5C375.8 304.2 341.3 337.1 341.3 337.1S350.4 332.1 365.8 325C410.4 305.4 445.8 300 460.4 298.8 462.9 298.3 465 297.9 467.5 297.9 492.9 294.6 521.7 293.8 551.7 297.1 591.3 301.7 633.8 313.3 677.1 337.1 677.1 337.1 644.2 305.8 573.3 284.2L579.2 277.5S636.3 276.3 696.3 321.3C696.3 321.3 756.3 430 756.3 564.2 756.3 564.2 720.8 624.6 628.8 627.5Z",
|
||||
"width": 1021
|
||||
},
|
||||
"search": [
|
||||
"discord-logo-black"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "c2152732d525871cf35345955854f711",
|
||||
"css": "moon-inv",
|
||||
|
@ -789,6 +775,54 @@
|
|||
"css": "boolean",
|
||||
"code": 61957,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "47a1f80457068fbeab69fdb83d7d0817",
|
||||
"css": "youtube-play",
|
||||
"code": 61802,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "b6941a012434c6246e2ad74248b6453e",
|
||||
"css": "discord",
|
||||
"code": 59463,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M1006.7 168.6C940.3 138.1 869 115.6 794.5 102.8 793.2 102.5 791.8 103.2 791.1 104.4 781.9 120.7 771.8 142 764.7 158.7 684.6 146.7 604.9 146.7 526.4 158.7 519.3 141.6 508.7 120.7 499.5 104.4 498.8 103.2 497.5 102.6 496.1 102.8 421.7 115.6 350.4 138.1 284 168.6 283.4 168.8 282.9 169.3 282.6 169.8 147.4 371.7 110.4 568.6 128.6 763.1 128.6 764.1 129.2 765 129.9 765.5 219.1 831 305.4 870.8 390.2 897.1 391.6 897.5 393 897 393.9 895.9 413.9 868.5 431.8 839.7 447.1 809.3 448 807.5 447.2 805.4 445.3 804.7 417 794 390 780.9 364 766 361.9 764.8 361.8 761.8 363.7 760.4 369.1 756.3 374.6 752.1 379.8 747.8 380.8 747 382.1 746.8 383.2 747.3 553.8 825.2 738.5 825.2 907.1 747.3 908.2 746.8 909.5 746.9 910.5 747.7 915.7 752 921.1 756.3 926.7 760.4 928.5 761.8 928.4 764.8 926.4 766 900.4 781.1 873.4 794 845 804.7 843.2 805.4 842.3 807.5 843.2 809.3 858.9 839.6 876.8 868.5 896.5 895.9 897.3 897 898.8 897.5 900.1 897.1 985.3 870.8 1071.7 831 1160.8 765.5 1161.6 765 1162.1 764.1 1162.2 763.2 1183.9 538.3 1125.8 343 1008 169.8 1007.8 169.3 1007.3 168.8 1006.7 168.6ZM472.6 644.7C421.2 644.7 378.9 597.5 378.9 539.6 378.9 481.7 420.4 434.6 472.6 434.6 525.2 434.6 567.1 482.1 566.3 539.6 566.3 597.5 524.8 644.7 472.6 644.7ZM819 644.7C767.6 644.7 725.3 597.5 725.3 539.6 725.3 481.7 766.8 434.6 819 434.6 871.6 434.6 913.5 482.1 912.6 539.6 912.6 597.5 871.6 644.7 819 644.7Z",
|
||||
"width": 1291
|
||||
},
|
||||
"search": [
|
||||
"discord-logo-color"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "00d86f3e46c3c3d768e7246eb0eadd7f",
|
||||
"css": "discord",
|
||||
"code": 59463,
|
||||
"src": "custom_icons",
|
||||
"selected": false,
|
||||
"svg": {
|
||||
"path": "M435 432.9C411.3 432.9 392.5 453.8 392.5 479.2S411.7 525.4 435 525.4C458.8 525.4 477.5 504.6 477.5 479.2 477.9 453.8 458.8 432.9 435 432.9ZM587.1 432.9C563.3 432.9 544.6 453.8 544.6 479.2S563.8 525.4 587.1 525.4C610.8 525.4 629.6 504.6 629.6 479.2S610.8 432.9 587.1 432.9ZM789.6 83.3H231.3C184.2 83.3 145.8 121.7 145.8 169.2V732.5C145.8 780 184.2 818.3 231.3 818.3H703.8L681.7 741.3 735 790.8 785.4 837.5 875 916.7V169.2C875 121.7 836.7 83.3 789.6 83.3ZM628.8 627.5S613.8 609.6 601.3 593.8C655.8 578.3 676.7 544.2 676.7 544.2 659.6 555.4 643.3 563.3 628.8 568.8 607.9 577.5 587.9 583.3 568.3 586.7 528.3 594.2 491.7 592.1 460.4 586.3 436.7 581.7 416.3 575 399.2 568.3 389.6 564.6 379.2 560 368.8 554.2 367.5 553.3 366.3 552.9 365 552.1 364.2 551.7 363.8 551.3 363.3 550.8 355.8 546.7 351.7 543.8 351.7 543.8S371.7 577.1 424.6 592.9C412.1 608.8 396.7 627.5 396.7 627.5 304.6 624.6 269.6 564.2 269.6 564.2 269.6 430 329.6 321.3 329.6 321.3 389.6 276.3 446.7 277.5 446.7 277.5L450.8 282.5C375.8 304.2 341.3 337.1 341.3 337.1S350.4 332.1 365.8 325C410.4 305.4 445.8 300 460.4 298.8 462.9 298.3 465 297.9 467.5 297.9 492.9 294.6 521.7 293.8 551.7 297.1 591.3 301.7 633.8 313.3 677.1 337.1 677.1 337.1 644.2 305.8 573.3 284.2L579.2 277.5S636.3 276.3 696.3 321.3C696.3 321.3 756.3 430 756.3 564.2 756.3 564.2 720.8 624.6 628.8 627.5Z",
|
||||
"width": 1021
|
||||
},
|
||||
"search": [
|
||||
"discord-logo-black"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "097a1d40f8e785fbd260946461a6ea9c",
|
||||
"css": "discord",
|
||||
"code": 59463,
|
||||
"src": "custom_icons",
|
||||
"selected": false,
|
||||
"svg": {
|
||||
"path": "M1092.8 89.1C1010.5 51.3 922.3 23.5 830 7.6 828.4 7.2 826.7 8 825.8 9.6 814.5 29.7 801.9 56.1 793.1 76.8 693.9 61.9 595.2 61.9 498 76.8 489.2 55.6 476.2 29.7 464.8 9.6 463.9 8.1 462.2 7.3 460.5 7.6 368.3 23.4 280.1 51.2 197.8 89.1 197.1 89.4 196.5 89.9 196 90.5 28.7 340.6-17.2 584.4 5.3 825.3 5.4 826.5 6.1 827.6 7 828.3 117.4 909.4 224.4 958.6 329.4 991.3 331 991.8 332.8 991.2 333.9 989.8 358.7 955.9 380.9 920.1 399.8 882.5 401 880.3 399.9 877.7 397.6 876.8 362.5 863.5 329.1 847.3 296.9 828.8 294.4 827.3 294.1 823.7 296.5 822 303.3 816.9 310 811.6 316.5 806.3 317.7 805.3 319.3 805.1 320.7 805.7 531.9 902.2 760.6 902.2 969.4 805.7 970.8 805.1 972.4 805.3 973.6 806.2 980.1 811.6 986.9 816.9 993.7 822 996 823.7 995.9 827.3 993.3 828.8 961.2 847.6 927.7 863.5 892.6 876.8 890.3 877.6 889.3 880.3 890.4 882.5 909.8 920.1 931.9 955.8 956.3 989.7 957.3 991.2 959.1 991.8 960.8 991.3 1066.3 958.6 1173.3 909.4 1283.7 828.3 1284.6 827.6 1285.2 826.5 1285.4 825.3 1312.3 546.9 1240.3 305 1094.5 90.6 1094.1 89.9 1093.5 89.4 1092.8 89.1ZM431.4 678.6C367.8 678.6 315.4 620.2 315.4 548.5 315.4 476.8 366.8 418.4 431.4 418.4 496.5 418.4 548.4 477.3 547.4 548.5 547.4 620.2 496 678.6 431.4 678.6ZM860.3 678.6C796.7 678.6 744.3 620.2 744.3 548.5 744.3 476.8 795.7 418.4 860.3 418.4 925.5 418.4 977.4 477.3 976.3 548.5 976.3 620.2 925.5 678.6 860.3 678.6Z",
|
||||
"width": 1291
|
||||
},
|
||||
"search": [
|
||||
"discord-logo-color"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -242,9 +242,9 @@ class Auth
|
|||
public static function isPrivilegedUser(array $roles): bool
|
||||
{
|
||||
if (
|
||||
array_key_exists('role:'.self::USER_ROLE_OWNER, $roles) ||
|
||||
array_key_exists('role:'.self::USER_ROLE_DEVELOPER, $roles) ||
|
||||
array_key_exists('role:'.self::USER_ROLE_ADMIN, $roles)
|
||||
in_array('role:'.self::USER_ROLE_OWNER, $roles) ||
|
||||
in_array('role:'.self::USER_ROLE_DEVELOPER, $roles) ||
|
||||
in_array('role:'.self::USER_ROLE_ADMIN, $roles)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
@ -261,7 +261,7 @@ class Auth
|
|||
*/
|
||||
public static function isAppUser(array $roles): bool
|
||||
{
|
||||
if (array_key_exists('role:'.self::USER_ROLE_APP, $roles)) {
|
||||
if (in_array('role:'.self::USER_ROLE_APP, $roles)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -278,7 +278,7 @@ class Auth
|
|||
{
|
||||
$roles = [];
|
||||
|
||||
if (!self::isPrivilegedUser(Authorization::$roles) && !self::isAppUser(Authorization::$roles)) {
|
||||
if (!self::isPrivilegedUser(Authorization::getRoles()) && !self::isAppUser(Authorization::getRoles())) {
|
||||
if ($user->getId()) {
|
||||
$roles[] = 'user:'.$user->getId();
|
||||
$roles[] = 'role:'.Auth::USER_ROLE_MEMBER;
|
||||
|
|
|
@ -18,7 +18,7 @@ class Password extends Validator
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Password must be at least 8 characters';
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ class Password extends Validator
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value)
|
||||
public function isValid($value): bool
|
||||
{
|
||||
if (!\is_string($value)) {
|
||||
return false;
|
||||
|
|
|
@ -46,7 +46,7 @@ class Authorization extends Validator
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ class Authorization extends Validator
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($permissions)
|
||||
public function isValid($permissions): bool
|
||||
{
|
||||
if (!self::$status) {
|
||||
return true;
|
||||
|
|
|
@ -39,7 +39,7 @@ class Collection extends Structure
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($document)
|
||||
public function isValid($document): bool
|
||||
{
|
||||
$document = new Document(
|
||||
\array_merge($this->merge, ($document instanceof Document) ? $document->getArrayCopy() : $document)
|
||||
|
|
|
@ -13,7 +13,7 @@ class CustomId extends Key {
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value)
|
||||
public function isValid($value): bool
|
||||
{
|
||||
|
||||
return $value == 'unique()' || parent::isValid($value);
|
||||
|
|
|
@ -42,7 +42,7 @@ class DocumentId extends Validator
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ class DocumentId extends Validator
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($id)
|
||||
public function isValid($id): bool
|
||||
{
|
||||
$document = $this->database->getDocument($id);
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ class Key extends Validator
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ class Key extends Validator
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value)
|
||||
public function isValid($value): bool
|
||||
{
|
||||
if (!\is_string($value)) {
|
||||
return false;
|
||||
|
|
|
@ -34,7 +34,7 @@ class Permissions extends Validator
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ class Permissions extends Validator
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value)
|
||||
public function isValid($value): bool
|
||||
{
|
||||
if (!\is_array($value) && !empty($value)) {
|
||||
$this->message = 'Invalid permissions data structure';
|
||||
|
|
|
@ -109,7 +109,7 @@ class Structure extends Validator
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Invalid document structure: '.$this->message;
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ class Structure extends Validator
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($document)
|
||||
public function isValid($document): bool
|
||||
{
|
||||
$document = (\is_array($document)) ? new Document($document) : $document;
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ class UID extends Validator
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Invalid UID format';
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class UID extends Validator
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value)
|
||||
public function isValid($value): bool
|
||||
{
|
||||
if ($value === 0) { // TODO Deprecate confition when we get the chance.
|
||||
return true;
|
||||
|
|
|
@ -47,7 +47,18 @@ class Detector
|
|||
*/
|
||||
public function getClient(): array
|
||||
{
|
||||
if (strpos($this->userAgent, 'Appwrite CLI') !== false) {
|
||||
$version = explode(' ', $this->userAgent)[0];
|
||||
$version = explode('/', $version)[1];
|
||||
$client = [
|
||||
'type' => 'desktop',
|
||||
'short_name' => 'cli',
|
||||
'name' => 'Appwrite CLI',
|
||||
'version' => $version
|
||||
];
|
||||
} else {
|
||||
$client = $this->getDetector()->getClient();
|
||||
}
|
||||
|
||||
return [
|
||||
'clientType' => (isset($client['type'])) ? $client['type'] : '',
|
||||
|
@ -86,4 +97,16 @@ class Detector
|
|||
|
||||
return $this->detctor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to skip bot detection.
|
||||
* It is needed if we want bots to be processed as a simple clients. So we can detect if it is mobile client,
|
||||
* or desktop, or enything else. By default all this information is not retrieved for the bots.
|
||||
*
|
||||
* @param bool $skip
|
||||
*/
|
||||
public function skipBotDetection(bool $skip = true): void
|
||||
{
|
||||
$this->getDetector()->skipBotDetection($skip);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,13 +237,15 @@ class Realtime extends Adapter
|
|||
*
|
||||
* @param string $event
|
||||
* @param Document $payload
|
||||
* @param Document|null $project
|
||||
* @return array
|
||||
*/
|
||||
public static function fromPayload(string $event, Document $payload): array
|
||||
public static function fromPayload(string $event, Document $payload, Document $project = null, Document $collection = null): array
|
||||
{
|
||||
$channels = [];
|
||||
$roles = [];
|
||||
$permissionsChanged = false;
|
||||
$projectId = null;
|
||||
|
||||
switch (true) {
|
||||
case strpos($event, 'account.recovery.') === 0:
|
||||
|
@ -273,11 +275,29 @@ class Realtime extends Adapter
|
|||
$channels[] = 'teams.' . $payload->getId();
|
||||
$roles = ['team:' . $payload->getId()];
|
||||
|
||||
break;
|
||||
case strpos($event, 'database.attributes.') === 0:
|
||||
case strpos($event, 'database.indexes.') === 0:
|
||||
$channels[] = 'console';
|
||||
$projectId = 'console';
|
||||
$roles = ['team:' . $project->getAttribute('teamId')];
|
||||
|
||||
break;
|
||||
case strpos($event, 'database.documents.') === 0:
|
||||
if ($collection->isEmpty()) {
|
||||
throw new \Exception('Collection need to be passed to to Realtime for Document events in the Database.');
|
||||
}
|
||||
|
||||
$channels[] = 'documents';
|
||||
$channels[] = 'collections.' . $payload->getAttribute('$collection') . '.documents';
|
||||
$channels[] = 'documents.' . $payload->getId();
|
||||
|
||||
$roles = ($collection->getAttribute('permission') === 'collection') ? $collection->getRead() : $payload->getRead();
|
||||
|
||||
break;
|
||||
case strpos($event, 'storage.buckets.') === 0:
|
||||
$channels[] = 'buckets';
|
||||
$channels[] = 'buckets.' . $payload->getId();
|
||||
$roles = $payload->getRead();
|
||||
|
||||
break;
|
||||
|
@ -301,7 +321,8 @@ class Realtime extends Adapter
|
|||
return [
|
||||
'channels' => $channels,
|
||||
'roles' => $roles,
|
||||
'permissionsChanged' => $permissionsChanged
|
||||
'permissionsChanged' => $permissionsChanged,
|
||||
'projectId' => $projectId
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,8 +46,8 @@ class V06 extends Migration
|
|||
$document->setAttribute('secret', json_encode([
|
||||
'data' => OpenSSL::encrypt($document->getAttribute('secret'), OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
|
||||
'method' => OpenSSL::CIPHER_AES_128_GCM,
|
||||
'iv' => bin2hex($iv),
|
||||
'tag' => bin2hex($tag),
|
||||
'iv' => \bin2hex($iv),
|
||||
'tag' => \bin2hex($tag ?? ''),
|
||||
'version' => '1',
|
||||
]));
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ class CNAME extends Validator
|
|||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Invalid CNAME record';
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ class CNAME extends Validator
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($domain)
|
||||
public function isValid($domain): bool
|
||||
{
|
||||
if (!is_string($domain)) {
|
||||
return false;
|
||||
|
|
|
@ -20,7 +20,7 @@ class Domain extends Validator
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Value must be a valid domain';
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ class Domain extends Validator
|
|||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value)
|
||||
public function isValid($value): bool
|
||||
{
|
||||
if (empty($value)) {
|
||||
return false;
|
||||
|
|
|
@ -20,7 +20,7 @@ class Email extends Validator
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Value must be a valid email address';
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class Email extends Validator
|
|||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value)
|
||||
public function isValid($value): bool
|
||||
{
|
||||
if (!\filter_var($value, FILTER_VALIDATE_EMAIL)) {
|
||||
return false;
|
||||
|
|
|
@ -30,7 +30,7 @@ class Host extends Validator
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'URL host must be one of: ' . \implode(', ', $this->whitelist);
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ class Host extends Validator
|
|||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value)
|
||||
public function isValid($value): bool
|
||||
{
|
||||
$urlValidator = new URL();
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ class IP extends Validator
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Value must be a valid IP address';
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ class IP extends Validator
|
|||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value)
|
||||
public function isValid($value): bool
|
||||
{
|
||||
switch ($this->type) {
|
||||
case self::ALL:
|
||||
|
|
|
@ -84,7 +84,7 @@ class Origin extends Validator
|
|||
}
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
public function getDescription(): string
|
||||
{
|
||||
if (!\array_key_exists($this->client, $this->platforms)) {
|
||||
return 'Unsupported platform';
|
||||
|
@ -102,7 +102,7 @@ class Origin extends Validator
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($origin)
|
||||
public function isValid($origin): bool
|
||||
{
|
||||
if (!is_string($origin)) {
|
||||
return false;
|
||||
|
|
|
@ -20,7 +20,7 @@ class URL extends Validator
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Value must be a valid URL';
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class URL extends Validator
|
|||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value)
|
||||
public function isValid($value): bool
|
||||
{
|
||||
if (\filter_var($value, FILTER_VALIDATE_URL) === false) {
|
||||
return false;
|
||||
|
|
|
@ -392,10 +392,7 @@ class Swagger2 extends Format
|
|||
|
||||
foreach ($this->models as $model) {
|
||||
foreach ($model->getRules() as $rule) {
|
||||
if (
|
||||
in_array($model->getType(), $usedModels)
|
||||
&& !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])
|
||||
) {
|
||||
if (!in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])) {
|
||||
$usedModels[] = $rule['type'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace Appwrite\Template;
|
||||
|
||||
use Appwrite\Utopia\View;
|
||||
use Exception;
|
||||
use Utopia\View;
|
||||
|
||||
class Template extends View
|
||||
{
|
||||
|
@ -63,7 +63,7 @@ class Template extends View
|
|||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function render($minify = true)
|
||||
public function render($minify = true): string
|
||||
{
|
||||
if ($this->rendered) { // Don't render any template
|
||||
return '';
|
||||
|
|
|
@ -34,7 +34,6 @@ use Appwrite\Utopia\Response\Model\File;
|
|||
use Appwrite\Utopia\Response\Model\Bucket;
|
||||
use Appwrite\Utopia\Response\Model\Func;
|
||||
use Appwrite\Utopia\Response\Model\Index;
|
||||
use Appwrite\Utopia\Response\Model\FuncPermissions;
|
||||
use Appwrite\Utopia\Response\Model\JWT;
|
||||
use Appwrite\Utopia\Response\Model\Key;
|
||||
use Appwrite\Utopia\Response\Model\Language;
|
||||
|
@ -55,6 +54,7 @@ use Appwrite\Utopia\Response\Model\Token;
|
|||
use Appwrite\Utopia\Response\Model\Webhook;
|
||||
use Appwrite\Utopia\Response\Model\Preferences;
|
||||
use Appwrite\Utopia\Response\Model\Mock; // Keep last
|
||||
use Appwrite\Utopia\Response\Model\Runtime;
|
||||
use Appwrite\Utopia\Response\Model\UsageBuckets;
|
||||
use Appwrite\Utopia\Response\Model\UsageCollection;
|
||||
use Appwrite\Utopia\Response\Model\UsageDatabase;
|
||||
|
@ -143,6 +143,8 @@ class Response extends SwooleResponse
|
|||
// Functions
|
||||
const MODEL_FUNCTION = 'function';
|
||||
const MODEL_FUNCTION_LIST = 'functionList';
|
||||
const MODEL_RUNTIME = 'runtime';
|
||||
const MODEL_RUNTIME_LIST = 'runtimeList';
|
||||
const MODEL_TAG = 'tag';
|
||||
const MODEL_TAG_LIST = 'tagList';
|
||||
const MODEL_EXECUTION = 'execution';
|
||||
|
@ -204,6 +206,7 @@ class Response extends SwooleResponse
|
|||
->setModel(new BaseList('Teams List', self::MODEL_TEAM_LIST, 'teams', self::MODEL_TEAM))
|
||||
->setModel(new BaseList('Memberships List', self::MODEL_MEMBERSHIP_LIST, 'memberships', self::MODEL_MEMBERSHIP))
|
||||
->setModel(new BaseList('Functions List', self::MODEL_FUNCTION_LIST, 'functions', self::MODEL_FUNCTION))
|
||||
->setModel(new BaseList('Runtimes List', self::MODEL_RUNTIME_LIST, 'runtimes', self::MODEL_RUNTIME))
|
||||
->setModel(new BaseList('Tags List', self::MODEL_TAG_LIST, 'tags', self::MODEL_TAG))
|
||||
->setModel(new BaseList('Executions List', self::MODEL_EXECUTION_LIST, 'executions', self::MODEL_EXECUTION))
|
||||
->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT, true, false))
|
||||
|
@ -243,7 +246,7 @@ class Response extends SwooleResponse
|
|||
->setModel(new Team())
|
||||
->setModel(new Membership())
|
||||
->setModel(new Func())
|
||||
->setModel(new FuncPermissions())
|
||||
->setModel(new Runtime())
|
||||
->setModel(new Tag())
|
||||
->setModel(new Execution())
|
||||
->setModel(new Project())
|
||||
|
|
|
@ -36,6 +36,12 @@ class Collection extends Model
|
|||
'default' => '',
|
||||
'example' => 'My Collection',
|
||||
])
|
||||
->addRule('enabled', [
|
||||
'type' => self::TYPE_BOOLEAN,
|
||||
'description' => 'Collection enabled.',
|
||||
'default' => true,
|
||||
'example' => false,
|
||||
])
|
||||
->addRule('permission', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Collection permission model. Possible values: `document` or `collection`',
|
||||
|
|
|
@ -17,10 +17,10 @@ class Func extends Model
|
|||
'example' => '5e5ea5c16897e',
|
||||
])
|
||||
->addRule('execute', [
|
||||
'type' => Response::MODEL_FUNC_PERMISSIONS,
|
||||
'description' => 'Function permissions.',
|
||||
'default' => new \stdClass,
|
||||
'example' => new \stdClass,
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Execution permissions.',
|
||||
'default' => '',
|
||||
'example' => 'role:member',
|
||||
'array' => false,
|
||||
])
|
||||
->addRule('name', [
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class FuncPermissions extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->addRule('execute', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Execution permissions.',
|
||||
'default' => [],
|
||||
'example' => 'user:5e5ea5c16897e',
|
||||
'array' => true,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
{
|
||||
return 'FuncPermissions';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType():string
|
||||
{
|
||||
return Response::MODEL_FUNC_PERMISSIONS;
|
||||
}
|
||||
}
|
78
src/Appwrite/Utopia/Response/Model/Runtime.php
Normal file
78
src/Appwrite/Utopia/Response/Model/Runtime.php
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class Runtime extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->addRule('$id', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Runtime ID.',
|
||||
'default' => '',
|
||||
'example' => 'python-3.8',
|
||||
])
|
||||
->addRule('name', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Runtime Name.',
|
||||
'default' => '',
|
||||
'example' => 'Python'
|
||||
])
|
||||
->addRule('version', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Runtime version.',
|
||||
'default' => '',
|
||||
'example' => '3.8',
|
||||
])
|
||||
->addRule('base', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Base Docker image used to build the runtime.',
|
||||
'default' => '',
|
||||
'example' => 'python:3.8-alpine',
|
||||
])
|
||||
->addRule('image', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Image name of Docker Hub.',
|
||||
'default' => '',
|
||||
'example' => 'appwrite\/runtime-for-python:3.8',
|
||||
])
|
||||
->addRule('logo', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Name of the logo image.',
|
||||
'default' => '',
|
||||
'example' => 'python.png',
|
||||
])
|
||||
->addRule('supports', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'List of supported architectures.',
|
||||
'default' => '',
|
||||
'example' => 'amd64',
|
||||
'array' => true,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
{
|
||||
return 'Runtime';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType():string
|
||||
{
|
||||
return Response::MODEL_RUNTIME;
|
||||
}
|
||||
}
|
22
src/Appwrite/Utopia/View.php
Normal file
22
src/Appwrite/Utopia/View.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia;
|
||||
|
||||
use Utopia\View as OldView;
|
||||
|
||||
class View extends OldView
|
||||
{
|
||||
/**
|
||||
* Escape
|
||||
*
|
||||
* Convert all applicable characters to HTML entities
|
||||
*
|
||||
* @param string $str
|
||||
* @return string
|
||||
* @deprecated Use print method with escape filter
|
||||
*/
|
||||
public function escape($str)
|
||||
{
|
||||
return \htmlentities($str, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
}
|
|
@ -19,8 +19,8 @@ trait DatabaseBase
|
|||
]), [
|
||||
'collectionId' => 'unique()',
|
||||
'name' => 'Movies',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'read' => [],
|
||||
'write' => [],
|
||||
'permission' => 'document',
|
||||
]);
|
||||
|
||||
|
@ -30,6 +30,70 @@ trait DatabaseBase
|
|||
return ['moviesId' => $movies['body']['$id']];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateCollection
|
||||
*/
|
||||
public function testDisableCollection(array $data): void
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_PUT, '/database/collections/' . $data['moviesId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'name' => 'Movies',
|
||||
'enabled' => false,
|
||||
'permission' => 'document',
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertFalse($response['body']['enabled']);
|
||||
|
||||
if ($this->getSide() === 'client') {
|
||||
$responseCreateDocument = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => 'unique()',
|
||||
'data' => [
|
||||
'title' => 'Captain America',
|
||||
],
|
||||
'read' => ['user:'.$this->getUser()['$id']],
|
||||
'write' => ['user:'.$this->getUser()['$id']],
|
||||
]);
|
||||
|
||||
$responseListDocument = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$responseGetDocument = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents/someID', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals($responseCreateDocument['headers']['status-code'], 404);
|
||||
$this->assertEquals($responseListDocument['headers']['status-code'], 404);
|
||||
$this->assertEquals($responseGetDocument['headers']['status-code'], 404);
|
||||
}
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PUT, '/database/collections/' . $data['moviesId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'name' => 'Movies',
|
||||
'enabled' => true,
|
||||
'permission' => 'document',
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertTrue($response['body']['enabled']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @depends testCreateCollection
|
||||
*/
|
||||
|
@ -40,7 +104,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'title',
|
||||
'key' => 'title',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -50,7 +114,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'releaseYear',
|
||||
'key' => 'releaseYear',
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
|
@ -59,7 +123,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'actors',
|
||||
'key' => 'actors',
|
||||
'size' => 256,
|
||||
'required' => false,
|
||||
'array' => true,
|
||||
|
@ -113,8 +177,8 @@ trait DatabaseBase
|
|||
]), [
|
||||
'collectionId' => 'unique()',
|
||||
'name' => 'Response Models',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'read' => [],
|
||||
'write' => [],
|
||||
'permission' => 'document',
|
||||
]);
|
||||
|
||||
|
@ -128,7 +192,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'string',
|
||||
'key' => 'string',
|
||||
'size' => 16,
|
||||
'required' => false,
|
||||
'default' => 'default',
|
||||
|
@ -139,7 +203,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'email',
|
||||
'key' => 'email',
|
||||
'required' => false,
|
||||
'default' => 'default@example.com',
|
||||
]);
|
||||
|
@ -149,7 +213,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'enum',
|
||||
'key' => 'enum',
|
||||
'elements' => ['yes', 'no', 'maybe'],
|
||||
'required' => false,
|
||||
'default' => 'maybe',
|
||||
|
@ -160,7 +224,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'ip',
|
||||
'key' => 'ip',
|
||||
'required' => false,
|
||||
'default' => '192.0.2.0',
|
||||
]);
|
||||
|
@ -170,7 +234,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'url',
|
||||
'key' => 'url',
|
||||
'required' => false,
|
||||
'default' => 'http://example.com',
|
||||
]);
|
||||
|
@ -180,7 +244,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'integer',
|
||||
'key' => 'integer',
|
||||
'required' => false,
|
||||
'min' => 1,
|
||||
'max' => 5,
|
||||
|
@ -192,7 +256,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'float',
|
||||
'key' => 'float',
|
||||
'required' => false,
|
||||
'min' => 1.5,
|
||||
'max' => 5.5,
|
||||
|
@ -204,7 +268,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'boolean',
|
||||
'key' => 'boolean',
|
||||
'required' => false,
|
||||
'default' => true,
|
||||
]);
|
||||
|
@ -568,7 +632,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'enum',
|
||||
'key' => 'enum',
|
||||
'elements' => ['yes', 'no', ''],
|
||||
'required' => false,
|
||||
'default' => 'maybe',
|
||||
|
@ -590,7 +654,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'titleIndex',
|
||||
'key' => 'titleIndex',
|
||||
'type' => 'fulltext',
|
||||
'attributes' => ['title'],
|
||||
]);
|
||||
|
@ -606,7 +670,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'releaseYear',
|
||||
'key' => 'releaseYear',
|
||||
'type' => 'key',
|
||||
'attributes' => ['releaseYear'],
|
||||
]);
|
||||
|
@ -1229,8 +1293,8 @@ trait DatabaseBase
|
|||
]), [
|
||||
'collectionId' => 'unique()',
|
||||
'name' => 'invalidDocumentStructure',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'read' => [],
|
||||
'write' => [],
|
||||
'permission' => 'document',
|
||||
]);
|
||||
|
||||
|
@ -1244,7 +1308,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'email',
|
||||
'key' => 'email',
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
|
@ -1253,7 +1317,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'enum',
|
||||
'key' => 'enum',
|
||||
'elements' => ['yes', 'no', 'maybe'],
|
||||
'required' => false,
|
||||
]);
|
||||
|
@ -1263,7 +1327,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'ip',
|
||||
'key' => 'ip',
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
|
@ -1272,7 +1336,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'url',
|
||||
'key' => 'url',
|
||||
'size' => 256,
|
||||
'required' => false,
|
||||
]);
|
||||
|
@ -1282,7 +1346,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'range',
|
||||
'key' => 'range',
|
||||
'required' => false,
|
||||
'min' => 1,
|
||||
'max' => 10,
|
||||
|
@ -1294,7 +1358,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'floatRange',
|
||||
'key' => 'floatRange',
|
||||
'required' => false,
|
||||
'min' => 1.1,
|
||||
'max' => 1.4,
|
||||
|
@ -1305,7 +1369,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'probability',
|
||||
'key' => 'probability',
|
||||
'required' => false,
|
||||
'min' => 0,
|
||||
'max' => 1,
|
||||
|
@ -1316,7 +1380,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'upperBound',
|
||||
'key' => 'upperBound',
|
||||
'required' => false,
|
||||
'max' => 10,
|
||||
]);
|
||||
|
@ -1326,7 +1390,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'lowerBound',
|
||||
'key' => 'lowerBound',
|
||||
'required' => false,
|
||||
'min' => 5,
|
||||
]);
|
||||
|
@ -1339,7 +1403,7 @@ trait DatabaseBase
|
|||
'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'invalidRange',
|
||||
'key' => 'invalidRange',
|
||||
'required' => false,
|
||||
'min' => 4,
|
||||
'max' => 3,
|
||||
|
@ -1349,12 +1413,42 @@ trait DatabaseBase
|
|||
'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'defaultArray',
|
||||
'key' => 'defaultArray',
|
||||
'required' => false,
|
||||
'default' => 42,
|
||||
'array' => true,
|
||||
]);
|
||||
|
||||
$defaultRequired = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/integer', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'defaultRequired',
|
||||
'required' => true,
|
||||
'default' => 12
|
||||
]);
|
||||
|
||||
$enumDefault = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/enum', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'enumDefault',
|
||||
'elements' => ['north', 'west'],
|
||||
'default' => 'south'
|
||||
]);
|
||||
|
||||
$enumDefaultStrict = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/enum', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'enumDefault',
|
||||
'elements' => ['north', 'west'],
|
||||
'default' => 'NORTH'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $email['headers']['status-code']);
|
||||
$this->assertEquals(201, $ip['headers']['status-code']);
|
||||
$this->assertEquals(201, $url['headers']['status-code']);
|
||||
|
@ -1363,8 +1457,12 @@ trait DatabaseBase
|
|||
$this->assertEquals(201, $probability['headers']['status-code']);
|
||||
$this->assertEquals(201, $upperBound['headers']['status-code']);
|
||||
$this->assertEquals(201, $lowerBound['headers']['status-code']);
|
||||
$this->assertEquals(201, $enum['headers']['status-code']);
|
||||
$this->assertEquals(400, $invalidRange['headers']['status-code']);
|
||||
$this->assertEquals(400, $defaultArray['headers']['status-code']);
|
||||
$this->assertEquals(400, $defaultRequired['headers']['status-code']);
|
||||
$this->assertEquals(400, $enumDefault['headers']['status-code']);
|
||||
$this->assertEquals(400, $enumDefaultStrict['headers']['status-code']);
|
||||
$this->assertEquals('Minimum value must be lesser than maximum value', $invalidRange['body']['message']);
|
||||
$this->assertEquals('Cannot set default value for array attributes', $defaultArray['body']['message']);
|
||||
|
||||
|
@ -1788,7 +1886,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'attribute',
|
||||
'key' => 'attribute',
|
||||
'size' => 64,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -1804,7 +1902,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'key_attribute',
|
||||
'key' => 'key_attribute',
|
||||
'type' => 'key',
|
||||
'attributes' => [$attribute['body']['key']],
|
||||
]);
|
||||
|
@ -1829,13 +1927,41 @@ trait DatabaseBase
|
|||
|
||||
$this->assertEquals(201, $document1['headers']['status-code']);
|
||||
|
||||
$document2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => 'unique()',
|
||||
'data' => [
|
||||
'attribute' => 'one',
|
||||
],
|
||||
'read' => [],
|
||||
'write' => [$user],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $document2['headers']['status-code']);
|
||||
|
||||
$document3 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => 'unique()',
|
||||
'data' => [
|
||||
'attribute' => 'one',
|
||||
],
|
||||
'read' => [],
|
||||
'write' => [],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $document3['headers']['status-code']);
|
||||
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(1, $documents['body']['sum']);
|
||||
$this->assertCount(1, $documents['body']['documents']);
|
||||
$this->assertEquals(3, $documents['body']['sum']);
|
||||
$this->assertCount(3, $documents['body']['documents']);
|
||||
|
||||
/*
|
||||
* Test for Failure
|
||||
|
@ -1894,7 +2020,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]));
|
||||
|
||||
$this->assertEquals(404, $documents['headers']['status-code']);
|
||||
$this->assertEquals(401, $documents['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1907,7 +2033,7 @@ trait DatabaseBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'unique_title',
|
||||
'key' => 'unique_title',
|
||||
'type' => 'unique',
|
||||
'attributes' => ['title'],
|
||||
]);
|
||||
|
|
|
@ -165,7 +165,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'firstName',
|
||||
'key' => 'firstName',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -175,7 +175,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'lastName',
|
||||
'key' => 'lastName',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -185,7 +185,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'unneeded',
|
||||
'key' => 'unneeded',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -214,7 +214,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'key_lastName',
|
||||
'key' => 'key_lastName',
|
||||
'type' => 'key',
|
||||
'attributes' => [
|
||||
'lastName',
|
||||
|
@ -275,7 +275,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
|
||||
return [
|
||||
'collectionId' => $actors['body']['$id'],
|
||||
'indexId' => $index['body']['key'],
|
||||
'key' => $index['body']['key'],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -284,7 +284,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
*/
|
||||
public function testDeleteIndex($data): array
|
||||
{
|
||||
$index = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/indexes/'. $data['indexId'], array_merge([
|
||||
$index = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/indexes/'. $data['key'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
|
@ -316,7 +316,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'attribute1',
|
||||
'key' => 'attribute1',
|
||||
'size' => 16,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -326,7 +326,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'attribute2',
|
||||
'key' => 'attribute2',
|
||||
'size' => 16,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -343,7 +343,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'index1',
|
||||
'key' => 'index1',
|
||||
'type' => 'key',
|
||||
'attributes' => ['attribute1', 'attribute2'],
|
||||
'orders' => ['ASC', 'ASC'],
|
||||
|
@ -354,7 +354,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'index2',
|
||||
'key' => 'index2',
|
||||
'type' => 'key',
|
||||
'attributes' => ['attribute2'],
|
||||
]);
|
||||
|
@ -428,7 +428,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'attribute1',
|
||||
'key' => 'attribute1',
|
||||
'size' => 16,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -438,7 +438,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'attribute2',
|
||||
'key' => 'attribute2',
|
||||
'size' => 16,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -455,7 +455,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'index1',
|
||||
'key' => 'index1',
|
||||
'type' => 'key',
|
||||
'attributes' => ['attribute1', 'attribute2'],
|
||||
'orders' => ['ASC', 'ASC'],
|
||||
|
@ -466,7 +466,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'index2',
|
||||
'key' => 'index2',
|
||||
'type' => 'key',
|
||||
'attributes' => ['attribute2'],
|
||||
]);
|
||||
|
@ -615,7 +615,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
// 'x-appwrite-project' => $this->getProject()['$id'],
|
||||
// 'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
// ]), [
|
||||
// 'attributeId' => "attribute{$i}",
|
||||
// 'key' => "attribute{$i}",
|
||||
// 'required' => false,
|
||||
// ]);
|
||||
|
||||
|
@ -629,7 +629,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
// 'x-appwrite-project' => $this->getProject()['$id'],
|
||||
// 'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
// ]), [
|
||||
// 'attributeId' => "tooMany",
|
||||
// 'key' => "tooMany",
|
||||
// 'required' => false,
|
||||
// ]);
|
||||
|
||||
|
@ -663,7 +663,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => "attribute{$i}",
|
||||
'key' => "attribute{$i}",
|
||||
'size' => 1024,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -678,7 +678,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'tooWide',
|
||||
'key' => 'tooWide',
|
||||
'size' => 1024,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -714,7 +714,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => "attribute{$i}",
|
||||
'key' => "attribute{$i}",
|
||||
'size' => 64,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -751,7 +751,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => "key_attribute{$i}",
|
||||
'key' => "key_attribute{$i}",
|
||||
'type' => 'key',
|
||||
'attributes' => ["attribute{$i}"],
|
||||
]);
|
||||
|
@ -780,7 +780,7 @@ class DatabaseCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'tooMany',
|
||||
'key' => 'tooMany',
|
||||
'type' => 'key',
|
||||
'attributes' => ['attribute61'],
|
||||
]);
|
||||
|
|
|
@ -26,7 +26,7 @@ class DatabasePermissionsGuestTest extends Scope
|
|||
$collection = ['id' => $movies['body']['$id']];
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/database/collections/' . $collection['id'] . '/attributes/string', $this->getServerHeader(), [
|
||||
'attributeId' => 'title',
|
||||
'key' => 'title',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
|
|
@ -65,7 +65,7 @@ class DatabasePermissionsMemberTest extends Scope
|
|||
$this->collections = ['public' => $public['body']['$id']];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['public'] . '/attributes/string', $this->getServerHeader(), [
|
||||
'attributeId' => 'title',
|
||||
'key' => 'title',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -83,7 +83,7 @@ class DatabasePermissionsMemberTest extends Scope
|
|||
$this->collections['private'] = $private['body']['$id'];
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['private'] . '/attributes/string', $this->getServerHeader(), [
|
||||
'attributeId' => 'title',
|
||||
'key' => 'title',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
|
|
@ -45,7 +45,7 @@ class DatabasePermissionsTeamTest extends Scope
|
|||
$this->collections['collection1'] = $collection1['body']['$id'];
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['collection1'] . '/attributes/string', $this->getServerHeader(), [
|
||||
'attributeId' => 'title',
|
||||
'key' => 'title',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -61,7 +61,7 @@ class DatabasePermissionsTeamTest extends Scope
|
|||
$this->collections['collection2'] = $collection2['body']['$id'];
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['collection2'] . '/attributes/string', $this->getServerHeader(), [
|
||||
'attributeId' => 'title',
|
||||
'key' => 'title',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -160,7 +160,7 @@ class DatabasePermissionsTeamTest extends Scope
|
|||
if ($success) {
|
||||
$this->assertCount(1, $documents['body']['documents']);
|
||||
} else {
|
||||
$this->assertEquals(404, $documents['headers']['status-code']);
|
||||
$this->assertEquals(401, $documents['headers']['status-code']);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -830,4 +830,27 @@ class FunctionsCustomServerTest extends Scope
|
|||
$this->assertEquals($executions['body']['executions'][0]['trigger'], 'http');
|
||||
$this->assertStringContainsString('foobar', $executions['body']['executions'][0]['stdout']);
|
||||
}
|
||||
|
||||
public function testGetRuntimes()
|
||||
{
|
||||
|
||||
$runtimes = $this->client->call(Client::METHOD_GET, '/functions/runtimes', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $runtimes['headers']['status-code']);
|
||||
$this->assertGreaterThan(0, $runtimes['body']['sum']);
|
||||
|
||||
$runtime = $runtimes['body']['runtimes'][0];
|
||||
|
||||
$this->assertArrayHasKey('$id', $runtime);
|
||||
$this->assertArrayHasKey('name', $runtime);
|
||||
$this->assertArrayHasKey('version', $runtime);
|
||||
$this->assertArrayHasKey('logo', $runtime);
|
||||
$this->assertArrayHasKey('image', $runtime);
|
||||
$this->assertArrayHasKey('base', $runtime);
|
||||
$this->assertArrayHasKey('supports', $runtime);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
276
tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php
Normal file
276
tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php
Normal file
|
@ -0,0 +1,276 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\E2E\Services\Realtime;
|
||||
|
||||
use Exception;
|
||||
use SebastianBergmann\RecursionContext\InvalidArgumentException;
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
use PHPUnit\Framework\Exception as FrameworkException;
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\SideConsole;
|
||||
use WebSocket\BadOpcodeException;
|
||||
use WebSocket\ConnectionException;
|
||||
use WebSocket\TimeoutException;
|
||||
|
||||
class RealtimeConsoleClientTest extends Scope
|
||||
{
|
||||
use RealtimeBase;
|
||||
use ProjectCustom;
|
||||
use SideConsole;
|
||||
|
||||
public function testAttributes()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$session = $user['session'] ?? '';
|
||||
$projectId = 'console';
|
||||
|
||||
$client = $this->getWebsocket(['console'], [
|
||||
'origin' => 'http://localhost',
|
||||
'cookie' => 'a_session_console='. $this->getRoot()['session'],
|
||||
], $projectId);
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
|
||||
$this->assertArrayHasKey('type', $response);
|
||||
$this->assertArrayHasKey('data', $response);
|
||||
$this->assertEquals('connected', $response['type']);
|
||||
$this->assertNotEmpty($response['data']);
|
||||
$this->assertCount(1, $response['data']['channels']);
|
||||
$this->assertContains('console', $response['data']['channels']);
|
||||
$this->assertNotEmpty($response['data']['user']);
|
||||
|
||||
/**
|
||||
* Test Attributes
|
||||
*/
|
||||
$actors = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'collectionId' => 'unique()',
|
||||
'name' => 'Actors',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'collection'
|
||||
]);
|
||||
|
||||
$data = ['actorsId' => $actors['body']['$id']];
|
||||
|
||||
$name = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/attributes/string', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'key' => 'name',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals($name['headers']['status-code'], 201);
|
||||
$this->assertEquals($name['body']['key'], 'name');
|
||||
$this->assertEquals($name['body']['type'], 'string');
|
||||
$this->assertEquals($name['body']['size'], 256);
|
||||
$this->assertEquals($name['body']['required'], true);
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
|
||||
$this->assertArrayHasKey('type', $response);
|
||||
$this->assertArrayHasKey('data', $response);
|
||||
$this->assertEquals('event', $response['type']);
|
||||
$this->assertNotEmpty($response['data']);
|
||||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(1, $response['data']['channels']);
|
||||
$this->assertContains('console', $response['data']['channels']);
|
||||
$this->assertEquals('database.attributes.create', $response['data']['event']);
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
$this->assertEquals('processing', $response['data']['payload']['status']);
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
|
||||
$this->assertArrayHasKey('type', $response);
|
||||
$this->assertArrayHasKey('data', $response);
|
||||
$this->assertEquals('event', $response['type']);
|
||||
$this->assertNotEmpty($response['data']);
|
||||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(1, $response['data']['channels']);
|
||||
$this->assertContains('console', $response['data']['channels']);
|
||||
$this->assertEquals('database.attributes.update', $response['data']['event']);
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
$this->assertEquals('available', $response['data']['payload']['status']);
|
||||
|
||||
$client->close();
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAttributes
|
||||
*/
|
||||
public function testIndexes(array $data)
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$session = $user['session'] ?? '';
|
||||
$projectId = 'console';
|
||||
|
||||
$client = $this->getWebsocket(['console'], [
|
||||
'origin' => 'http://localhost',
|
||||
'cookie' => 'a_session_console='. $this->getRoot()['session'],
|
||||
], $projectId);
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
|
||||
$this->assertArrayHasKey('type', $response);
|
||||
$this->assertArrayHasKey('data', $response);
|
||||
$this->assertEquals('connected', $response['type']);
|
||||
$this->assertNotEmpty($response['data']);
|
||||
$this->assertCount(1, $response['data']['channels']);
|
||||
$this->assertContains('console', $response['data']['channels']);
|
||||
$this->assertNotEmpty($response['data']['user']);
|
||||
|
||||
/**
|
||||
* Test Indexes
|
||||
*/
|
||||
$index = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/indexes', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'key' => 'key_name',
|
||||
'type' => 'key',
|
||||
'attributes' => [
|
||||
'name',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals($index['headers']['status-code'], 201);
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
|
||||
$this->assertArrayHasKey('type', $response);
|
||||
$this->assertArrayHasKey('data', $response);
|
||||
$this->assertEquals('event', $response['type']);
|
||||
$this->assertNotEmpty($response['data']);
|
||||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(1, $response['data']['channels']);
|
||||
$this->assertContains('console', $response['data']['channels']);
|
||||
$this->assertEquals('database.indexes.create', $response['data']['event']);
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
$this->assertEquals('processing', $response['data']['payload']['status']);
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
|
||||
$this->assertArrayHasKey('type', $response);
|
||||
$this->assertArrayHasKey('data', $response);
|
||||
$this->assertEquals('event', $response['type']);
|
||||
$this->assertNotEmpty($response['data']);
|
||||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(1, $response['data']['channels']);
|
||||
$this->assertContains('console', $response['data']['channels']);
|
||||
$this->assertEquals('database.indexes.update', $response['data']['event']);
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
$this->assertEquals('available', $response['data']['payload']['status']);
|
||||
|
||||
$client->close();
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testIndexes
|
||||
*/
|
||||
public function testDeleteIndex(array $data)
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$session = $user['session'] ?? '';
|
||||
$projectId = 'console';
|
||||
|
||||
$client = $this->getWebsocket(['console'], [
|
||||
'origin' => 'http://localhost',
|
||||
'cookie' => 'a_session_console='. $this->getRoot()['session'],
|
||||
], $projectId);
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
|
||||
$this->assertArrayHasKey('type', $response);
|
||||
$this->assertArrayHasKey('data', $response);
|
||||
$this->assertEquals('connected', $response['type']);
|
||||
$this->assertNotEmpty($response['data']);
|
||||
$this->assertCount(1, $response['data']['channels']);
|
||||
$this->assertContains('console', $response['data']['channels']);
|
||||
$this->assertNotEmpty($response['data']['user']);
|
||||
|
||||
/**
|
||||
* Test Delete Index
|
||||
*/
|
||||
$attribute = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['actorsId'] . '/indexes/key_name', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals($attribute['headers']['status-code'], 204);
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
|
||||
$this->assertArrayHasKey('type', $response);
|
||||
$this->assertArrayHasKey('data', $response);
|
||||
$this->assertEquals('event', $response['type']);
|
||||
$this->assertNotEmpty($response['data']);
|
||||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(1, $response['data']['channels']);
|
||||
$this->assertContains('console', $response['data']['channels']);
|
||||
$this->assertEquals('database.indexes.delete', $response['data']['event']);
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
|
||||
$client->close();
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testDeleteIndex
|
||||
*/
|
||||
public function testDeleteAttribute(array $data)
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$session = $user['session'] ?? '';
|
||||
$projectId = 'console';
|
||||
|
||||
$client = $this->getWebsocket(['console'], [
|
||||
'origin' => 'http://localhost',
|
||||
'cookie' => 'a_session_console='. $this->getRoot()['session'],
|
||||
], $projectId);
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
|
||||
$this->assertArrayHasKey('type', $response);
|
||||
$this->assertArrayHasKey('data', $response);
|
||||
$this->assertEquals('connected', $response['type']);
|
||||
$this->assertNotEmpty($response['data']);
|
||||
$this->assertCount(1, $response['data']['channels']);
|
||||
$this->assertContains('console', $response['data']['channels']);
|
||||
$this->assertNotEmpty($response['data']['user']);
|
||||
|
||||
/**
|
||||
* Test Delete Attribute
|
||||
*/
|
||||
$attribute = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['actorsId'] . '/attributes/name', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals($attribute['headers']['status-code'], 204);
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
|
||||
$this->assertArrayHasKey('type', $response);
|
||||
$this->assertArrayHasKey('data', $response);
|
||||
$this->assertEquals('event', $response['type']);
|
||||
$this->assertNotEmpty($response['data']);
|
||||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(1, $response['data']['channels']);
|
||||
$this->assertContains('console', $response['data']['channels']);
|
||||
$this->assertEquals('database.attributes.delete', $response['data']['event']);
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
|
||||
$client->close();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -406,8 +406,8 @@ trait StorageBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'read' => ['role:all', 'user:x'],
|
||||
'write' => ['role:all', 'user:x'],
|
||||
'read' => ['user:'.$this->getUser()['$id']],
|
||||
'write' => ['user:'.$this->getUser()['$id']],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $file['headers']['status-code']);
|
||||
|
@ -424,8 +424,8 @@ trait StorageBase
|
|||
//$this->assertNotEmpty($file['body']['fileOpenSSLIV']);
|
||||
$this->assertIsArray($file['body']['$read']);
|
||||
$this->assertIsArray($file['body']['$write']);
|
||||
$this->assertCount(2, $file['body']['$read']);
|
||||
$this->assertCount(2, $file['body']['$write']);
|
||||
$this->assertCount(1, $file['body']['$read']);
|
||||
$this->assertCount(1, $file['body']['$write']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE unknown Bucket
|
||||
|
@ -435,8 +435,8 @@ trait StorageBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'read' => ['role:all', 'user:x'],
|
||||
'write' => ['role:all', 'user:x'],
|
||||
'read' => ['user:'.$this->getUser()['$id']],
|
||||
'write' => ['user:'.$this->getUser()['$id']],
|
||||
]);
|
||||
|
||||
$this->assertEquals(404, $file['headers']['status-code']);
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
namespace Tests\E2E\Services\Storage;
|
||||
|
||||
use CURLFile;
|
||||
use Exception;
|
||||
use SebastianBergmann\RecursionContext\InvalidArgumentException;
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
|
@ -14,7 +17,7 @@ class StorageCustomClientTest extends Scope
|
|||
use ProjectCustom;
|
||||
use SideClient;
|
||||
|
||||
public function testCreateFileDefaultPermissions():void
|
||||
public function testCreateFileDefaultPermissions(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
@ -49,5 +52,114 @@ class StorageCustomClientTest extends Scope
|
|||
$this->assertEquals('permissions.png', $file['body']['name']);
|
||||
$this->assertEquals('image/png', $file['body']['mimeType']);
|
||||
$this->assertEquals(47218, $file['body']['sizeOriginal']);
|
||||
|
||||
return ['fileId' => $file['body']['$id'], 'bucketId' => $bucket['body']['$id']];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateFileDefaultPermissions
|
||||
*/
|
||||
public function testCreateFileAbusePermissions(array $data): void
|
||||
{
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'unique()',
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'permissions.png'),
|
||||
'folderId' => 'xyz',
|
||||
'read' => ['user:notme']
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $file['headers']['status-code']);
|
||||
$this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:all', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:member', $file['body']['message']);
|
||||
$this->assertStringContainsString('user:'.$this->getUser()['$id'], $file['body']['message']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'unique()',
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'permissions.png'),
|
||||
'folderId' => 'xyz',
|
||||
'write' => ['user:notme']
|
||||
]);
|
||||
|
||||
$this->assertEquals($file['headers']['status-code'], 400);
|
||||
$this->assertStringStartsWith('Write permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:all', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:member', $file['body']['message']);
|
||||
$this->assertStringContainsString('user:'.$this->getUser()['$id'], $file['body']['message']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'unique()',
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'permissions.png'),
|
||||
'folderId' => 'xyz',
|
||||
'read' => ['user:notme'],
|
||||
'write' => ['user:notme']
|
||||
]);
|
||||
|
||||
$this->assertEquals($file['headers']['status-code'], 400);
|
||||
$this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:all', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:member', $file['body']['message']);
|
||||
$this->assertStringContainsString('user:'.$this->getUser()['$id'], $file['body']['message']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateFileDefaultPermissions
|
||||
*/
|
||||
public function testUpdateFileAbusePermissions(array $data): void
|
||||
{
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$file = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $data['bucketId'] . '/files/' . $data['fileId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'read' => ['user:notme']
|
||||
]);
|
||||
|
||||
$this->assertEquals($file['headers']['status-code'], 400);
|
||||
$this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:all', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:member', $file['body']['message']);
|
||||
$this->assertStringContainsString('user:'.$this->getUser()['$id'], $file['body']['message']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $data['bucketId'] . '/files/' . $data['fileId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'write' => ['user:notme']
|
||||
]);
|
||||
|
||||
$this->assertEquals($file['headers']['status-code'], 400);
|
||||
$this->assertStringStartsWith('Write permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:all', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:member', $file['body']['message']);
|
||||
$this->assertStringContainsString('user:'.$this->getUser()['$id'], $file['body']['message']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $data['bucketId'] . '/files/' . $data['fileId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'read' => ['user:notme'],
|
||||
'write' => ['user:notme']
|
||||
]);
|
||||
|
||||
$this->assertEquals($file['headers']['status-code'], 400);
|
||||
$this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:all', $file['body']['message']);
|
||||
$this->assertStringContainsString('role:member', $file['body']['message']);
|
||||
$this->assertStringContainsString('user:'.$this->getUser()['$id'], $file['body']['message']);
|
||||
}
|
||||
}
|
|
@ -57,7 +57,7 @@ trait WebhooksBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'firstName',
|
||||
'key' => 'firstName',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -67,7 +67,7 @@ trait WebhooksBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'lastName',
|
||||
'key' => 'lastName',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
@ -77,7 +77,7 @@ trait WebhooksBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'extra',
|
||||
'key' => 'extra',
|
||||
'size' => 64,
|
||||
'required' => false,
|
||||
]);
|
||||
|
|
|
@ -64,7 +64,7 @@ class WebhooksCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'fullname',
|
||||
'key' => 'fullname',
|
||||
'type' => 'key',
|
||||
'attributes' => ['lastName', 'firstName'],
|
||||
'orders' => ['ASC', 'ASC'],
|
||||
|
|
|
@ -172,35 +172,35 @@ class AuthTest extends TestCase
|
|||
public function testIsPrivilegedUser()
|
||||
{
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser([]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_GUEST => true]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_MEMBER => true]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_ADMIN => true]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_DEVELOPER => true]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_OWNER => true]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_APP => true]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_SYSTEM => true]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_GUEST]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_MEMBER]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_ADMIN]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_DEVELOPER]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_OWNER]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_APP]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_SYSTEM]));
|
||||
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_APP => true, 'role:'.Auth::USER_ROLE_APP => true]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_APP => true, 'role:'.Auth::USER_ROLE_GUEST => true]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_OWNER => true, 'role:'.Auth::USER_ROLE_GUEST => true]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_OWNER => true, 'role:'.Auth::USER_ROLE_ADMIN => true, 'role:'.Auth::USER_ROLE_DEVELOPER => true]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_APP, 'role:'.Auth::USER_ROLE_APP]));
|
||||
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_APP, 'role:'.Auth::USER_ROLE_GUEST]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_OWNER, 'role:'.Auth::USER_ROLE_GUEST]));
|
||||
$this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_OWNER, 'role:'.Auth::USER_ROLE_ADMIN, 'role:'.Auth::USER_ROLE_DEVELOPER]));
|
||||
}
|
||||
|
||||
public function testIsAppUser()
|
||||
{
|
||||
$this->assertEquals(false, Auth::isAppUser([]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_GUEST => true]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_MEMBER => true]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_ADMIN => true]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_DEVELOPER => true]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_OWNER => true]));
|
||||
$this->assertEquals(true, Auth::isAppUser(['role:'.Auth::USER_ROLE_APP => true]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_SYSTEM => true]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_GUEST]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_MEMBER]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_ADMIN]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_DEVELOPER]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_OWNER]));
|
||||
$this->assertEquals(true, Auth::isAppUser(['role:'.Auth::USER_ROLE_APP]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_SYSTEM]));
|
||||
|
||||
$this->assertEquals(true, Auth::isAppUser(['role:'.Auth::USER_ROLE_APP => true, 'role:'.Auth::USER_ROLE_APP => true]));
|
||||
$this->assertEquals(true, Auth::isAppUser(['role:'.Auth::USER_ROLE_APP => true, 'role:'.Auth::USER_ROLE_GUEST => true]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_OWNER => true, 'role:'.Auth::USER_ROLE_GUEST => true]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_OWNER => true, 'role:'.Auth::USER_ROLE_ADMIN => true, 'role:'.Auth::USER_ROLE_DEVELOPER => true]));
|
||||
$this->assertEquals(true, Auth::isAppUser(['role:'.Auth::USER_ROLE_APP, 'role:'.Auth::USER_ROLE_APP]));
|
||||
$this->assertEquals(true, Auth::isAppUser(['role:'.Auth::USER_ROLE_APP, 'role:'.Auth::USER_ROLE_GUEST]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_OWNER, 'role:'.Auth::USER_ROLE_GUEST]));
|
||||
$this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_OWNER, 'role:'.Auth::USER_ROLE_ADMIN, 'role:'.Auth::USER_ROLE_DEVELOPER]));
|
||||
}
|
||||
|
||||
public function testGuestRoles()
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Appwrite\Tests;
|
||||
|
||||
use Appwrite\Database\Document;
|
||||
use Utopia\Database\Document;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
|
@ -195,4 +195,51 @@ class MessagingTest extends TestCase
|
|||
$this->assertArrayHasKey('account', $channels);
|
||||
$this->assertArrayNotHasKey('account.456', $channels);
|
||||
}
|
||||
|
||||
public function testFromPayloadCollectionLevelPermissions(): void
|
||||
{
|
||||
/**
|
||||
* Test Collection Level Permissions
|
||||
*/
|
||||
$result = Realtime::fromPayload(
|
||||
event: 'database.documents.create',
|
||||
payload: new Document([
|
||||
'$id' => 'test',
|
||||
'$collection' => 'collection',
|
||||
'$read' => ['role:admin'],
|
||||
'$write' => ['role:admin']
|
||||
]),
|
||||
collection: new Document([
|
||||
'$id' => 'collection',
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'permission' => 'collection'
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertContains('role:all', $result['roles']);
|
||||
$this->assertNotContains('role:admin', $result['roles']);
|
||||
|
||||
/**
|
||||
* Test Document Level Permissions
|
||||
*/
|
||||
$result = Realtime::fromPayload(
|
||||
event: 'database.documents.create',
|
||||
payload: new Document([
|
||||
'$id' => 'test',
|
||||
'$collection' => 'collection',
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all']
|
||||
]),
|
||||
collection: new Document([
|
||||
'$id' => 'collection',
|
||||
'$read' => ['role:admin'],
|
||||
'$write' => ['role:admin'],
|
||||
'permission' => 'document'
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertContains('role:all', $result['roles']);
|
||||
$this->assertNotContains('role:admin', $result['roles']);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue