@@ -170,3 +173,7 @@ Join our growing community around the world! See our official [Blog](https://med
## License
This repository is available under the [BSD 3-Clause License](./LICENSE).
+
+## Translations
+- [English](README.md)
+- [简体中文](README-CN.md)
diff --git a/app/config/auth.php b/app/config/auth.php
index 086ba9a7b2..aab6833b65 100644
--- a/app/config/auth.php
+++ b/app/config/auth.php
@@ -7,35 +7,35 @@ return [
'name' => 'Email/Password',
'key' => 'emailPassword',
'icon' => '/images/users/email.png',
- 'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreateSession',
+ 'docs' => 'https://appwrite.io/docs/client/account?sdk=web-default#accountCreateSession',
'enabled' => true,
],
'magic-url' => [
'name' => 'Magic URL',
'key' => 'usersAuthMagicURL',
'icon' => '/images/users/magic-url.png',
- 'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreateMagicURLSession',
+ 'docs' => 'https://appwrite.io/docs/client/account?sdk=web-default#accountCreateMagicURLSession',
'enabled' => true,
],
'anonymous' => [
'name' => 'Anonymous',
'key' => 'anonymous',
'icon' => '/images/users/anonymous.png',
- 'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreateAnonymousSession',
+ 'docs' => 'https://appwrite.io/docs/client/account?sdk=web-default#accountCreateAnonymousSession',
'enabled' => true,
],
'invites' => [
'name' => 'Invites',
'key' => 'invites',
'icon' => '/images/users/invites.png',
- 'docs' => 'https://appwrite.io/docs/client/teams?sdk=web#teamsCreateMembership',
+ 'docs' => 'https://appwrite.io/docs/client/teams?sdk=web-default#teamsCreateMembership',
'enabled' => true,
],
'jwt' => [
'name' => 'JWT',
'key' => 'JWT',
'icon' => '/images/users/jwt.png',
- 'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreateJWT',
+ 'docs' => 'https://appwrite.io/docs/client/account?sdk=web-default#accountCreateJWT',
'enabled' => true,
],
'phone' => [
diff --git a/app/config/providers.php b/app/config/providers.php
index bb823c9ab0..3aabbf60c2 100644
--- a/app/config/providers.php
+++ b/app/config/providers.php
@@ -131,6 +131,16 @@ return [ // Ordered by ABC.
'beta' => false,
'mock' => false,
],
+ 'notion' => [
+ 'name' => 'Notion',
+ 'developers' => 'https://developers.notion.com/docs',
+ 'icon' => 'icon-notion',
+ 'enabled' => true,
+ 'sandbox' => false,
+ 'form' => false,
+ 'beta' => false,
+ 'mock' => false,
+ ],
'paypal' => [
'name' => 'PayPal',
'developers' => 'https://developer.paypal.com/docs/api/overview/',
diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php
index 2ff087b725..19c8cf3b27 100644
--- a/app/controllers/api/account.php
+++ b/app/controllers/api/account.php
@@ -44,7 +44,7 @@ App::post('/v1/account')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->label('abuse-limit', 10)
- ->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(), '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('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)
@@ -615,7 +615,7 @@ App::post('/v1/account/sessions/magic-url')
->label('sdk.response.model', Response::MODEL_TOKEN)
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},email:{param-email}')
- ->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(), '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('email', '', new Email(), 'User email.')
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the magic URL login. 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.', true, ['clients'])
->inject('request')
diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php
index c6fb149700..9d5ac716b3 100644
--- a/app/controllers/api/database.php
+++ b/app/controllers/api/database.php
@@ -148,7 +148,7 @@ App::post('/v1/database/collections')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_COLLECTION)
- ->param('collectionId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
+ ->param('collectionId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('name', '', new Text(128), 'Collection name. Max length: 128 chars.')
->param('permission', null, new WhiteList(['document', 'collection']), 'Permissions type model to use for reading documents in this collection. You can use collection-level permission set once on the collection using the `read` and `write` params, or you can set document-level permission where each document read and write params will decide who has access to read and write to each document individually. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->param('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.')
@@ -230,7 +230,7 @@ App::get('/v1/database/collections')
$queries = [];
if (!empty($search)) {
- $queries[] = new Query('name', Query::TYPE_SEARCH, [$search]);
+ $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
}
$usage->setParam('database.collections.read', 1);
@@ -1563,7 +1563,7 @@ App::post('/v1/database/collections/:collectionId/documents')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DOCUMENT)
- ->param('documentId', '', new CustomId(), 'Document 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('documentId', '', new CustomId(), 'Document 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('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection). Make sure to define attributes before creating documents.')
->param('data', [], new JSON(), 'Document data as JSON object.')
->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)
@@ -1832,7 +1832,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
$usage
->setParam('database.documents.read', 1)
->setParam('collectionId', $collectionId)
- ;
+ ;
$response->dynamic($document, Response::MODEL_DOCUMENT);
});
diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php
index 42c840476a..3955a49e9d 100644
--- a/app/controllers/api/functions.php
+++ b/app/controllers/api/functions.php
@@ -39,14 +39,14 @@ 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(), '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('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](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 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, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Function maximum execution time in seconds.', true)
->inject('response')
->inject('dbForProject')
->action(function ($functionId, $name, $execute, $runtime, $vars, $events, $schedule, $timeout, $response, $dbForProject) {
@@ -294,7 +294,7 @@ App::put('/v1/functions/:functionId')
->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), 'Maximum execution time in seconds.', true)
+ ->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Maximum execution time in seconds.', true)
->inject('response')
->inject('dbForProject')
->inject('project')
@@ -777,7 +777,7 @@ App::post('/v1/functions/:functionId/executions')
throw new Exception('Tag not found. Deploy tag before trying to execute a function', 404);
}
- $validator = new Authorization($function, 'execute');
+ $validator = new Authorization('execute');
if (!$validator->isValid($function->getAttribute('execute'))) { // Check if user has write access to execute function
throw new Exception($validator->getDescription(), 401);
diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php
index 963e9d092e..108fe6967e 100644
--- a/app/controllers/api/projects.php
+++ b/app/controllers/api/projects.php
@@ -43,7 +43,7 @@ App::post('/v1/projects')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
- ->param('projectId', '', 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('projectId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('name', null, new Text(128), 'Project name. Max length: 128 chars.')
->param('teamId', '', new UID(), 'Team unique ID.')
->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true)
diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php
index 3b86af8492..844a60a42c 100644
--- a/app/controllers/api/storage.php
+++ b/app/controllers/api/storage.php
@@ -348,7 +348,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
->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 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('fileId', '', new CustomId(), 'File 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](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)
diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php
index ab5534edc6..d9809bf305 100644
--- a/app/controllers/api/teams.php
+++ b/app/controllers/api/teams.php
@@ -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(), '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('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')
diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php
index 61a9e24a41..ac030f7e7d 100644
--- a/app/controllers/api/users.php
+++ b/app/controllers/api/users.php
@@ -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(), '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('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)
@@ -725,6 +725,11 @@ App::delete('/v1/users/:userId')
throw new Exception('User not found', 404);
}
+ /**
+ * DO NOT DELETE THE USER RECORD ITSELF.
+ * WE RETAIN THE USER RECORD TO RESERVE THE USER ID AND ENSURE THAT THE USER ID IS NOT REUSED.
+ */
+
// clone user object to send to workers
$clone = clone $user;
@@ -733,6 +738,8 @@ App::delete('/v1/users/:userId')
->setAttribute("email", null)
->setAttribute("password", null)
->setAttribute("deleted", true)
+ ->setAttribute("tokens", [])
+ ->setAttribute("search", null)
;
$dbForProject->updateDocument('users', $userId, $user);
diff --git a/app/controllers/general.php b/app/controllers/general.php
index c58de0863a..8dec8ff804 100644
--- a/app/controllers/general.php
+++ b/app/controllers/general.php
@@ -167,6 +167,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
break;
case version_compare ($responseFormat , '0.8.0', '<=') :
Response::setFilter(new V08());
+ break;
case version_compare ($responseFormat , '0.11.0', '<=') :
Response::setFilter(new V11());
break;
diff --git a/app/init.php b/app/init.php
index ef2d031125..daf49853cb 100644
--- a/app/init.php
+++ b/app/init.php
@@ -94,6 +94,9 @@ const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244';
const APP_SOCIAL_DEV = 'https://dev.to/appwrite';
const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1';
+// Database Reconnect
+const DATABASE_RECONNECT_SLEEP = 2;
+const DATABASE_RECONNECT_MAX_ATTEMPTS = 10;
// Database Worker Types
const DATABASE_TYPE_CREATE_ATTRIBUTE = 'createAttribute';
const DATABASE_TYPE_CREATE_INDEX = 'createIndex';
diff --git a/app/realtime.php b/app/realtime.php
index 9acb37570d..b2ddf67634 100644
--- a/app/realtime.php
+++ b/app/realtime.php
@@ -91,13 +91,32 @@ $server->error($logError);
function getDatabase(Registry &$register, string $namespace)
{
- $db = $register->get('dbPool')->get();
- $redis = $register->get('redisPool')->get();
+ $attempts = 0;
- $cache = new Cache(new RedisCache($redis));
- $database = new Database(new MariaDB($db), $cache);
- $database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
- $database->setNamespace($namespace);
+ do {
+ try {
+ $attempts++;
+
+ $db = $register->get('dbPool')->get();
+ $redis = $register->get('redisPool')->get();
+
+ $cache = new Cache(new RedisCache($redis));
+ $database = new Database(new MariaDB($db), $cache);
+ $database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
+ $database->setNamespace($namespace);
+
+ if (!$database->exists($database->getDefaultDatabase(), 'realtime')) {
+ throw new Exception('Collection not ready');
+ }
+ break; // leave loop if successful
+ } catch(\Exception $e) {
+ Console::warning("Database not ready. Retrying connection ({$attempts})...");
+ if ($attempts >= DATABASE_RECONNECT_MAX_ATTEMPTS) {
+ throw new \Exception('Failed to connect to database: '. $e->getMessage());
+ }
+ sleep(DATABASE_RECONNECT_SLEEP);
+ }
+ } while ($attempts < DATABASE_RECONNECT_MAX_ATTEMPTS);
return [
$database,
@@ -106,6 +125,7 @@ function getDatabase(Registry &$register, string $namespace)
$register->get('redisPool')->put($redis);
}
];
+
};
$server->onStart(function () use ($stats, $register, $containerId, &$statsDocument, $logError) {
diff --git a/app/views/console/database/collection.phtml b/app/views/console/database/collection.phtml
index 2091209e52..93db293ad1 100644
--- a/app/views/console/database/collection.phtml
+++ b/app/views/console/database/collection.phtml
@@ -620,6 +620,7 @@ $logs = $this->getParam('logs', null);
data-failure="alert"
data-failure-param-alert-text="Failed to create attribute"
data-failure-param-alert-classname="error"
+ @reset="array = required = false"
x-data="{ array: false, required: false }">
@@ -675,6 +676,7 @@ $logs = $this->getParam('logs', null);
data-failure="alert"
data-failure-param-alert-text="Failed to create attribute"
data-failure-param-alert-classname="error"
+ @reset="array = required = false"
x-data="{ array: false, required: false }">
@@ -706,10 +708,10 @@ $logs = $this->getParam('logs', null);
-
+
-
+