1
0
Fork 0
mirror of synced 2024-09-18 18:40:24 +12:00

Merge branch '1.6.x' of https://github.com/appwrite/appwrite into mock-numbers

This commit is contained in:
Christy Jacob 2024-06-25 15:17:22 +00:00
commit 7cec278d27
35 changed files with 301 additions and 684 deletions

2
.env
View file

@ -17,7 +17,7 @@ _APP_OPTIONS_ROUTER_PROTECTION=disabled
_APP_OPTIONS_FORCE_HTTPS=disabled
_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled
_APP_OPENSSL_KEY_V1=your-secret-key
_APP_DOMAIN=traefik
_APP_DOMAIN=localhost
_APP_DOMAIN_FUNCTIONS=functions.localhost
_APP_DOMAIN_TARGET=localhost
_APP_REDIS_HOST=redis

View file

@ -145,3 +145,6 @@ jobs:
- name: Run ${{matrix.service}} Tests
run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug
- name: Run ${{matrix.service}} Shared Tables Tests
run: _APP_DATABASE_SHARED_TABLES=database_db_main docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug

View file

@ -29,7 +29,7 @@ ENV VITE_APPWRITE_GROWTH_ENDPOINT=$VITE_APPWRITE_GROWTH_ENDPOINT
RUN npm ci
RUN npm run build
FROM appwrite/base:0.9.0 as final
FROM appwrite/base:0.9.1 as final
LABEL maintainer="team@appwrite.io"

View file

@ -109,7 +109,7 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole,
if (isset($databases[$dsn->getHost()])) {
$database = $databases[$dsn->getHost()];
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
@ -133,7 +133,7 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole,
$databases[$dsn->getHost()] = $database;
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())

View file

@ -15,7 +15,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
'version' => '14.0.2',
'version' => '15.0.0',
'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true,
@ -138,7 +138,7 @@ return [
[
'key' => 'react-native',
'name' => 'React Native',
'version' => '0.3.2',
'version' => '0.4.0',
'url' => 'https://github.com/appwrite/sdk-for-react-native',
'package' => 'https://npmjs.com/package/react-native-appwrite',
'enabled' => true,
@ -267,7 +267,7 @@ return [
[
'key' => 'deno',
'name' => 'Deno',
'version' => '10.0.2',
'version' => '11.0.0',
'url' => 'https://github.com/appwrite/sdk-for-deno',
'package' => 'https://deno.land/x/appwrite',
'enabled' => true,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -468,7 +468,7 @@ return [
],
[
'name' => '_APP_SMS_FROM',
'description' => 'Phone number used for sending out messages. Must start with a leading \'+\' and maximum of 15 digits without spaces (+123456789).',
'description' => 'Phone number used for sending out messages. If using Twilio, this may be a Messaging Service SID, starting with MG. Otherwise, the number must start with a leading \'+\' and maximum of 15 digits without spaces (+123456789). ',
'introduction' => '0.15.0',
'default' => '',
'required' => false,

View file

@ -86,8 +86,8 @@ $createSession = function (string $userId, string $secret, Request $request, Res
$factor = (match ($verifiedToken->getAttribute('type')) {
Auth::TOKEN_TYPE_MAGIC_URL,
Auth::TOKEN_TYPE_OAUTH2,
Auth::TOKEN_TYPE_EMAIL => 'email',
Auth::TOKEN_TYPE_PHONE => 'phone',
Auth::TOKEN_TYPE_EMAIL => Type::EMAIL,
Auth::TOKEN_TYPE_PHONE => Type::PHONE,
Auth::TOKEN_TYPE_GENERIC => 'token',
default => throw new Exception(Exception::USER_INVALID_TOKEN)
});
@ -1508,7 +1508,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
'factors' => ['email'],
'factors' => [TYPE::EMAIL, 'oauth2'], // include a special oauth2 factor to bypass MFA checks
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
'expire' => DateTime::addSeconds(new \DateTime(), $duration)
], $detector->getOS(), $detector->getClient(), $detector->getDevice()));
@ -2536,6 +2536,7 @@ App::patch('/v1/account/password')
->label('sdk.response.model', Response::MODEL_USER)
->label('sdk.offline.model', '/account')
->label('sdk.offline.key', 'current')
->label('abuse-limit', 10)
->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'New user password. Must be at least 8 chars.', false, ['project', 'passwordsDictionary'])
->param('oldPassword', '', new Password(), 'Current user password. Must be at least 8 chars.', true)
->inject('requestTimestamp')

View file

@ -1164,6 +1164,7 @@ App::post('/v1/functions/:functionId/deployments')
}
$activate = (bool) filter_var($activate, FILTER_VALIDATE_BOOLEAN);
$type = $request->getHeader('x-sdk-language') === 'cli' ? 'cli' : 'manual';
if ($chunksUploaded === $chunks) {
if ($activate) {
@ -1201,7 +1202,7 @@ App::post('/v1/functions/:functionId/deployments')
'search' => implode(' ', [$deploymentId, $entrypoint]),
'activate' => $activate,
'metadata' => $metadata,
'type' => 'manual'
'type' => $type
]));
} else {
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('size', $fileSize)->setAttribute('metadata', $metadata));
@ -1234,7 +1235,7 @@ App::post('/v1/functions/:functionId/deployments')
'search' => implode(' ', [$deploymentId, $entrypoint]),
'activate' => $activate,
'metadata' => $metadata,
'type' => 'manual'
'type' => $type
]));
} else {
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('chunksUploaded', $chunksUploaded)->setAttribute('metadata', $metadata));

View file

@ -852,7 +852,7 @@ App::get('/v1/health/queue/failed/:name')
Event::FUNCTIONS_QUEUE_NAME,
Event::USAGE_QUEUE_NAME,
Event::USAGE_DUMP_QUEUE_NAME,
Event::WEBHOOK_CLASS_NAME,
Event::WEBHOOK_QUEUE_NAME,
Event::CERTIFICATES_QUEUE_NAME,
Event::BUILDS_QUEUE_NAME,
Event::MESSAGING_QUEUE_NAME,

View file

