1
0
Fork 0
mirror of synced 2024-06-03 03:14:50 +12:00

Merge branch 'feat-database-indexing' of https://github.com/appwrite/appwrite into feat-database-indexing

This commit is contained in:
Torsten Dittmann 2021-12-28 13:16:15 +01:00
commit e20b99ef2f
28 changed files with 902 additions and 911 deletions

2
.env
View file

@ -18,7 +18,7 @@ _APP_REDIS_PORT=6379
_APP_DB_HOST=mariadb _APP_DB_HOST=mariadb
_APP_DB_PORT=3306 _APP_DB_PORT=3306
_APP_DB_SCHEMA=appwrite _APP_DB_SCHEMA=appwrite
_APP_DB_USER=root _APP_DB_USER=user
_APP_DB_PASS=password _APP_DB_PASS=password
_APP_STORAGE_ANTIVIRUS=disabled _APP_STORAGE_ANTIVIRUS=disabled
_APP_STORAGE_ANTIVIRUS_HOST=clamav _APP_STORAGE_ANTIVIRUS_HOST=clamav

View file

@ -729,7 +729,7 @@ $collections = [
'format' => '', 'format' => '',
'size' => Database::LENGTH_KEY, 'size' => Database::LENGTH_KEY,
'signed' => true, 'signed' => true,
'required' => true, 'required' => false,
'default' => null, 'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
@ -740,7 +740,7 @@ $collections = [
'format' => '', 'format' => '',
'size' => Database::LENGTH_KEY, 'size' => Database::LENGTH_KEY,
'signed' => true, 'signed' => true,
'required' => true, 'required' => false,
'default' => null, 'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
@ -751,7 +751,7 @@ $collections = [
'format' => '', 'format' => '',
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => true, 'required' => false,
'default' => null, 'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
@ -1001,7 +1001,7 @@ $collections = [
'size' => 16384, 'size' => 16384,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => [], 'default' => new \stdClass(),
'array' => false, 'array' => false,
'filters' => ['json'], 'filters' => ['json'],
], ],

View file

@ -51,14 +51,14 @@ App::post('/v1/account')
->inject('request') ->inject('request')
->inject('response') ->inject('response')
->inject('project') ->inject('project')
->inject('dbForInternal') ->inject('dbForProject')
->inject('audits') ->inject('audits')
->inject('usage') ->inject('usage')
->action(function ($userId, $email, $password, $name, $request, $response, $project, $dbForInternal, $audits, $usage) { ->action(function ($userId, $email, $password, $name, $request, $response, $project, $dbForProject, $audits, $usage) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
@ -79,7 +79,7 @@ App::post('/v1/account')
$limit = $project->getAttribute('auths', [])['limit'] ?? 0; $limit = $project->getAttribute('auths', [])['limit'] ?? 0;
if ($limit !== 0) { if ($limit !== 0) {
$sum = $dbForInternal->count('users', [ $sum = $dbForProject->count('users', [
new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('deleted', Query::TYPE_EQUAL, [false]),
], APP_LIMIT_USERS); ], APP_LIMIT_USERS);
@ -89,8 +89,8 @@ App::post('/v1/account')
} }
try { try {
$userId = $userId == 'unique()' ? $dbForInternal->getId() : $userId; $userId = $userId == 'unique()' ? $dbForProject->getId() : $userId;
$user = Authorization::skip(fn() => $dbForInternal->createDocument('users', new Document([ $user = Authorization::skip(fn() => $dbForProject->createDocument('users', new Document([
'$id' => $userId, '$id' => $userId,
'$read' => ['role:all'], '$read' => ['role:all'],
'$write' => ['user:' . $userId], '$write' => ['user:' . $userId],
@ -102,7 +102,7 @@ App::post('/v1/account')
'registration' => \time(), 'registration' => \time(),
'reset' => false, 'reset' => false,
'name' => $name, 'name' => $name,
'prefs' => [], 'prefs' => new \stdClass(),
'sessions' => [], 'sessions' => [],
'tokens' => [], 'tokens' => [],
'memberships' => [], 'memberships' => [],
@ -149,15 +149,15 @@ App::post('/v1/account/sessions')
->param('password', '', new Password(), 'User password. Must be at least 8 chars.') ->param('password', '', new Password(), 'User password. Must be at least 8 chars.')
->inject('request') ->inject('request')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('locale') ->inject('locale')
->inject('geodb') ->inject('geodb')
->inject('audits') ->inject('audits')
->inject('usage') ->inject('usage')
->action(function ($email, $password, $request, $response, $dbForInternal, $locale, $geodb, $audits, $usage) { ->action(function ($email, $password, $request, $response, $dbForProject, $locale, $geodb, $audits, $usage) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Locale\Locale $locale */
/** @var MaxMind\Db\Reader $geodb */ /** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
@ -166,7 +166,7 @@ App::post('/v1/account/sessions')
$email = \strtolower($email); $email = \strtolower($email);
$protocol = $request->getProtocol(); $protocol = $request->getProtocol();
$profile = $dbForInternal->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address $profile = $dbForProject->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
if (!$profile || !Auth::passwordVerify($password, $profile->getAttribute('password'))) { if (!$profile || !Auth::passwordVerify($password, $profile->getAttribute('password'))) {
$audits $audits
@ -188,7 +188,7 @@ App::post('/v1/account/sessions')
$secret = Auth::tokenGenerator(); $secret = Auth::tokenGenerator();
$session = new Document(array_merge( $session = new Document(array_merge(
[ [
'$id' => $dbForInternal->getId(), '$id' => $dbForProject->getId(),
'userId' => $profile->getId(), 'userId' => $profile->getId(),
'provider' => Auth::SESSION_PROVIDER_EMAIL, 'provider' => Auth::SESSION_PROVIDER_EMAIL,
'providerUid' => $email, 'providerUid' => $email,
@ -202,13 +202,13 @@ App::post('/v1/account/sessions')
Authorization::setRole('user:' . $profile->getId()); Authorization::setRole('user:' . $profile->getId());
$session = $dbForInternal->createDocument('sessions', $session $session = $dbForProject->createDocument('sessions', $session
->setAttribute('$read', ['user:' . $profile->getId()]) ->setAttribute('$read', ['user:' . $profile->getId()])
->setAttribute('$write', ['user:' . $profile->getId()]) ->setAttribute('$write', ['user:' . $profile->getId()])
); );
$profile->setAttribute('sessions', $session, Document::SET_TYPE_APPEND); $profile->setAttribute('sessions', $session, Document::SET_TYPE_APPEND);
$profile = $dbForInternal->updateDocument('users', $profile->getId(), $profile); $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile);
$audits $audits
->setParam('userId', $profile->getId()) ->setParam('userId', $profile->getId())
@ -376,17 +376,17 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
->inject('response') ->inject('response')
->inject('project') ->inject('project')
->inject('user') ->inject('user')
->inject('dbForInternal') ->inject('dbForProject')
->inject('geodb') ->inject('geodb')
->inject('audits') ->inject('audits')
->inject('events') ->inject('events')
->inject('usage') ->inject('usage')
->action(function ($provider, $code, $state, $request, $response, $project, $user, $dbForInternal, $geodb, $audits, $events, $usage) use ($oauthDefaultSuccess) { ->action(function ($provider, $code, $state, $request, $response, $project, $user, $dbForProject, $geodb, $audits, $events, $usage) use ($oauthDefaultSuccess) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var MaxMind\Db\Reader $geodb */ /** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
@ -458,13 +458,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
if ($current === $session['$id']) { if ($current === $session['$id']) {
unset($sessions[$key]); unset($sessions[$key]);
$dbForInternal->deleteDocument('sessions', $session->getId()); $dbForProject->deleteDocument('sessions', $session->getId());
$dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('sessions', $sessions)); $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', $sessions));
} }
} }
} }
$user = ($user->isEmpty()) ? $dbForInternal->findOne('sessions', [ // Get user by provider id $user = ($user->isEmpty()) ? $dbForProject->findOne('sessions', [ // Get user by provider id
new Query('provider', QUERY::TYPE_EQUAL, [$provider]), new Query('provider', QUERY::TYPE_EQUAL, [$provider]),
new Query('providerUid', QUERY::TYPE_EQUAL, [$oauth2ID]), new Query('providerUid', QUERY::TYPE_EQUAL, [$oauth2ID]),
]) : $user; ]) : $user;
@ -473,13 +473,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$name = $oauth2->getUserName($accessToken); $name = $oauth2->getUserName($accessToken);
$email = $oauth2->getUserEmail($accessToken); $email = $oauth2->getUserEmail($accessToken);
$user = $dbForInternal->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address $user = $dbForProject->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
if ($user === false || $user->isEmpty()) { // Last option -> create the user, generate random password if ($user === false || $user->isEmpty()) { // Last option -> create the user, generate random password
$limit = $project->getAttribute('auths', [])['limit'] ?? 0; $limit = $project->getAttribute('auths', [])['limit'] ?? 0;
if ($limit !== 0) { if ($limit !== 0) {
$sum = $dbForInternal->count('users', [ new Query('deleted', Query::TYPE_EQUAL, [false]),], APP_LIMIT_COUNT); $sum = $dbForProject->count('users', [ new Query('deleted', Query::TYPE_EQUAL, [false]),], APP_LIMIT_COUNT);
if ($sum >= $limit) { if ($sum >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501); throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
@ -487,8 +487,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
} }
try { try {
$userId = $dbForInternal->getId(); $userId = $dbForProject->getId();
$user = Authorization::skip(fn() => $dbForInternal->createDocument('users', new Document([ $user = Authorization::skip(fn() => $dbForProject->createDocument('users', new Document([
'$id' => $userId, '$id' => $userId,
'$read' => ['role:all'], '$read' => ['role:all'],
'$write' => ['user:' . $userId], '$write' => ['user:' . $userId],
@ -500,7 +500,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'registration' => \time(), 'registration' => \time(),
'reset' => false, 'reset' => false,
'name' => $name, 'name' => $name,
'prefs' => [], 'prefs' => new \stdClass(),
'sessions' => [], 'sessions' => [],
'tokens' => [], 'tokens' => [],
'memberships' => [], 'memberships' => [],
@ -524,7 +524,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$secret = Auth::tokenGenerator(); $secret = Auth::tokenGenerator();
$expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG; $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$session = new Document(array_merge([ $session = new Document(array_merge([
'$id' => $dbForInternal->getId(), '$id' => $dbForProject->getId(),
'userId' => $user->getId(), 'userId' => $user->getId(),
'provider' => $provider, 'provider' => $provider,
'providerUid' => $oauth2ID, 'providerUid' => $oauth2ID,
@ -552,12 +552,12 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
Authorization::setRole('user:' . $user->getId()); Authorization::setRole('user:' . $user->getId());
$session = $dbForInternal->createDocument('sessions', $session $session = $dbForProject->createDocument('sessions', $session
->setAttribute('$read', ['user:' . $user->getId()]) ->setAttribute('$read', ['user:' . $user->getId()])
->setAttribute('$write', ['user:' . $user->getId()]) ->setAttribute('$write', ['user:' . $user->getId()])
); );
$user = $dbForInternal->updateDocument('users', $user->getId(), $user); $user = $dbForProject->updateDocument('users', $user->getId(), $user);
$audits $audits
->setParam('userId', $user->getId()) ->setParam('userId', $user->getId())
@ -621,16 +621,16 @@ App::post('/v1/account/sessions/magic-url')
->inject('request') ->inject('request')
->inject('response') ->inject('response')
->inject('project') ->inject('project')
->inject('dbForInternal') ->inject('dbForProject')
->inject('locale') ->inject('locale')
->inject('audits') ->inject('audits')
->inject('events') ->inject('events')
->inject('mails') ->inject('mails')
->action(function ($userId, $email, $url, $request, $response, $project, $dbForInternal, $locale, $audits, $events, $mails) { ->action(function ($userId, $email, $url, $request, $response, $project, $dbForProject, $locale, $audits, $events, $mails) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $events */ /** @var Appwrite\Event\Event $events */
@ -644,13 +644,13 @@ App::post('/v1/account/sessions/magic-url')
$isPrivilegedUser = Auth::isPrivilegedUser($roles); $isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles); $isAppUser = Auth::isAppUser($roles);
$user = $dbForInternal->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); $user = $dbForProject->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]);
if (!$user) { if (!$user) {
$limit = $project->getAttribute('auths', [])['limit'] ?? 0; $limit = $project->getAttribute('auths', [])['limit'] ?? 0;
if ($limit !== 0) { if ($limit !== 0) {
$sum = $dbForInternal->count('users', [ $sum = $dbForProject->count('users', [
new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('deleted', Query::TYPE_EQUAL, [false]),
], APP_LIMIT_COUNT); ], APP_LIMIT_COUNT);
@ -659,9 +659,9 @@ App::post('/v1/account/sessions/magic-url')
} }
} }
$userId = $userId == 'unique()' ? $dbForInternal->getId() : $userId; $userId = $userId == 'unique()' ? $dbForProject->getId() : $userId;
$user = Authorization::skip(fn () => $dbForInternal->createDocument('users', new Document([ $user = Authorization::skip(fn () => $dbForProject->createDocument('users', new Document([
'$id' => $userId, '$id' => $userId,
'$read' => ['role:all'], '$read' => ['role:all'],
'$write' => ['user:' . $userId], '$write' => ['user:' . $userId],
@ -672,7 +672,7 @@ App::post('/v1/account/sessions/magic-url')
'passwordUpdate' => \time(), 'passwordUpdate' => \time(),
'registration' => \time(), 'registration' => \time(),
'reset' => false, 'reset' => false,
'prefs' => [], 'prefs' => new \stdClass(),
'sessions' => [], 'sessions' => [],
'tokens' => [], 'tokens' => [],
'memberships' => [], 'memberships' => [],
@ -689,7 +689,7 @@ App::post('/v1/account/sessions/magic-url')
$expire = \time() + Auth::TOKEN_EXPIRATION_CONFIRM; $expire = \time() + Auth::TOKEN_EXPIRATION_CONFIRM;
$token = new Document([ $token = new Document([
'$id' => $dbForInternal->getId(), '$id' => $dbForProject->getId(),
'userId' => $user->getId(), 'userId' => $user->getId(),
'type' => Auth::TOKEN_TYPE_MAGIC_URL, 'type' => Auth::TOKEN_TYPE_MAGIC_URL,
'secret' => Auth::hash($loginSecret), // One way hash encryption to protect DB leak 'secret' => Auth::hash($loginSecret), // One way hash encryption to protect DB leak
@ -702,7 +702,7 @@ App::post('/v1/account/sessions/magic-url')
$user->setAttribute('tokens', $token, Document::SET_TYPE_APPEND); $user->setAttribute('tokens', $token, Document::SET_TYPE_APPEND);
$user = $dbForInternal->updateDocument('users', $user->getId(), $user); $user = $dbForProject->updateDocument('users', $user->getId(), $user);
if (false === $user) { if (false === $user) {
throw new Exception('Failed to save user to DB', 500); throw new Exception('Failed to save user to DB', 500);
@ -766,21 +766,21 @@ App::put('/v1/account/sessions/magic-url')
->param('secret', '', new Text(256), 'Valid verification token.') ->param('secret', '', new Text(256), 'Valid verification token.')
->inject('request') ->inject('request')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('locale') ->inject('locale')
->inject('geodb') ->inject('geodb')
->inject('audits') ->inject('audits')
->action(function ($userId, $secret, $request, $response, $dbForInternal, $locale, $geodb, $audits) { ->action(function ($userId, $secret, $request, $response, $dbForProject, $locale, $geodb, $audits) {
/** @var string $userId */ /** @var string $userId */
/** @var string $secret */ /** @var string $secret */
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Locale\Locale $locale */
/** @var MaxMind\Db\Reader $geodb */ /** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
$user = $dbForInternal->getDocument('users', $userId); $user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) { if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
@ -798,7 +798,7 @@ App::put('/v1/account/sessions/magic-url')
$expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG; $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$session = new Document(array_merge( $session = new Document(array_merge(
[ [
'$id' => $dbForInternal->getId(), '$id' => $dbForProject->getId(),
'userId' => $user->getId(), 'userId' => $user->getId(),
'provider' => Auth::SESSION_PROVIDER_MAGIC_URL, 'provider' => Auth::SESSION_PROVIDER_MAGIC_URL,
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
@ -814,7 +814,7 @@ App::put('/v1/account/sessions/magic-url')
Authorization::setRole('user:' . $user->getId()); Authorization::setRole('user:' . $user->getId());
$session = $dbForInternal->createDocument('sessions', $session $session = $dbForProject->createDocument('sessions', $session
->setAttribute('$read', ['user:' . $user->getId()]) ->setAttribute('$read', ['user:' . $user->getId()])
->setAttribute('$write', ['user:' . $user->getId()]) ->setAttribute('$write', ['user:' . $user->getId()])
); );
@ -836,7 +836,7 @@ App::put('/v1/account/sessions/magic-url')
->setAttribute('tokens', $tokens); ->setAttribute('tokens', $tokens);
$user = $dbForInternal->updateDocument('users', $user->getId(), $user); $user = $dbForProject->updateDocument('users', $user->getId(), $user);
if (false === $user) { if (false === $user) {
throw new Exception('Failed saving user to DB', 500); throw new Exception('Failed saving user to DB', 500);
@ -894,17 +894,17 @@ App::post('/v1/account/sessions/anonymous')
->inject('locale') ->inject('locale')
->inject('user') ->inject('user')
->inject('project') ->inject('project')
->inject('dbForInternal') ->inject('dbForProject')
->inject('geodb') ->inject('geodb')
->inject('audits') ->inject('audits')
->inject('usage') ->inject('usage')
->action(function ($request, $response, $locale, $user, $project, $dbForInternal, $geodb, $audits, $usage) { ->action(function ($request, $response, $locale, $user, $project, $dbForProject, $geodb, $audits, $usage) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Locale\Locale $locale */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var MaxMind\Db\Reader $geodb */ /** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
@ -922,7 +922,7 @@ App::post('/v1/account/sessions/anonymous')
$limit = $project->getAttribute('auths', [])['limit'] ?? 0; $limit = $project->getAttribute('auths', [])['limit'] ?? 0;
if ($limit !== 0) { if ($limit !== 0) {
$sum = $dbForInternal->count('users', [ $sum = $dbForProject->count('users', [
new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('deleted', Query::TYPE_EQUAL, [false]),
], APP_LIMIT_COUNT); ], APP_LIMIT_COUNT);
@ -931,8 +931,8 @@ App::post('/v1/account/sessions/anonymous')
} }
} }
$userId = $dbForInternal->getId(); $userId = $dbForProject->getId();
$user = Authorization::skip(fn() => $dbForInternal->createDocument('users', new Document([ $user = Authorization::skip(fn() => $dbForProject->createDocument('users', new Document([
'$id' => $userId, '$id' => $userId,
'$read' => ['role:all'], '$read' => ['role:all'],
'$write' => ['user:' . $userId], '$write' => ['user:' . $userId],
@ -944,7 +944,7 @@ App::post('/v1/account/sessions/anonymous')
'registration' => \time(), 'registration' => \time(),
'reset' => false, 'reset' => false,
'name' => null, 'name' => null,
'prefs' => [], 'prefs' => new \stdClass(),
'sessions' => [], 'sessions' => [],
'tokens' => [], 'tokens' => [],
'memberships' => [], 'memberships' => [],
@ -960,7 +960,7 @@ App::post('/v1/account/sessions/anonymous')
$expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG; $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$session = new Document(array_merge( $session = new Document(array_merge(
[ [
'$id' => $dbForInternal->getId(), '$id' => $dbForProject->getId(),
'userId' => $user->getId(), 'userId' => $user->getId(),
'provider' => Auth::SESSION_PROVIDER_ANONYMOUS, 'provider' => Auth::SESSION_PROVIDER_ANONYMOUS,
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
@ -976,12 +976,12 @@ App::post('/v1/account/sessions/anonymous')
Authorization::setRole('user:' . $user->getId()); Authorization::setRole('user:' . $user->getId());
$session = $dbForInternal->createDocument('sessions', $session $session = $dbForProject->createDocument('sessions', $session
->setAttribute('$read', ['user:' . $user->getId()]) ->setAttribute('$read', ['user:' . $user->getId()])
->setAttribute('$write', ['user:' . $user->getId()]) ->setAttribute('$write', ['user:' . $user->getId()])
); );
$user = $dbForInternal->updateDocument('users', $user->getId(), $user = $dbForProject->updateDocument('users', $user->getId(),
$user->setAttribute('sessions', $session, Document::SET_TYPE_APPEND)); $user->setAttribute('sessions', $session, Document::SET_TYPE_APPEND));
$audits $audits
@ -1178,18 +1178,18 @@ App::get('/v1/account/logs')
->inject('user') ->inject('user')
->inject('locale') ->inject('locale')
->inject('geodb') ->inject('geodb')
->inject('dbForInternal') ->inject('dbForProject')
->inject('usage') ->inject('usage')
->action(function ($limit, $offset, $response, $user, $locale, $geodb, $dbForInternal, $usage) { ->action(function ($limit, $offset, $response, $user, $locale, $geodb, $dbForProject, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Locale\Locale $locale */
/** @var MaxMind\Db\Reader $geodb */ /** @var MaxMind\Db\Reader $geodb */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$audit = new Audit($dbForInternal); $audit = new Audit($dbForProject);
$auditEvents = [ $auditEvents = [
'account.create', 'account.create',
'account.delete', 'account.delete',
@ -1262,13 +1262,13 @@ App::get('/v1/account/sessions/:sessionId')
->inject('response') ->inject('response')
->inject('user') ->inject('user')
->inject('locale') ->inject('locale')
->inject('dbForInternal') ->inject('dbForProject')
->inject('usage') ->inject('usage')
->action(function ($sessionId, $response, $user, $locale, $dbForInternal, $usage) { ->action(function ($sessionId, $response, $user, $locale, $dbForProject, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Locale\Locale $locale */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$sessions = $user->getAttribute('sessions', []); $sessions = $user->getAttribute('sessions', []);
@ -1314,17 +1314,17 @@ App::patch('/v1/account/name')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.')
->inject('response') ->inject('response')
->inject('user') ->inject('user')
->inject('dbForInternal') ->inject('dbForProject')
->inject('audits') ->inject('audits')
->inject('usage') ->inject('usage')
->action(function ($name, $response, $user, $dbForInternal, $audits, $usage) { ->action(function ($name, $response, $user, $dbForProject, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$user = $dbForInternal->updateDocument('users', $user->getId(), $user $user = $dbForProject->updateDocument('users', $user->getId(), $user
->setAttribute('name', $name) ->setAttribute('name', $name)
->setAttribute('search', implode(' ', [$user->getId(), $name, $user->getAttribute('email')])) ->setAttribute('search', implode(' ', [$user->getId(), $name, $user->getAttribute('email')]))
); );
@ -1358,13 +1358,13 @@ App::patch('/v1/account/password')
->param('oldPassword', '', new Password(), 'Current user password. Must be at least 8 chars.', true) ->param('oldPassword', '', new Password(), 'Current user password. Must be at least 8 chars.', true)
->inject('response') ->inject('response')
->inject('user') ->inject('user')
->inject('dbForInternal') ->inject('dbForProject')
->inject('audits') ->inject('audits')
->inject('usage') ->inject('usage')
->action(function ($password, $oldPassword, $response, $user, $dbForInternal, $audits, $usage) { ->action(function ($password, $oldPassword, $response, $user, $dbForProject, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
@ -1373,7 +1373,7 @@ App::patch('/v1/account/password')
throw new Exception('Invalid credentials', 401); throw new Exception('Invalid credentials', 401);
} }
$user = $dbForInternal->updateDocument('users', $user->getId(), $user $user = $dbForProject->updateDocument('users', $user->getId(), $user
->setAttribute('password', Auth::passwordHash($password)) ->setAttribute('password', Auth::passwordHash($password))
->setAttribute('passwordUpdate', \time()) ->setAttribute('passwordUpdate', \time())
); );
@ -1406,13 +1406,13 @@ App::patch('/v1/account/email')
->param('password', '', new Password(), 'User password. Must be at least 8 chars.') ->param('password', '', new Password(), 'User password. Must be at least 8 chars.')
->inject('response') ->inject('response')
->inject('user') ->inject('user')
->inject('dbForInternal') ->inject('dbForProject')
->inject('audits') ->inject('audits')
->inject('usage') ->inject('usage')
->action(function ($email, $password, $response, $user, $dbForInternal, $audits, $usage) { ->action(function ($email, $password, $response, $user, $dbForProject, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
@ -1426,14 +1426,14 @@ App::patch('/v1/account/email')
} }
$email = \strtolower($email); $email = \strtolower($email);
$profile = $dbForInternal->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address $profile = $dbForProject->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
if ($profile) { if ($profile) {
throw new Exception('User already registered', 409); throw new Exception('User already registered', 409);
} }
try { try {
$user = $dbForInternal->updateDocument('users', $user->getId(), $user $user = $dbForProject->updateDocument('users', $user->getId(), $user
->setAttribute('password', $isAnonymousUser ? Auth::passwordHash($password) : $user->getAttribute('password', '')) ->setAttribute('password', $isAnonymousUser ? Auth::passwordHash($password) : $user->getAttribute('password', ''))
->setAttribute('email', $email) ->setAttribute('email', $email)
->setAttribute('emailVerification', false) // After this user needs to confirm mail again ->setAttribute('emailVerification', false) // After this user needs to confirm mail again
@ -1470,17 +1470,17 @@ App::patch('/v1/account/prefs')
->param('prefs', [], new Assoc(), 'Prefs key-value JSON object.') ->param('prefs', [], new Assoc(), 'Prefs key-value JSON object.')
->inject('response') ->inject('response')
->inject('user') ->inject('user')
->inject('dbForInternal') ->inject('dbForProject')
->inject('audits') ->inject('audits')
->inject('usage') ->inject('usage')
->action(function ($prefs, $response, $user, $dbForInternal, $audits, $usage) { ->action(function ($prefs, $response, $user, $dbForProject, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs)); $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs));
$audits $audits
->setParam('event', 'account.update.prefs') ->setParam('event', 'account.update.prefs')
@ -1507,21 +1507,21 @@ App::delete('/v1/account')
->inject('request') ->inject('request')
->inject('response') ->inject('response')
->inject('user') ->inject('user')
->inject('dbForInternal') ->inject('dbForProject')
->inject('audits') ->inject('audits')
->inject('events') ->inject('events')
->inject('usage') ->inject('usage')
->action(function ($request, $response, $user, $dbForInternal, $audits, $events, $usage) { ->action(function ($request, $response, $user, $dbForProject, $audits, $events, $usage) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $events */ /** @var Appwrite\Event\Event $events */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$protocol = $request->getProtocol(); $protocol = $request->getProtocol();
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('status', false)); $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('status', false));
// TODO Seems to be related to users.php/App::delete('/v1/users/:userId'). Can we share code between these two? Do todos below apply to users.php? // TODO Seems to be related to users.php/App::delete('/v1/users/:userId'). Can we share code between these two? Do todos below apply to users.php?
@ -1576,16 +1576,16 @@ App::delete('/v1/account/sessions/:sessionId')
->inject('request') ->inject('request')
->inject('response') ->inject('response')
->inject('user') ->inject('user')
->inject('dbForInternal') ->inject('dbForProject')
->inject('locale') ->inject('locale')
->inject('audits') ->inject('audits')
->inject('events') ->inject('events')
->inject('usage') ->inject('usage')
->action(function ($sessionId, $request, $response, $user, $dbForInternal, $locale, $audits, $events, $usage) { ->action(function ($sessionId, $request, $response, $user, $dbForProject, $locale, $audits, $events, $usage) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $events */ /** @var Appwrite\Event\Event $events */
@ -1602,7 +1602,7 @@ App::delete('/v1/account/sessions/:sessionId')
if ($sessionId == $session->getId()) { if ($sessionId == $session->getId()) {
unset($sessions[$key]); unset($sessions[$key]);
$dbForInternal->deleteDocument('sessions', $session->getId()); $dbForProject->deleteDocument('sessions', $session->getId());
$audits $audits
->setParam('userId', $user->getId()) ->setParam('userId', $user->getId())
@ -1630,7 +1630,7 @@ App::delete('/v1/account/sessions/:sessionId')
; ;
} }
$dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('sessions', $sessions)); $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', $sessions));
$events $events
->setParam('eventData', $response->output($session, Response::MODEL_SESSION)) ->setParam('eventData', $response->output($session, Response::MODEL_SESSION))
@ -1662,16 +1662,16 @@ App::delete('/v1/account/sessions')
->inject('request') ->inject('request')
->inject('response') ->inject('response')
->inject('user') ->inject('user')
->inject('dbForInternal') ->inject('dbForProject')
->inject('locale') ->inject('locale')
->inject('audits') ->inject('audits')
->inject('events') ->inject('events')
->inject('usage') ->inject('usage')
->action(function ($request, $response, $user, $dbForInternal, $locale, $audits, $events, $usage) { ->action(function ($request, $response, $user, $dbForProject, $locale, $audits, $events, $usage) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $events */ /** @var Appwrite\Event\Event $events */
@ -1681,7 +1681,7 @@ App::delete('/v1/account/sessions')
$sessions = $user->getAttribute('sessions', []); $sessions = $user->getAttribute('sessions', []);
foreach ($sessions as $session) {/** @var Document $session */ foreach ($sessions as $session) {/** @var Document $session */
$dbForInternal->deleteDocument('sessions', $session->getId()); $dbForProject->deleteDocument('sessions', $session->getId());
$audits $audits
->setParam('userId', $user->getId()) ->setParam('userId', $user->getId())
@ -1709,7 +1709,7 @@ App::delete('/v1/account/sessions')
} }
} }
$dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('sessions', [])); $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', []));
$numOfSessions = count($sessions); $numOfSessions = count($sessions);
@ -1745,17 +1745,17 @@ App::post('/v1/account/recovery')
->param('url', '', function ($clients) {return new Host($clients);}, 'URL to redirect the user back to your app from the recovery 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']) ->param('url', '', function ($clients) {return new Host($clients);}, 'URL to redirect the user back to your app from the recovery 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'])
->inject('request') ->inject('request')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('project') ->inject('project')
->inject('locale') ->inject('locale')
->inject('mails') ->inject('mails')
->inject('audits') ->inject('audits')
->inject('events') ->inject('events')
->inject('usage') ->inject('usage')
->action(function ($email, $url, $request, $response, $dbForInternal, $project, $locale, $mails, $audits, $events, $usage) { ->action(function ($email, $url, $request, $response, $dbForProject, $project, $locale, $mails, $audits, $events, $usage) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $project */
/** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Event\Event $mails */ /** @var Appwrite\Event\Event $mails */
@ -1772,7 +1772,7 @@ App::post('/v1/account/recovery')
$isAppUser = Auth::isAppUser($roles); $isAppUser = Auth::isAppUser($roles);
$email = \strtolower($email); $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 $profile = $dbForProject->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
if (!$profile) { if (!$profile) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
@ -1786,7 +1786,7 @@ App::post('/v1/account/recovery')
$secret = Auth::tokenGenerator(); $secret = Auth::tokenGenerator();
$recovery = new Document([ $recovery = new Document([
'$id' => $dbForInternal->getId(), '$id' => $dbForProject->getId(),
'userId' => $profile->getId(), 'userId' => $profile->getId(),
'type' => Auth::TOKEN_TYPE_RECOVERY, 'type' => Auth::TOKEN_TYPE_RECOVERY,
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
@ -1799,7 +1799,7 @@ App::post('/v1/account/recovery')
$profile->setAttribute('tokens', $recovery, Document::SET_TYPE_APPEND); $profile->setAttribute('tokens', $recovery, Document::SET_TYPE_APPEND);
$profile = $dbForInternal->updateDocument('users', $profile->getId(), $profile); $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile);
$url = Template::parseURL($url); $url = Template::parseURL($url);
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $profile->getId(), 'secret' => $secret, 'expire' => $expire]); $url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $profile->getId(), 'secret' => $secret, 'expire' => $expire]);
@ -1860,12 +1860,12 @@ App::put('/v1/account/recovery')
->param('password', '', new Password(), 'New user password. 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.') ->param('passwordAgain', '', new Password(), 'Repeat new user password. Must be at least 8 chars.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('audits') ->inject('audits')
->inject('usage') ->inject('usage')
->action(function ($userId, $secret, $password, $passwordAgain, $response, $dbForInternal, $audits, $usage) { ->action(function ($userId, $secret, $password, $passwordAgain, $response, $dbForProject, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
@ -1873,7 +1873,7 @@ App::put('/v1/account/recovery')
throw new Exception('Passwords must match', 400); throw new Exception('Passwords must match', 400);
} }
$profile = $dbForInternal->getDocument('users', $userId); $profile = $dbForProject->getDocument('users', $userId);
if ($profile->isEmpty() || $profile->getAttribute('deleted')) { if ($profile->isEmpty() || $profile->getAttribute('deleted')) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
@ -1888,7 +1888,7 @@ App::put('/v1/account/recovery')
Authorization::setRole('user:' . $profile->getId()); Authorization::setRole('user:' . $profile->getId());
$profile = $dbForInternal->updateDocument('users', $profile->getId(), $profile $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile
->setAttribute('password', Auth::passwordHash($password)) ->setAttribute('password', Auth::passwordHash($password))
->setAttribute('passwordUpdate', \time()) ->setAttribute('passwordUpdate', \time())
->setAttribute('emailVerification', true) ->setAttribute('emailVerification', true)
@ -1905,7 +1905,7 @@ App::put('/v1/account/recovery')
} }
} }
$dbForInternal->updateDocument('users', $profile->getId(), $profile->setAttribute('tokens', $tokens)); $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('tokens', $tokens));
$audits $audits
->setParam('userId', $profile->getId()) ->setParam('userId', $profile->getId())
@ -1938,18 +1938,18 @@ App::post('/v1/account/verification')
->inject('response') ->inject('response')
->inject('project') ->inject('project')
->inject('user') ->inject('user')
->inject('dbForInternal') ->inject('dbForProject')
->inject('locale') ->inject('locale')
->inject('audits') ->inject('audits')
->inject('events') ->inject('events')
->inject('mails') ->inject('mails')
->inject('usage') ->inject('usage')
->action(function ($url, $request, $response, $project, $user, $dbForInternal, $locale, $audits, $events, $mails, $usage) { ->action(function ($url, $request, $response, $project, $user, $dbForProject, $locale, $audits, $events, $mails, $usage) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $events */ /** @var Appwrite\Event\Event $events */
@ -1969,7 +1969,7 @@ App::post('/v1/account/verification')
$expire = \time() + Auth::TOKEN_EXPIRATION_CONFIRM; $expire = \time() + Auth::TOKEN_EXPIRATION_CONFIRM;
$verification = new Document([ $verification = new Document([
'$id' => $dbForInternal->getId(), '$id' => $dbForProject->getId(),
'userId' => $user->getId(), 'userId' => $user->getId(),
'type' => Auth::TOKEN_TYPE_VERIFICATION, 'type' => Auth::TOKEN_TYPE_VERIFICATION,
'secret' => Auth::hash($verificationSecret), // One way hash encryption to protect DB leak 'secret' => Auth::hash($verificationSecret), // One way hash encryption to protect DB leak
@ -1982,7 +1982,7 @@ App::post('/v1/account/verification')
$user->setAttribute('tokens', $verification, Document::SET_TYPE_APPEND); $user->setAttribute('tokens', $verification, Document::SET_TYPE_APPEND);
$user = $dbForInternal->updateDocument('users', $user->getId(), $user); $user = $dbForProject->updateDocument('users', $user->getId(), $user);
$url = Template::parseURL($url); $url = Template::parseURL($url);
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $user->getId(), 'secret' => $verificationSecret, 'expire' => $expire]); $url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $user->getId(), 'secret' => $verificationSecret, 'expire' => $expire]);
@ -2042,17 +2042,17 @@ App::put('/v1/account/verification')
->param('secret', '', new Text(256), 'Valid verification token.') ->param('secret', '', new Text(256), 'Valid verification token.')
->inject('response') ->inject('response')
->inject('user') ->inject('user')
->inject('dbForInternal') ->inject('dbForProject')
->inject('audits') ->inject('audits')
->inject('usage') ->inject('usage')
->action(function ($userId, $secret, $response, $user, $dbForInternal, $audits, $usage) { ->action(function ($userId, $secret, $response, $user, $dbForProject, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$profile = $dbForInternal->getDocument('users', $userId); $profile = $dbForProject->getDocument('users', $userId);
if ($profile->isEmpty()) { if ($profile->isEmpty()) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
@ -2067,7 +2067,7 @@ App::put('/v1/account/verification')
Authorization::setRole('user:' . $profile->getId()); Authorization::setRole('user:' . $profile->getId());
$profile = $dbForInternal->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true)); $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true));
/** /**
* We act like we're updating and validating * We act like we're updating and validating
@ -2080,7 +2080,7 @@ App::put('/v1/account/verification')
} }
} }
$dbForInternal->updateDocument('users', $profile->getId(), $profile->setAttribute('tokens', $tokens)); $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('tokens', $tokens));
$audits $audits
->setParam('userId', $profile->getId()) ->setParam('userId', $profile->getId())

File diff suppressed because it is too large Load diff

View file

@ -48,13 +48,13 @@ App::post('/v1/functions')
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', 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), 'Function maximum execution time in seconds.', true)
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->action(function ($functionId, $name, $execute, $runtime, $vars, $events, $schedule, $timeout, $response, $dbForInternal) { ->action(function ($functionId, $name, $execute, $runtime, $vars, $events, $schedule, $timeout, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
$functionId = ($functionId == 'unique()') ? $dbForInternal->getId() : $functionId; $functionId = ($functionId == 'unique()') ? $dbForProject->getId() : $functionId;
$function = $dbForInternal->createDocument('functions', new Document([ $function = $dbForProject->createDocument('functions', new Document([
'$id' => $functionId, '$id' => $functionId,
'execute' => $execute, 'execute' => $execute,
'dateCreated' => time(), 'dateCreated' => time(),
@ -94,13 +94,13 @@ App::get('/v1/functions')
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', 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) ->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal) { ->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
if (!empty($cursor)) { if (!empty($cursor)) {
$cursorFunction = $dbForInternal->getDocument('functions', $cursor); $cursorFunction = $dbForProject->getDocument('functions', $cursor);
if ($cursorFunction->isEmpty()) { if ($cursorFunction->isEmpty()) {
throw new Exception("Function '{$cursor}' for the 'cursor' value not found.", 400); throw new Exception("Function '{$cursor}' for the 'cursor' value not found.", 400);
@ -114,8 +114,8 @@ App::get('/v1/functions')
} }
$response->dynamic(new Document([ $response->dynamic(new Document([
'functions' => $dbForInternal->find('functions', $queries, $limit, $offset, [], [$orderType], $cursorFunction ?? null, $cursorDirection), 'functions' => $dbForProject->find('functions', $queries, $limit, $offset, [], [$orderType], $cursorFunction ?? null, $cursorDirection),
'sum' => $dbForInternal->count('functions', $queries, APP_LIMIT_COUNT), 'sum' => $dbForProject->count('functions', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_FUNCTION_LIST); ]), Response::MODEL_FUNCTION_LIST);
}); });
@ -160,12 +160,12 @@ App::get('/v1/functions/:functionId')
->label('sdk.response.model', Response::MODEL_FUNCTION) ->label('sdk.response.model', Response::MODEL_FUNCTION)
->param('functionId', '', new UID(), 'Function ID.') ->param('functionId', '', new UID(), 'Function ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->action(function ($functionId, $response, $dbForInternal) { ->action(function ($functionId, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
$function = $dbForInternal->getDocument('functions', $functionId); $function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) { if ($function->isEmpty()) {
throw new Exception('Function not found', 404); throw new Exception('Function not found', 404);
@ -187,16 +187,14 @@ App::get('/v1/functions/:functionId/usage')
->param('functionId', '', new UID(), 'Function ID.') ->param('functionId', '', new UID(), 'Function ID.')
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d']), 'Date range.', true) ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d']), 'Date range.', true)
->inject('response') ->inject('response')
->inject('project') ->inject('dbForProject')
->inject('dbForInternal') ->action(function ($functionId, $range, $response, $dbForProject) {
->inject('register')
->action(function ($functionId, $range, $response, $project, $dbForInternal, $register) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Registry\Registry $register */ /** @var Utopia\Registry\Registry $register */
$function = $dbForInternal->getDocument('functions', $functionId); $function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) { if ($function->isEmpty()) {
throw new Exception('Function not found', 404); throw new Exception('Function not found', 404);
@ -231,12 +229,12 @@ App::get('/v1/functions/:functionId/usage')
$stats = []; $stats = [];
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) { Authorization::skip(function() use ($dbForProject, $periods, $range, $metrics, &$stats) {
foreach ($metrics as $metric) { foreach ($metrics as $metric) {
$limit = $periods[$range]['limit']; $limit = $periods[$range]['limit'];
$period = $periods[$range]['period']; $period = $periods[$range]['period'];
$requestDocs = $dbForInternal->find('stats', [ $requestDocs = $dbForProject->find('stats', [
new Query('period', Query::TYPE_EQUAL, [$period]), new Query('period', Query::TYPE_EQUAL, [$period]),
new Query('metric', Query::TYPE_EQUAL, [$metric]), new Query('metric', Query::TYPE_EQUAL, [$metric]),
], $limit, 0, ['time'], [Database::ORDER_DESC]); ], $limit, 0, ['time'], [Database::ORDER_DESC]);
@ -298,14 +296,14 @@ App::put('/v1/functions/:functionId')
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', 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, 900), 'Maximum execution time in seconds.', true)
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('project') ->inject('project')
->action(function ($functionId, $name, $execute, $vars, $events, $schedule, $timeout, $response, $dbForInternal, $project) { ->action(function ($functionId, $name, $execute, $vars, $events, $schedule, $timeout, $response, $dbForProject, $project) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $project */
$function = $dbForInternal->getDocument('functions', $functionId); $function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) { if ($function->isEmpty()) {
throw new Exception('Function not found', 404); throw new Exception('Function not found', 404);
@ -315,7 +313,7 @@ App::put('/v1/functions/:functionId')
$cron = (!empty($function->getAttribute('tag', null)) && !empty($schedule)) ? new CronExpression($schedule) : null; $cron = (!empty($function->getAttribute('tag', null)) && !empty($schedule)) ? new CronExpression($schedule) : null;
$next = (!empty($function->getAttribute('tag', null)) && !empty($schedule)) ? $cron->getNextRunDate()->format('U') : 0; $next = (!empty($function->getAttribute('tag', null)) && !empty($schedule)) ? $cron->getNextRunDate()->format('U') : 0;
$function = $dbForInternal->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [ $function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
'execute' => $execute, 'execute' => $execute,
'dateUpdated' => time(), 'dateUpdated' => time(),
'name' => $name, 'name' => $name,
@ -355,15 +353,15 @@ App::patch('/v1/functions/:functionId/tag')
->param('functionId', '', new UID(), 'Function ID.') ->param('functionId', '', new UID(), 'Function ID.')
->param('tag', '', new UID(), 'Tag ID.') ->param('tag', '', new UID(), 'Tag ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('project') ->inject('project')
->action(function ($functionId, $tag, $response, $dbForInternal, $project) { ->action(function ($functionId, $tag, $response, $dbForProject, $project) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $project */
$function = $dbForInternal->getDocument('functions', $functionId); $function = $dbForProject->getDocument('functions', $functionId);
$tag = $dbForInternal->getDocument('tags', $tag); $tag = $dbForProject->getDocument('tags', $tag);
if ($function->isEmpty()) { if ($function->isEmpty()) {
throw new Exception('Function not found', 404); throw new Exception('Function not found', 404);
@ -377,7 +375,7 @@ App::patch('/v1/functions/:functionId/tag')
$cron = (empty($function->getAttribute('tag')) && !empty($schedule)) ? new CronExpression($schedule) : null; $cron = (empty($function->getAttribute('tag')) && !empty($schedule)) ? new CronExpression($schedule) : null;
$next = (empty($function->getAttribute('tag')) && !empty($schedule)) ? $cron->getNextRunDate()->format('U') : 0; $next = (empty($function->getAttribute('tag')) && !empty($schedule)) ? $cron->getNextRunDate()->format('U') : 0;
$function = $dbForInternal->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [ $function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
'tag' => $tag->getId(), 'tag' => $tag->getId(),
'scheduleNext' => (int)$next, 'scheduleNext' => (int)$next,
]))); ])));
@ -408,20 +406,20 @@ App::delete('/v1/functions/:functionId')
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('functionId', '', new UID(), 'Function ID.') ->param('functionId', '', new UID(), 'Function ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('deletes') ->inject('deletes')
->action(function ($functionId, $response, $dbForInternal, $deletes) { ->action(function ($functionId, $response, $dbForProject, $deletes) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $deletes */ /** @var Appwrite\Event\Event $deletes */
$function = $dbForInternal->getDocument('functions', $functionId); $function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) { if ($function->isEmpty()) {
throw new Exception('Function not found', 404); throw new Exception('Function not found', 404);
} }
if (!$dbForInternal->deleteDocument('functions', $function->getId())) { if (!$dbForProject->deleteDocument('functions', $function->getId())) {
throw new Exception('Failed to remove function from DB', 500); throw new Exception('Failed to remove function from DB', 500);
} }
@ -452,15 +450,15 @@ App::post('/v1/functions/:functionId/tags')
->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) ->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') ->inject('request')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('usage') ->inject('usage')
->action(function ($functionId, $command, $file, $request, $response, $dbForInternal, $usage) { ->action(function ($functionId, $command, $file, $request, $response, $dbForProject, $usage) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $usage */ /** @var Appwrite\Event\Event $usage */
$function = $dbForInternal->getDocument('functions', $functionId); $function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) { if ($function->isEmpty()) {
throw new Exception('Function not found', 404); throw new Exception('Function not found', 404);
@ -501,8 +499,8 @@ App::post('/v1/functions/:functionId/tags')
throw new Exception('Failed moving file', 500); throw new Exception('Failed moving file', 500);
} }
$tagId = $dbForInternal->getId(); $tagId = $dbForProject->getId();
$tag = $dbForInternal->createDocument('tags', new Document([ $tag = $dbForProject->createDocument('tags', new Document([
'$id' => $tagId, '$id' => $tagId,
'$read' => [], '$read' => [],
'$write' => [], '$write' => [],
@ -541,19 +539,19 @@ App::get('/v1/functions/:functionId/tags')
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', 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) ->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->action(function ($functionId, $search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal) { ->action(function ($functionId, $search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
$function = $dbForInternal->getDocument('functions', $functionId); $function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) { if ($function->isEmpty()) {
throw new Exception('Function not found', 404); throw new Exception('Function not found', 404);
} }
if (!empty($cursor)) { if (!empty($cursor)) {
$cursorTag = $dbForInternal->getDocument('tags', $cursor); $cursorTag = $dbForProject->getDocument('tags', $cursor);
if ($cursorTag->isEmpty()) { if ($cursorTag->isEmpty()) {
throw new Exception("Tag '{$cursor}' for the 'cursor' value not found.", 400); throw new Exception("Tag '{$cursor}' for the 'cursor' value not found.", 400);
@ -568,8 +566,8 @@ App::get('/v1/functions/:functionId/tags')
$queries[] = new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]); $queries[] = new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]);
$results = $dbForInternal->find('tags', $queries, $limit, $offset, [], [$orderType], $cursorTag ?? null, $cursorDirection); $results = $dbForProject->find('tags', $queries, $limit, $offset, [], [$orderType], $cursorTag ?? null, $cursorDirection);
$sum = $dbForInternal->count('tags', $queries, APP_LIMIT_COUNT); $sum = $dbForProject->count('tags', $queries, APP_LIMIT_COUNT);
$response->dynamic(new Document([ $response->dynamic(new Document([
'tags' => $results, 'tags' => $results,
@ -591,18 +589,18 @@ App::get('/v1/functions/:functionId/tags/:tagId')
->param('functionId', '', new UID(), 'Function ID.') ->param('functionId', '', new UID(), 'Function ID.')
->param('tagId', '', new UID(), 'Tag ID.') ->param('tagId', '', new UID(), 'Tag ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->action(function ($functionId, $tagId, $response, $dbForInternal) { ->action(function ($functionId, $tagId, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
$function = $dbForInternal->getDocument('functions', $functionId); $function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) { if ($function->isEmpty()) {
throw new Exception('Function not found', 404); throw new Exception('Function not found', 404);
} }
$tag = $dbForInternal->getDocument('tags', $tagId); $tag = $dbForProject->getDocument('tags', $tagId);
if ($tag->getAttribute('functionId') !== $function->getId()) { if ($tag->getAttribute('functionId') !== $function->getId()) {
throw new Exception('Tag not found', 404); throw new Exception('Tag not found', 404);
@ -629,20 +627,20 @@ App::delete('/v1/functions/:functionId/tags/:tagId')
->param('functionId', '', new UID(), 'Function ID.') ->param('functionId', '', new UID(), 'Function ID.')
->param('tagId', '', new UID(), 'Tag ID.') ->param('tagId', '', new UID(), 'Tag ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('usage') ->inject('usage')
->action(function ($functionId, $tagId, $response, $dbForInternal, $usage) { ->action(function ($functionId, $tagId, $response, $dbForProject, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $usage */ /** @var Appwrite\Event\Event $usage */
$function = $dbForInternal->getDocument('functions', $functionId); $function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) { if ($function->isEmpty()) {
throw new Exception('Function not found', 404); throw new Exception('Function not found', 404);
} }
$tag = $dbForInternal->getDocument('tags', $tagId); $tag = $dbForProject->getDocument('tags', $tagId);
if ($tag->getAttribute('functionId') !== $function->getId()) { if ($tag->getAttribute('functionId') !== $function->getId()) {
throw new Exception('Tag not found', 404); throw new Exception('Tag not found', 404);
@ -655,13 +653,13 @@ App::delete('/v1/functions/:functionId/tags/:tagId')
$device = Storage::getDevice('functions'); $device = Storage::getDevice('functions');
if ($device->delete($tag->getAttribute('path', ''))) { if ($device->delete($tag->getAttribute('path', ''))) {
if (!$dbForInternal->deleteDocument('tags', $tag->getId())) { if (!$dbForProject->deleteDocument('tags', $tag->getId())) {
throw new Exception('Failed to remove tag from DB', 500); throw new Exception('Failed to remove tag from DB', 500);
} }
} }
if($function->getAttribute('tag') === $tag->getId()) { // Reset function tag if($function->getAttribute('tag') === $tag->getId()) { // Reset function tag
$function = $dbForInternal->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [ $function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
'tag' => '', 'tag' => '',
]))); ])));
} }
@ -692,21 +690,21 @@ App::post('/v1/functions/:functionId/executions')
// ->param('async', 1, new Range(0, 1), 'Execute code asynchronously. Pass 1 for true, 0 for false. Default value is 1.', 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') ->inject('response')
->inject('project') ->inject('project')
->inject('dbForInternal') ->inject('dbForProject')
->inject('user') ->inject('user')
->action(function ($functionId, $data, /*$async,*/ $response, $project, $dbForInternal, $user) { ->action(function ($functionId, $data, /*$async,*/ $response, $project, $dbForProject, $user) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
$function = Authorization::skip(fn() => $dbForInternal->getDocument('functions', $functionId)); $function = Authorization::skip(fn() => $dbForProject->getDocument('functions', $functionId));
if ($function->isEmpty()) { if ($function->isEmpty()) {
throw new Exception('Function not found', 404); throw new Exception('Function not found', 404);
} }
$tag = Authorization::skip(fn() => $dbForInternal->getDocument('tags', $function->getAttribute('tag'))); $tag = Authorization::skip(fn() => $dbForProject->getDocument('tags', $function->getAttribute('tag')));
if ($tag->getAttribute('functionId') !== $function->getId()) { if ($tag->getAttribute('functionId') !== $function->getId()) {
throw new Exception('Tag not found. Deploy tag before trying to execute a function', 404); throw new Exception('Tag not found. Deploy tag before trying to execute a function', 404);
@ -722,9 +720,9 @@ App::post('/v1/functions/:functionId/executions')
throw new Exception($validator->getDescription(), 401); throw new Exception($validator->getDescription(), 401);
} }
$executionId = $dbForInternal->getId(); $executionId = $dbForProject->getId();
$execution = Authorization::skip(fn() => $dbForInternal->createDocument('executions', new Document([ $execution = Authorization::skip(fn() => $dbForProject->createDocument('executions', new Document([
'$id' => $executionId, '$id' => $executionId,
'$read' => (!$user->isEmpty()) ? ['user:' . $user->getId()] : [], '$read' => (!$user->isEmpty()) ? ['user:' . $user->getId()] : [],
'$write' => [], '$write' => [],
@ -794,19 +792,19 @@ App::get('/v1/functions/:functionId/executions')
->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('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) ->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->action(function ($functionId, $limit, $offset, $search, $cursor, $cursorDirection, $response, $dbForInternal) { ->action(function ($functionId, $limit, $offset, $search, $cursor, $cursorDirection, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
$function = Authorization::skip(fn() => $dbForInternal->getDocument('functions', $functionId)); $function = Authorization::skip(fn() => $dbForProject->getDocument('functions', $functionId));
if ($function->isEmpty()) { if ($function->isEmpty()) {
throw new Exception('Function not found', 404); throw new Exception('Function not found', 404);
} }
if (!empty($cursor)) { if (!empty($cursor)) {
$cursorExecution = $dbForInternal->getDocument('executions', $cursor); $cursorExecution = $dbForProject->getDocument('executions', $cursor);
if ($cursorExecution->isEmpty()) { if ($cursorExecution->isEmpty()) {
throw new Exception("Execution '{$cursor}' for the 'cursor' value not found.", 400); throw new Exception("Execution '{$cursor}' for the 'cursor' value not found.", 400);
@ -821,8 +819,8 @@ App::get('/v1/functions/:functionId/executions')
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
} }
$results = $dbForInternal->find('executions', $queries, $limit, $offset, [], [Database::ORDER_DESC], $cursorExecution ?? null, $cursorDirection); $results = $dbForProject->find('executions', $queries, $limit, $offset, [], [Database::ORDER_DESC], $cursorExecution ?? null, $cursorDirection);
$sum = $dbForInternal->count('executions', $queries, APP_LIMIT_COUNT); $sum = $dbForProject->count('executions', $queries, APP_LIMIT_COUNT);
$response->dynamic(new Document([ $response->dynamic(new Document([
'executions' => $results, 'executions' => $results,
@ -844,18 +842,18 @@ App::get('/v1/functions/:functionId/executions/:executionId')
->param('functionId', '', new UID(), 'Function ID.') ->param('functionId', '', new UID(), 'Function ID.')
->param('executionId', '', new UID(), 'Execution ID.') ->param('executionId', '', new UID(), 'Execution ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->action(function ($functionId, $executionId, $response, $dbForInternal) { ->action(function ($functionId, $executionId, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
$function = Authorization::skip(fn() => $dbForInternal->getDocument('functions', $functionId)); $function = Authorization::skip(fn() => $dbForProject->getDocument('functions', $functionId));
if ($function->isEmpty()) { if ($function->isEmpty()) {
throw new Exception('Function not found', 404); throw new Exception('Function not found', 404);
} }
$execution = $dbForInternal->getDocument('executions', $executionId); $execution = $dbForProject->getDocument('executions', $executionId);
if ($execution->getAttribute('functionId') !== $function->getId()) { if ($execution->getAttribute('functionId') !== $function->getId()) {
throw new Exception('Execution not found', 404); throw new Exception('Execution not found', 404);

View file

@ -224,9 +224,7 @@ App::get('/v1/locale/currencies')
$list = Config::getParam('locale-currencies'); $list = Config::getParam('locale-currencies');
$list = array_map(function($node) { $list = array_map(fn($node) => new Document($node), $list);
return new Document($node);
}, $list);
$response->dynamic(new Document(['currencies' => $list, 'sum' => \count($list)]), Response::MODEL_CURRENCY_LIST); $response->dynamic(new Document(['currencies' => $list, 'sum' => \count($list)]), Response::MODEL_CURRENCY_LIST);
}); });
@ -249,9 +247,7 @@ App::get('/v1/locale/languages')
$list = Config::getParam('locale-languages'); $list = Config::getParam('locale-languages');
$list = array_map(function($node) { $list = array_map(fn ($node) => new Document($node), $list);
return new Document($node);
}, $list);
$response->dynamic(new Document(['languages' => $list, 'sum' => \count($list)]), Response::MODEL_LANGUAGE_LIST); $response->dynamic(new Document(['languages' => $list, 'sum' => \count($list)]), Response::MODEL_LANGUAGE_LIST);
}); });

View file

@ -57,13 +57,11 @@ App::post('/v1/projects')
->param('legalTaxId', '', new Text(256), 'Project legal Tax ID. Max length: 256 chars.', true) ->param('legalTaxId', '', new Text(256), 'Project legal Tax ID. Max length: 256 chars.', true)
->inject('response') ->inject('response')
->inject('dbForConsole') ->inject('dbForConsole')
->inject('dbForInternal') ->inject('dbForProject')
->inject('dbForExternal') ->action(function ($projectId, $name, $teamId, $description, $logo, $url, $legalName, $legalCountry, $legalState, $legalCity, $legalAddress, $legalTaxId, $response, $dbForConsole, $dbForProject) {
->action(function ($projectId, $name, $teamId, $description, $logo, $url, $legalName, $legalCountry, $legalState, $legalCity, $legalAddress, $legalTaxId, $response, $dbForConsole, $dbForInternal, $dbForExternal) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */ /** @var Utopia\Database\Database $dbForConsole */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Database $dbForExternal */
$team = $dbForConsole->getDocument('teams', $teamId); $team = $dbForConsole->getDocument('teams', $teamId);
@ -106,15 +104,13 @@ App::post('/v1/projects')
$collections = Config::getParam('collections', []); /** @var array $collections */ $collections = Config::getParam('collections', []); /** @var array $collections */
$dbForInternal->setNamespace('project_' . $project->getId() . '_internal'); $dbForProject->setNamespace('_project_' . $project->getId());
$dbForInternal->create(); $dbForProject->create('appwrite');
$dbForExternal->setNamespace('project_' . $project->getId() . '_external');
$dbForExternal->create();
$audit = new Audit($dbForInternal); $audit = new Audit($dbForProject);
$audit->setup(); $audit->setup();
$adapter = new TimeLimit('', 0, 1, $dbForInternal); $adapter = new TimeLimit('', 0, 1, $dbForProject);
$adapter->setup(); $adapter->setup();
foreach ($collections as $key => $collection) { foreach ($collections as $key => $collection) {
@ -143,7 +139,7 @@ App::post('/v1/projects')
]); ]);
} }
$dbForInternal->createCollection($key, $attributes, $indexes); $dbForProject->createCollection($key, $attributes, $indexes);
} }
$response->setStatusCode(Response::STATUS_CODE_CREATED); $response->setStatusCode(Response::STATUS_CODE_CREATED);
@ -235,12 +231,12 @@ App::get('/v1/projects/:projectId/usage')
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
->inject('response') ->inject('response')
->inject('dbForConsole') ->inject('dbForConsole')
->inject('dbForInternal') ->inject('dbForProject')
->inject('register') ->inject('register')
->action(function ($projectId, $range, $response, $dbForConsole, $dbForInternal, $register) { ->action(function ($projectId, $range, $response, $dbForConsole, $dbForProject, $register) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */ /** @var Utopia\Database\Database $dbForConsole */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Registry\Registry $register */ /** @var Utopia\Registry\Registry $register */
$project = $dbForConsole->getDocument('projects', $projectId); $project = $dbForConsole->getDocument('projects', $projectId);
@ -270,11 +266,11 @@ App::get('/v1/projects/:projectId/usage')
], ],
]; ];
$dbForInternal->setNamespace('project_' . $projectId . '_internal'); $dbForProject->setNamespace('_project_' . $projectId);
$metrics = [ $metrics = [
'requests', 'requests',
'network', 'network',
'executions', 'executions',
'users.count', 'users.count',
'database.documents.count', 'database.documents.count',
@ -284,12 +280,12 @@ App::get('/v1/projects/:projectId/usage')
$stats = []; $stats = [];
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) { Authorization::skip(function() use ($dbForProject, $periods, $range, $metrics, &$stats) {
foreach ($metrics as $metric) { foreach ($metrics as $metric) {
$limit = $periods[$range]['limit']; $limit = $periods[$range]['limit'];
$period = $periods[$range]['period']; $period = $periods[$range]['period'];
$requestDocs = $dbForInternal->find('stats', [ $requestDocs = $dbForProject->find('stats', [
new Query('period', Query::TYPE_EQUAL, [$period]), new Query('period', Query::TYPE_EQUAL, [$period]),
new Query('metric', Query::TYPE_EQUAL, [$metric]), new Query('metric', Query::TYPE_EQUAL, [$metric]),
], $limit, 0, ['time'], [Database::ORDER_DESC]); ], $limit, 0, ['time'], [Database::ORDER_DESC]);

View file

@ -47,14 +47,14 @@ App::post('/v1/storage/files')
->param('write', null, new ArrayList(new Text(64)), '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) ->param('write', null, new ArrayList(new Text(64)), '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('request')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('user') ->inject('user')
->inject('audits') ->inject('audits')
->inject('usage') ->inject('usage')
->action(function ($fileId, $file, $read, $write, $request, $response, $dbForInternal, $user, $audits, $usage) { ->action(function ($fileId, $file, $read, $write, $request, $response, $dbForProject, $user, $audits, $usage) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
@ -146,8 +146,8 @@ App::post('/v1/storage/files')
$sizeActual = $device->getFileSize($path); $sizeActual = $device->getFileSize($path);
$fileId = ($fileId == 'unique()') ? $dbForInternal->getId() : $fileId; $fileId = ($fileId == 'unique()') ? $dbForProject->getId() : $fileId;
$file = $dbForInternal->createDocument('files', new Document([ $file = $dbForProject->createDocument('files', new Document([
'$id' => $fileId, '$id' => $fileId,
'$read' => (is_null($read) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $read ?? [], // By default set read permissions for user '$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 ?? [], // By default set write permissions for user '$write' => (is_null($write) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $write ?? [], // By default set write permissions for user
@ -202,15 +202,15 @@ App::get('/v1/storage/files')
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', 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) ->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('usage') ->inject('usage')
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal, $usage) { ->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
if (!empty($cursor)) { if (!empty($cursor)) {
$cursorFile = $dbForInternal->getDocument('files', $cursor); $cursorFile = $dbForProject->getDocument('files', $cursor);
if ($cursorFile->isEmpty()) { if ($cursorFile->isEmpty()) {
throw new Exception("File '{$cursor}' for the 'cursor' value not found.", 400); throw new Exception("File '{$cursor}' for the 'cursor' value not found.", 400);
@ -229,8 +229,8 @@ App::get('/v1/storage/files')
; ;
$response->dynamic(new Document([ $response->dynamic(new Document([
'files' => $dbForInternal->find('files', $queries, $limit, $offset, [], [$orderType], $cursorFile ?? null, $cursorDirection), 'files' => $dbForProject->find('files', $queries, $limit, $offset, [], [$orderType], $cursorFile ?? null, $cursorDirection),
'sum' => $dbForInternal->count('files', $queries, APP_LIMIT_COUNT), 'sum' => $dbForProject->count('files', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_FILE_LIST); ]), Response::MODEL_FILE_LIST);
}); });
@ -247,14 +247,14 @@ App::get('/v1/storage/files/:fileId')
->label('sdk.response.model', Response::MODEL_FILE) ->label('sdk.response.model', Response::MODEL_FILE)
->param('fileId', '', new UID(), 'File ID.') ->param('fileId', '', new UID(), 'File ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('usage') ->inject('usage')
->action(function ($fileId, $response, $dbForInternal, $usage) { ->action(function ($fileId, $response, $dbForProject, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$file = $dbForInternal->getDocument('files', $fileId); $file = $dbForProject->getDocument('files', $fileId);
if (empty($file->getId())) { if (empty($file->getId())) {
throw new Exception('File not found', 404); throw new Exception('File not found', 404);
@ -292,13 +292,13 @@ App::get('/v1/storage/files/:fileId/preview')
->inject('request') ->inject('request')
->inject('response') ->inject('response')
->inject('project') ->inject('project')
->inject('dbForInternal') ->inject('dbForProject')
->inject('usage') ->inject('usage')
->action(function ($fileId, $width, $height, $gravity, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $request, $response, $project, $dbForInternal, $usage) { ->action(function ($fileId, $width, $height, $gravity, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $request, $response, $project, $dbForProject, $usage) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $stats */ /** @var Appwrite\Stats\Stats $stats */
$storage = 'files'; $storage = 'files';
@ -322,7 +322,7 @@ App::get('/v1/storage/files/:fileId/preview')
$date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache $date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
$key = \md5($fileId.$width.$height.$gravity.$quality.$borderWidth.$borderColor.$borderRadius.$opacity.$rotation.$background.$storage.$output); $key = \md5($fileId.$width.$height.$gravity.$quality.$borderWidth.$borderColor.$borderRadius.$opacity.$rotation.$background.$storage.$output);
$file = $dbForInternal->getDocument('files', $fileId); $file = $dbForProject->getDocument('files', $fileId);
if (empty($file->getId())) { if (empty($file->getId())) {
throw new Exception('File not found', 404); throw new Exception('File not found', 404);
@ -440,14 +440,14 @@ App::get('/v1/storage/files/:fileId/download')
->label('sdk.methodType', 'location') ->label('sdk.methodType', 'location')
->param('fileId', '', new UID(), 'File ID.') ->param('fileId', '', new UID(), 'File ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('usage') ->inject('usage')
->action(function ($fileId, $response, $dbForInternal, $usage) { ->action(function ($fileId, $response, $dbForProject, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$file = $dbForInternal->getDocument('files', $fileId); $file = $dbForProject->getDocument('files', $fileId);
if (empty($file->getId())) { if (empty($file->getId())) {
throw new Exception('File not found', 404); throw new Exception('File not found', 404);
@ -505,14 +505,14 @@ App::get('/v1/storage/files/:fileId/view')
->label('sdk.methodType', 'location') ->label('sdk.methodType', 'location')
->param('fileId', '', new UID(), 'File ID.') ->param('fileId', '', new UID(), 'File ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('usage') ->inject('usage')
->action(function ($fileId, $response, $dbForInternal, $usage) { ->action(function ($fileId, $response, $dbForProject, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$file = $dbForInternal->getDocument('files', $fileId); $file = $dbForProject->getDocument('files', $fileId);
$mimes = Config::getParam('storage-mimes'); $mimes = Config::getParam('storage-mimes');
if (empty($file->getId())) { if (empty($file->getId())) {
@ -583,13 +583,13 @@ App::put('/v1/storage/files/:fileId')
->param('read', [], new ArrayList(new Text(64)), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.') ->param('read', [], new ArrayList(new Text(64)), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->param('write', [], new ArrayList(new Text(64)), '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.') ->param('write', [], new ArrayList(new Text(64)), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('user') ->inject('user')
->inject('audits') ->inject('audits')
->inject('usage') ->inject('usage')
->action(function ($fileId, $read, $write, $response, $dbForInternal, $user, $audits, $usage) { ->action(function ($fileId, $read, $write, $response, $dbForProject, $user, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
@ -612,13 +612,13 @@ App::put('/v1/storage/files/:fileId')
} }
} }
$file = $dbForInternal->getDocument('files', $fileId); $file = $dbForProject->getDocument('files', $fileId);
if (empty($file->getId())) { if (empty($file->getId())) {
throw new Exception('File not found', 404); throw new Exception('File not found', 404);
} }
$file = $dbForInternal->updateDocument('files', $fileId, new Document(\array_merge($file->getArrayCopy(), [ $file = $dbForProject->updateDocument('files', $fileId, new Document(\array_merge($file->getArrayCopy(), [
'$read' => $read, '$read' => $read,
'$write' => $write, '$write' => $write,
'bucketId' => '', 'bucketId' => '',
@ -650,18 +650,18 @@ App::delete('/v1/storage/files/:fileId')
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('fileId', '', new UID(), 'File ID.') ->param('fileId', '', new UID(), 'File ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('events') ->inject('events')
->inject('audits') ->inject('audits')
->inject('usage') ->inject('usage')
->action(function ($fileId, $response, $dbForInternal, $events, $audits, $usage) { ->action(function ($fileId, $response, $dbForProject, $events, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $events */ /** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$file = $dbForInternal->getDocument('files', $fileId); $file = $dbForProject->getDocument('files', $fileId);
if (empty($file->getId())) { if (empty($file->getId())) {
throw new Exception('File not found', 404); throw new Exception('File not found', 404);
@ -670,7 +670,7 @@ App::delete('/v1/storage/files/:fileId')
$device = Storage::getDevice('files'); $device = Storage::getDevice('files');
if ($device->delete($file->getAttribute('path', ''))) { if ($device->delete($file->getAttribute('path', ''))) {
if (!$dbForInternal->deleteDocument('files', $fileId)) { if (!$dbForProject->deleteDocument('files', $fileId)) {
throw new Exception('Failed to remove file from DB', 500); throw new Exception('Failed to remove file from DB', 500);
} }
} }
@ -705,10 +705,10 @@ App::get('/v1/storage/usage')
->label('sdk.response.model', Response::MODEL_USAGE_STORAGE) ->label('sdk.response.model', Response::MODEL_USAGE_STORAGE)
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->action(function ($range, $response, $dbForInternal) { ->action(function ($range, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
$usage = []; $usage = [];
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
@ -738,12 +738,12 @@ App::get('/v1/storage/usage')
$stats = []; $stats = [];
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) { Authorization::skip(function() use ($dbForProject, $periods, $range, $metrics, &$stats) {
foreach ($metrics as $metric) { foreach ($metrics as $metric) {
$limit = $periods[$range]['limit']; $limit = $periods[$range]['limit'];
$period = $periods[$range]['period']; $period = $periods[$range]['period'];
$requestDocs = $dbForInternal->find('stats', [ $requestDocs = $dbForProject->find('stats', [
new Query('period', Query::TYPE_EQUAL, [$period]), new Query('period', Query::TYPE_EQUAL, [$period]),
new Query('metric', Query::TYPE_EQUAL, [$metric]), new Query('metric', Query::TYPE_EQUAL, [$metric]),
], $limit, 0, ['time'], [Database::ORDER_DESC]); ], $limit, 0, ['time'], [Database::ORDER_DESC]);
@ -797,10 +797,10 @@ App::get('/v1/storage/:bucketId/usage')
->param('bucketId', '', new UID(), 'Bucket ID.') ->param('bucketId', '', new UID(), 'Bucket ID.')
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->action(function ($bucketId, $range, $response, $dbForInternal) { ->action(function ($bucketId, $range, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
// TODO: Check if the storage bucket exists else throw 404 // TODO: Check if the storage bucket exists else throw 404
@ -835,12 +835,12 @@ App::get('/v1/storage/:bucketId/usage')
$stats = []; $stats = [];
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) { Authorization::skip(function() use ($dbForProject, $periods, $range, $metrics, &$stats) {
foreach ($metrics as $metric) { foreach ($metrics as $metric) {
$limit = $periods[$range]['limit']; $limit = $periods[$range]['limit'];
$period = $periods[$range]['period']; $period = $periods[$range]['period'];
$requestDocs = $dbForInternal->find('stats', [ $requestDocs = $dbForProject->find('stats', [
new Query('period', Query::TYPE_EQUAL, [$period]), new Query('period', Query::TYPE_EQUAL, [$period]),
new Query('metric', Query::TYPE_EQUAL, [$metric]), new Query('metric', Query::TYPE_EQUAL, [$metric]),
], $limit, 0, ['time'], [Database::ORDER_DESC]); ], $limit, 0, ['time'], [Database::ORDER_DESC]);

View file

@ -39,19 +39,19 @@ App::post('/v1/teams')
->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) ->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') ->inject('response')
->inject('user') ->inject('user')
->inject('dbForInternal') ->inject('dbForProject')
->inject('events') ->inject('events')
->action(function ($teamId, $name, $roles, $response, $user, $dbForInternal, $events) { ->action(function ($teamId, $name, $roles, $response, $user, $dbForProject, $events) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $events */ /** @var Appwrite\Event\Event $events */
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAppUser = Auth::isAppUser(Authorization::getRoles()); $isAppUser = Auth::isAppUser(Authorization::getRoles());
$teamId = $teamId == 'unique()' ? $dbForInternal->getId() : $teamId; $teamId = $teamId == 'unique()' ? $dbForProject->getId() : $teamId;
$team = Authorization::skip(fn() => $dbForInternal->createDocument('teams', new Document([ $team = Authorization::skip(fn() => $dbForProject->createDocument('teams', new Document([
'$id' => $teamId , '$id' => $teamId ,
'$read' => ['team:'.$teamId], '$read' => ['team:'.$teamId],
'$write' => ['team:'.$teamId .'/owner'], '$write' => ['team:'.$teamId .'/owner'],
@ -74,11 +74,11 @@ App::post('/v1/teams')
'secret' => '', 'secret' => '',
]); ]);
$membership = $dbForInternal->createDocument('memberships', $membership); $membership = $dbForProject->createDocument('memberships', $membership);
// Attach user to team // Attach user to team
$user->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND); $user->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND);
$user = $dbForInternal->updateDocument('users', $user->getId(), $user); $user = $dbForProject->updateDocument('users', $user->getId(), $user);
} }
if (!empty($user->getId())) { if (!empty($user->getId())) {
@ -107,13 +107,13 @@ App::get('/v1/teams')
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', 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) ->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal) { ->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
if (!empty($cursor)) { if (!empty($cursor)) {
$cursorTeam = $dbForInternal->getDocument('teams', $cursor); $cursorTeam = $dbForProject->getDocument('teams', $cursor);
if ($cursorTeam->isEmpty()) { if ($cursorTeam->isEmpty()) {
throw new Exception("Team '{$cursor}' for the 'cursor' value not found.", 400); throw new Exception("Team '{$cursor}' for the 'cursor' value not found.", 400);
@ -126,8 +126,8 @@ App::get('/v1/teams')
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
} }
$results = $dbForInternal->find('teams', $queries, $limit, $offset, [], [$orderType], $cursorTeam ?? null, $cursorDirection); $results = $dbForProject->find('teams', $queries, $limit, $offset, [], [$orderType], $cursorTeam ?? null, $cursorDirection);
$sum = $dbForInternal->count('teams', $queries, APP_LIMIT_COUNT); $sum = $dbForProject->count('teams', $queries, APP_LIMIT_COUNT);
$response->dynamic(new Document([ $response->dynamic(new Document([
'teams' => $results, 'teams' => $results,
@ -148,12 +148,12 @@ App::get('/v1/teams/:teamId')
->label('sdk.response.model', Response::MODEL_TEAM) ->label('sdk.response.model', Response::MODEL_TEAM)
->param('teamId', '', new UID(), 'Team ID.') ->param('teamId', '', new UID(), 'Team ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->action(function ($teamId, $response, $dbForInternal) { ->action(function ($teamId, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
$team = $dbForInternal->getDocument('teams', $teamId); $team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) { if ($team->isEmpty()) {
throw new Exception('Team not found', 404); throw new Exception('Team not found', 404);
@ -177,18 +177,18 @@ App::put('/v1/teams/:teamId')
->param('teamId', '', new UID(), 'Team ID.') ->param('teamId', '', new UID(), 'Team ID.')
->param('name', null, new Text(128), 'New team name. Max length: 128 chars.') ->param('name', null, new Text(128), 'New team name. Max length: 128 chars.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->action(function ($teamId, $name, $response, $dbForInternal) { ->action(function ($teamId, $name, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
$team = $dbForInternal->getDocument('teams', $teamId); $team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) { if ($team->isEmpty()) {
throw new Exception('Team not found', 404); throw new Exception('Team not found', 404);
} }
$team = $dbForInternal->updateDocument('teams', $team->getId(),$team $team = $dbForProject->updateDocument('teams', $team->getId(),$team
->setAttribute('name', $name) ->setAttribute('name', $name)
->setAttribute('search', implode(' ', [$teamId, $name])) ->setAttribute('search', implode(' ', [$teamId, $name]))
); );
@ -209,33 +209,33 @@ App::delete('/v1/teams/:teamId')
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('teamId', '', new UID(), 'Team ID.') ->param('teamId', '', new UID(), 'Team ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('events') ->inject('events')
->inject('deletes') ->inject('deletes')
->action(function ($teamId, $response, $dbForInternal, $events, $deletes) { ->action(function ($teamId, $response, $dbForProject, $events, $deletes) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $events */ /** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $deletes */ /** @var Appwrite\Event\Event $deletes */
$team = $dbForInternal->getDocument('teams', $teamId); $team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) { if ($team->isEmpty()) {
throw new Exception('Team not found', 404); throw new Exception('Team not found', 404);
} }
$memberships = $dbForInternal->find('memberships', [ $memberships = $dbForProject->find('memberships', [
new Query('teamId', Query::TYPE_EQUAL, [$teamId]), new Query('teamId', Query::TYPE_EQUAL, [$teamId]),
], 2000, 0); // TODO fix members limit ], 2000, 0); // TODO fix members limit
// TODO delete all members individually from the user object // TODO delete all members individually from the user object
foreach ($memberships as $membership) { foreach ($memberships as $membership) {
if (!$dbForInternal->deleteDocument('memberships', $membership->getId())) { if (!$dbForProject->deleteDocument('memberships', $membership->getId())) {
throw new Exception('Failed to remove membership for team from DB', 500); throw new Exception('Failed to remove membership for team from DB', 500);
} }
} }
if (!$dbForInternal->deleteDocument('teams', $teamId)) { if (!$dbForProject->deleteDocument('teams', $teamId)) {
throw new Exception('Failed to remove team from DB', 500); throw new Exception('Failed to remove team from DB', 500);
} }
@ -273,15 +273,15 @@ App::post('/v1/teams/:teamId/memberships')
->inject('response') ->inject('response')
->inject('project') ->inject('project')
->inject('user') ->inject('user')
->inject('dbForInternal') ->inject('dbForProject')
->inject('locale') ->inject('locale')
->inject('audits') ->inject('audits')
->inject('mails') ->inject('mails')
->action(function ($teamId, $email, $roles, $url, $name, $response, $project, $user, $dbForInternal, $locale, $audits, $mails) { ->action(function ($teamId, $email, $roles, $url, $name, $response, $project, $user, $dbForProject, $locale, $audits, $mails) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $mails */ /** @var Appwrite\Event\Event $mails */
@ -294,20 +294,20 @@ App::post('/v1/teams/:teamId/memberships')
$email = \strtolower($email); $email = \strtolower($email);
$name = (empty($name)) ? $email : $name; $name = (empty($name)) ? $email : $name;
$team = $dbForInternal->getDocument('teams', $teamId); $team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) { if ($team->isEmpty()) {
throw new Exception('Team not found', 404); throw new Exception('Team not found', 404);
} }
$invitee = $dbForInternal->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address $invitee = $dbForProject->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
if (empty($invitee)) { // Create new user if no user with same email found if (empty($invitee)) { // Create new user if no user with same email found
$limit = $project->getAttribute('auths', [])['limit'] ?? 0; $limit = $project->getAttribute('auths', [])['limit'] ?? 0;
if ($limit !== 0 && $project->getId() !== 'console') { // check users limit, console invites are allways allowed. if ($limit !== 0 && $project->getId() !== 'console') { // check users limit, console invites are allways allowed.
$sum = $dbForInternal->count('users', [], APP_LIMIT_USERS); $sum = $dbForProject->count('users', [], APP_LIMIT_USERS);
if($sum >= $limit) { if($sum >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501); throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
@ -315,8 +315,8 @@ App::post('/v1/teams/:teamId/memberships')
} }
try { try {
$userId = $dbForInternal->getId(); $userId = $dbForProject->getId();
$invitee = Authorization::skip(fn() => $dbForInternal->createDocument('users', new Document([ $invitee = Authorization::skip(fn() => $dbForProject->createDocument('users', new Document([
'$id' => $userId, '$id' => $userId,
'$read' => ['user:'.$userId, 'role:all'], '$read' => ['user:'.$userId, 'role:all'],
'$write' => ['user:'.$userId], '$write' => ['user:'.$userId],
@ -333,7 +333,7 @@ App::post('/v1/teams/:teamId/memberships')
'registration' => \time(), 'registration' => \time(),
'reset' => false, 'reset' => false,
'name' => $name, 'name' => $name,
'prefs' => [], 'prefs' => new \stdClass(),
'sessions' => [], 'sessions' => [],
'tokens' => [], 'tokens' => [],
'memberships' => [], 'memberships' => [],
@ -353,7 +353,7 @@ App::post('/v1/teams/:teamId/memberships')
$secret = Auth::tokenGenerator(); $secret = Auth::tokenGenerator();
$membership = new Document([ $membership = new Document([
'$id' => $dbForInternal->getId(), '$id' => $dbForProject->getId(),
'$read' => ['role:all'], '$read' => ['role:all'],
'$write' => ['user:'.$invitee->getId(), 'team:'.$team->getId().'/owner'], '$write' => ['user:'.$invitee->getId(), 'team:'.$team->getId().'/owner'],
'userId' => $invitee->getId(), 'userId' => $invitee->getId(),
@ -367,20 +367,20 @@ App::post('/v1/teams/:teamId/memberships')
if ($isPrivilegedUser || $isAppUser) { // Allow admin to create membership if ($isPrivilegedUser || $isAppUser) { // Allow admin to create membership
try { try {
$membership = Authorization::skip(fn() => $dbForInternal->createDocument('memberships', $membership)); $membership = Authorization::skip(fn() => $dbForProject->createDocument('memberships', $membership));
} catch (Duplicate $th) { } catch (Duplicate $th) {
throw new Exception('User has already been invited or is already a member of this team', 409); throw new Exception('User has already been invited or is already a member of this team', 409);
} }
$team->setAttribute('sum', $team->getAttribute('sum', 0) + 1); $team->setAttribute('sum', $team->getAttribute('sum', 0) + 1);
$team = Authorization::skip(fn() => $dbForInternal->updateDocument('teams', $team->getId(), $team)); $team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team));
// Attach user to team // Attach user to team
$invitee->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND); $invitee->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND);
$invitee = Authorization::skip(fn() => $dbForInternal->updateDocument('users', $invitee->getId(), $invitee)); $invitee = Authorization::skip(fn() => $dbForProject->updateDocument('users', $invitee->getId(), $invitee));
} else { } else {
try { try {
$membership = $dbForInternal->createDocument('memberships', $membership); $membership = $dbForProject->createDocument('memberships', $membership);
} catch (Duplicate $th) { } catch (Duplicate $th) {
throw new Exception('User has already been invited or is already a member of this team', 409); throw new Exception('User has already been invited or is already a member of this team', 409);
} }
@ -438,32 +438,32 @@ App::get('/v1/teams/:teamId/memberships')
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', 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) ->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->action(function ($teamId, $search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal) { ->action(function ($teamId, $search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
$team = $dbForInternal->getDocument('teams', $teamId); $team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) { if ($team->isEmpty()) {
throw new Exception('Team not found', 404); throw new Exception('Team not found', 404);
} }
if (!empty($cursor)) { if (!empty($cursor)) {
$cursorMembership = $dbForInternal->getDocument('memberships', $cursor); $cursorMembership = $dbForProject->getDocument('memberships', $cursor);
if ($cursorMembership->isEmpty()) { if ($cursorMembership->isEmpty()) {
throw new Exception("Membership '{$cursor}' for the 'cursor' value not found.", 400); throw new Exception("Membership '{$cursor}' for the 'cursor' value not found.", 400);
} }
} }
$memberships = $dbForInternal->find('memberships', [new Query('teamId', Query::TYPE_EQUAL, [$teamId])], $limit, $offset, [], [$orderType], $cursorMembership ?? null, $cursorDirection); $memberships = $dbForProject->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); $sum = $dbForProject->count('memberships', [new Query('teamId', Query::TYPE_EQUAL, [$teamId])], APP_LIMIT_COUNT);
$memberships = array_filter($memberships, fn(Document $membership) => !empty($membership->getAttribute('userId'))); $memberships = array_filter($memberships, fn(Document $membership) => !empty($membership->getAttribute('userId')));
$memberships = array_map(function($membership) use ($dbForInternal) { $memberships = array_map(function($membership) use ($dbForProject) {
$user = $dbForInternal->getDocument('users', $membership->getAttribute('userId')); $user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
$membership $membership
->setAttribute('name', $user->getAttribute('name')) ->setAttribute('name', $user->getAttribute('name'))
@ -493,24 +493,24 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
->param('teamId', '', new UID(), 'Team ID.') ->param('teamId', '', new UID(), 'Team ID.')
->param('membershipId', '', new UID(), 'Membership ID.') ->param('membershipId', '', new UID(), 'Membership ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->action(function ($teamId, $membershipId, $response, $dbForInternal) { ->action(function ($teamId, $membershipId, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
$team = $dbForInternal->getDocument('teams', $teamId); $team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) { if ($team->isEmpty()) {
throw new Exception('Team not found', 404); throw new Exception('Team not found', 404);
} }
$membership = $dbForInternal->getDocument('memberships', $membershipId); $membership = $dbForProject->getDocument('memberships', $membershipId);
if($membership->isEmpty() || empty($membership->getAttribute('userId'))) { if($membership->isEmpty() || empty($membership->getAttribute('userId'))) {
throw new Exception('Membership not found', 404); throw new Exception('Membership not found', 404);
} }
$user = $dbForInternal->getDocument('users', $membership->getAttribute('userId')); $user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
$membership $membership
->setAttribute('name', $user->getAttribute('name')) ->setAttribute('name', $user->getAttribute('name'))
@ -538,26 +538,26 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->inject('request') ->inject('request')
->inject('response') ->inject('response')
->inject('user') ->inject('user')
->inject('dbForInternal') ->inject('dbForProject')
->inject('audits') ->inject('audits')
->action(function ($teamId, $membershipId, $roles, $request, $response, $user, $dbForInternal, $audits) { ->action(function ($teamId, $membershipId, $roles, $request, $response, $user, $dbForProject, $audits) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
$team = $dbForInternal->getDocument('teams', $teamId); $team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) { if ($team->isEmpty()) {
throw new Exception('Team not found', 404); throw new Exception('Team not found', 404);
} }
$membership = $dbForInternal->getDocument('memberships', $membershipId); $membership = $dbForProject->getDocument('memberships', $membershipId);
if ($membership->isEmpty()) { if ($membership->isEmpty()) {
throw new Exception('Membership not found', 404); throw new Exception('Membership not found', 404);
} }
$profile = $dbForInternal->getDocument('users', $membership->getAttribute('userId')); $profile = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
if ($profile->isEmpty()) { if ($profile->isEmpty()) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
} }
@ -572,7 +572,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
// Update the roles // Update the roles
$membership->setAttribute('roles', $roles); $membership->setAttribute('roles', $roles);
$membership = $dbForInternal->updateDocument('memberships', $membership->getId(), $membership); $membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership);
// TODO sync updated membership in the user $profile object using TYPE_REPLACE // TODO sync updated membership in the user $profile object using TYPE_REPLACE
@ -604,20 +604,20 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->inject('request') ->inject('request')
->inject('response') ->inject('response')
->inject('user') ->inject('user')
->inject('dbForInternal') ->inject('dbForProject')
->inject('geodb') ->inject('geodb')
->inject('audits') ->inject('audits')
->action(function ($teamId, $membershipId, $userId, $secret, $request, $response, $user, $dbForInternal, $geodb, $audits) { ->action(function ($teamId, $membershipId, $userId, $secret, $request, $response, $user, $dbForProject, $geodb, $audits) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var MaxMind\Db\Reader $geodb */ /** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
$protocol = $request->getProtocol(); $protocol = $request->getProtocol();
$membership = $dbForInternal->getDocument('memberships', $membershipId); $membership = $dbForProject->getDocument('memberships', $membershipId);
if ($membership->isEmpty()) { if ($membership->isEmpty()) {
throw new Exception('Membership not found', 404); throw new Exception('Membership not found', 404);
@ -627,7 +627,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
throw new Exception('Team IDs don\'t match', 404); throw new Exception('Team IDs don\'t match', 404);
} }
$team = Authorization::skip(fn() => $dbForInternal->getDocument('teams', $teamId)); $team = Authorization::skip(fn() => $dbForProject->getDocument('teams', $teamId));
if ($team->isEmpty()) { if ($team->isEmpty()) {
throw new Exception('Team not found', 404); throw new Exception('Team not found', 404);
@ -642,7 +642,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
} }
if ($user->isEmpty()) { if ($user->isEmpty()) {
$user = $dbForInternal->getDocument('users', $userId); // Get user $user = $dbForProject->getDocument('users', $userId); // Get user
} }
if ($membership->getAttribute('userId') !== $user->getId()) { if ($membership->getAttribute('userId') !== $user->getId()) {
@ -668,7 +668,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
$expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG; $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$secret = Auth::tokenGenerator(); $secret = Auth::tokenGenerator();
$session = new Document(array_merge([ $session = new Document(array_merge([
'$id' => $dbForInternal->getId(), '$id' => $dbForProject->getId(),
'userId' => $user->getId(), 'userId' => $user->getId(),
'provider' => Auth::SESSION_PROVIDER_EMAIL, 'provider' => Auth::SESSION_PROVIDER_EMAIL,
'providerUid' => $user->getAttribute('email'), 'providerUid' => $user->getAttribute('email'),
@ -679,7 +679,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--', 'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
], $detector->getOS(), $detector->getClient(), $detector->getDevice())); ], $detector->getOS(), $detector->getClient(), $detector->getDevice()));
$session = $dbForInternal->createDocument('sessions', $session $session = $dbForProject->createDocument('sessions', $session
->setAttribute('$read', ['user:'.$user->getId()]) ->setAttribute('$read', ['user:'.$user->getId()])
->setAttribute('$write', ['user:'.$user->getId()]) ->setAttribute('$write', ['user:'.$user->getId()])
); );
@ -688,10 +688,10 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
Authorization::setRole('user:'.$userId); Authorization::setRole('user:'.$userId);
$user = $dbForInternal->updateDocument('users', $user->getId(), $user); $user = $dbForProject->updateDocument('users', $user->getId(), $user);
$membership = $dbForInternal->updateDocument('memberships', $membership->getId(), $membership); $membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership);
$team = Authorization::skip(fn() => $dbForInternal->updateDocument('teams', $team->getId(), $team->setAttribute('sum', $team->getAttribute('sum', 0) + 1))); $team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team->setAttribute('sum', $team->getAttribute('sum', 0) + 1)));
$audits $audits
->setParam('userId', $user->getId()) ->setParam('userId', $user->getId())
@ -730,16 +730,16 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
->param('teamId', '', new UID(), 'Team ID.') ->param('teamId', '', new UID(), 'Team ID.')
->param('membershipId', '', new UID(), 'Membership ID.') ->param('membershipId', '', new UID(), 'Membership ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('audits') ->inject('audits')
->inject('events') ->inject('events')
->action(function ($teamId, $membershipId, $response, $dbForInternal, $audits, $events) { ->action(function ($teamId, $membershipId, $response, $dbForProject, $audits, $events) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $events */ /** @var Appwrite\Event\Event $events */
$membership = $dbForInternal->getDocument('memberships', $membershipId); $membership = $dbForProject->getDocument('memberships', $membershipId);
if ($membership->isEmpty()) { if ($membership->isEmpty()) {
throw new Exception('Invite not found', 404); throw new Exception('Invite not found', 404);
@ -749,19 +749,19 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
throw new Exception('Team IDs don\'t match', 404); throw new Exception('Team IDs don\'t match', 404);
} }
$user = $dbForInternal->getDocument('users', $membership->getAttribute('userId')); $user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
if ($user->isEmpty()) { if ($user->isEmpty()) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
} }
$team = $dbForInternal->getDocument('teams', $teamId); $team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) { if ($team->isEmpty()) {
throw new Exception('Team not found', 404); throw new Exception('Team not found', 404);
} }
if (!$dbForInternal->deleteDocument('memberships', $membership->getId())) { if (!$dbForProject->deleteDocument('memberships', $membership->getId())) {
throw new Exception('Failed to remove membership from DB', 500); throw new Exception('Failed to remove membership from DB', 500);
} }
@ -778,11 +778,11 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
$user->setAttribute('memberships', $memberships); $user->setAttribute('memberships', $memberships);
Authorization::skip(fn() => $dbForInternal->updateDocument('users', $user->getId(), $user)); Authorization::skip(fn() => $dbForProject->updateDocument('users', $user->getId(), $user));
if ($membership->getAttribute('confirm')) { // Count only confirmed members if ($membership->getAttribute('confirm')) { // Count only confirmed members
$team->setAttribute('sum', \max($team->getAttribute('sum', 0) - 1, 0)); $team->setAttribute('sum', \max($team->getAttribute('sum', 0) - 1, 0));
$team = $dbForInternal->updateDocument('teams', $team->getId(), $team); $team = $dbForProject->updateDocument('teams', $team->getId(), $team);
} }
$audits $audits

View file

@ -39,18 +39,18 @@ App::post('/v1/users')
->param('password', '', new Password(), 'User password. Must be at least 8 chars.') ->param('password', '', new Password(), 'User password. Must be at least 8 chars.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('usage') ->inject('usage')
->action(function ($userId, $email, $password, $name, $response, $dbForInternal, $usage) { ->action(function ($userId, $email, $password, $name, $response, $dbForProject, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$email = \strtolower($email); $email = \strtolower($email);
try { try {
$userId = $userId == 'unique()' ? $dbForInternal->getId() : $userId; $userId = $userId == 'unique()' ? $dbForProject->getId() : $userId;
$user = $dbForInternal->createDocument('users', new Document([ $user = $dbForProject->createDocument('users', new Document([
'$id' => $userId, '$id' => $userId,
'$read' => ['role:all'], '$read' => ['role:all'],
'$write' => ['user:'.$userId], '$write' => ['user:'.$userId],
@ -62,7 +62,7 @@ App::post('/v1/users')
'registration' => \time(), 'registration' => \time(),
'reset' => false, 'reset' => false,
'name' => $name, 'name' => $name,
'prefs' => [], 'prefs' => new \stdClass(),
'sessions' => [], 'sessions' => [],
'tokens' => [], 'tokens' => [],
'memberships' => [], 'memberships' => [],
@ -99,15 +99,15 @@ App::get('/v1/users')
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', 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) ->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('usage') ->inject('usage')
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal, $usage) { ->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
if (!empty($cursor)) { if (!empty($cursor)) {
$cursorUser = $dbForInternal->getDocument('users', $cursor); $cursorUser = $dbForProject->getDocument('users', $cursor);
if ($cursorUser->isEmpty()) { if ($cursorUser->isEmpty()) {
throw new Exception("User '{$cursor}' for the 'cursor' value not found.", 400); throw new Exception("User '{$cursor}' for the 'cursor' value not found.", 400);
@ -127,8 +127,8 @@ App::get('/v1/users')
; ;
$response->dynamic(new Document([ $response->dynamic(new Document([
'users' => $dbForInternal->find('users', $queries, $limit, $offset, [], [$orderType], $cursorUser ?? null, $cursorDirection), 'users' => $dbForProject->find('users', $queries, $limit, $offset, [], [$orderType], $cursorUser ?? null, $cursorDirection),
'sum' => $dbForInternal->count('users', $queries, APP_LIMIT_COUNT), 'sum' => $dbForProject->count('users', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_USER_LIST); ]), Response::MODEL_USER_LIST);
}); });
@ -145,14 +145,14 @@ App::get('/v1/users/:userId')
->label('sdk.response.model', Response::MODEL_USER) ->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new UID(), 'User ID.') ->param('userId', '', new UID(), 'User ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('usage') ->inject('usage')
->action(function ($userId, $response, $dbForInternal, $usage) { ->action(function ($userId, $response, $dbForProject, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$user = $dbForInternal->getDocument('users', $userId); $user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) { if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
@ -177,14 +177,14 @@ App::get('/v1/users/:userId/prefs')
->label('sdk.response.model', Response::MODEL_PREFERENCES) ->label('sdk.response.model', Response::MODEL_PREFERENCES)
->param('userId', '', new UID(), 'User ID.') ->param('userId', '', new UID(), 'User ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('usage') ->inject('usage')
->action(function ($userId, $response, $dbForInternal, $usage) { ->action(function ($userId, $response, $dbForProject, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$user = $dbForInternal->getDocument('users', $userId); $user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) { if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
@ -211,16 +211,16 @@ App::get('/v1/users/:userId/sessions')
->label('sdk.response.model', Response::MODEL_SESSION_LIST) ->label('sdk.response.model', Response::MODEL_SESSION_LIST)
->param('userId', '', new UID(), 'User ID.') ->param('userId', '', new UID(), 'User ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('locale') ->inject('locale')
->inject('usage') ->inject('usage')
->action(function ($userId, $response, $dbForInternal, $locale, $usage) { ->action(function ($userId, $response, $dbForProject, $locale, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$user = $dbForInternal->getDocument('users', $userId); $user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) { if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
@ -245,7 +245,7 @@ App::get('/v1/users/:userId/sessions')
'sessions' => $sessions, 'sessions' => $sessions,
'sum' => count($sessions), 'sum' => count($sessions),
]), Response::MODEL_SESSION_LIST); ]), Response::MODEL_SESSION_LIST);
}, ['response', 'dbForInternal', 'locale']); });
App::get('/v1/users/:userId/logs') App::get('/v1/users/:userId/logs')
->desc('Get User Logs') ->desc('Get User Logs')
@ -262,25 +262,25 @@ App::get('/v1/users/:userId/logs')
->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('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) ->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('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('locale') ->inject('locale')
->inject('geodb') ->inject('geodb')
->inject('usage') ->inject('usage')
->action(function ($userId, $limit, $offset, $response, $dbForInternal, $locale, $geodb, $usage) { ->action(function ($userId, $limit, $offset, $response, $dbForProject, $locale, $geodb, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Locale\Locale $locale */
/** @var MaxMind\Db\Reader $geodb */ /** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$user = $dbForInternal->getDocument('users', $userId); $user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) { if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
} }
$audit = new Audit($dbForInternal); $audit = new Audit($dbForProject);
$auditEvents = [ $auditEvents = [
'account.create', 'account.create',
'account.delete', 'account.delete',
@ -367,20 +367,20 @@ App::patch('/v1/users/:userId/status')
->param('userId', '', new UID(), 'User ID.') ->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`.') ->param('status', null, new Boolean(true), 'User Status. To activate the user pass `true` and to block the user pass `false`.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('usage') ->inject('usage')
->action(function ($userId, $status, $response, $dbForInternal, $usage) { ->action(function ($userId, $status, $response, $dbForProject, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$user = $dbForInternal->getDocument('users', $userId); $user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) { if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
} }
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('status', (bool) $status)); $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('status', (bool) $status));
$usage $usage
->setParam('users.update', 1) ->setParam('users.update', 1)
@ -403,20 +403,20 @@ App::patch('/v1/users/:userId/verification')
->param('userId', '', new UID(), 'User ID.') ->param('userId', '', new UID(), 'User ID.')
->param('emailVerification', false, new Boolean(), 'User email verification status.') ->param('emailVerification', false, new Boolean(), 'User email verification status.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('usage') ->inject('usage')
->action(function ($userId, $emailVerification, $response, $dbForInternal, $usage) { ->action(function ($userId, $emailVerification, $response, $dbForProject, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$user = $dbForInternal->getDocument('users', $userId); $user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) { if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
} }
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', $emailVerification)); $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', $emailVerification));
$usage $usage
->setParam('users.update', 1) ->setParam('users.update', 1)
@ -439,20 +439,20 @@ App::patch('/v1/users/:userId/name')
->param('userId', '', new UID(), 'User ID.') ->param('userId', '', new UID(), 'User ID.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('audits') ->inject('audits')
->action(function ($userId, $name, $response, $dbForInternal, $audits) { ->action(function ($userId, $name, $response, $dbForProject, $audits) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
$user = $dbForInternal->getDocument('users', $userId); $user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) { if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
} }
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('name', $name)); $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('name', $name));
$audits $audits
->setParam('userId', $user->getId()) ->setParam('userId', $user->getId())
@ -478,14 +478,14 @@ App::patch('/v1/users/:userId/password')
->param('userId', '', new UID(), 'User ID.') ->param('userId', '', new UID(), 'User ID.')
->param('password', '', new Password(), 'New user password. Must be at least 8 chars.') ->param('password', '', new Password(), 'New user password. Must be at least 8 chars.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('audits') ->inject('audits')
->action(function ($userId, $password, $response, $dbForInternal, $audits) { ->action(function ($userId, $password, $response, $dbForProject, $audits) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
$user = $dbForInternal->getDocument('users', $userId); $user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) { if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
@ -495,7 +495,7 @@ App::patch('/v1/users/:userId/password')
->setAttribute('password', Auth::passwordHash($password)) ->setAttribute('password', Auth::passwordHash($password))
->setAttribute('passwordUpdate', \time()); ->setAttribute('passwordUpdate', \time());
$user = $dbForInternal->updateDocument('users', $user->getId(), $user); $user = $dbForProject->updateDocument('users', $user->getId(), $user);
$audits $audits
->setParam('userId', $user->getId()) ->setParam('userId', $user->getId())
@ -521,14 +521,14 @@ App::patch('/v1/users/:userId/email')
->param('userId', '', new UID(), 'User ID.') ->param('userId', '', new UID(), 'User ID.')
->param('email', '', new Email(), 'User email.') ->param('email', '', new Email(), 'User email.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('audits') ->inject('audits')
->action(function ($userId, $email, $response, $dbForInternal, $audits) { ->action(function ($userId, $email, $response, $dbForProject, $audits) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
$user = $dbForInternal->getDocument('users', $userId); $user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) { if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
@ -542,7 +542,7 @@ App::patch('/v1/users/:userId/email')
$email = \strtolower($email); $email = \strtolower($email);
try { try {
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('email', $email)); $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('email', $email));
} catch(Duplicate $th) { } catch(Duplicate $th) {
throw new Exception('Email already exists', 409); throw new Exception('Email already exists', 409);
} }
@ -571,20 +571,20 @@ App::patch('/v1/users/:userId/prefs')
->param('userId', '', new UID(), 'User ID.') ->param('userId', '', new UID(), 'User ID.')
->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.') ->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('usage') ->inject('usage')
->action(function ($userId, $prefs, $response, $dbForInternal, $usage) { ->action(function ($userId, $prefs, $response, $dbForProject, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$user = $dbForInternal->getDocument('users', $userId); $user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) { if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
} }
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs)); $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs));
$usage $usage
->setParam('users.update', 1) ->setParam('users.update', 1)
@ -606,16 +606,16 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
->param('userId', '', new UID(), 'User ID.') ->param('userId', '', new UID(), 'User ID.')
->param('sessionId', null, new UID(), 'Session ID.') ->param('sessionId', null, new UID(), 'Session ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('events') ->inject('events')
->inject('usage') ->inject('usage')
->action(function ($userId, $sessionId, $response, $dbForInternal, $events, $usage) { ->action(function ($userId, $sessionId, $response, $dbForProject, $events, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $events */ /** @var Appwrite\Event\Event $events */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$user = $dbForInternal->getDocument('users', $userId); $user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) { if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
@ -628,7 +628,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
if ($sessionId == $session->getId()) { if ($sessionId == $session->getId()) {
unset($sessions[$key]); unset($sessions[$key]);
$dbForInternal->deleteDocument('sessions', $session->getId()); $dbForProject->deleteDocument('sessions', $session->getId());
$user->setAttribute('sessions', $sessions); $user->setAttribute('sessions', $sessions);
@ -636,7 +636,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
->setParam('eventData', $response->output($user, Response::MODEL_USER)) ->setParam('eventData', $response->output($user, Response::MODEL_USER))
; ;
$dbForInternal->updateDocument('users', $user->getId(), $user); $dbForProject->updateDocument('users', $user->getId(), $user);
} }
} }
@ -661,16 +661,16 @@ App::delete('/v1/users/:userId/sessions')
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('userId', '', new UID(), 'User ID.') ->param('userId', '', new UID(), 'User ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('events') ->inject('events')
->inject('usage') ->inject('usage')
->action(function ($userId, $response, $dbForInternal, $events, $usage) { ->action(function ($userId, $response, $dbForProject, $events, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $events */ /** @var Appwrite\Event\Event $events */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$user = $dbForInternal->getDocument('users', $userId); $user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) { if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
@ -679,10 +679,10 @@ App::delete('/v1/users/:userId/sessions')
$sessions = $user->getAttribute('sessions', []); $sessions = $user->getAttribute('sessions', []);
foreach ($sessions as $key => $session) { /** @var Document $session */ foreach ($sessions as $key => $session) { /** @var Document $session */
$dbForInternal->deleteDocument('sessions', $session->getId()); $dbForProject->deleteDocument('sessions', $session->getId());
} }
$dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('sessions', [])); $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', []));
$events $events
->setParam('eventData', $response->output($user, Response::MODEL_USER)) ->setParam('eventData', $response->output($user, Response::MODEL_USER))
@ -708,18 +708,18 @@ App::delete('/v1/users/:userId')
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('userId', '', function () {return new UID();}, 'User ID.') ->param('userId', '', function () {return new UID();}, 'User ID.')
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('events') ->inject('events')
->inject('deletes') ->inject('deletes')
->inject('usage') ->inject('usage')
->action(function ($userId, $response, $dbForInternal, $events, $deletes, $usage) { ->action(function ($userId, $response, $dbForProject, $events, $deletes, $usage) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $events */ /** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $deletes */ /** @var Appwrite\Event\Event $deletes */
/** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Stats\Stats $usage */
$user = $dbForInternal->getDocument('users', $userId); $user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty() || $user->getAttribute('deleted')) { if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404); throw new Exception('User not found', 404);
@ -735,7 +735,7 @@ App::delete('/v1/users/:userId')
->setAttribute("deleted", true) ->setAttribute("deleted", true)
; ;
$dbForInternal->updateDocument('users', $userId, $user); $dbForProject->updateDocument('users', $userId, $user);
$deletes $deletes
->setParam('type', DELETE_TYPE_DOCUMENT) ->setParam('type', DELETE_TYPE_DOCUMENT)
@ -764,13 +764,13 @@ App::get('/v1/users/usage')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USAGE_USERS) ->label('sdk.response.model', Response::MODEL_USAGE_USERS)
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
->param('provider', '', new WhiteList(\array_merge(['email', 'anonymous'], \array_map(function($value) { return "oauth-".$value; }, \array_keys(Config::getParam('providers', [])))), true), 'Provider Name.', true) ->param('provider', '', new WhiteList(\array_merge(['email', 'anonymous'], \array_map(fn($value) => "oauth-".$value, \array_keys(Config::getParam('providers', [])))), true), 'Provider Name.', true)
->inject('response') ->inject('response')
->inject('dbForInternal') ->inject('dbForProject')
->inject('register') ->inject('register')
->action(function ($range, $provider, $response, $dbForInternal) { ->action(function ($range, $provider, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
$usage = []; $usage = [];
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
@ -806,12 +806,12 @@ App::get('/v1/users/usage')
$stats = []; $stats = [];
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) { Authorization::skip(function() use ($dbForProject, $periods, $range, $metrics, &$stats) {
foreach ($metrics as $metric) { foreach ($metrics as $metric) {
$limit = $periods[$range]['limit']; $limit = $periods[$range]['limit'];
$period = $periods[$range]['period']; $period = $periods[$range]['period'];
$requestDocs = $dbForInternal->find('stats', [ $requestDocs = $dbForProject->find('stats', [
new Query('period', Query::TYPE_EQUAL, [$period]), new Query('period', Query::TYPE_EQUAL, [$period]),
new Query('metric', Query::TYPE_EQUAL, [$metric]), new Query('metric', Query::TYPE_EQUAL, [$metric]),
], $limit, 0, ['time'], [Database::ORDER_DESC]); ], $limit, 0, ['time'], [Database::ORDER_DESC]);

View file

@ -47,20 +47,25 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
} else { } else {
Authorization::disable(); Authorization::disable();
$certificate = $dbForConsole->findOne('certificates', [ $domainDocument = $dbForConsole->findOne('domains', [
new Query('domain', QUERY::TYPE_EQUAL, [$domain->get()]) new Query('domain', QUERY::TYPE_EQUAL, [$domain->get()])
]); ]);
if (empty($certificate)) { if (!$domainDocument) {
$certificate = new Document([ $domainDocument = new Document([
'domain' => $domain->get(), 'domain' => $domain->get(),
'tld' => $domain->getSuffix(),
'registerable' => $domain->getRegisterable(),
'verification' => false,
'certificateId' => null,
]); ]);
$certificate = $dbForConsole->createDocument('certificates', $certificate);
$domainDocument = $dbForConsole->createDocument('domains', $domainDocument);
Console::info('Issuing a TLS certificate for the master domain (' . $domain->get() . ') in a few seconds...'); Console::info('Issuing a TLS certificate for the master domain (' . $domain->get() . ') in a few seconds...');
Resque::enqueue('v1-certificates', 'CertificatesV1', [ Resque::enqueue('v1-certificates', 'CertificatesV1', [
'document' => $certificate, 'document' => $domainDocument,
'domain' => $domain->get(), 'domain' => $domain->get(),
'validateTarget' => false, 'validateTarget' => false,
'validateCNAME' => false, 'validateCNAME' => false,
@ -74,7 +79,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
Config::setParam('domains', $domains); Config::setParam('domains', $domains);
} }
$localeParam = (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', '')); $localeParam = (string) $request->getParam('locale', $request->getHeader('x-appwrite-locale', ''));
if (\in_array($localeParam, Config::getParam('locale-codes'))) { if (\in_array($localeParam, Config::getParam('locale-codes'))) {
$locale->setDefault($localeParam); $locale->setDefault($localeParam);

View file

@ -11,7 +11,7 @@ use Utopia\Database\Document;
use Utopia\Storage\Device\Local; use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage; use Utopia\Storage\Storage;
App::init(function ($utopia, $request, $response, $project, $user, $events, $audits, $usage, $deletes, $database, $dbForInternal, $mode) { App::init(function ($utopia, $request, $response, $project, $user, $events, $audits, $usage, $deletes, $database, $dbForProject, $mode) {
/** @var Utopia\App $utopia */ /** @var Utopia\App $utopia */
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
@ -24,7 +24,7 @@ App::init(function ($utopia, $request, $response, $project, $user, $events, $aud
/** @var Appwrite\Event\Event $deletes */ /** @var Appwrite\Event\Event $deletes */
/** @var Appwrite\Event\Event $database */ /** @var Appwrite\Event\Event $database */
/** @var Appwrite\Event\Event $functions */ /** @var Appwrite\Event\Event $functions */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
Storage::setDevice('files', new Local(APP_STORAGE_UPLOADS.'/app-'.$project->getId())); Storage::setDevice('files', new Local(APP_STORAGE_UPLOADS.'/app-'.$project->getId()));
Storage::setDevice('functions', new Local(APP_STORAGE_FUNCTIONS.'/app-'.$project->getId())); Storage::setDevice('functions', new Local(APP_STORAGE_FUNCTIONS.'/app-'.$project->getId()));
@ -38,7 +38,7 @@ App::init(function ($utopia, $request, $response, $project, $user, $events, $aud
/* /*
* Abuse Check * Abuse Check
*/ */
$timeLimit = new TimeLimit($route->getLabel('abuse-key', 'url:{url},ip:{ip}'), $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForInternal); $timeLimit = new TimeLimit($route->getLabel('abuse-key', 'url:{url},ip:{ip}'), $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject);
$timeLimit $timeLimit
->setParam('{userId}', $user->getId()) ->setParam('{userId}', $user->getId())
->setParam('{userAgent}', $request->getUserAgent('')) ->setParam('{userAgent}', $request->getUserAgent(''))
@ -120,7 +120,7 @@ App::init(function ($utopia, $request, $response, $project, $user, $events, $aud
$database $database
->setParam('projectId', $project->getId()) ->setParam('projectId', $project->getId())
; ;
}, ['utopia', 'request', 'response', 'project', 'user', 'events', 'audits', 'usage', 'deletes', 'database', 'dbForInternal', 'mode'], 'api'); }, ['utopia', 'request', 'response', 'project', 'user', 'events', 'audits', 'usage', 'deletes', 'database', 'dbForProject', 'mode'], 'api');
App::init(function ($utopia, $request, $project) { App::init(function ($utopia, $request, $project) {
/** @var Utopia\App $utopia */ /** @var Utopia\App $utopia */

View file

@ -78,54 +78,70 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
$dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */ $dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */
if(!$dbForConsole->exists()) { Console::success('[Setup] - Server database init started...');
Console::success('[Setup] - Server database init started...'); $collections = Config::getParam('collections', []); /** @var array $collections */
$collections = Config::getParam('collections', []); /** @var array $collections */
if(!$dbForConsole->exists(App::getEnv('_APP_DB_SCHEMA', 'appwrite'))) {
$redis->flushAll(); $redis->flushAll();
$dbForConsole->create(); Console::success('[Setup] - Creating database: appwrite...');
$dbForConsole->create(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
}
try {
Console::success('[Setup] - Creating metadata table: appwrite...');
$dbForConsole->createMetadata();
} catch (\Throwable $th) {
Console::success('[Setup] - Skip: metadata table already exists');
}
if($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) {
$audit = new Audit($dbForConsole); $audit = new Audit($dbForConsole);
$audit->setup(); $audit->setup();
}
if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) {
$adapter = new TimeLimit("", 0, 1, $dbForConsole); $adapter = new TimeLimit("", 0, 1, $dbForConsole);
$adapter->setup(); $adapter->setup();
}
foreach ($collections as $key => $collection) { foreach ($collections as $key => $collection) {
Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...'); if(!$dbForConsole->getCollection($key)->isEmpty()) {
continue;
$attributes = [];
$indexes = [];
foreach ($collection['attributes'] as $attribute) {
$attributes[] = new Document([
'$id' => $attribute['$id'],
'type' => $attribute['type'],
'size' => $attribute['size'],
'required' => $attribute['required'],
'signed' => $attribute['signed'],
'array' => $attribute['array'],
'filters' => $attribute['filters'],
]);
}
foreach ($collection['indexes'] as $index) {
$indexes[] = new Document([
'$id' => $index['$id'],
'type' => $index['type'],
'attributes' => $index['attributes'],
'lengths' => $index['lengths'],
'orders' => $index['orders'],
]);
}
$dbForConsole->createCollection($key, $attributes, $indexes);
} }
Console::success('[Setup] - Server database init completed...'); Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...');
$attributes = [];
$indexes = [];
foreach ($collection['attributes'] as $attribute) {
$attributes[] = new Document([
'$id' => $attribute['$id'],
'type' => $attribute['type'],
'size' => $attribute['size'],
'required' => $attribute['required'],
'signed' => $attribute['signed'],
'array' => $attribute['array'],
'filters' => $attribute['filters'],
]);
}
foreach ($collection['indexes'] as $index) {
$indexes[] = new Document([
'$id' => $index['$id'],
'type' => $index['type'],
'attributes' => $index['attributes'],
'lengths' => $index['lengths'],
'orders' => $index['orders'],
]);
}
$dbForConsole->createCollection($key, $attributes, $indexes);
} }
Console::success('[Setup] - Server database init completed...');
}); });
Console::success('Server started successfully (max payload is '.number_format($payloadSize).' bytes)'); Console::success('Server started successfully (max payload is '.number_format($payloadSize).' bytes)');

View file

@ -392,7 +392,7 @@ $register->set('dbPool', function () { // Register DB connection
->withOptions([ ->withOptions([
PDO::ATTR_ERRMODE => App::isDevelopment() ? PDO::ERRMODE_WARNING : PDO::ERRMODE_SILENT, // If in production mode, warnings are not displayed PDO::ATTR_ERRMODE => App::isDevelopment() ? PDO::ERRMODE_WARNING : PDO::ERRMODE_SILENT, // If in production mode, warnings are not displayed
]) ])
, 16); , 64);
return $pool; return $pool;
}); });
@ -412,7 +412,7 @@ $register->set('redisPool', function () {
->withPort($redisPort) ->withPort($redisPort)
->withAuth($redisAuth) ->withAuth($redisAuth)
->withDbIndex(0) ->withDbIndex(0)
, 16); , 64);
return $pool; return $pool;
}); });
@ -602,7 +602,7 @@ App::setResource('usage', function($register) {
return new Stats($register->get('statsd')); return new Stats($register->get('statsd'));
}, ['register']); }, ['register']);
App::setResource('clients', function($request, $console, $project) { App::setResource('clients', function ($request, $console, $project) {
$console->setAttribute('platforms', [ // Always allow current host $console->setAttribute('platforms', [ // Always allow current host
'$collection' => 'platforms', '$collection' => 'platforms',
'name' => 'Current Host', 'name' => 'Current Host',
@ -614,34 +614,35 @@ App::setResource('clients', function($request, $console, $project) {
* Get All verified client URLs for both console and current projects * Get All verified client URLs for both console and current projects
* + Filter for duplicated entries * + Filter for duplicated entries
*/ */
$clientsConsole = \array_map(function ($node) { $clientsConsole = \array_map(
return $node['hostname']; fn ($node) => $node['hostname'],
}, \array_filter($console->getAttribute('platforms', []), function ($node) { \array_filter(
if (isset($node['type']) && $node['type'] === 'web' && isset($node['hostname']) && !empty($node['hostname'])) { $console->getAttribute('platforms', []),
return true; fn ($node) => (isset($node['type']) && $node['type'] === 'web' && isset($node['hostname']) && !empty($node['hostname']))
} )
);
return false; $clients = \array_unique(
})); \array_merge(
$clientsConsole,
$clients = \array_unique(\array_merge($clientsConsole, \array_map(function ($node) { \array_map(
return $node['hostname']; fn ($node) => $node['hostname'],
}, \array_filter($project->getAttribute('platforms', []), function ($node) { \array_filter(
if (isset($node['type']) && $node['type'] === 'web' && isset($node['hostname']) && !empty($node['hostname'])) { $project->getAttribute('platforms', []),
return true; fn ($node) => (isset($node['type']) && $node['type'] === 'web' && isset($node['hostname']) && !empty($node['hostname']))
} )
)
return false; )
})))); );
return $clients; return $clients;
}, ['request', 'console', 'project']); }, ['request', 'console', 'project']);
App::setResource('user', function($mode, $project, $console, $request, $response, $dbForInternal, $dbForConsole) { App::setResource('user', function($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Database $dbForConsole */ /** @var Utopia\Database\Database $dbForConsole */
/** @var string $mode */ /** @var string $mode */
@ -675,7 +676,7 @@ App::setResource('user', function($mode, $project, $console, $request, $response
$user = new Document(['$id' => '', '$collection' => 'users']); $user = new Document(['$id' => '', '$collection' => 'users']);
} }
else { else {
$user = $dbForInternal->getDocument('users', Auth::$unique); $user = $dbForProject->getDocument('users', Auth::$unique);
} }
} }
else { else {
@ -710,7 +711,7 @@ App::setResource('user', function($mode, $project, $console, $request, $response
$jwtSessionId = $payload['sessionId'] ?? ''; $jwtSessionId = $payload['sessionId'] ?? '';
if($jwtUserId && $jwtSessionId) { if($jwtUserId && $jwtSessionId) {
$user = $dbForInternal->getDocument('users', $jwtUserId); $user = $dbForProject->getDocument('users', $jwtUserId);
} }
if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token
@ -719,7 +720,7 @@ App::setResource('user', function($mode, $project, $console, $request, $response
} }
return $user; return $user;
}, ['mode', 'project', 'console', 'request', 'response', 'dbForInternal', 'dbForConsole']); }, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole']);
App::setResource('project', function($dbForConsole, $request, $console) { App::setResource('project', function($dbForConsole, $request, $console) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
@ -781,20 +782,12 @@ App::setResource('console', function() {
]); ]);
}, []); }, []);
App::setResource('dbForInternal', function($db, $cache, $project) { App::setResource('dbForProject', function($db, $cache, $project) {
$cache = new Cache(new RedisCache($cache)); $cache = new Cache(new RedisCache($cache));
$database = new Database(new MariaDB($db), $cache); $database = new Database(new MariaDB($db), $cache);
$database->setNamespace('project_'.$project->getId().'_internal'); $database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$database->setNamespace('_project_'.$project->getId());
return $database;
}, ['db', 'cache', 'project']);
App::setResource('dbForExternal', function($db, $cache, $project) {
$cache = new Cache(new RedisCache($cache));
$database = new Database(new MariaDB($db), $cache);
$database->setNamespace('project_'.$project->getId().'_external');
return $database; return $database;
}, ['db', 'cache', 'project']); }, ['db', 'cache', 'project']);
@ -803,7 +796,8 @@ App::setResource('dbForConsole', function($db, $cache) {
$cache = new Cache(new RedisCache($cache)); $cache = new Cache(new RedisCache($cache));
$database = new Database(new MariaDB($db), $cache); $database = new Database(new MariaDB($db), $cache);
$database->setNamespace('project_console_internal'); $database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$database->setNamespace('_project_console');
return $database; return $database;
}, ['db', 'cache']); }, ['db', 'cache']);

View file

@ -58,6 +58,7 @@ function getDatabase(Registry &$register, string $namespace)
$cache = new Cache(new RedisCache($redis)); $cache = new Cache(new RedisCache($redis));
$database = new Database(new MariaDB($db), $cache); $database = new Database(new MariaDB($db), $cache);
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$database->setNamespace($namespace); $database->setNamespace($namespace);
return [ return [
@ -77,7 +78,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
*/ */
go(function () use ($register, $containerId, &$statsDocument) { go(function () use ($register, $containerId, &$statsDocument) {
try { try {
[$database, $returnDatabase] = getDatabase($register, 'project_console_internal'); [$database, $returnDatabase] = getDatabase($register, '_project_console');
$document = new Document([ $document = new Document([
'$id' => $database->getId(), '$id' => $database->getId(),
'$collection' => 'realtime', '$collection' => 'realtime',
@ -133,7 +134,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
} }
try { try {
[$database, $returnDatabase] = getDatabase($register, 'project_console_internal'); [$database, $returnDatabase] = getDatabase($register, '_project_console');
$statsDocument $statsDocument
->setAttribute('timestamp', time()) ->setAttribute('timestamp', time())
@ -163,7 +164,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
*/ */
if ($realtime->hasSubscriber('console', 'role:member', 'project')) { if ($realtime->hasSubscriber('console', 'role:member', 'project')) {
[$database, $returnDatabase] = getDatabase($register, 'project_console_internal'); [$database, $returnDatabase] = getDatabase($register, '_project_console');
$payload = []; $payload = [];
@ -267,7 +268,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
return; return;
} }
[$database, $returnDatabase] = getDatabase($register, 'project_' . $projectId . '_internal'); [$database, $returnDatabase] = getDatabase($register, '_project_' . $projectId);
$user = $database->getDocument('users', $userId); $user = $database->getDocument('users', $userId);
@ -340,7 +341,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
$cache = new Cache(new RedisCache($redis)); $cache = new Cache(new RedisCache($redis));
$database = new Database(new MariaDB($db), $cache); $database = new Database(new MariaDB($db), $cache);
$database->setNamespace('project_' . $project->getId() . '_internal'); $database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$database->setNamespace('_project_' . $project->getId());
/* /*
* Project Check * Project Check
@ -444,7 +446,8 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
$cache = new Cache(new RedisCache($redis)); $cache = new Cache(new RedisCache($redis));
$database = new Database(new MariaDB($db), $cache); $database = new Database(new MariaDB($db), $cache);
$database->setNamespace('project_' . $realtime->connections[$connection]['projectId'] . '_internal'); $database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$database->setNamespace('_project_' . $realtime->connections[$connection]['projectId']);
/* /*
* Abuse Check * Abuse Check

View file

@ -223,7 +223,9 @@ $cli
$cacheAdapter = new Cache(new Redis($redis)); $cacheAdapter = new Cache(new Redis($redis));
$dbForProject = new Database(new MariaDB($db), $cacheAdapter); $dbForProject = new Database(new MariaDB($db), $cacheAdapter);
$dbForConsole = new Database(new MariaDB($db), $cacheAdapter); $dbForConsole = new Database(new MariaDB($db), $cacheAdapter);
$dbForConsole->setNamespace('project_console_internal'); $dbForProject->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$dbForConsole->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$dbForConsole->setNamespace('_project_console');
$latestTime = []; $latestTime = [];
@ -276,9 +278,7 @@ $cli
$filters = $options['filters'] ?? []; // Some metrics might have additional filters, like function's status $filters = $options['filters'] ?? []; // Some metrics might have additional filters, like function's status
if (!empty($filters)) { if (!empty($filters)) {
$filters = ' AND ' . implode(' AND ', array_map(function ($filter, $value) { $filters = ' AND ' . implode(' AND ', array_map(fn ($filter, $value) => "\"{$filter}\"='{$value}'", array_keys($filters), array_values($filters)));
return "\"{$filter}\"='{$value}'";
}, array_keys($filters), array_values($filters)));
} else { } else {
$filters = ''; $filters = '';
} }
@ -291,7 +291,7 @@ $cli
$projectId = $point['projectId']; $projectId = $point['projectId'];
if (!empty($projectId) && $projectId !== 'console') { if (!empty($projectId) && $projectId !== 'console') {
$dbForProject->setNamespace('project_' . $projectId . '_internal'); $dbForProject->setNamespace('_project_' . $projectId);
$metricUpdated = $metric; $metricUpdated = $metric;
if (!empty($groupBy)) { if (!empty($groupBy)) {
@ -371,7 +371,7 @@ $cli
$projectId = $project->getId(); $projectId = $project->getId();
// Get total storage // Get total storage
$dbForProject->setNamespace('project_' . $projectId . '_internal'); $dbForProject->setNamespace('_project_' . $projectId);
$storageTotal = $dbForProject->sum('files', 'sizeOriginal') + $dbForProject->sum('tags', 'size'); $storageTotal = $dbForProject->sum('files', 'sizeOriginal') + $dbForProject->sum('tags', 'size');
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes $time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
@ -435,9 +435,8 @@ $cli
foreach ($collections as $collection => $options) { foreach ($collections as $collection => $options) {
try { try {
$dbForProject->setNamespace("project_{$projectId}_{$options['namespace']}"); $dbForProject->setNamespace("_project_{$projectId}");
$count = $dbForProject->count($collection); $count = $dbForProject->count($collection);
$dbForProject->setNamespace("project_{$projectId}_internal");
$metricPrefix = $options['metricPrefix'] ?? ''; $metricPrefix = $options['metricPrefix'] ?? '';
$metric = empty($metricPrefix) ? "{$collection}.count" : "{$metricPrefix}.{$collection}.count"; $metric = empty($metricPrefix) ? "{$collection}.count" : "{$metricPrefix}.{$collection}.count";
@ -491,7 +490,7 @@ $cli
$subCollectionCounts = []; //total project level count of sub collections $subCollectionCounts = []; //total project level count of sub collections
do { // Loop over all the parent collection document for each sub collection do { // Loop over all the parent collection document for each sub collection
$dbForProject->setNamespace("project_{$projectId}_{$options['namespace']}"); $dbForProject->setNamespace("_project_{$projectId}");
$parents = $dbForProject->find($collection, [], 100, cursor: $latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections $parents = $dbForProject->find($collection, [], 100, cursor: $latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections
if (empty($parents)) { if (empty($parents)) {
@ -502,12 +501,12 @@ $cli
foreach ($parents as $parent) { foreach ($parents as $parent) {
foreach ($subCollections as $subCollection => $subOptions) { // Sub collection counts, like database.collections.collectionId.documents.count foreach ($subCollections as $subCollection => $subOptions) { // Sub collection counts, like database.collections.collectionId.documents.count
$dbForProject->setNamespace("project_{$projectId}_{$subOptions['namespace']}"); $dbForProject->setNamespace("_project_{$projectId}");
$count = $dbForProject->count($parent->getId()); $count = $dbForProject->count($parent->getId());
$subCollectionCounts[$subCollection] = ($subCollectionCounts[$subCollection] ?? 0) + $count; // Project level counts for sub collections like database.documents.count $subCollectionCounts[$subCollection] = ($subCollectionCounts[$subCollection] ?? 0) + $count; // Project level counts for sub collections like database.documents.count
$dbForProject->setNamespace("project_{$projectId}_internal"); $dbForProject->setNamespace("_project_{$projectId}");
$metric = empty($metricPrefix) ? "{$collection}.{$parent->getId()}.{$subCollection}.count" : "{$metricPrefix}.{$collection}.{$parent->getId()}.{$subCollection}.count"; $metric = empty($metricPrefix) ? "{$collection}.{$parent->getId()}.{$subCollection}.count" : "{$metricPrefix}.{$collection}.{$parent->getId()}.{$subCollection}.count";
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes $time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
@ -557,7 +556,7 @@ $cli
* Inserting project level counts for sub collections like database.documents.count * Inserting project level counts for sub collections like database.documents.count
*/ */
foreach ($subCollectionCounts as $subCollection => $count) { foreach ($subCollectionCounts as $subCollection => $count) {
$dbForProject->setNamespace("project_{$projectId}_internal"); $dbForProject->setNamespace("_project_{$projectId}");
$metric = empty($metricPrefix) ? "{$subCollection}.count" : "{$metricPrefix}.{$subCollection}.count"; $metric = empty($metricPrefix) ? "{$subCollection}.count" : "{$metricPrefix}.{$subCollection}.count";

View file

@ -380,7 +380,7 @@ services:
- _APP_REDIS_PASS - _APP_REDIS_PASS
mariadb: mariadb:
image: appwrite/mariadb:1.2.0 # fix issues when upgrading using: mysql_upgrade -u root -p image: mariadb:10.7 # fix issues when upgrading using: mysql_upgrade -u root -p
container_name: appwrite-mariadb container_name: appwrite-mariadb
restart: unless-stopped restart: unless-stopped
networks: networks:

View file

@ -28,8 +28,8 @@ class AuditsV1 extends Worker
$ip = $this->args['ip']; $ip = $this->args['ip'];
$data = $this->args['data']; $data = $this->args['data'];
$dbForInternal = $this->getInternalDB($projectId); $dbForProject = $this->getProjectDB($projectId);
$audit = new Audit($dbForInternal); $audit = new Audit($dbForProject);
$audit->log($userId, $event, $resource, $userAgent, $ip, '', [ $audit->log($userId, $event, $resource, $userAgent, $ip, '', [
'userName' => $userName, 'userName' => $userName,

View file

@ -160,7 +160,7 @@ class CertificatesV1 extends Worker
'certificateId' => $certificate->getId(), 'certificateId' => $certificate->getId(),
])); ]));
$certificate = $dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate); $certificate = $dbForConsole->updateDocument('domains', $certificate->getId(), $certificate);
if(!$certificate) { if(!$certificate) {
throw new Exception('Failed saving domain to DB'); throw new Exception('Failed saving domain to DB');

View file

@ -69,8 +69,7 @@ class DatabaseV1 extends Worker
protected function createAttribute(Document $collection, Document $attribute, string $projectId): void protected function createAttribute(Document $collection, Document $attribute, string $projectId): void
{ {
$dbForConsole = $this->getConsoleDB(); $dbForConsole = $this->getConsoleDB();
$dbForInternal = $this->getInternalDB($projectId); $dbForProject = $this->getProjectDB($projectId);
$dbForExternal = $this->getExternalDB($projectId);
$event = 'database.attributes.update'; $event = 'database.attributes.update';
$collectionId = $collection->getId(); $collectionId = $collection->getId();
@ -87,13 +86,13 @@ class DatabaseV1 extends Worker
$project = $dbForConsole->getDocument('projects', $projectId); $project = $dbForConsole->getDocument('projects', $projectId);
try { try {
if(!$dbForExternal->createAttribute($collectionId, $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { if(!$dbForProject->createAttribute('collection_' . $collectionId, $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) {
throw new Exception('Failed to create Attribute'); throw new Exception('Failed to create Attribute');
} }
$dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available')); $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available'));
} catch (\Throwable $th) { } catch (\Throwable $th) {
Console::error($th->getMessage()); Console::error($th->getMessage());
$dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed')); $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed'));
} finally { } finally {
$target = Realtime::fromPayload($event, $attribute, $project); $target = Realtime::fromPayload($event, $attribute, $project);
@ -110,7 +109,7 @@ class DatabaseV1 extends Worker
); );
} }
$dbForInternal->deleteCachedDocument('collections', $collectionId); $dbForProject->deleteCachedDocument('collections', $collectionId);
} }
/** /**
@ -121,8 +120,7 @@ class DatabaseV1 extends Worker
protected function deleteAttribute(Document $collection, Document $attribute, string $projectId): void protected function deleteAttribute(Document $collection, Document $attribute, string $projectId): void
{ {
$dbForConsole = $this->getConsoleDB(); $dbForConsole = $this->getConsoleDB();
$dbForInternal = $this->getInternalDB($projectId); $dbForProject = $this->getProjectDB($projectId);
$dbForExternal = $this->getExternalDB($projectId);
$event = 'database.attributes.delete'; $event = 'database.attributes.delete';
$collectionId = $collection->getId(); $collectionId = $collection->getId();
@ -137,13 +135,13 @@ class DatabaseV1 extends Worker
// - failed: attribute was never created // - failed: attribute was never created
// - stuck: attribute was available but cannot be removed // - stuck: attribute was available but cannot be removed
try { try {
if($status !== 'failed' && !$dbForExternal->deleteAttribute($collectionId, $key)) { if($status !== 'failed' && !$dbForProject->deleteAttribute('collection_' . $collectionId, $key)) {
throw new Exception('Failed to delete Attribute'); throw new Exception('Failed to delete Attribute');
} }
$dbForInternal->deleteDocument('attributes', $attribute->getId()); $dbForProject->deleteDocument('attributes', $attribute->getId());
} catch (\Throwable $th) { } catch (\Throwable $th) {
Console::error($th->getMessage()); Console::error($th->getMessage());
$dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'stuck')); $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'stuck'));
} finally { } finally {
$target = Realtime::fromPayload($event, $attribute, $project); $target = Realtime::fromPayload($event, $attribute, $project);
@ -182,7 +180,7 @@ class DatabaseV1 extends Worker
$orders = \array_values(\array_diff($orders, [$orders[$found]])); $orders = \array_values(\array_diff($orders, [$orders[$found]]));
if (empty($attributes)) { if (empty($attributes)) {
$dbForInternal->deleteDocument('indexes', $index->getId()); $dbForProject->deleteDocument('indexes', $index->getId());
} else { } else {
$index $index
->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN) ->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN)
@ -205,13 +203,13 @@ class DatabaseV1 extends Worker
if ($exists) { // Delete the duplicate if created, else update in db if ($exists) { // Delete the duplicate if created, else update in db
$this->deleteIndex($collection, $index, $projectId); $this->deleteIndex($collection, $index, $projectId);
} else { } else {
$dbForInternal->updateDocument('indexes', $index->getId(), $index); $dbForProject->updateDocument('indexes', $index->getId(), $index);
} }
} }
} }
} }
$dbForInternal->deleteCachedDocument('collections', $collectionId); $dbForProject->deleteCachedDocument('collections', $collectionId);
} }
/** /**
@ -222,8 +220,7 @@ class DatabaseV1 extends Worker
protected function createIndex(Document $collection, Document $index, string $projectId): void protected function createIndex(Document $collection, Document $index, string $projectId): void
{ {
$dbForConsole = $this->getConsoleDB(); $dbForConsole = $this->getConsoleDB();
$dbForInternal = $this->getInternalDB($projectId); $dbForProject = $this->getProjectDB($projectId);
$dbForExternal = $this->getExternalDB($projectId);
$event = 'database.indexes.update'; $event = 'database.indexes.update';
$collectionId = $collection->getId(); $collectionId = $collection->getId();
@ -235,13 +232,13 @@ class DatabaseV1 extends Worker
$project = $dbForConsole->getDocument('projects', $projectId); $project = $dbForConsole->getDocument('projects', $projectId);
try { try {
if(!$dbForExternal->createIndex($collectionId, $key, $type, $attributes, $lengths, $orders)) { if(!$dbForProject->createIndex('collection_' . $collectionId, $key, $type, $attributes, $lengths, $orders)) {
throw new Exception('Failed to create Index'); throw new Exception('Failed to create Index');
} }
$dbForInternal->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available')); $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available'));
} catch (\Throwable $th) { } catch (\Throwable $th) {
Console::error($th->getMessage()); Console::error($th->getMessage());
$dbForInternal->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'failed')); $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'failed'));
} finally { } finally {
$target = Realtime::fromPayload($event, $index, $project); $target = Realtime::fromPayload($event, $index, $project);
@ -258,7 +255,7 @@ class DatabaseV1 extends Worker
); );
} }
$dbForInternal->deleteCachedDocument('collections', $collectionId); $dbForProject->deleteCachedDocument('collections', $collectionId);
} }
/** /**
@ -269,8 +266,7 @@ class DatabaseV1 extends Worker
protected function deleteIndex(Document $collection, Document $index, string $projectId): void protected function deleteIndex(Document $collection, Document $index, string $projectId): void
{ {
$dbForConsole = $this->getConsoleDB(); $dbForConsole = $this->getConsoleDB();
$dbForInternal = $this->getInternalDB($projectId); $dbForProject = $this->getProjectDB($projectId);
$dbForExternal = $this->getExternalDB($projectId);
$collectionId = $collection->getId(); $collectionId = $collection->getId();
$key = $index->getAttribute('key'); $key = $index->getAttribute('key');
@ -279,13 +275,13 @@ class DatabaseV1 extends Worker
$project = $dbForConsole->getDocument('projects', $projectId); $project = $dbForConsole->getDocument('projects', $projectId);
try { try {
if($status !== 'failed' && !$dbForExternal->deleteIndex($collectionId, $key)) { if($status !== 'failed' && !$dbForProject->deleteIndex('collection_' . $collectionId, $key)) {
throw new Exception('Failed to delete index'); throw new Exception('Failed to delete index');
} }
$dbForInternal->deleteDocument('indexes', $index->getId()); $dbForProject->deleteDocument('indexes', $index->getId());
} catch (\Throwable $th) { } catch (\Throwable $th) {
Console::error($th->getMessage()); Console::error($th->getMessage());
$dbForInternal->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'stuck')); $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'stuck'));
} finally { } finally {
$target = Realtime::fromPayload($event, $index, $project); $target = Realtime::fromPayload($event, $index, $project);
@ -302,6 +298,6 @@ class DatabaseV1 extends Worker
); );
} }
$dbForInternal->deleteCachedDocument('collections', $collectionId); $dbForProject->deleteCachedDocument('collections', $collectionId);
} }
} }

View file

@ -100,18 +100,17 @@ class DeletesV1 extends Worker
{ {
$collectionId = $document->getId(); $collectionId = $document->getId();
$dbForInternal = $this->getInternalDB($projectId); $dbForProject = $this->getProjectDB($projectId);
$dbForExternal = $this->getExternalDB($projectId);
$dbForExternal->deleteCollection($collectionId); $dbForProject->deleteCollection($collectionId);
$this->deleteByGroup('attributes', [ $this->deleteByGroup('attributes', [
new Query('collectionId', Query::TYPE_EQUAL, [$collectionId]) new Query('collectionId', Query::TYPE_EQUAL, [$collectionId])
], $dbForInternal); ], $dbForProject);
$this->deleteByGroup('indexes', [ $this->deleteByGroup('indexes', [
new Query('collectionId', Query::TYPE_EQUAL, [$collectionId]) new Query('collectionId', Query::TYPE_EQUAL, [$collectionId])
], $dbForInternal); ], $dbForProject);
} }
/** /**
@ -121,17 +120,17 @@ class DeletesV1 extends Worker
protected function deleteUsageStats(int $timestamp1d, int $timestamp30m) protected function deleteUsageStats(int $timestamp1d, int $timestamp30m)
{ {
$this->deleteForProjectIds(function (string $projectId) use ($timestamp1d, $timestamp30m) { $this->deleteForProjectIds(function (string $projectId) use ($timestamp1d, $timestamp30m) {
$dbForInternal = $this->getInternalDB($projectId); $dbForProject = $this->getProjectDB($projectId);
// Delete Usage stats // Delete Usage stats
$this->deleteByGroup('stats', [ $this->deleteByGroup('stats', [
new Query('time', Query::TYPE_LESSER, [$timestamp1d]), new Query('time', Query::TYPE_LESSER, [$timestamp1d]),
new Query('period', Query::TYPE_EQUAL, ['1d']), new Query('period', Query::TYPE_EQUAL, ['1d']),
], $dbForInternal); ], $dbForProject);
$this->deleteByGroup('stats', [ $this->deleteByGroup('stats', [
new Query('time', Query::TYPE_LESSER, [$timestamp30m]), new Query('time', Query::TYPE_LESSER, [$timestamp30m]),
new Query('period', Query::TYPE_EQUAL, ['30m']), new Query('period', Query::TYPE_EQUAL, ['30m']),
], $dbForInternal); ], $dbForProject);
}); });
} }
@ -146,7 +145,7 @@ class DeletesV1 extends Worker
// Delete Memberships // Delete Memberships
$this->deleteByGroup('memberships', [ $this->deleteByGroup('memberships', [
new Query('teamId', Query::TYPE_EQUAL, [$teamId]) new Query('teamId', Query::TYPE_EQUAL, [$teamId])
], $this->getInternalDB($projectId)); ], $this->getProjectDB($projectId));
} }
/** /**
@ -157,8 +156,7 @@ class DeletesV1 extends Worker
$projectId = $document->getId(); $projectId = $document->getId();
// Delete all DBs // Delete all DBs
$this->getExternalDB($projectId)->delete(); $this->getProjectDB($projectId)->delete($projectId);
$this->getInternalDB($projectId)->delete();
// Delete all storage directories // Delete all storage directories
$uploads = new Local(APP_STORAGE_UPLOADS . '/app-' . $document->getId()); $uploads = new Local(APP_STORAGE_UPLOADS . '/app-' . $document->getId());
@ -180,13 +178,13 @@ class DeletesV1 extends Worker
// Delete Memberships and decrement team membership counts // Delete Memberships and decrement team membership counts
$this->deleteByGroup('memberships', [ $this->deleteByGroup('memberships', [
new Query('userId', Query::TYPE_EQUAL, [$userId]) new Query('userId', Query::TYPE_EQUAL, [$userId])
], $this->getInternalDB($projectId), function (Document $document) use ($projectId) { ], $this->getProjectDB($projectId), function (Document $document) use ($projectId) {
if ($document->getAttribute('confirm')) { // Count only confirmed members if ($document->getAttribute('confirm')) { // Count only confirmed members
$teamId = $document->getAttribute('teamId'); $teamId = $document->getAttribute('teamId');
$team = $this->getInternalDB($projectId)->getDocument('teams', $teamId); $team = $this->getProjectDB($projectId)->getDocument('teams', $teamId);
if (!$team->isEmpty()) { if (!$team->isEmpty()) {
$team = $this->getInternalDB($projectId)->updateDocument('teams', $teamId, new Document(\array_merge($team->getArrayCopy(), [ $team = $this->getProjectDB($projectId)->updateDocument('teams', $teamId, new Document(\array_merge($team->getArrayCopy(), [
'sum' => \max($team->getAttribute('sum', 0) - 1, 0), // Ensure that sum >= 0 'sum' => \max($team->getAttribute('sum', 0) - 1, 0), // Ensure that sum >= 0
]))); ])));
} }
@ -200,11 +198,11 @@ class DeletesV1 extends Worker
protected function deleteExecutionLogs(int $timestamp): void protected function deleteExecutionLogs(int $timestamp): void
{ {
$this->deleteForProjectIds(function (string $projectId) use ($timestamp) { $this->deleteForProjectIds(function (string $projectId) use ($timestamp) {
$dbForInternal = $this->getInternalDB($projectId); $dbForProject = $this->getProjectDB($projectId);
// Delete Executions // Delete Executions
$this->deleteByGroup('executions', [ $this->deleteByGroup('executions', [
new Query('dateCreated', Query::TYPE_LESSER, [$timestamp]) new Query('dateCreated', Query::TYPE_LESSER, [$timestamp])
], $dbForInternal); ], $dbForProject);
}); });
} }
@ -214,11 +212,11 @@ class DeletesV1 extends Worker
protected function deleteRealtimeUsage(int $timestamp): void protected function deleteRealtimeUsage(int $timestamp): void
{ {
$this->deleteForProjectIds(function (string $projectId) use ($timestamp) { $this->deleteForProjectIds(function (string $projectId) use ($timestamp) {
$dbForInternal = $this->getInternalDB($projectId); $dbForProject = $this->getProjectDB($projectId);
// Delete Dead Realtime Logs // Delete Dead Realtime Logs
$this->deleteByGroup('realtime', [ $this->deleteByGroup('realtime', [
new Query('timestamp', Query::TYPE_LESSER, [$timestamp]) new Query('timestamp', Query::TYPE_LESSER, [$timestamp])
], $dbForInternal); ], $dbForProject);
}); });
} }
@ -232,8 +230,8 @@ class DeletesV1 extends Worker
} }
$this->deleteForProjectIds(function (string $projectId) use ($timestamp) { $this->deleteForProjectIds(function (string $projectId) use ($timestamp) {
$dbForInternal = $this->getInternalDB($projectId); $dbForProject = $this->getProjectDB($projectId);
$timeLimit = new TimeLimit("", 0, 1, $dbForInternal); $timeLimit = new TimeLimit("", 0, 1, $dbForProject);
$abuse = new Abuse($timeLimit); $abuse = new Abuse($timeLimit);
$status = $abuse->cleanup($timestamp); $status = $abuse->cleanup($timestamp);
@ -252,8 +250,8 @@ class DeletesV1 extends Worker
throw new Exception('Failed to delete audit logs. No timestamp provided'); throw new Exception('Failed to delete audit logs. No timestamp provided');
} }
$this->deleteForProjectIds(function (string $projectId) use ($timestamp) { $this->deleteForProjectIds(function (string $projectId) use ($timestamp) {
$dbForInternal = $this->getInternalDB($projectId); $dbForProject = $this->getProjectDB($projectId);
$audit = new Audit($dbForInternal); $audit = new Audit($dbForProject);
$status = $audit->cleanup($timestamp); $status = $audit->cleanup($timestamp);
if (!$status) { if (!$status) {
throw new Exception('Failed to delete Audit logs for project' . $projectId); throw new Exception('Failed to delete Audit logs for project' . $projectId);
@ -267,13 +265,13 @@ class DeletesV1 extends Worker
*/ */
protected function deleteFunction(Document $document, string $projectId): void protected function deleteFunction(Document $document, string $projectId): void
{ {
$dbForInternal = $this->getInternalDB($projectId); $dbForProject = $this->getProjectDB($projectId);
$device = new Local(APP_STORAGE_FUNCTIONS . '/app-' . $projectId); $device = new Local(APP_STORAGE_FUNCTIONS . '/app-' . $projectId);
// Delete Tags // Delete Tags
$this->deleteByGroup('tags', [ $this->deleteByGroup('tags', [
new Query('functionId', Query::TYPE_EQUAL, [$document->getId()]) new Query('functionId', Query::TYPE_EQUAL, [$document->getId()])
], $dbForInternal, function (Document $document) use ($device) { ], $dbForProject, function (Document $document) use ($device) {
if ($device->delete($document->getAttribute('path', ''))) { if ($device->delete($document->getAttribute('path', ''))) {
Console::success('Delete code tag: ' . $document->getAttribute('path', '')); Console::success('Delete code tag: ' . $document->getAttribute('path', ''));
@ -285,7 +283,7 @@ class DeletesV1 extends Worker
// Delete Executions // Delete Executions
$this->deleteByGroup('executions', [ $this->deleteByGroup('executions', [
new Query('functionId', Query::TYPE_EQUAL, [$document->getId()]) new Query('functionId', Query::TYPE_EQUAL, [$document->getId()])
], $dbForInternal); ], $dbForProject);
} }

View file

@ -118,7 +118,7 @@ class FunctionsV1 extends Worker
$userId = $this->args['userId'] ?? ''; $userId = $this->args['userId'] ?? '';
$jwt = $this->args['jwt'] ?? ''; $jwt = $this->args['jwt'] ?? '';
$database = $this->getInternalDB($projectId); $database = $this->getProjectDB($projectId);
switch ($trigger) { switch ($trigger) {
case 'event': case 'event':

View file

@ -39,13 +39,13 @@
"appwrite/php-runtimes": "0.6.*", "appwrite/php-runtimes": "0.6.*",
"utopia-php/framework": "0.19.*", "utopia-php/framework": "0.19.*",
"utopia-php/abuse": "0.6.*", "utopia-php/abuse": "0.7.*",
"utopia-php/analytics": "0.2.*", "utopia-php/analytics": "0.2.*",
"utopia-php/audit": "0.7.*", "utopia-php/audit": "0.8.*",
"utopia-php/cache": "0.4.*", "utopia-php/cache": "0.4.*",
"utopia-php/cli": "0.11.*", "utopia-php/cli": "0.11.*",
"utopia-php/config": "0.2.*", "utopia-php/config": "0.2.*",
"utopia-php/database": "0.12.*", "utopia-php/database": "0.13.*",
"utopia-php/locale": "0.4.*", "utopia-php/locale": "0.4.*",
"utopia-php/orchestration": "0.2.*", "utopia-php/orchestration": "0.2.*",
"utopia-php/registry": "0.5.*", "utopia-php/registry": "0.5.*",

44
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "c755b0ae991777da3e44b0442690fa46", "content-hash": "2b1ed15e618832ee86b69cff2dcdd6ac",
"packages": [ "packages": [
{ {
"name": "adhocore/jwt", "name": "adhocore/jwt",
@ -1820,22 +1820,22 @@
}, },
{ {
"name": "utopia-php/abuse", "name": "utopia-php/abuse",
"version": "0.6.3", "version": "0.7.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/utopia-php/abuse.git", "url": "https://github.com/utopia-php/abuse.git",
"reference": "d63e928c2c50b367495a499a85ba9806ee274c5e" "reference": "52fb20e39e2e9619948bc0a73b52e10caa71350d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/d63e928c2c50b367495a499a85ba9806ee274c5e", "url": "https://api.github.com/repos/utopia-php/abuse/zipball/52fb20e39e2e9619948bc0a73b52e10caa71350d",
"reference": "d63e928c2c50b367495a499a85ba9806ee274c5e", "reference": "52fb20e39e2e9619948bc0a73b52e10caa71350d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-pdo": "*", "ext-pdo": "*",
"php": ">=7.4", "php": ">=8.0",
"utopia-php/database": ">=0.6 <1.0" "utopia-php/database": ">=0.11 <1.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^9.4", "phpunit/phpunit": "^9.4",
@ -1867,9 +1867,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/utopia-php/abuse/issues", "issues": "https://github.com/utopia-php/abuse/issues",
"source": "https://github.com/utopia-php/abuse/tree/0.6.3" "source": "https://github.com/utopia-php/abuse/tree/0.7.0"
}, },
"time": "2021-08-16T18:38:31+00:00" "time": "2021-12-27T13:06:45+00:00"
}, },
{ {
"name": "utopia-php/analytics", "name": "utopia-php/analytics",
@ -1928,21 +1928,21 @@
}, },
{ {
"name": "utopia-php/audit", "name": "utopia-php/audit",
"version": "0.7.0", "version": "0.8.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/utopia-php/audit.git", "url": "https://github.com/utopia-php/audit.git",
"reference": "485cdd2354db7eb8f7aa74bbe39c39b583e99c04" "reference": "b46dc42614a69437c45eb229249b6a6d000122c1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/485cdd2354db7eb8f7aa74bbe39c39b583e99c04", "url": "https://api.github.com/repos/utopia-php/audit/zipball/b46dc42614a69437c45eb229249b6a6d000122c1",
"reference": "485cdd2354db7eb8f7aa74bbe39c39b583e99c04", "reference": "b46dc42614a69437c45eb229249b6a6d000122c1",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-pdo": "*", "ext-pdo": "*",
"php": ">=7.4", "php": ">=8.0",
"utopia-php/database": ">=0.11 <1.0" "utopia-php/database": ">=0.11 <1.0"
}, },
"require-dev": { "require-dev": {
@ -1975,9 +1975,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/utopia-php/audit/issues", "issues": "https://github.com/utopia-php/audit/issues",
"source": "https://github.com/utopia-php/audit/tree/0.7.0" "source": "https://github.com/utopia-php/audit/tree/0.8.0"
}, },
"time": "2021-11-17T17:23:42+00:00" "time": "2021-12-27T13:05:56+00:00"
}, },
{ {
"name": "utopia-php/cache", "name": "utopia-php/cache",
@ -2138,16 +2138,16 @@
}, },
{ {
"name": "utopia-php/database", "name": "utopia-php/database",
"version": "0.12.1", "version": "0.13.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/utopia-php/database.git", "url": "https://github.com/utopia-php/database.git",
"reference": "af512b7a00cc7c6e30fa03efbc5fd7e77a93e2df" "reference": "2e13987364f4966ec8a36784d4fb5df3a84e4e78"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/af512b7a00cc7c6e30fa03efbc5fd7e77a93e2df", "url": "https://api.github.com/repos/utopia-php/database/zipball/2e13987364f4966ec8a36784d4fb5df3a84e4e78",
"reference": "af512b7a00cc7c6e30fa03efbc5fd7e77a93e2df", "reference": "2e13987364f4966ec8a36784d4fb5df3a84e4e78",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2195,9 +2195,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/utopia-php/database/issues", "issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.12.1" "source": "https://github.com/utopia-php/database/tree/0.13.0"
}, },
"time": "2021-12-13T14:57:32+00:00" "time": "2021-12-27T12:59:50+00:00"
}, },
{ {
"name": "utopia-php/domains", "name": "utopia-php/domains",

View file

@ -288,6 +288,7 @@ services:
- mariadb - mariadb
environment: environment:
- _APP_ENV - _APP_ENV
- _APP_DOMAIN_TARGET
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
- _APP_REDIS_HOST - _APP_REDIS_HOST
- _APP_REDIS_PORT - _APP_REDIS_PORT
@ -441,7 +442,7 @@ services:
- _APP_REDIS_PASS - _APP_REDIS_PASS
mariadb: mariadb:
image: appwrite/mariadb:1.2.0 # fix issues when upgrading using: mysql_upgrade -u root -p image: mariadb:10.7 # fix issues when upgrading using: mysql_upgrade -u root -p
container_name: appwrite-mariadb container_name: appwrite-mariadb
networks: networks:
- appwrite - appwrite

View file

@ -11,6 +11,7 @@ use Redis;
use Swoole\Runtime; use Swoole\Runtime;
use Throwable; use Throwable;
use Utopia\Abuse\Adapters\TimeLimit; use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\App;
use Utopia\Audit\Audit; use Utopia\Audit\Audit;
use Utopia\Cache\Cache; use Utopia\Cache\Cache;
use Utopia\CLI\Console; use Utopia\CLI\Console;
@ -29,8 +30,7 @@ global $register;
class V11 extends Migration class V11 extends Migration
{ {
protected Database $dbInternal; protected Database $dbProject;
protected Database $dbExternal;
protected Database $dbConsole; protected Database $dbConsole;
protected array $oldCollections; protected array $oldCollections;
@ -43,10 +43,12 @@ class V11 extends Migration
if (!is_null($cache)) { if (!is_null($cache)) {
$cacheAdapter = new Cache(new RedisCache($this->cache)); $cacheAdapter = new Cache(new RedisCache($this->cache));
$this->dbInternal = new Database(new MariaDB($this->db), $cacheAdapter); // namespace is set on execution $this->dbProject = new Database(new MariaDB($this->db), $cacheAdapter); // namespace is set on execution
$this->dbExternal = new Database(new MariaDB($this->db), $cacheAdapter); // namespace is set on execution
$this->dbConsole = new Database(new MariaDB($this->db), $cacheAdapter); $this->dbConsole = new Database(new MariaDB($this->db), $cacheAdapter);
$this->dbConsole->setNamespace('project_console_internal');
$this->dbProject->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$this->dbConsole->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$this->dbConsole->setNamespace('_project_console');
} }
$this->newCollections = Config::getParam('collections', []); $this->newCollections = Config::getParam('collections', []);
@ -60,8 +62,7 @@ class V11 extends Migration
$oldProject = $this->project; $oldProject = $this->project;
$this->dbInternal->setNamespace('project_' . $oldProject->getId() . '_internal'); $this->dbProject->setNamespace('_project_' . $oldProject->getId());
$this->dbExternal->setNamespace('project_' . $oldProject->getId() . '_external');
Console::info(''); Console::info('');
Console::info('------------------------------------'); Console::info('------------------------------------');
@ -85,26 +86,18 @@ class V11 extends Migration
} }
/** /**
* Create internal DB tables * Create internal tables
*/ */
if (!$this->dbInternal->exists()) { if (!$this->dbProject->exists('appwrite')) {
$this->dbInternal->create(); $this->dbProject->create('appwrite');
Console::log('Created internal tables for : ' . $project->getAttribute('name') . ' (' . $project->getId() . ')'); Console::log('Created internal tables for : ' . $project->getAttribute('name') . ' (' . $project->getId() . ')');
} }
/**
* Create external DB tables
*/
if (!$this->dbExternal->exists()) {
$this->dbExternal->create();
Console::log('Created external tables for : ' . $project->getAttribute('name') . ' (' . $project->getId() . ')');
}
/** /**
* Create Audit tables * Create Audit tables
*/ */
if ($this->dbInternal->getCollection(Audit::COLLECTION)->isEmpty()) { if ($this->dbProject->getCollection(Audit::COLLECTION)->isEmpty()) {
$audit = new Audit($this->dbInternal); $audit = new Audit($this->dbProject);
$audit->setup(); $audit->setup();
Console::log('Created audit tables for : ' . $project->getAttribute('name') . ' (' . $project->getId() . ')'); Console::log('Created audit tables for : ' . $project->getAttribute('name') . ' (' . $project->getId() . ')');
} }
@ -112,8 +105,8 @@ class V11 extends Migration
/** /**
* Create Abuse tables * Create Abuse tables
*/ */
if ($this->dbInternal->getCollection(TimeLimit::COLLECTION)->isEmpty()) { if ($this->dbProject->getCollection(TimeLimit::COLLECTION)->isEmpty()) {
$adapter = new TimeLimit("", 0, 1, $this->dbInternal); $adapter = new TimeLimit("", 0, 1, $this->dbProject);
$adapter->setup(); $adapter->setup();
Console::log('Created abuse tables for : ' . $project->getAttribute('name') . ' (' . $project->getId() . ')'); Console::log('Created abuse tables for : ' . $project->getAttribute('name') . ' (' . $project->getId() . ')');
} }
@ -122,7 +115,7 @@ class V11 extends Migration
* Create internal collections for Project * Create internal collections for Project
*/ */
foreach ($this->newCollections as $key => $collection) { foreach ($this->newCollections as $key => $collection) {
if (!$this->dbInternal->getCollection($key)->isEmpty()) continue; // Skip if project collection already exists if (!$this->dbProject->getCollection($key)->isEmpty()) continue; // Skip if project collection already exists
$attributes = []; $attributes = [];
$indexes = []; $indexes = [];
@ -149,7 +142,7 @@ class V11 extends Migration
]); ]);
} }
$this->dbInternal->createCollection($key, $attributes, $indexes); $this->dbProject->createCollection($key, $attributes, $indexes);
} }
if ($this->options['migrateCollections']) { if ($this->options['migrateCollections']) {
$this->migrateExternalCollections(); $this->migrateExternalCollections();
@ -198,8 +191,8 @@ class V11 extends Migration
} }
try { try {
if ($this->dbInternal->getDocument($new->getCollection(), $new->getId())->isEmpty()) { if ($this->dbProject->getDocument($new->getCollection(), $new->getId())->isEmpty()) {
$this->dbInternal->createDocument($new->getCollection(), $new); $this->dbProject->createDocument($new->getCollection(), $new);
} }
} catch (\Throwable $th) { } catch (\Throwable $th) {
Console::error('Failed to update document: ' . $th->getMessage()); Console::error('Failed to update document: ' . $th->getMessage());
@ -249,10 +242,10 @@ class V11 extends Migration
$id = $oldCollection->getId(); $id = $oldCollection->getId();
$permissions = $oldCollection->getPermissions(); $permissions = $oldCollection->getPermissions();
$name = $oldCollection->getAttribute('name'); $name = $oldCollection->getAttribute('name');
$newCollection = $this->dbExternal->getCollection($id); $newCollection = $this->dbProject->getCollection('collection_' . $id);
if ($newCollection->isEmpty()) { if ($newCollection->isEmpty()) {
$this->dbExternal->createCollection($id); $this->dbProject->createCollection('collection_' . $id);
/** /**
* Migrate permissions * Migrate permissions
*/ */
@ -263,13 +256,13 @@ class V11 extends Migration
* Suffix collection name with a subsequent number to make it unique if possible. * Suffix collection name with a subsequent number to make it unique if possible.
*/ */
$suffix = 1; $suffix = 1;
while ($this->dbInternal->findOne('collections', [ while ($this->dbProject->findOne('collections', [
new Query('name', Query::TYPE_EQUAL, [$name]) new Query('name', Query::TYPE_EQUAL, [$name])
])) { ])) {
$name .= ' - ' . $suffix++; $name .= ' - ' . $suffix++;
} }
$this->dbInternal->createDocument('collections', new Document([ $this->dbProject->createDocument('collections', new Document([
'$id' => $id, '$id' => $id,
'$read' => [], '$read' => [],
'$write' => [], '$write' => [],
@ -290,7 +283,7 @@ class V11 extends Migration
foreach ($attributes as $attribute) { foreach ($attributes as $attribute) {
try { try {
$this->dbExternal->createAttribute( $this->dbProject->createAttribute(
collection: $attribute['$collection'], collection: $attribute['$collection'],
id: $attribute['$id'], id: $attribute['$id'],
type: $attribute['type'], type: $attribute['type'],
@ -304,7 +297,7 @@ class V11 extends Migration
filters: $attribute['filters'] filters: $attribute['filters']
); );
$this->dbInternal->createDocument('attributes', new Document([ $this->dbProject->createDocument('attributes', new Document([
'$id' => $attribute['$collection'] . '_' . $attribute['$id'], '$id' => $attribute['$collection'] . '_' . $attribute['$id'],
'key' => $attribute['$id'], 'key' => $attribute['$id'],
'collectionId' => $attribute['$collection'], 'collectionId' => $attribute['$collection'],
@ -361,7 +354,7 @@ class V11 extends Migration
Console::log('Migrating External Documents for Collection ' . $collection . ': ' . $offset . ' / ' . $this->oldProjectDB->getSum()); Console::log('Migrating External Documents for Collection ' . $collection . ': ' . $offset . ' / ' . $this->oldProjectDB->getSum());
foreach ($allDocs as $document) { foreach ($allDocs as $document) {
if (!$this->dbExternal->getDocument($collection, $document->getId())->isEmpty()) { if (!$this->dbProject->getDocument('collection_' . $collection, $document->getId())->isEmpty()) {
continue; continue;
} }
go(function ($document) { go(function ($document) {
@ -398,7 +391,7 @@ class V11 extends Migration
}, $document); }, $document);
$document = new Document($document->getArrayCopy()); $document = new Document($document->getArrayCopy());
$document = $this->migratePermissions($document); $document = $this->migratePermissions($document);
$this->dbExternal->createDocument($collection, $document); $this->dbProject->createDocument('collection_' . $collection, $document);
} }
$offset += $this->limit; $offset += $this->limit;
} }
@ -522,7 +515,7 @@ class V11 extends Migration
* Set default values for arrays if not set. * Set default values for arrays if not set.
*/ */
if (empty($document->getAttribute('prefs', []))) { if (empty($document->getAttribute('prefs', []))) {
$document->setAttribute('prefs', []); $document->setAttribute('prefs', new \stdClass());
} }
if (empty($document->getAttribute('sessions', []))) { if (empty($document->getAttribute('sessions', []))) {
$document->setAttribute('sessions', []); $document->setAttribute('sessions', []);

View file

@ -2,6 +2,7 @@
namespace Appwrite\Resque; namespace Appwrite\Resque;
use Utopia\App;
use Utopia\Cache\Cache; use Utopia\Cache\Cache;
use Utopia\Cache\Adapter\Redis as RedisCache; use Utopia\Cache\Adapter\Redis as RedisCache;
use Utopia\CLI\Console; use Utopia\CLI\Console;
@ -21,8 +22,7 @@ abstract class Worker
const MAX_ATTEMPTS = 10; const MAX_ATTEMPTS = 10;
const SLEEP_TIME = 2; const SLEEP_TIME = 2;
const DATABASE_INTERNAL = 'internal'; const DATABASE_PROJECT = 'project';
const DATABASE_EXTERNAL = 'external';
const DATABASE_CONSOLE = 'console'; const DATABASE_CONSOLE = 'console';
public function setUp(): void public function setUp(): void
@ -44,19 +44,9 @@ abstract class Worker
* @param string $projectId * @param string $projectId
* @return Database * @return Database
*/ */
protected function getInternalDB(string $projectId): Database protected function getProjectDB(string $projectId): Database
{ {
return $this->getDB(self::DATABASE_INTERNAL, $projectId); return $this->getDB(self::DATABASE_PROJECT, $projectId);
}
/**
* Get external project database
* @param string $projectId
* @return Database
*/
protected function getExternalDB(string $projectId): Database
{
return $this->getDB(self::DATABASE_EXTERNAL, $projectId);
} }
/** /**
@ -82,20 +72,14 @@ abstract class Worker
$sleep = self::SLEEP_TIME; // overwritten when necessary $sleep = self::SLEEP_TIME; // overwritten when necessary
switch ($type) { switch ($type) {
case self::DATABASE_INTERNAL: case self::DATABASE_PROJECT:
if (!$projectId) { if (!$projectId) {
throw new \Exception('ProjectID not provided - cannot get database'); throw new \Exception('ProjectID not provided - cannot get database');
} }
$namespace = "project_{$projectId}_internal"; $namespace = "_project_{$projectId}";
break;
case self::DATABASE_EXTERNAL:
if (!$projectId) {
throw new \Exception('ProjectID not provided - cannot get database');
}
$namespace = "project_{$projectId}_external";
break; break;
case self::DATABASE_CONSOLE: case self::DATABASE_CONSOLE:
$namespace = "project_console_internal"; $namespace = "_project_console";
$sleep = 5; // ConsoleDB needs extra sleep time to ensure tables are created $sleep = 5; // ConsoleDB needs extra sleep time to ensure tables are created
break; break;
default: default:
@ -110,9 +94,10 @@ abstract class Worker
$attempts++; $attempts++;
$cache = new Cache(new RedisCache($register->get('cache'))); $cache = new Cache(new RedisCache($register->get('cache')));
$database = new Database(new MariaDB($register->get('db')), $cache); $database = new Database(new MariaDB($register->get('db')), $cache);
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$database->setNamespace($namespace); // Main DB $database->setNamespace($namespace); // Main DB
if (!$database->exists()) { if (!empty($projectId) && !$database->getDocument('projects', $projectId)->isEmpty()) {
throw new \Exception("Table does not exist: {$database->getNamespace()}"); throw new \Exception("Project does not exist: {$projectId}");
} }
break; // leave loop if successful break; // leave loop if successful
} catch(\Exception $e) { } catch(\Exception $e) {

View file

@ -343,49 +343,49 @@ trait DatabaseBase
// wait for database worker to create attributes // wait for database worker to create attributes
sleep(30); sleep(30);
$stringResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$string['body']['key']}",array_merge([ $stringResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$string['body']['key']}",array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'] 'x-appwrite-key' => $this->getProject()['apiKey']
])); ]));
$emailResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$email['body']['key']}",array_merge([ $emailResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$email['body']['key']}",array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'] 'x-appwrite-key' => $this->getProject()['apiKey']
])); ]));
$enumResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$enum['body']['key']}",array_merge([ $enumResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$enum['body']['key']}",array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'] 'x-appwrite-key' => $this->getProject()['apiKey']
])); ]));
$ipResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$ip['body']['key']}",array_merge([ $ipResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$ip['body']['key']}",array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'] 'x-appwrite-key' => $this->getProject()['apiKey']
])); ]));
$urlResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$url['body']['key']}",array_merge([ $urlResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$url['body']['key']}",array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'] 'x-appwrite-key' => $this->getProject()['apiKey']
])); ]));
$integerResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$integer['body']['key']}",array_merge([ $integerResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$integer['body']['key']}",array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'] 'x-appwrite-key' => $this->getProject()['apiKey']
])); ]));
$floatResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$float['body']['key']}",array_merge([ $floatResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$float['body']['key']}",array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'] 'x-appwrite-key' => $this->getProject()['apiKey']
])); ]));
$booleanResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$boolean['body']['key']}",array_merge([ $booleanResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$boolean['body']['key']}",array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'] 'x-appwrite-key' => $this->getProject()['apiKey']
@ -827,6 +827,10 @@ trait DatabaseBase
$this->assertEquals(2019, $documents['body']['documents'][2]['releaseYear']); $this->assertEquals(2019, $documents['body']['documents'][2]['releaseYear']);
$this->assertCount(3, $documents['body']['documents']); $this->assertCount(3, $documents['body']['documents']);
foreach ($documents['body']['documents'] as $document) {
$this->assertEquals($data['moviesId'], $document['$collection']);
}
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([ $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
@ -841,7 +845,28 @@ trait DatabaseBase
$this->assertEquals(2019, $documents['body']['documents'][0]['releaseYear']); $this->assertEquals(2019, $documents['body']['documents'][0]['releaseYear']);
$this->assertCount(3, $documents['body']['documents']); $this->assertCount(3, $documents['body']['documents']);
return []; return $documents['body']['documents'];
}
/**
* @depends testListDocuments
*/
public function testGetDocument(array $documents): void
{
foreach ($documents as $document) {
$response = $this->client->call(Client::METHOD_GET, '/database/collections/' . $document['$collection'] . '/documents/' . $document['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($response['headers']['status-code'], 200);
$this->assertEquals($response['body']['$id'], $document['$id']);
$this->assertEquals($response['body']['$collection'], $document['$collection']);
$this->assertEquals($response['body']['title'], $document['title']);
$this->assertEquals($response['body']['releaseYear'], $document['releaseYear']);
$this->assertEquals($response['body']['$read'], $document['$read']);
$this->assertEquals($response['body']['$write'], $document['$write']);
}
} }
/** /**
@ -1218,6 +1243,8 @@ trait DatabaseBase
]); ]);
$this->assertEquals($document['headers']['status-code'], 200); $this->assertEquals($document['headers']['status-code'], 200);
$this->assertEquals($document['body']['$id'], $id);
$this->assertEquals($document['body']['$collection'], $data['moviesId']);
$this->assertEquals($document['body']['title'], 'Thor: Ragnarok'); $this->assertEquals($document['body']['title'], 'Thor: Ragnarok');
$this->assertEquals($document['body']['releaseYear'], 2017); $this->assertEquals($document['body']['releaseYear'], 2017);
$this->assertEquals('role:member', $document['body']['$read'][0]); $this->assertEquals('role:member', $document['body']['$read'][0]);
@ -1280,7 +1307,7 @@ trait DatabaseBase
], $this->getHeaders())); ], $this->getHeaders()));
$this->assertEquals($document['headers']['status-code'], 404); $this->assertEquals($document['headers']['status-code'], 404);
return $data; return $data;
} }