appwrite/app/init/resources.php

846 lines
30 KiB
PHP
Raw Normal View History

2025-03-15 11:00:36 +00:00
<?php
use Ahc\Jwt\JWT;
use Ahc\Jwt\JWTException;
use Appwrite\Auth\Auth;
2025-03-15 13:32:18 +00:00
use Appwrite\Auth\Key;
2025-03-15 11:00:36 +00:00
use Appwrite\Event\Audit;
use Appwrite\Event\Build;
use Appwrite\Event\Certificate;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Func;
use Appwrite\Event\Mail;
use Appwrite\Event\Messaging;
use Appwrite\Event\Migration;
2025-03-15 13:32:18 +00:00
use Appwrite\Event\Realtime;
use Appwrite\Event\StatsUsage;
use Appwrite\Event\Webhook;
2025-03-15 11:00:36 +00:00
use Appwrite\Extend\Exception;
use Appwrite\GraphQL\Schema;
use Appwrite\Network\Validator\Origin;
2025-03-15 13:32:18 +00:00
use Appwrite\Utopia\Request;
use Executor\Executor;
2025-03-15 13:38:01 +00:00
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
2025-03-15 11:00:36 +00:00
use Utopia\App;
use Utopia\Cache\Adapter\Sharding;
use Utopia\Cache\Cache;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\DSN\DSN;
use Utopia\Locale\Locale;
use Utopia\Logger\Log;
use Utopia\Pools\Group;
2025-03-15 13:32:18 +00:00
use Utopia\Queue\Publisher;
2025-03-15 11:00:36 +00:00
use Utopia\Storage\Device;
use Utopia\Storage\Device\AWS;
2025-03-15 11:00:36 +00:00
use Utopia\Storage\Device\Backblaze;
use Utopia\Storage\Device\DOSpaces;
use Utopia\Storage\Device\Linode;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Device\S3;
use Utopia\Storage\Device\Wasabi;
use Utopia\Storage\Storage;
use Utopia\System\System;
use Utopia\Validator\Hostname;
use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub;
2025-04-08 11:12:28 +00:00
use Utopia\Telemetry\Adapter as Telemetry;
use Utopia\Telemetry\Adapter\None as NoTelemetry;
2025-03-15 11:00:36 +00:00
2025-03-15 12:18:34 +00:00
// Runtime Execution
2025-03-15 11:00:36 +00:00
App::setResource('log', fn () => new Log());
App::setResource('logger', function ($register) {
return $register->get('logger');
}, ['register']);
App::setResource('hooks', function ($register) {
return $register->get('hooks');
}, ['register']);
App::setResource('register', fn () => $register);
App::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en')));
App::setResource('localeCodes', function () {
return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []));
});
// Queues
2025-03-15 12:18:34 +00:00
App::setResource('publisher', function (Group $pools) {
return $pools->get('publisher')->pop()->getResource();
2025-03-15 11:00:36 +00:00
}, ['pools']);
2025-03-15 12:18:34 +00:00
App::setResource('consumer', function (Group $pools) {
return $pools->get('consumer')->pop()->getResource();
}, ['pools']);
2025-03-15 13:32:18 +00:00
App::setResource('queueForMessaging', function (Publisher $publisher) {
2025-03-15 12:18:34 +00:00
return new Messaging($publisher);
}, ['publisher']);
2025-03-15 13:32:18 +00:00
App::setResource('queueForMails', function (Publisher $publisher) {
2025-03-15 12:18:34 +00:00
return new Mail($publisher);
}, ['publisher']);
2025-03-15 13:32:18 +00:00
App::setResource('queueForBuilds', function (Publisher $publisher) {
2025-03-15 12:18:34 +00:00
return new Build($publisher);
}, ['publisher']);
2025-03-15 13:32:18 +00:00
App::setResource('queueForDatabase', function (Publisher $publisher) {
2025-03-15 12:18:34 +00:00
return new EventDatabase($publisher);
}, ['publisher']);
2025-03-15 13:32:18 +00:00
App::setResource('queueForDeletes', function (Publisher $publisher) {
2025-03-15 12:18:34 +00:00
return new Delete($publisher);
}, ['publisher']);
2025-03-15 13:32:18 +00:00
App::setResource('queueForEvents', function (Publisher $publisher) {
2025-03-15 12:18:34 +00:00
return new Event($publisher);
}, ['publisher']);
2025-03-15 13:32:18 +00:00
App::setResource('queueForWebhooks', function (Publisher $publisher) {
2025-03-15 12:18:34 +00:00
return new Webhook($publisher);
}, ['publisher']);
App::setResource('queueForRealtime', function () {
return new Realtime();
}, []);
2025-03-15 13:32:18 +00:00
App::setResource('queueForStatsUsage', function (Publisher $publisher) {
2025-03-15 12:18:34 +00:00
return new StatsUsage($publisher);
}, ['publisher']);
2025-03-15 13:32:18 +00:00
App::setResource('queueForAudits', function (Publisher $publisher) {
2025-03-15 12:18:34 +00:00
return new Audit($publisher);
}, ['publisher']);
2025-03-15 13:32:18 +00:00
App::setResource('queueForFunctions', function (Publisher $publisher) {
2025-03-15 12:18:34 +00:00
return new Func($publisher);
}, ['publisher']);
2025-03-15 13:32:18 +00:00
App::setResource('queueForCertificates', function (Publisher $publisher) {
2025-03-15 12:18:34 +00:00
return new Certificate($publisher);
}, ['publisher']);
2025-03-15 13:32:18 +00:00
App::setResource('queueForMigrations', function (Publisher $publisher) {
2025-03-15 12:18:34 +00:00
return new Migration($publisher);
}, ['publisher']);
2025-03-15 11:00:36 +00:00
App::setResource('clients', function ($request, $console, $project) {
$console->setAttribute('platforms', [ // Always allow current host
'$collection' => ID::custom('platforms'),
'name' => 'Current Host',
'type' => Origin::CLIENT_TYPE_WEB,
'hostname' => $request->getHostname(),
], Document::SET_TYPE_APPEND);
$hostnames = explode(',', System::getEnv('_APP_CONSOLE_HOSTNAMES', ''));
$validator = new Hostname();
foreach ($hostnames as $hostname) {
$hostname = trim($hostname);
if (!$validator->isValid($hostname)) {
continue;
}
$console->setAttribute('platforms', [
'$collection' => ID::custom('platforms'),
'type' => Origin::CLIENT_TYPE_WEB,
'name' => $hostname,
'hostname' => $hostname,
], Document::SET_TYPE_APPEND);
}
/**
* Get All verified client URLs for both console and current projects
* + Filter for duplicated entries
*/
$clientsConsole = \array_map(
fn ($node) => $node['hostname'],
\array_filter(
$console->getAttribute('platforms', []),
fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname']))
)
);
$clients = $clientsConsole;
$platforms = $project->getAttribute('platforms', []);
foreach ($platforms as $node) {
if (
isset($node['type']) &&
($node['type'] === Origin::CLIENT_TYPE_WEB ||
$node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) &&
!empty($node['hostname'])
) {
$clients[] = $node['hostname'];
}
}
return \array_unique($clients);
}, ['request', 'console', 'project']);
2025-03-15 12:18:34 +00:00
App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform) {
2025-03-15 11:00:36 +00:00
/** @var Appwrite\Utopia\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForProject */
2025-03-15 12:18:34 +00:00
/** @var Utopia\Database\Database $dbForPlatform */
2025-03-15 11:00:36 +00:00
/** @var string $mode */
Authorization::setDefaultStatus(true);
Auth::setCookieName('a_session_' . $project->getId());
if (APP_MODE_ADMIN === $mode) {
Auth::setCookieName('a_session_' . $console->getId());
}
$session = Auth::decodeSession(
$request->getCookie(
Auth::$cookieName, // Get sessions
$request->getCookie(Auth::$cookieName . '_legacy', '')
)
);
// Get session from header for SSR clients
if (empty($session['id']) && empty($session['secret'])) {
$sessionHeader = $request->getHeader('x-appwrite-session', '');
if (!empty($sessionHeader)) {
$session = Auth::decodeSession($sessionHeader);
}
}
// Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies
if ($response) {
$response->addHeader('X-Debug-Fallback', 'false');
}
if (empty($session['id']) && empty($session['secret'])) {
if ($response) {
$response->addHeader('X-Debug-Fallback', 'true');
}
$fallback = $request->getHeader('x-fallback-cookies', '');
$fallback = \json_decode($fallback, true);
$session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : ''));
}
Auth::$unique = $session['id'] ?? '';
Auth::$secret = $session['secret'] ?? '';
if (APP_MODE_ADMIN !== $mode) {
if ($project->isEmpty()) {
$user = new Document([]);
} else {
if ($project->getId() === 'console') {
2025-03-15 12:18:34 +00:00
$user = $dbForPlatform->getDocument('users', Auth::$unique);
2025-03-15 11:00:36 +00:00
} else {
$user = $dbForProject->getDocument('users', Auth::$unique);
}
}
} else {
2025-03-15 12:18:34 +00:00
$user = $dbForPlatform->getDocument('users', Auth::$unique);
2025-03-15 11:00:36 +00:00
}
if (
$user->isEmpty() // Check a document has been found in the DB
|| !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret)
) { // Validate user has valid login token
$user = new Document([]);
}
2025-03-15 12:18:34 +00:00
// if (APP_MODE_ADMIN === $mode) {
// if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) {
// Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
// } else {
// $user = new Document([]);
// }
// }
2025-03-15 11:00:36 +00:00
$authJWT = $request->getHeader('x-appwrite-jwt', '');
if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
try {
$payload = $jwt->decode($authJWT);
} catch (JWTException $error) {
throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage());
}
$jwtUserId = $payload['userId'] ?? '';
if (!empty($jwtUserId)) {
$user = $dbForProject->getDocument('users', $jwtUserId);
}
$jwtSessionId = $payload['sessionId'] ?? '';
if (!empty($jwtSessionId)) {
if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token
$user = new Document([]);
}
}
}
$dbForProject->setMetadata('user', $user->getId());
2025-03-15 12:18:34 +00:00
$dbForPlatform->setMetadata('user', $user->getId());
2025-03-15 11:00:36 +00:00
return $user;
2025-03-15 12:18:34 +00:00
}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform']);
2025-03-15 11:00:36 +00:00
2025-03-15 12:18:34 +00:00
App::setResource('project', function ($dbForPlatform, $request, $console) {
2025-03-15 11:00:36 +00:00
/** @var Appwrite\Utopia\Request $request */
2025-03-15 12:18:34 +00:00
/** @var Utopia\Database\Database $dbForPlatform */
2025-03-15 11:00:36 +00:00
/** @var Utopia\Database\Document $console */
$projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', ''));
if (empty($projectId) || $projectId === 'console') {
return $console;
}
2025-03-15 12:18:34 +00:00
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
2025-03-15 11:00:36 +00:00
return $project;
2025-03-15 12:18:34 +00:00
}, ['dbForPlatform', 'request', 'console']);
2025-03-15 11:00:36 +00:00
App::setResource('session', function (Document $user) {
if ($user->isEmpty()) {
return;
}
$sessions = $user->getAttribute('sessions', []);
$sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret);
if (!$sessionId) {
return;
}
foreach ($sessions as $session) {/** @var Document $session */
if ($sessionId === $session->getId()) {
return $session;
}
}
return;
}, ['user']);
App::setResource('console', function () {
2025-03-15 12:18:34 +00:00
return new Document(Config::getParam('console'));
2025-03-15 11:00:36 +00:00
}, []);
2025-03-15 12:18:34 +00:00
App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project) {
2025-03-15 11:00:36 +00:00
if ($project->isEmpty() || $project->getId() === 'console') {
2025-03-15 12:18:34 +00:00
return $dbForPlatform;
2025-03-15 11:00:36 +00:00
}
try {
$dsn = new DSN($project->getAttribute('database'));
} catch (\InvalidArgumentException) {
// TODO: Temporary until all projects are using shared tables
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
}
$dbAdapter = $pools
->get($dsn->getHost())
->pop()
->getResource();
$database = new Database($dbAdapter, $cache);
$database
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId())
2025-03-15 12:18:34 +00:00
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
2025-03-15 11:00:36 +00:00
2025-03-15 12:18:34 +00:00
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
2025-03-15 11:00:36 +00:00
2025-03-15 12:18:34 +00:00
if (\in_array($dsn->getHost(), $sharedTables)) {
2025-03-15 11:00:36 +00:00
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getInternalId());
}
return $database;
2025-03-15 12:18:34 +00:00
}, ['pools', 'dbForPlatform', 'cache', 'project']);
2025-03-15 11:00:36 +00:00
2025-03-15 12:18:34 +00:00
App::setResource('dbForPlatform', function (Group $pools, Cache $cache) {
2025-03-15 11:00:36 +00:00
$dbAdapter = $pools
->get('console')
->pop()
->getResource();
$database = new Database($dbAdapter, $cache);
$database
->setNamespace('_console')
->setMetadata('host', \gethostname())
->setMetadata('project', 'console')
2025-03-15 12:18:34 +00:00
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
2025-03-15 11:00:36 +00:00
return $database;
}, ['pools', 'cache']);
2025-03-15 12:18:34 +00:00
App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
2025-03-15 11:00:36 +00:00
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
2025-03-15 12:18:34 +00:00
return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases) {
2025-03-15 11:00:36 +00:00
if ($project->isEmpty() || $project->getId() === 'console') {
2025-03-15 12:18:34 +00:00
return $dbForPlatform;
2025-03-15 11:00:36 +00:00
}
try {
$dsn = new DSN($project->getAttribute('database'));
} catch (\InvalidArgumentException) {
// TODO: Temporary until all projects are using shared tables
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
}
$configure = (function (Database $database) use ($project, $dsn) {
$database
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId())
2025-03-15 12:18:34 +00:00
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
2025-03-15 11:00:36 +00:00
2025-03-15 12:18:34 +00:00
if (\in_array($dsn->getHost(), $sharedTables)) {
2025-03-15 11:00:36 +00:00
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getInternalId());
}
});
if (isset($databases[$dsn->getHost()])) {
$database = $databases[$dsn->getHost()];
$configure($database);
return $database;
}
$dbAdapter = $pools
->get($dsn->getHost())
->pop()
->getResource();
$database = new Database($dbAdapter, $cache);
$databases[$dsn->getHost()] = $database;
$configure($database);
return $database;
};
2025-03-15 12:18:34 +00:00
}, ['pools', 'dbForPlatform', 'cache']);
App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database = null;
return function (?Document $project = null) use ($pools, $cache, $database) {
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant($project->getInternalId());
return $database;
}
$dbAdapter = $pools
->get('logs')
->pop()
->getResource();
$database = new Database(
$dbAdapter,
$cache
);
$database
->setSharedTables(true)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
// set tenant
if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant($project->getInternalId());
}
return $database;
};
}, ['pools', 'cache']);
2025-03-15 11:00:36 +00:00
2025-04-08 11:12:28 +00:00
App::setResource('telemetry', fn () => new NoTelemetry());
App::setResource('cache', function (Group $pools, Telemetry $telemetry) {
2025-03-15 11:00:36 +00:00
$list = Config::getParam('pools-cache', []);
$adapters = [];
foreach ($list as $value) {
$adapters[] = $pools
->get($value)
->pop()
2025-04-08 11:12:28 +00:00
->getResource();
2025-03-15 11:00:36 +00:00
}
2025-04-08 11:12:28 +00:00
$cache = new Cache(new Sharding($adapters));
$cache->setTelemetry($telemetry);
return $cache;
}, ['pools', 'telemetry']);
2025-03-15 11:00:36 +00:00
2025-03-15 12:18:34 +00:00
App::setResource('redis', function () {
$host = System::getEnv('_APP_REDIS_HOST', 'localhost');
$port = System::getEnv('_APP_REDIS_PORT', 6379);
$pass = System::getEnv('_APP_REDIS_PASS', '');
$redis = new \Redis();
@$redis->pconnect($host, (int)$port);
if ($pass) {
$redis->auth($pass);
}
$redis->setOption(\Redis::OPT_READ_TIMEOUT, -1);
return $redis;
});
App::setResource('timelimit', function (\Redis $redis) {
return function (string $key, int $limit, int $time) use ($redis) {
return new TimeLimitRedis($key, $limit, $time, $redis);
};
}, ['redis']);
2025-03-15 11:00:36 +00:00
App::setResource('deviceForLocal', function () {
return new Local();
});
App::setResource('deviceForFiles', function ($project) {
return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
}, ['project']);
App::setResource('deviceForFunctions', function ($project) {
return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId());
}, ['project']);
App::setResource('deviceForBuilds', function ($project) {
return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId());
}, ['project']);
2025-03-15 12:18:34 +00:00
function getDevice(string $root, string $connection = ''): Device
2025-03-15 11:00:36 +00:00
{
2025-03-15 12:18:34 +00:00
$connection = !empty($connection) ? $connection : System::getEnv('_APP_CONNECTIONS_STORAGE', '');
2025-03-15 11:00:36 +00:00
if (!empty($connection)) {
$acl = 'private';
$device = Storage::DEVICE_LOCAL;
$accessKey = '';
$accessSecret = '';
$bucket = '';
$region = '';
2025-03-16 09:36:32 +00:00
$url = System::getEnv('_APP_STORAGE_S3_ENDPOINT', '');
2025-03-15 11:00:36 +00:00
try {
$dsn = new DSN($connection);
$device = $dsn->getScheme();
$accessKey = $dsn->getUser() ?? '';
$accessSecret = $dsn->getPassword() ?? '';
$bucket = $dsn->getPath() ?? '';
$region = $dsn->getParam('region');
} catch (\Throwable $e) {
Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.');
}
switch ($device) {
case Storage::DEVICE_S3:
if (!empty($url)) {
return new S3($root, $accessKey, $accessSecret, $url, $region, $acl);
} else {
return new AWS($root, $accessKey, $accessSecret, $bucket, $region, $acl);
}
// no break
2025-03-15 11:00:36 +00:00
case STORAGE::DEVICE_DO_SPACES:
$device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl);
$device->setHttpVersion(S3::HTTP_VERSION_1_1);
return $device;
case Storage::DEVICE_BACKBLAZE:
return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl);
case Storage::DEVICE_LINODE:
return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl);
case Storage::DEVICE_WASABI:
return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl);
case Storage::DEVICE_LOCAL:
default:
return new Local($root);
}
} else {
switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) {
case Storage::DEVICE_LOCAL:
default:
return new Local($root);
case Storage::DEVICE_S3:
$s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', '');
$s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', '');
$s3Region = System::getEnv('_APP_STORAGE_S3_REGION', '');
$s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', '');
$s3Acl = 'private';
2025-03-16 09:36:32 +00:00
$s3EndpointUrl = System::getEnv('_APP_STORAGE_S3_ENDPOINT', '');
if (!empty($s3EndpointUrl)) {
return new S3($root, $s3AccessKey, $s3SecretKey, $s3EndpointUrl, $s3Region, $s3Acl);
} else {
return new AWS($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
}
// no break
2025-03-15 11:00:36 +00:00
case Storage::DEVICE_DO_SPACES:
$doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', '');
$doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', '');
$doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', '');
$doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', '');
$doSpacesAcl = 'private';
$device = new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
$device->setHttpVersion(S3::HTTP_VERSION_1_1);
return $device;
case Storage::DEVICE_BACKBLAZE:
$backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', '');
$backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', '');
$backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', '');
$backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', '');
$backblazeAcl = 'private';
return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl);
case Storage::DEVICE_LINODE:
$linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', '');
$linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', '');
$linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', '');
$linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', '');
$linodeAcl = 'private';
return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl);
case Storage::DEVICE_WASABI:
$wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', '');
$wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', '');
$wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', '');
$wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', '');
$wasabiAcl = 'private';
return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl);
}
}
}
App::setResource('mode', function ($request) {
/** @var Appwrite\Utopia\Request $request */
/**
* Defines the mode for the request:
* - 'default' => Requests for Client and Server Side
* - 'admin' => Request from the Console on non-console projects
*/
return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT));
}, ['request']);
App::setResource('geodb', function ($register) {
/** @var Utopia\Registry\Registry $register */
return $register->get('geodb');
}, ['register']);
App::setResource('passwordsDictionary', function ($register) {
/** @var Utopia\Registry\Registry $register */
return $register->get('passwordsDictionary');
}, ['register']);
App::setResource('servers', function () {
$platforms = Config::getParam('platforms');
$server = $platforms[APP_PLATFORM_SERVER];
$languages = array_map(function ($language) {
return strtolower($language['name']);
}, $server['sdks']);
return $languages;
});
App::setResource('promiseAdapter', function ($register) {
return $register->get('promiseAdapter');
}, ['register']);
App::setResource('schema', function ($utopia, $dbForProject) {
$complexity = function (int $complexity, array $args) {
$queries = Query::parseQueries($args['queries'] ?? []);
$query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null;
$limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT;
return $complexity * $limit;
};
$attributes = function (int $limit, int $offset) use ($dbForProject) {
$attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [
Query::limit($limit),
Query::offset($offset),
]));
return \array_map(function ($attr) {
return $attr->getArrayCopy();
}, $attrs);
};
$urls = [
'list' => function (string $databaseId, string $collectionId, array $args) {
return "/v1/databases/$databaseId/collections/$collectionId/documents";
},
'create' => function (string $databaseId, string $collectionId, array $args) {
return "/v1/databases/$databaseId/collections/$collectionId/documents";
},
'read' => function (string $databaseId, string $collectionId, array $args) {
return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}";
},
'update' => function (string $databaseId, string $collectionId, array $args) {
return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}";
},
'delete' => function (string $databaseId, string $collectionId, array $args) {
return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}";
},
];
$params = [
'list' => function (string $databaseId, string $collectionId, array $args) {
return [ 'queries' => $args['queries']];
},
'create' => function (string $databaseId, string $collectionId, array $args) {
$id = $args['id'] ?? 'unique()';
$permissions = $args['permissions'] ?? null;
unset($args['id']);
unset($args['permissions']);
// Order must be the same as the route params
return [
'databaseId' => $databaseId,
'documentId' => $id,
'collectionId' => $collectionId,
'data' => $args,
'permissions' => $permissions,
];
},
'update' => function (string $databaseId, string $collectionId, array $args) {
$documentId = $args['id'];
$permissions = $args['permissions'] ?? null;
unset($args['id']);
unset($args['permissions']);
// Order must be the same as the route params
return [
'databaseId' => $databaseId,
'collectionId' => $collectionId,
'documentId' => $documentId,
'data' => $args,
'permissions' => $permissions,
];
},
];
return Schema::build(
$utopia,
$complexity,
$attributes,
$urls,
$params,
);
}, ['utopia', 'dbForProject']);
App::setResource('contributors', function () {
$path = 'app/config/contributors.json';
$list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
return $list;
});
App::setResource('employees', function () {
$path = 'app/config/employees.json';
$list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
return $list;
});
App::setResource('heroes', function () {
$path = 'app/config/heroes.json';
$list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
return $list;
});
App::setResource('gitHub', function (Cache $cache) {
return new VcsGitHub($cache);
}, ['cache']);
App::setResource('requestTimestamp', function ($request) {
//TODO: Move this to the Request class itself
$timestampHeader = $request->getHeader('x-appwrite-timestamp');
$requestTimestamp = null;
if (!empty($timestampHeader)) {
try {
$requestTimestamp = new \DateTime($timestampHeader);
} catch (\Throwable $e) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid X-Appwrite-Timestamp header value');
}
}
return $requestTimestamp;
}, ['request']);
2025-03-15 12:18:34 +00:00
2025-03-15 11:00:36 +00:00
App::setResource('plan', function (array $plan = []) {
return [];
});
2025-03-15 12:18:34 +00:00
App::setResource('smsRates', function () {
return [];
});
App::setResource('team', function (Document $project, Database $dbForPlatform, App $utopia, Request $request) {
$teamInternalId = '';
if ($project->getId() !== 'console') {
$teamInternalId = $project->getAttribute('teamInternalId', '');
} else {
$route = $utopia->match($request);
$path = $route->getPath();
if (str_starts_with($path, '/v1/projects/:projectId')) {
$uri = $request->getURI();
$pid = explode('/', $uri)[3];
$p = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $pid));
$teamInternalId = $p->getAttribute('teamInternalId', '');
} elseif ($path === '/v1/projects') {
$teamId = $request->getParam('teamId', '');
$team = Authorization::skip(fn () => $dbForPlatform->getDocument('teams', $teamId));
return $team;
}
}
$team = Authorization::skip(function () use ($dbForPlatform, $teamInternalId) {
return $dbForPlatform->findOne('teams', [
Query::equal('$internalId', [$teamInternalId]),
]);
});
return $team;
}, ['project', 'dbForPlatform', 'utopia', 'request']);
App::setResource(
'isResourceBlocked',
fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false
);
App::setResource('previewHostname', function (Request $request) {
if (App::isDevelopment()) {
$host = $request->getQuery('appwrite-hostname') ?? '';
if (!empty($host)) {
return $host;
}
}
return '';
}, ['request']);
App::setResource('apiKey', function (Request $request, Document $project): ?Key {
$key = $request->getHeader('x-appwrite-key');
if (empty($key)) {
return null;
}
return Key::decode($project, $key);
}, ['request', 'project']);
App::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));