@ -113,35 +113,8 @@ App::post('/v1/projects')
$projectId = ($projectId == 'unique()') ? ID::unique() : $projectId;
$backups['database_db_fra1_v14x_02'] = ['from' => '03:00', 'to' => '05:00'];
$backups['database_db_fra1_v14x_03'] = ['from' => '00:00', 'to' => '02:00'];
$backups['database_db_fra1_v14x_04'] = ['from' => '00:00', 'to' => '02:00'];
$backups['database_db_fra1_v14x_05'] = ['from' => '00:00', 'to' => '02:00'];
$backups['database_db_fra1_v14x_06'] = ['from' => '00:00', 'to' => '02:00'];
$backups['database_db_fra1_v14x_07'] = ['from' => '00:00', 'to' => '02:00'];
$databases = Config::getParam('pools-database', []);
/**
* Remove databases from the list that are currently undergoing an backup
*/
if (count($databases) > 1) {
$now = new \DateTime();
foreach ($databases as $index => $database) {
if (empty($backups[$database])) {
continue;
}
$backup = $backups[$database];
$from = \DateTime::createFromFormat('H:i', $backup['from']);
$to = \DateTime::createFromFormat('H:i', $backup['to']);
if ($now >= $from && $now <= $to) {
unset($databases[$index]);
break;
}
}
}
$databaseOverride = System::getEnv('_APP_DATABASE_OVERRIDE');
$index = \array_search($databaseOverride, $databases);
if ($index !== false) {
@ -154,37 +127,12 @@ App::post('/v1/projects')
throw new Exception(Exception::PROJECT_RESERVED_PROJECT, "'console' is a reserved project.");
}
// TODO: 1 in 5 projects use shared tables. Temporary until all projects are using shared tables.
if (
(
!\mt_rand(0, 4)
&& System::getEnv('_APP_DATABASE_SHARED_TABLES', 'enabled') === 'enabled'
&& System::getEnv('_APP_EDITION', 'self-hosted') !== 'self-hosted'
) ||
(
$dsn === DATABASE_SHARED_TABLES
)
) {
// TODO: Temporary until all projects are using shared tables.
if ($dsn === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$schema = 'appwrite';
$database = 'appwrite';
$namespace = System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', '');
$dsn = $schema . '://' . DATABASE_SHARED_TABLES . '?database=' . $database;
if (!empty($namespace)) {
$dsn .= '&namespace=' . $namespace;
}
}
// TODO: Allow overriding in development mode. Temporary until all projects are using shared tables.
if (
App::isDevelopment()
&& System::getEnv('_APP_EDITION', 'self-hosted') !== 'self-hosted'
&& $request->getHeader('x-appwrited-share-tables', false)
) {
$schema = 'appwrite';
$database = 'appwrite';
$namespace = System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', '');
$dsn = $schema . '://' . DATABASE_SHARED_TABLES . '?database=' . $database;
$dsn = $schema . '://' . System::getEnv('_APP_DATABASE_SHARED_TABLES', '') . '?database=' . $database;
if (!empty($namespace)) {
$dsn .= '&namespace=' . $namespace;
@ -238,7 +186,7 @@ App::post('/v1/projects')
$adapter = $pools->get($dsn->getHost())->pop()->getResource();
$dbForProject = new Database($adapter, $cache);
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$dbForProject
->setSharedTables(true)
->setTenant($project->getInternalId())

View file

@ -63,7 +63,7 @@ App::post('/v1/storage/buckets')
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('enabled', true, new Boolean(true), 'Is bucket enabled? When set to \'disabled\', users cannot access the files in this bucket but Server SDKs with and API key can still access the bucket. No files are lost when this is toggled.', true)
->param('maximumFileSize', fn (array $plan) => empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1024 * 1024, fn (array $plan) => new Range(1, empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1024 * 1024 * 1024), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true, ['plan'])
->param('maximumFileSize', fn (array $plan) => empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1024 * 1024, fn (array $plan) => new Range(1, empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1024 * 1024), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true, ['plan'])
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
->param('compression', Compression::NONE, new WhiteList([Compression::NONE, Compression::GZIP, Compression::ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . Compression::NONE . ', [' . Compression::GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . Compression::ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true)
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true)
@ -240,7 +240,7 @@ App::put('/v1/storage/buckets/:bucketId')
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('enabled', true, new Boolean(true), 'Is bucket enabled? When set to \'disabled\', users cannot access the files in this bucket but Server SDKs with and API key can still access the bucket. No files are lost when this is toggled.', true)
->param('maximumFileSize', null, new Range(1, (int) System::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true)
->param('maximumFileSize', fn (array $plan) => empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1024 * 1024, fn (array $plan) => new Range(1, empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1024 * 1024), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true, ['plan'])
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
->param('compression', Compression::NONE, new WhiteList([Compression::NONE, Compression::GZIP, Compression::ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . Compression::NONE . ', [' . Compression::GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . Compression::ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true)
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true)

View file

@ -2,6 +2,7 @@
require_once __DIR__ . '/../init.php';
use Appwrite\Auth\Auth;
use Appwrite\Event\Certificate;
use Appwrite\Event\Event;
use Appwrite\Event\Usage;
@ -583,7 +584,7 @@ App::init()
->addHeader('Server', 'Appwrite')
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-Appwrite-Shared-Tables, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Forwarded-For, X-Forwarded-User-Agent')
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Forwarded-For, X-Forwarded-User-Agent')
->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies')
->addHeader('Access-Control-Allow-Origin', $refDomain)
->addHeader('Access-Control-Allow-Credentials', 'true');
@ -634,7 +635,7 @@ App::options()
$response
->addHeader('Server', 'Appwrite')
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-Appwrite-Shared-Tables, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent')
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent')
->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies')
->addHeader('Access-Control-Allow-Origin', $origin)
->addHeader('Access-Control-Allow-Credentials', 'true')
@ -649,7 +650,8 @@ App::error()
->inject('project')
->inject('logger')
->inject('log')
->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log) {
->inject('queueForUsage')
->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Usage $queueForUsage) {
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
$route = $utopia->getRoute();
$class = \get_class($error);
@ -738,6 +740,26 @@ App::error()
}
}
if ($publish && $project->getId() !== 'console') {
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
$fileSize = 0;
$file = $request->getFiles('file');
if (!empty($file)) {
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
}
$queueForUsage
->addMetric(METRIC_NETWORK_REQUESTS, 1)
->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize)
->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize());
}
$queueForUsage
->setProject($project)
->trigger();
}
if ($logger && $publish) {
try {
/** @var Utopia\Database\Document $user */

View file

@ -143,9 +143,6 @@ const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1';
const APP_HOSTNAME_INTERNAL = 'appwrite';
// Databases
const DATABASE_SHARED_TABLES = 'database_db_fra1_self_hosted_16_0';
// Database Reconnect
const DATABASE_RECONNECT_SLEEP = 2;
const DATABASE_RECONNECT_MAX_ATTEMPTS = 10;
@ -1369,7 +1366,7 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForConsole,
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
}
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
@ -1422,7 +1419,7 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole,
->setMetadata('project', $project->getId())
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS);
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())

View file

@ -92,7 +92,7 @@ if (!function_exists("getProjectDB")) {
$database = new Database($adapter, getCache());
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())

View file

@ -93,7 +93,7 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register,
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
}
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
@ -126,7 +126,7 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForConso
if (isset($databases[$dsn->getHost()])) {
$database = $databases[$dsn->getHost()];
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
@ -150,7 +150,7 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForConso
$databases[$dsn->getHost()] = $database;
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())

View file

@ -47,7 +47,7 @@
"utopia-php/abuse": "0.37.*",
"utopia-php/analytics": "0.10.*",
"utopia-php/audit": "0.39.*",
"utopia-php/cache": "0.9.*",
"utopia-php/cache": "0.10.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.49.*",
@ -58,7 +58,7 @@
"utopia-php/image": "0.6.*",
"utopia-php/locale": "0.4.*",
"utopia-php/logger": "0.5.*",
"utopia-php/messaging": "0.11.*",
"utopia-php/messaging": "0.12.*",
"utopia-php/migration": "0.4.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.*",

165
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "18bab1e5f72cc82896aef9e64bf566e8",
"content-hash": "e002600539435ca8eaaace6e73b4004d",
"packages": [
{
"name": "adhocore/jwt",
@ -1128,16 +1128,16 @@
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.29.0",
"version": "v1.30.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c",
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c",
"shasum": ""
},
"require": {
@ -1188,7 +1188,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0"
},
"funding": [
{
@ -1204,20 +1204,20 @@
"type": "tidelift"
}
],
"time": "2024-01-29T20:11:03+00:00"
"time": "2024-06-19T12:30:46+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.29.0",
"version": "v1.30.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b"
"reference": "77fa7995ac1b21ab60769b7323d600a991a90433"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
"reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433",
"reference": "77fa7995ac1b21ab60769b7323d600a991a90433",
"shasum": ""
},
"require": {
@ -1268,7 +1268,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0"
"source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0"
},
"funding": [
{
@ -1284,7 +1284,7 @@
"type": "tidelift"
}
],
"time": "2024-01-29T20:11:03+00:00"
"time": "2024-05-31T15:07:36+00:00"
},
{
"name": "thecodingmachine/safe",
@ -1569,16 +1569,16 @@
},
{
"name": "utopia-php/cache",
"version": "0.9.1",
"version": "0.10.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/cache.git",
"reference": "552b4c554bb14d0c529631ce304cdf4a2b9d06a6"
"reference": "87ee4fc91e50d4ddfef650aa999ea12be3a99583"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/cache/zipball/552b4c554bb14d0c529631ce304cdf4a2b9d06a6",
"reference": "552b4c554bb14d0c529631ce304cdf4a2b9d06a6",
"url": "https://api.github.com/repos/utopia-php/cache/zipball/87ee4fc91e50d4ddfef650aa999ea12be3a99583",
"reference": "87ee4fc91e50d4ddfef650aa999ea12be3a99583",
"shasum": ""
},
"require": {
@ -1613,9 +1613,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/cache/issues",
"source": "https://github.com/utopia-php/cache/tree/0.9.1"
"source": "https://github.com/utopia-php/cache/tree/0.10.1"
},
"time": "2024-03-19T17:07:20+00:00"
"time": "2024-06-18T13:20:25+00:00"
},
{
"name": "utopia-php/cli",
@ -1719,23 +1719,23 @@
},
{
"name": "utopia-php/database",
"version": "0.49.11",
"version": "0.49.14",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "4f4b35d99ecdee971c3042279bb1ac8264825030"
"reference": "415588c0b98edee9d72cdfe269ff79b14cd8f56d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/4f4b35d99ecdee971c3042279bb1ac8264825030",
"reference": "4f4b35d99ecdee971c3042279bb1ac8264825030",
"url": "https://api.github.com/repos/utopia-php/database/zipball/415588c0b98edee9d72cdfe269ff79b14cd8f56d",
"reference": "415588c0b98edee9d72cdfe269ff79b14cd8f56d",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"ext-pdo": "*",
"php": ">=8.0",
"utopia-php/cache": "0.9.*",
"utopia-php/cache": "0.10.*",
"utopia-php/framework": "0.33.*",
"utopia-php/mongo": "0.3.*"
},
@ -1769,9 +1769,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.49.11"
"source": "https://github.com/utopia-php/database/tree/0.49.14"
},
"time": "2024-05-30T12:40:27+00:00"
"time": "2024-06-20T02:39:23+00:00"
},
{
"name": "utopia-php/domains",
@ -1880,6 +1880,9 @@
},
"time": "2024-05-07T02:01:25+00:00"
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> 91fe8b7a8bccd9def26e7858dc15c049ade953a5
},
{
"name": "utopia-php/fetch",
@ -1919,8 +1922,11 @@
"source": "https://github.com/utopia-php/fetch/tree/0.2.1"
},
"time": "2024-03-18T11:50:59+00:00"
<<<<<<< HEAD
=======
>>>>>>> 83d60612f2b93055e757913af49deab12d60c60f
=======
>>>>>>> 91fe8b7a8bccd9def26e7858dc15c049ade953a5
},
{
"name": "utopia-php/framework",
@ -2122,16 +2128,16 @@
},
{
"name": "utopia-php/messaging",
"version": "0.11.0",
"version": "0.12.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/messaging.git",
"reference": "b499c3ad11af711c28252c62d83f24e6106a2154"
"reference": "6e466d3511981291843c6ebf9ce3f44fc75e37b0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/b499c3ad11af711c28252c62d83f24e6106a2154",
"reference": "b499c3ad11af711c28252c62d83f24e6106a2154",
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/6e466d3511981291843c6ebf9ce3f44fc75e37b0",
"reference": "6e466d3511981291843c6ebf9ce3f44fc75e37b0",
"shasum": ""
},
"require": {
@ -2167,9 +2173,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/messaging/issues",
"source": "https://github.com/utopia-php/messaging/tree/0.11.0"
"source": "https://github.com/utopia-php/messaging/tree/0.12.0"
},
"time": "2024-05-08T17:10:02+00:00"
"time": "2024-05-30T14:58:25+00:00"
},
{
"name": "utopia-php/migration",
@ -2331,6 +2337,9 @@
{
"name": "utopia-php/platform",
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> 91fe8b7a8bccd9def26e7858dc15c049ade953a5
"version": "0.7.0",
"source": {
"type": "git",
@ -2341,6 +2350,7 @@
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/platform/zipball/beeea0f2c9bce14a6869fc5c87a1047cdecb5c52",
"reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52",
<<<<<<< HEAD
=======
"version": "0.5.2",
"source": {
@ -2353,6 +2363,8 @@
"url": "https://api.github.com/repos/utopia-php/platform/zipball/b9feabc79b92dc2b05683a986ad43bce5c1583e3",
"reference": "b9feabc79b92dc2b05683a986ad43bce5c1583e3",
>>>>>>> 83d60612f2b93055e757913af49deab12d60c60f
=======
>>>>>>> 91fe8b7a8bccd9def26e7858dc15c049ade953a5
"shasum": ""
},
"require": {
@ -2360,12 +2372,17 @@
"ext-redis": "*",
"php": ">=8.0",
"utopia-php/cli": "0.15.*",
<<<<<<< HEAD
<<<<<<< HEAD
"utopia-php/framework": "0.33.*",
"utopia-php/queue": "0.7.*"
=======
"utopia-php/framework": "0.33.*"
>>>>>>> 83d60612f2b93055e757913af49deab12d60c60f
=======
"utopia-php/framework": "0.33.*",
"utopia-php/queue": "0.7.*"
>>>>>>> 91fe8b7a8bccd9def26e7858dc15c049ade953a5
},
"require-dev": {
"laravel/pint": "1.2.*",
@ -2391,6 +2408,7 @@
],
"support": {
"issues": "https://github.com/utopia-php/platform/issues",
<<<<<<< HEAD
<<<<<<< HEAD
"source": "https://github.com/utopia-php/platform/tree/0.7.0"
},
@ -2400,6 +2418,11 @@
},
"time": "2024-05-22T12:50:35+00:00"
>>>>>>> 83d60612f2b93055e757913af49deab12d60c60f
=======
"source": "https://github.com/utopia-php/platform/tree/0.7.0"
},
"time": "2024-05-08T17:00:55+00:00"
>>>>>>> 91fe8b7a8bccd9def26e7858dc15c049ade953a5
},
{
"name": "utopia-php/pools",
@ -2782,22 +2805,22 @@
},
{
"name": "utopia-php/vcs",
"version": "0.6.6",
"version": "0.6.7",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/vcs.git",
"reference": "e538264cfee5e3efdfe1771efba04750cf20b2c4"
"reference": "8d8ff1ac68e991b95adb6f91fcde8f9bb8f24974"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/e538264cfee5e3efdfe1771efba04750cf20b2c4",
"reference": "e538264cfee5e3efdfe1771efba04750cf20b2c4",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/8d8ff1ac68e991b95adb6f91fcde8f9bb8f24974",
"reference": "8d8ff1ac68e991b95adb6f91fcde8f9bb8f24974",
"shasum": ""
},
"require": {
"adhocore/jwt": "^1.1",
"php": ">=8.0",
"utopia-php/cache": "^0.9.0",
"utopia-php/cache": "^0.10.0",
"utopia-php/framework": "0.*.*"
},
"require-dev": {
@ -2825,9 +2848,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/vcs/issues",
"source": "https://github.com/utopia-php/vcs/tree/0.6.6"
"source": "https://github.com/utopia-php/vcs/tree/0.6.7"
},
"time": "2024-05-17T09:36:30+00:00"
"time": "2024-06-05T17:38:29+00:00"
},
{
"name": "utopia-php/websocket",
@ -3014,6 +3037,7 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
<<<<<<< HEAD
<<<<<<< HEAD
"version": "0.38.7",
"source": {
@ -3027,16 +3051,24 @@
"reference": "0a66c1149ef05ed9f45ce1c897c4a0ce9ee5e95a",
=======
"version": "0.38.6",
=======
"version": "0.38.7",
>>>>>>> 91fe8b7a8bccd9def26e7858dc15c049ade953a5
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "d7016d6d72545e84709892faca972eb4bf5bd699"
"reference": "0a66c1149ef05ed9f45ce1c897c4a0ce9ee5e95a"
},
"dist": {
"type": "zip",
<<<<<<< HEAD
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/d7016d6d72545e84709892faca972eb4bf5bd699",
"reference": "d7016d6d72545e84709892faca972eb4bf5bd699",
>>>>>>> 83d60612f2b93055e757913af49deab12d60c60f
=======
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/0a66c1149ef05ed9f45ce1c897c4a0ce9ee5e95a",
"reference": "0a66c1149ef05ed9f45ce1c897c4a0ce9ee5e95a",
>>>>>>> 91fe8b7a8bccd9def26e7858dc15c049ade953a5
"shasum": ""
},
"require": {
@ -3072,6 +3104,7 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": {
"issues": "https://github.com/appwrite/sdk-generator/issues",
<<<<<<< HEAD
<<<<<<< HEAD
"source": "https://github.com/appwrite/sdk-generator/tree/0.38.7"
},
@ -3081,6 +3114,11 @@
},
"time": "2024-05-20T18:00:16+00:00"
>>>>>>> 83d60612f2b93055e757913af49deab12d60c60f
=======
"source": "https://github.com/appwrite/sdk-generator/tree/0.38.7"
},
"time": "2024-06-10T00:23:02+00:00"
>>>>>>> 91fe8b7a8bccd9def26e7858dc15c049ade953a5
},
{
"name": "doctrine/deprecations",
@ -3201,6 +3239,7 @@
},
{
"name": "laravel/pint",
<<<<<<< HEAD
<<<<<<< HEAD
"version": "v1.16.1",
"source": {
@ -3214,16 +3253,24 @@
"reference": "9266a47f1b9231b83e0cfd849009547329d871b1",
=======
"version": "v1.16.0",
=======
"version": "v1.16.1",
>>>>>>> 91fe8b7a8bccd9def26e7858dc15c049ade953a5
"source": {
"type": "git",
"url": "https://github.com/laravel/pint.git",
"reference": "1b3a3dc5bc6a81ff52828ba7277621f1d49d6d98"
"reference": "9266a47f1b9231b83e0cfd849009547329d871b1"
},
"dist": {
"type": "zip",
<<<<<<< HEAD
"url": "https://api.github.com/repos/laravel/pint/zipball/1b3a3dc5bc6a81ff52828ba7277621f1d49d6d98",
"reference": "1b3a3dc5bc6a81ff52828ba7277621f1d49d6d98",
>>>>>>> 83d60612f2b93055e757913af49deab12d60c60f
=======
"url": "https://api.github.com/repos/laravel/pint/zipball/9266a47f1b9231b83e0cfd849009547329d871b1",
"reference": "9266a47f1b9231b83e0cfd849009547329d871b1",
>>>>>>> 91fe8b7a8bccd9def26e7858dc15c049ade953a5
"shasum": ""
},
"require": {
@ -3234,6 +3281,7 @@
"php": "^8.1.0"
},
"require-dev": {
<<<<<<< HEAD
<<<<<<< HEAD
"friendsofphp/php-cs-fixer": "^3.59.3",
"illuminate/view": "^10.48.12",
@ -3243,6 +3291,11 @@
"illuminate/view": "^10.48.10",
"larastan/larastan": "^2.9.6",
>>>>>>> 83d60612f2b93055e757913af49deab12d60c60f
=======
"friendsofphp/php-cs-fixer": "^3.59.3",
"illuminate/view": "^10.48.12",
"larastan/larastan": "^2.9.7",
>>>>>>> 91fe8b7a8bccd9def26e7858dc15c049ade953a5
"laravel-zero/framework": "^10.4.0",
"mockery/mockery": "^1.6.12",
"nunomaduro/termwind": "^1.15.1",
@ -3282,11 +3335,15 @@
"issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint"
},
<<<<<<< HEAD
<<<<<<< HEAD
"time": "2024-06-18T16:50:05+00:00"
=======
"time": "2024-05-21T18:08:25+00:00"
>>>>>>> 83d60612f2b93055e757913af49deab12d60c60f
=======
"time": "2024-06-18T16:50:05+00:00"
>>>>>>> 91fe8b7a8bccd9def26e7858dc15c049ade953a5
},
{
"name": "matthiasmullie/minify",
@ -3895,6 +3952,9 @@
{
"name": "phpstan/phpdoc-parser",
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> 91fe8b7a8bccd9def26e7858dc15c049ade953a5
"version": "1.29.1",
"source": {
"type": "git",
@ -3905,6 +3965,7 @@
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4",
"reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4",
<<<<<<< HEAD
=======
"version": "1.29.0",
"source": {
@ -3917,6 +3978,8 @@
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/536889f2b340489d328f5ffb7b02bb6b183ddedc",
"reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc",
>>>>>>> 83d60612f2b93055e757913af49deab12d60c60f
=======
>>>>>>> 91fe8b7a8bccd9def26e7858dc15c049ade953a5
"shasum": ""
},
"require": {
@ -3948,6 +4011,7 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
<<<<<<< HEAD
<<<<<<< HEAD
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1"
},
@ -3957,6 +4021,11 @@
},
"time": "2024-05-06T12:04:23+00:00"
>>>>>>> 83d60612f2b93055e757913af49deab12d60c60f
=======
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1"
},
"time": "2024-05-31T08:52:43+00:00"
>>>>>>> 91fe8b7a8bccd9def26e7858dc15c049ade953a5
},
{
"name": "phpunit/php-code-coverage",
@ -5427,16 +5496,16 @@
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.29.0",
"version": "v1.30.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4"
"reference": "0424dff1c58f028c451efff2045f5d92410bd540"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4",
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540",
"reference": "0424dff1c58f028c451efff2045f5d92410bd540",
"shasum": ""
},
"require": {
@ -5486,7 +5555,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0"
},
"funding": [
{
@ -5502,7 +5571,7 @@
"type": "tidelift"
}
],
"time": "2024-01-29T20:11:03+00:00"
"time": "2024-05-31T15:07:36+00:00"
},
{
"name": "textalk/websocket",
@ -5702,5 +5771,5 @@
"platform-overrides": {
"php": "8.3"
},
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.6.0"
}

View file

@ -189,6 +189,7 @@ services:
- _APP_CONSOLE_COUNTRIES_DENYLIST
- _APP_EXPERIMENT_LOGGING_PROVIDER
- _APP_EXPERIMENT_LOGGING_CONFIG
- _APP_DATABASE_SHARED_TABLES
appwrite-realtime:
entrypoint: realtime
@ -237,6 +238,7 @@ services:
- _APP_DB_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_CONFIG
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-audits:
entrypoint: worker-audits
@ -265,6 +267,7 @@ services:
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_CONFIG
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-webhooks:
entrypoint: worker-webhooks
@ -296,6 +299,7 @@ services:
- _APP_REDIS_PASS
- _APP_LOGGING_CONFIG
- _APP_WEBHOOK_MAX_FAILED_ATTEMPTS
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-deletes:
entrypoint: worker-deletes
@ -352,6 +356,7 @@ services:
- _APP_LOGGING_CONFIG
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-databases:
entrypoint: worker-databases
@ -382,6 +387,7 @@ services:
- _APP_LOGGING_CONFIG
- _APP_WORKERS_NUM
- _APP_QUEUE_NAME
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-builds:
entrypoint: worker-builds
@ -446,6 +452,7 @@ services:
- _APP_STORAGE_WASABI_SECRET
- _APP_STORAGE_WASABI_REGION
- _APP_STORAGE_WASABI_BUCKET
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-certificates:
entrypoint: worker-certificates
@ -480,6 +487,7 @@ services:
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_CONFIG
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-functions:
entrypoint: worker-functions
@ -520,6 +528,8 @@ services:
- _APP_DOCKER_HUB_USERNAME
- _APP_DOCKER_HUB_PASSWORD
- _APP_LOGGING_CONFIG
- _APP_LOGGING_PROVIDER
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-mails:
entrypoint: worker-mails
@ -553,6 +563,7 @@ services:
- _APP_LOGGING_CONFIG
- _APP_DOMAIN
- _APP_OPTIONS_FORCE_HTTPS
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-messaging:
entrypoint: worker-messaging
@ -606,6 +617,7 @@ services:
- _APP_STORAGE_WASABI_SECRET
- _APP_STORAGE_WASABI_REGION
- _APP_STORAGE_WASABI_BUCKET
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-migrations:
entrypoint: worker-migrations
@ -640,6 +652,7 @@ services:
- _APP_LOGGING_CONFIG
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
- _APP_DATABASE_SHARED_TABLES
appwrite-task-maintenance:
entrypoint: maintenance
@ -677,6 +690,7 @@ services:
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
- _APP_MAINTENANCE_RETENTION_SCHEDULES
- _APP_MAINTENANCE_DELAY
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-usage:
entrypoint: worker-usage
@ -707,6 +721,7 @@ services:
- _APP_USAGE_STATS
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-usage-dump:
entrypoint: worker-usage-dump
@ -737,6 +752,7 @@ services:
- _APP_USAGE_STATS
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
- _APP_DATABASE_SHARED_TABLES
appwrite-task-scheduler-functions:
entrypoint: schedule-functions
@ -764,6 +780,7 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_DATABASE_SHARED_TABLES
appwrite-task-scheduler-messages:
entrypoint: schedule-messages
@ -791,6 +808,7 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_DATABASE_SHARED_TABLES
appwrite-assistant:
container_name: appwrite-assistant
@ -887,20 +905,7 @@ services:
- MYSQL_USER=${_APP_DB_USER}
- MYSQL_PASSWORD=${_APP_DB_PASS}
- MARIADB_AUTO_UPGRADE=1
command: "mysqld --innodb-flush-method=fsync" # add ' --query_cache_size=0' for DB tests
# command: mv /var/lib/mysql/ib_logfile0 /var/lib/mysql/ib_logfile0.bu && mv /var/lib/mysql/ib_logfile1 /var/lib/mysql/ib_logfile1.bu
# smtp:
# image: appwrite/smtp:1.2.0
# container_name: appwrite-smtp
# restart: unless-stopped
# networks:
# - appwrite
# environment:
# - LOCAL_DOMAINS=@
# - RELAY_FROM_HOSTS=192.168.0.0/16 ; *.yourdomain.com
# - SMARTHOST_HOST=smtp
# - SMARTHOST_PORT=587
command: "mysqld --innodb-flush-method=fsync"
redis:
image: redis:7.2.4-alpine
@ -918,14 +923,6 @@ services:
volumes:
- appwrite-redis:/data:rw
# clamav:
# image: appwrite/clamav:1.2.0
# container_name: appwrite-clamav
# networks:
# - appwrite
# volumes:
# - appwrite-uploads:/storage/uploads
# Dev Tools Start ------------------------------------------------------------------------------------------
#
# The Appwrite Team uses the following tools to help debug, monitor and diagnose the Appwrite stack

View file

@ -1 +1 @@
Verify an authenticator app after adding it using the [add authenticator](/docs/references/cloud/client-web/account#createMfaAuthenticator) method. add
Verify an authenticator app after adding it using the [add authenticator](/docs/references/cloud/client-web/account#createMfaAuthenticator) method.

View file

@ -93,6 +93,7 @@ class Migrate extends Action
// TODO: Iterate through all project DBs
/** @var Database $projectDB */
$projectDB = $getProjectDB($project);
$projectDB->disableValidation();
$migration
->setProject($project, $projectDB, $dbForConsole)
->setPDO($register->get('db', true))

View file

@ -498,14 +498,14 @@ class Deletes extends Action
$collections = $dbForProject->listCollections($limit);
foreach ($collections as $collection) {
if ($dsn->getHost() !== DATABASE_SHARED_TABLES || !\in_array($collection->getId(), $projectCollectionIds)) {
if ($dsn->getHost() !== System::getEnv('_APP_DATABASE_SHARED_TABLES', '') || !\in_array($collection->getId(), $projectCollectionIds)) {
$dbForProject->deleteCollection($collection->getId());
} else {
$this->deleteByGroup($collection->getId(), [], database: $dbForProject);
}
}
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$collectionsIds = \array_map(fn ($collection) => $collection->getId(), $collections);
if (empty(\array_diff($collectionsIds, $projectCollectionIds))) {
@ -554,7 +554,7 @@ class Deletes extends Action
], $dbForConsole);
// Delete metadata table
if ($dsn->getHost() !== DATABASE_SHARED_TABLES) {
if ($dsn->getHost() !== System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$dbForProject->deleteCollection('_metadata');
} else {
$this->deleteByGroup('_metadata', [], $dbForProject);

View file

@ -399,7 +399,10 @@ class Messaging extends Action
'credentials' => match ($host) {
'twilio' => [
'accountSid' => $user,
'authToken' => $password
'authToken' => $password,
// Twilio Messaging Service SIDs always start with MG
// https://www.twilio.com/docs/messaging/services
'messagingServiceSid' => \str_starts_with($from, 'MG') ? $from : null
],
'textmagic' => [
'username' => $user,
@ -420,9 +423,14 @@ class Messaging extends Action
],
default => null
},
'options' => [
'from' => $from
]
'options' => match ($host) {
'twilio' => [
'from' => \str_starts_with($from, 'MG') ? null : $from
],
default => [
'from' => $from
]
}
]);
$adapter = $this->getSmsAdapter($provider);
@ -465,7 +473,7 @@ class Messaging extends Action
return match ($provider->getAttribute('provider')) {
'mock' => new Mock('username', 'password'),
'twilio' => new Twilio($credentials['accountSid'], $credentials['authToken']),
'twilio' => new Twilio($credentials['accountSid'], $credentials['authToken'], null, isset($credentials['messagingServiceSid']) ? $credentials['messagingServiceSid'] : null),
'textmagic' => new TextMagic($credentials['username'], $credentials['apiKey']),
'telesign' => new Telesign($credentials['customerId'], $credentials['apiKey']),
'msg91' => new Msg91($credentials['senderId'], $credentials['authKey'], $credentials['templateId']),

View file

@ -31,7 +31,7 @@ class HTTPTest extends Scope
$this->assertEquals(204, $response['headers']['status-code']);
$this->assertEquals('Appwrite', $response['headers']['server']);
$this->assertEquals('GET, POST, PUT, PATCH, DELETE', $response['headers']['access-control-allow-methods']);
$this->assertEquals('Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-Appwrite-Shared-Tables, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent', $response['headers']['access-control-allow-headers']);
$this->assertEquals('Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent', $response['headers']['access-control-allow-headers']);
$this->assertEquals('X-Appwrite-Session, X-Fallback-Cookies', $response['headers']['access-control-expose-headers']);
$this->assertEquals('http://localhost', $response['headers']['access-control-allow-origin']);
$this->assertEquals('true', $response['headers']['access-control-allow-credentials']);
@ -108,6 +108,7 @@ class HTTPTest extends Scope
'0.14.x',
];
// var_dump($files);
foreach ($files as $file) {
if (in_array($file, ['.', '..'])) {
continue;

View file

@ -2,6 +2,7 @@
namespace Tests\E2E\Services\Functions;
use Appwrite\Tests\Retry;
use CURLFile;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
@ -42,6 +43,7 @@ class FunctionsCustomClientTest extends Scope
return [];
}
#[Retry(count: 2)]
public function testCreateExecution(): array
{
/**
@ -164,7 +166,7 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals(202, $execution['headers']['status-code']);
// Wait for the first scheduled execution to be created
sleep(65);
sleep(90);
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions', [
'content-type' => 'application/json',

View file

@ -10,6 +10,7 @@ use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
@ -383,6 +384,79 @@ class FunctionsCustomServerTest extends Scope
return $data;
}
public function testCreateDeploymentFromCLI()
{
$function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'functionId' => ID::unique(),
'name' => 'Test',
'execute' => [Role::user($this->getUser()['$id'])->toString()],
'runtime' => 'php-8.0',
'entrypoint' => 'index.php',
'events' => [
'users.*.create',
'users.*.delete',
],
'schedule' => '0 0 1 1 *',
'timeout' => 10,
]);
$this->assertEquals(201, $function['headers']['status-code']);
$functionId = $function['body']['$id'];
$folder = 'php';
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
$this->packageCode($folder);
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/deployments', [
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
'x-sdk-language' => 'cli',
], [
'entrypoint' => 'index.php',
'code' => new CURLFile($code, 'application/x-gzip', \basename($code)),
'activate' => true
]);
$deploymentId = $deployment['body']['$id'] ?? '';
$this->assertEquals(202, $deployment['headers']['status-code']);
// Poll until deployment is built
while (true) {
$deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/deployments/' . $deploymentId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
if (
$deployment['headers']['status-code'] >= 400
|| \in_array($deployment['body']['status'], ['ready', 'failed'])
) {
break;
}
\sleep(1);
}
$this->assertEquals('ready', $deployment['body']['status'], \json_encode($deployment['body']));
$functionDetails = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], []);
$this->assertEquals(200, $functionDetails['headers']['status-code']);
$this->assertEquals('cli', $functionDetails['body']['type']);
}
/**
* @depends testUpdate
*/
@ -1055,7 +1129,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals($executions['body']['executions'][0]['logs'], '');
$this->assertStringContainsString('timed out', $executions['body']['executions'][0]['errors']);
sleep(70); //wait for scheduled execution to be created and time out
sleep(75); // Wait for scheduled execution to be created and time out
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json',
@ -1066,12 +1140,6 @@ class FunctionsCustomServerTest extends Scope
$this->assertCount(2, $executions['body']['executions']);
$this->assertIsArray($executions['body']['executions']);
$this->assertEquals($executions['body']['executions'][1]['trigger'], 'schedule');
$this->assertEquals($executions['body']['executions'][1]['status'], 'failed');
$this->assertEquals($executions['body']['executions'][1]['responseStatusCode'], 500);
$this->assertLessThan(20, $executions['body']['executions'][1]['duration']);
$this->assertEquals($executions['body']['executions'][1]['responseBody'], '');
$this->assertEquals($executions['body']['executions'][1]['logs'], '');
$this->assertStringContainsString('timed out', $executions['body']['executions'][1]['errors']);
// Cleanup : Delete function
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [

View file

@ -455,7 +455,7 @@ class HealthCustomServerTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('/CN=www.google.com', $response['body']['name']);
$this->assertEquals('www.google.com', $response['body']['subjectSN']);
$this->assertEquals('Google Trust Services LLC', $response['body']['issuerOrganisation']);
$this->assertEquals('Google Trust Services', $response['body']['issuerOrganisation']);
$this->assertIsInt($response['body']['validFrom']);
$this->assertIsInt($response['body']['validTo']);

View file

@ -9,7 +9,6 @@ use Tests\E2E\General\UsageTest;
use Tests\E2E\Scopes\ProjectConsole;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideClient;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
@ -3759,504 +3758,4 @@ class ProjectsConsoleClientTest extends Scope
return $data;
}
public function testTenantIsolation(): void
{
// Create a team and a project
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'teamId' => ID::unique(),
'name' => 'Amazing Team',
]);
$teamId = $team['body']['$id'];
// Project-level isolation
$project1 = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-shared-tables' => false
], $this->getHeaders()), [
'projectId' => ID::unique(),
'name' => 'Amazing Project',
'teamId' => $teamId,
'region' => 'default'
]);
// Application level isolation (shared tables)
$project2 = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-shared-tables' => true
], $this->getHeaders()), [
'projectId' => ID::unique(),
'name' => 'Amazing Project',
'teamId' => $teamId,
'region' => 'default'
]);
// Project-level isolation
$project3 = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-shared-tables' => false
], $this->getHeaders()), [
'projectId' => ID::unique(),
'name' => 'Amazing Project',
'teamId' => $teamId,
'region' => 'default'
]);
// Application level isolation (shared tables)
$project4 = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-shared-tables' => true
], $this->getHeaders()), [
'projectId' => ID::unique(),
'name' => 'Amazing Project',
'teamId' => $teamId,
'region' => 'default'
]);
// Create and API key in each project
$key1 = $this->client->call(Client::METHOD_POST, '/projects/' . $project1['body']['$id'] . '/keys', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Key Test',
'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.read', 'attributes.write', 'indexes.read', 'indexes.write', 'documents.read', 'documents.write', 'users.read', 'users.write'],
]);
$key2 = $this->client->call(Client::METHOD_POST, '/projects/' . $project2['body']['$id'] . '/keys', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Key Test',
'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.read', 'attributes.write', 'indexes.read', 'indexes.write', 'documents.read', 'documents.write', 'users.read', 'users.write'],
]);
$key3 = $this->client->call(Client::METHOD_POST, '/projects/' . $project3['body']['$id'] . '/keys', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Key Test',
'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.read', 'attributes.write', 'indexes.read', 'indexes.write', 'documents.read', 'documents.write', 'users.read', 'users.write'],
]);
$key4 = $this->client->call(Client::METHOD_POST, '/projects/' . $project4['body']['$id'] . '/keys', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Key Test',
'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.read', 'attributes.write', 'indexes.read', 'indexes.write', 'documents.read', 'documents.write', 'users.read', 'users.write'],
]);
// Create a database in each project
$database1 = $this->client->call(Client::METHOD_POST, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $project1['body']['$id'],
'x-appwrite-key' => $key1['body']['secret']
], [
'databaseId' => ID::unique(),
'name' => 'Amazing Database',
]);
$database2 = $this->client->call(Client::METHOD_POST, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $project2['body']['$id'],
'x-appwrite-key' => $key2['body']['secret']
], [
'databaseId' => ID::unique(),
'name' => 'Amazing Database',
]);
$database3 = $this->client->call(Client::METHOD_POST, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $project3['body']['$id'],
'x-appwrite-key' => $key3['body']['secret']
], [
'databaseId' => ID::unique(),
'name' => 'Amazing Database',
]);
$database4 = $this->client->call(Client::METHOD_POST, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $project4['body']['$id'],
'x-appwrite-key' => $key4['body']['secret']
], [
'databaseId' => ID::unique(),
'name' => 'Amazing Database',
]);
// Create a collection in each project
$collection1 = $this->client->call(Client::METHOD_POST, '/databases/' . $database1['body']['$id'] . '/collections', [
'content-type' => 'application/json',
'x-appwrite-project' => $project1['body']['$id'],
'x-appwrite-key' => $key1['body']['secret']
], [
'databaseId' => $database1['body']['$id'],
'collectionId' => ID::unique(),
'name' => 'Amazing Collection',
]);
$collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $database2['body']['$id'] . '/collections', [
'content-type' => 'application/json',
'x-appwrite-project' => $project2['body']['$id'],
'x-appwrite-key' => $key2['body']['secret']
], [
'databaseId' => $database2['body']['$id'],
'collectionId' => ID::unique(),
'name' => 'Amazing Collection',
]);
$collection3 = $this->client->call(Client::METHOD_POST, '/databases/' . $database3['body']['$id'] . '/collections', [
'content-type' => 'application/json',
'x-appwrite-project' => $project3['body']['$id'],
'x-appwrite-key' => $key3['body']['secret']
], [
'databaseId' => $database3['body']['$id'],
'collectionId' => ID::unique(),
'name' => 'Amazing Collection',
]);
$collection4 = $this->client->call(Client::METHOD_POST, '/databases/' . $database4['body']['$id'] . '/collections', [
'content-type' => 'application/json',
'x-appwrite-project' => $project4['body']['$id'],
'x-appwrite-key' => $key4['body']['secret']
], [
'databaseId' => $database4['body']['$id'],
'collectionId' => ID::unique(),
'name' => 'Amazing Collection',
]);
// Create an attribute in each project
$attribute1 = $this->client->call(Client::METHOD_POST, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'] . '/attributes/string', [
'content-type' => 'application/json',
'x-appwrite-project' => $project1['body']['$id'],
'x-appwrite-key' => $key1['body']['secret']
], [
'databaseId' => $database1['body']['$id'],
'collectionId' => $collection1['body']['$id'],
'key' => ID::unique(),
'size' => 255,
'required' => true
]);
$attribute2 = $this->client->call(Client::METHOD_POST, '/databases/' . $database2['body']['$id'] . '/collections/' . $collection2['body']['$id'] . '/attributes/string', [
'content-type' => 'application/json',
'x-appwrite-project' => $project2['body']['$id'],
'x-appwrite-key' => $key2['body']['secret']
], [
'databaseId' => $database2['body']['$id'],
'collectionId' => $collection2['body']['$id'],
'key' => ID::unique(),
'size' => 255,
'required' => true
]);
$attribute3 = $this->client->call(Client::METHOD_POST, '/databases/' . $database3['body']['$id'] . '/collections/' . $collection3['body']['$id'] . '/attributes/string', [
'content-type' => 'application/json',
'x-appwrite-project' => $project3['body']['$id'],
'x-appwrite-key' => $key3['body']['secret']
], [
'databaseId' => $database3['body']['$id'],
'collectionId' => $collection3['body']['$id'],
'key' => ID::unique(),
'size' => 255,
'required' => true
]);
$attribute4 = $this->client->call(Client::METHOD_POST, '/databases/' . $database4['body']['$id'] . '/collections/' . $collection4['body']['$id'] . '/attributes/string', [
'content-type' => 'application/json',
'x-appwrite-project' => $project4['body']['$id'],
'x-appwrite-key' => $key4['body']['secret']
], [
'databaseId' => $database4['body']['$id'],
'collectionId' => $collection4['body']['$id'],
'key' => ID::unique(),
'size' => 255,
'required' => true
]);
// Wait for attributes
\sleep(2);
// Create an index in each project
$index1 = $this->client->call(Client::METHOD_POST, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'] . '/indexes', [
'content-type' => 'application/json',
'x-appwrite-project' => $project1['body']['$id'],
'x-appwrite-key' => $key1['body']['secret']
], [
'databaseId' => $database1['body']['$id'],
'collectionId' => $collection1['body']['$id'],
'key' => ID::unique(),
'type' => Database::INDEX_KEY,
'attributes' => [$attribute1['body']['key']],
]);
$index2 = $this->client->call(Client::METHOD_POST, '/databases/' . $database2['body']['$id'] . '/collections/' . $collection2['body']['$id'] . '/indexes', [
'content-type' => 'application/json',
'x-appwrite-project' => $project2['body']['$id'],
'x-appwrite-key' => $key2['body']['secret']
], [
'databaseId' => $database2['body']['$id'],
'collectionId' => $collection2['body']['$id'],
'key' => ID::unique(),
'type' => Database::INDEX_KEY,
'attributes' => [$attribute2['body']['key']],
]);
$index3 = $this->client->call(Client::METHOD_POST, '/databases/' . $database3['body']['$id'] . '/collections/' . $collection3['body']['$id'] . '/indexes', [
'content-type' => 'application/json',
'x-appwrite-project' => $project3['body']['$id'],
'x-appwrite-key' => $key3['body']['secret']
], [
'databaseId' => $database3['body']['$id'],
'collectionId' => $collection3['body']['$id'],
'key' => ID::unique(),
'type' => Database::INDEX_KEY,
'attributes' => [$attribute3['body']['key']],
]);
$index4 = $this->client->call(Client::METHOD_POST, '/databases/' . $database4['body']['$id'] . '/collections/' . $collection4['body']['$id'] . '/indexes', [
'content-type' => 'application/json',
'x-appwrite-project' => $project4['body']['$id'],
'x-appwrite-key' => $key4['body']['secret']
], [
'databaseId' => $database4['body']['$id'],
'collectionId' => $collection4['body']['$id'],
'key' => ID::unique(),
'type' => Database::INDEX_KEY,
'attributes' => [$attribute4['body']['key']],
]);
// Wait for indexes
\sleep(2);
// Assert that each project has only 1 database, 1 collection, 1 attribute and 1 index
$databasesProject1 = $this->client->call(Client::METHOD_GET, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $project1['body']['$id'],
'x-appwrite-key' => $key1['body']['secret']
]);
$this->assertEquals(1, $databasesProject1['body']['total']);
$this->assertEquals(1, \count($databasesProject1['body']['databases']));
$databasesProject2 = $this->client->call(Client::METHOD_GET, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $project2['body']['$id'],
'x-appwrite-key' => $key2['body']['secret']
]);
$this->assertEquals(1, $databasesProject2['body']['total']);
$this->assertEquals(1, \count($databasesProject2['body']['databases']));
$databasesProject3 = $this->client->call(Client::METHOD_GET, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $project3['body']['$id'],
'x-appwrite-key' => $key3['body']['secret']
]);
$this->assertEquals(1, $databasesProject3['body']['total']);
$this->assertEquals(1, \count($databasesProject3['body']['databases']));
$databasesProject4 = $this->client->call(Client::METHOD_GET, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $project4['body']['$id'],
'x-appwrite-key' => $key4['body']['secret']
]);
$this->assertEquals(1, $databasesProject4['body']['total']);
$this->assertEquals(1, \count($databasesProject4['body']['databases']));
$collectionsProject1 = $this->client->call(Client::METHOD_GET, '/databases/' . $database1['body']['$id'] . '/collections', [
'content-type' => 'application/json',
'x-appwrite-project' => $project1['body']['$id'],
'x-appwrite-key' => $key1['body']['secret']
]);
$this->assertEquals(1, $collectionsProject1['body']['total']);
$this->assertEquals(1, \count($collectionsProject1['body']['collections']));
$collectionsProject2 = $this->client->call(Client::METHOD_GET, '/databases/' . $database2['body']['$id'] . '/collections', [
'content-type' => 'application/json',
'x-appwrite-project' => $project2['body']['$id'],
'x-appwrite-key' => $key2['body']['secret']
]);
$this->assertEquals(1, $collectionsProject2['body']['total']);
$this->assertEquals(1, \count($collectionsProject2['body']['collections']));
$collectionsProject3 = $this->client->call(Client::METHOD_GET, '/databases/' . $database3['body']['$id'] . '/collections', [
'content-type' => 'application/json',
'x-appwrite-project' => $project3['body']['$id'],
'x-appwrite-key' => $key3['body']['secret']
]);
$this->assertEquals(1, $collectionsProject3['body']['total']);
$this->assertEquals(1, \count($collectionsProject3['body']['collections']));
$collectionsProject4 = $this->client->call(Client::METHOD_GET, '/databases/' . $database4['body']['$id'] . '/collections', [
'content-type' => 'application/json',
'x-appwrite-project' => $project4['body']['$id'],
'x-appwrite-key' => $key4['body']['secret']
]);
$this->assertEquals(1, $collectionsProject4['body']['total']);
$this->assertEquals(1, \count($collectionsProject4['body']['collections']));
$attributesProject1 = $this->client->call(Client::METHOD_GET, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'] . '/attributes', [
'content-type' => 'application/json',
'x-appwrite-project' => $project1['body']['$id'],
'x-appwrite-key' => $key1['body']['secret']
]);
$this->assertEquals(1, $attributesProject1['body']['total']);
$this->assertEquals(1, \count($attributesProject1['body']['attributes']));
$this->assertEquals('available', $attributesProject1['body']['attributes'][0]['status']);
$attributesProject2 = $this->client->call(Client::METHOD_GET, '/databases/' . $database2['body']['$id'] . '/collections/' . $collection2['body']['$id'] . '/attributes', [
'content-type' => 'application/json',
'x-appwrite-project' => $project2['body']['$id'],
'x-appwrite-key' => $key2['body']['secret']
]);
$this->assertEquals(1, $attributesProject2['body']['total']);
$this->assertEquals(1, \count($attributesProject2['body']['attributes']));
$this->assertEquals('available', $attributesProject2['body']['attributes'][0]['status']);
$attributesProject3 = $this->client->call(Client::METHOD_GET, '/databases/' . $database3['body']['$id'] . '/collections/' . $collection3['body']['$id'] . '/attributes', [
'content-type' => 'application/json',
'x-appwrite-project' => $project3['body']['$id'],
'x-appwrite-key' => $key3['body']['secret']
]);
$this->assertEquals(1, $attributesProject3['body']['total']);
$this->assertEquals(1, \count($attributesProject3['body']['attributes']));
$this->assertEquals('available', $attributesProject3['body']['attributes'][0]['status']);
$attributesProject4 = $this->client->call(Client::METHOD_GET, '/databases/' . $database4['body']['$id'] . '/collections/' . $collection4['body']['$id'] . '/attributes', [
'content-type' => 'application/json',
'x-appwrite-project' => $project4['body']['$id'],
'x-appwrite-key' => $key4['body']['secret']
]);
$this->assertEquals(1, $attributesProject4['body']['total']);
$this->assertEquals(1, \count($attributesProject4['body']['attributes']));
$this->assertEquals('available', $attributesProject4['body']['attributes'][0]['status']);
$indexesProject1 = $this->client->call(Client::METHOD_GET, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'] . '/indexes', [
'content-type' => 'application/json',
'x-appwrite-project' => $project1['body']['$id'],
'x-appwrite-key' => $key1['body']['secret']
]);
$this->assertEquals(1, $indexesProject1['body']['total']);
$this->assertEquals(1, \count($indexesProject1['body']['indexes']));
$indexesProject2 = $this->client->call(Client::METHOD_GET, '/databases/' . $database2['body']['$id'] . '/collections/' . $collection2['body']['$id'] . '/indexes', [
'content-type' => 'application/json',
'x-appwrite-project' => $project2['body']['$id'],
'x-appwrite-key' => $key2['body']['secret']
]);
$this->assertEquals(1, $indexesProject2['body']['total']);
$this->assertEquals(1, \count($indexesProject2['body']['indexes']));
$indexesProject3 = $this->client->call(Client::METHOD_GET, '/databases/' . $database3['body']['$id'] . '/collections/' . $collection3['body']['$id'] . '/indexes', [
'content-type' => 'application/json',
'x-appwrite-project' => $project3['body']['$id'],
'x-appwrite-key' => $key3['body']['secret']
]);
$this->assertEquals(1, $indexesProject3['body']['total']);
$this->assertEquals(1, \count($indexesProject3['body']['indexes']));
$indexesProject4 = $this->client->call(Client::METHOD_GET, '/databases/' . $database4['body']['$id'] . '/collections/' . $collection4['body']['$id'] . '/indexes', [
'content-type' => 'application/json',
'x-appwrite-project' => $project4['body']['$id'],
'x-appwrite-key' => $key4['body']['secret']
]);
$this->assertEquals(1, $indexesProject4['body']['total']);
$this->assertEquals(1, \count($indexesProject4['body']['indexes']));
// Attempt to read cross-type resources
$collectionProject2WithProject1Key = $this->client->call(Client::METHOD_GET, '/databases/' . $database2['body']['$id'] . '/collections/' . $collection2['body']['$id'], [
'content-type' => 'application/json',
'x-appwrite-project' => $project1['body']['$id'],
'x-appwrite-key' => $key1['body']['secret']
]);
$this->assertEquals(404, $collectionProject2WithProject1Key['headers']['status-code']);
$collectionProject1WithProject2Key = $this->client->call(Client::METHOD_GET, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'], [
'content-type' => 'application/json',
'x-appwrite-project' => $project2['body']['$id'],
'x-appwrite-key' => $key2['body']['secret']
]);
$this->assertEquals(404, $collectionProject1WithProject2Key['headers']['status-code']);
// Attempt to read cross-tenant resources
$collectionProject3WithProject1Key = $this->client->call(Client::METHOD_GET, '/databases/' . $database3['body']['$id'] . '/collections/' . $collection3['body']['$id'], [
'content-type' => 'application/json',
'x-appwrite-project' => $project1['body']['$id'],
'x-appwrite-key' => $key1['body']['secret']
]);
$this->assertEquals(404, $collectionProject3WithProject1Key['headers']['status-code']);
$collectionProject1WithProject3Key = $this->client->call(Client::METHOD_GET, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'], [
'content-type' => 'application/json',
'x-appwrite-project' => $project3['body']['$id'],
'x-appwrite-key' => $key3['body']['secret']
]);
$this->assertEquals(404, $collectionProject1WithProject3Key['headers']['status-code']);
// Assert that shared project resources can have the same ID as they're unique on tenant + ID not just ID
$collection5 = $this->client->call(Client::METHOD_POST, '/databases/' . $database2['body']['$id'] . '/collections', [
'content-type' => 'application/json',
'x-appwrite-project' => $project2['body']['$id'],
'x-appwrite-key' => $key2['body']['secret']
], [
'databaseId' => $database2['body']['$id'],
'collectionId' => $collection4['body']['$id'],
'name' => 'Amazing Collection',
]);
$this->assertEquals(201, $collection5['headers']['status-code']);
// Assert that users across projects on shared tables can have the same email as they're unique on tenant + email not just email
$user1 = $this->client->call(Client::METHOD_POST, '/users', [
'content-type' => 'application/json',
'x-appwrite-project' => $project2['body']['$id'],
'x-appwrite-key' => $key2['body']['secret']
], [
'userId' => 'user',
'email' => 'test@appwrite.io',
'password' => 'password',
'name' => 'Test User',
]);
$this->assertEquals(201, $user1['headers']['status-code']);
$user2 = $this->client->call(Client::METHOD_POST, '/users', [
'content-type' => 'application/json',
'x-appwrite-project' => $project4['body']['$id'],
'x-appwrite-key' => $key4['body']['secret']
], [
'userId' => 'user',
'email' => 'test@appwrite.io',
'password' => 'password',
'name' => 'Test User',
]);
$this->assertEquals(201, $user2['headers']['status-code']);
}
}

View file

@ -225,7 +225,7 @@ trait StorageBase
'bucketId' => ID::unique(),
'name' => 'Test Bucket 2',
'fileSecurity' => true,
'maximumFileSize' => 200000000, //200MB
'maximumFileSize' => 6000000000, //6GB
'allowedFileExtensions' => ["jpg", "png"],
'permissions' => [
Permission::read(Role::any()),

View file

@ -986,7 +986,7 @@ trait UsersBase
'password' => 'password'
]);
$this->assertEquals($session['headers']['status-code'], 401);
$this->assertEquals(401, $session['headers']['status-code']);
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/password', array_merge([
'content-type' => 'application/json',