mirror of
https://github.com/appwrite/appwrite
synced 2026-05-23 17:08:45 +00:00
New init structure
This commit is contained in:
parent
9e234b7600
commit
6cade89369
8 changed files with 1483 additions and 6 deletions
|
|
@ -370,12 +370,28 @@ Http::init()
|
|||
->inject('locale')
|
||||
->inject('localeCodes')
|
||||
->inject('clients')
|
||||
->inject('geodb')
|
||||
->inject('queueForUsage')
|
||||
->inject('queueForEvents')
|
||||
// ->inject('geodb')
|
||||
// ->inject('queueForUsage')
|
||||
// ->inject('queueForEvents')
|
||||
->inject('queueForCertificates')
|
||||
->inject('authorization')
|
||||
->action(function (Request $request, Response $response, Route $route, Document $console, Document $project, Database $dbForConsole, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates, Authorization $authorization) {
|
||||
->action(function (
|
||||
Request $request,
|
||||
Response $response,
|
||||
Route $route,
|
||||
Document $console,
|
||||
Document $project,
|
||||
Database $dbForConsole,
|
||||
Locale $locale, array $localeCodes,
|
||||
array $clients,
|
||||
/**
|
||||
* @disregard P1009 Undefined type
|
||||
*/
|
||||
// Reader $geodb,
|
||||
// Usage $queueForUsage,
|
||||
// Event $queueForEvents,
|
||||
Certificate $queueForCertificates,
|
||||
Authorization $authorization) {
|
||||
/*
|
||||
* Appwrite Router
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -56,9 +56,14 @@ $http = new Http($server, $container, 'UTC');
|
|||
$http->setRequestClass(Request::class);
|
||||
$http->setResponseClass(Response::class);
|
||||
|
||||
require_once __DIR__ . '/init.php';
|
||||
//require_once __DIR__ . '/init.php';
|
||||
require_once __DIR__ . '/init/constants.php';
|
||||
require_once __DIR__ . '/init/config.php';
|
||||
require_once __DIR__ . '/init/locale.php';
|
||||
require_once __DIR__ . '/init/database/filters.php';
|
||||
require_once __DIR__ . '/init/database/formats.php';
|
||||
require_once __DIR__ . '/init2.php';
|
||||
include __DIR__ . '/controllers/general.php';
|
||||
require_once __DIR__ . '/controllers/general.php';
|
||||
|
||||
global $global;
|
||||
|
||||
|
|
|
|||
35
app/init/config.php
Normal file
35
app/init/config.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
|
||||
use Utopia\Config\Config;
|
||||
|
||||
Config::load('events', __DIR__ . '/../config/events.php');
|
||||
Config::load('auth', __DIR__ . '/../config/auth.php');
|
||||
Config::load('apis', __DIR__ . '/../config/apis.php');
|
||||
Config::load('errors', __DIR__ . '/../config/errors.php');
|
||||
Config::load('oAuthProviders', __DIR__ . '/../config/oAuthProviders.php');
|
||||
Config::load('platforms', __DIR__ . '/../config/platforms.php');
|
||||
Config::load('collections', __DIR__ . '/../config/collections.php');
|
||||
Config::load('runtimes', __DIR__ . '/../config/runtimes.php');
|
||||
Config::load('runtimes-v2', __DIR__ . '/../config/runtimes-v2.php');
|
||||
Config::load('usage', __DIR__ . '/../config/usage.php');
|
||||
Config::load('roles', __DIR__ . '/../config/roles.php'); // User roles and scopes
|
||||
Config::load('scopes', __DIR__ . '/../config/scopes.php'); // User roles and scopes
|
||||
Config::load('services', __DIR__ . '/../config/services.php'); // List of services
|
||||
Config::load('variables', __DIR__ . '/../config/variables.php'); // List of env variables
|
||||
Config::load('regions', __DIR__ . '/../config/regions.php'); // List of available regions
|
||||
Config::load('avatar-browsers', __DIR__ . '/../config/avatars/browsers.php');
|
||||
Config::load('avatar-credit-cards', __DIR__ . '/../config/avatars/credit-cards.php');
|
||||
Config::load('avatar-flags', __DIR__ . '/../config/avatars/flags.php');
|
||||
Config::load('locale-codes', __DIR__ . '/../config/locale/codes.php');
|
||||
Config::load('locale-currencies', __DIR__ . '/../config/locale/currencies.php');
|
||||
Config::load('locale-eu', __DIR__ . '/../config/locale/eu.php');
|
||||
Config::load('locale-languages', __DIR__ . '/../config/locale/languages.php');
|
||||
Config::load('locale-phones', __DIR__ . '/../config/locale/phones.php');
|
||||
Config::load('locale-countries', __DIR__ . '/../config/locale/countries.php');
|
||||
Config::load('locale-continents', __DIR__ . '/../config/locale/continents.php');
|
||||
Config::load('locale-templates', __DIR__ . '/../config/locale/templates.php');
|
||||
Config::load('storage-logos', __DIR__ . '/../config/storage/logos.php');
|
||||
Config::load('storage-mimes', __DIR__ . '/../config/storage/mimes.php');
|
||||
Config::load('storage-inputs', __DIR__ . '/../config/storage/inputs.php');
|
||||
Config::load('storage-outputs', __DIR__ . '/../config/storage/outputs.php');
|
||||
159
app/init/constants.php
Normal file
159
app/init/constants.php
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
|
||||
const APP_NAME = 'Appwrite';
|
||||
const APP_DOMAIN = 'appwrite.io';
|
||||
const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address
|
||||
const APP_EMAIL_SECURITY = ''; // Default security email address
|
||||
const APP_USERAGENT = APP_NAME . '-Server v%s. Please report abuse at %s';
|
||||
const APP_MODE_DEFAULT = 'default';
|
||||
const APP_MODE_ADMIN = 'admin';
|
||||
const APP_PAGING_LIMIT = 12;
|
||||
const APP_LIMIT_COUNT = 5000;
|
||||
const APP_LIMIT_USERS = 10_000;
|
||||
const APP_LIMIT_USER_PASSWORD_HISTORY = 20;
|
||||
const APP_LIMIT_USER_SESSIONS_MAX = 100;
|
||||
const APP_LIMIT_USER_SESSIONS_DEFAULT = 10;
|
||||
const APP_LIMIT_ANTIVIRUS = 20_000_000; //20MB
|
||||
const APP_LIMIT_ENCRYPTION = 20_000_000; //20MB
|
||||
const APP_LIMIT_COMPRESSION = 20_000_000; //20MB
|
||||
const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value
|
||||
const APP_LIMIT_ARRAY_LABELS_SIZE = 1000; // Default maximum of how many labels elements can there be in API parameter that expects array value
|
||||
const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length.
|
||||
const APP_LIMIT_SUBQUERY = 1000;
|
||||
const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1_000_000;
|
||||
const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period
|
||||
const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds
|
||||
const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls
|
||||
const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_BUSTER = 331;
|
||||
const APP_VERSION_STABLE = '1.5.0';
|
||||
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
||||
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
|
||||
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
|
||||
const APP_DATABASE_ATTRIBUTE_DATETIME = 'datetime';
|
||||
const APP_DATABASE_ATTRIBUTE_URL = 'url';
|
||||
const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange';
|
||||
const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange';
|
||||
const APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH = 1_073_741_824; // 2^32 bits / 4 bits per char
|
||||
const APP_DATABASE_TIMEOUT_MILLISECONDS = 15_000;
|
||||
const APP_STORAGE_UPLOADS = '/storage/uploads';
|
||||
const APP_STORAGE_FUNCTIONS = '/storage/functions';
|
||||
const APP_STORAGE_BUILDS = '/storage/builds';
|
||||
const APP_STORAGE_CACHE = '/storage/cache';
|
||||
const APP_STORAGE_CERTIFICATES = '/storage/certificates';
|
||||
const APP_STORAGE_CONFIG = '/storage/config';
|
||||
const APP_STORAGE_READ_BUFFER = 20 * (1000 * 1000); //20MB other names `APP_STORAGE_MEMORY_LIMIT`, `APP_STORAGE_MEMORY_BUFFER`, `APP_STORAGE_READ_LIMIT`, `APP_STORAGE_BUFFER_LIMIT`
|
||||
const APP_SOCIAL_TWITTER = 'https://twitter.com/appwrite';
|
||||
const APP_SOCIAL_TWITTER_HANDLE = 'appwrite';
|
||||
const APP_SOCIAL_FACEBOOK = 'https://www.facebook.com/appwrite.io';
|
||||
const APP_SOCIAL_LINKEDIN = 'https://www.linkedin.com/company/appwrite';
|
||||
const APP_SOCIAL_INSTAGRAM = 'https://www.instagram.com/appwrite.io';
|
||||
const APP_SOCIAL_GITHUB = 'https://github.com/appwrite';
|
||||
const APP_SOCIAL_DISCORD = 'https://appwrite.io/discord';
|
||||
const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244';
|
||||
const APP_SOCIAL_DEV = 'https://dev.to/appwrite';
|
||||
const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
|
||||
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1';
|
||||
const APP_HOSTNAME_INTERNAL = 'appwrite';
|
||||
|
||||
// Database Reconnect
|
||||
const DATABASE_RECONNECT_SLEEP = 2;
|
||||
const DATABASE_RECONNECT_MAX_ATTEMPTS = 10;
|
||||
|
||||
// Database Worker Types
|
||||
const DATABASE_TYPE_CREATE_ATTRIBUTE = 'createAttribute';
|
||||
const DATABASE_TYPE_CREATE_INDEX = 'createIndex';
|
||||
const DATABASE_TYPE_DELETE_ATTRIBUTE = 'deleteAttribute';
|
||||
const DATABASE_TYPE_DELETE_INDEX = 'deleteIndex';
|
||||
const DATABASE_TYPE_DELETE_COLLECTION = 'deleteCollection';
|
||||
const DATABASE_TYPE_DELETE_DATABASE = 'deleteDatabase';
|
||||
|
||||
// Build Worker Types
|
||||
const BUILD_TYPE_DEPLOYMENT = 'deployment';
|
||||
const BUILD_TYPE_RETRY = 'retry';
|
||||
|
||||
// Deletion Types
|
||||
const DELETE_TYPE_DATABASES = 'databases';
|
||||
const DELETE_TYPE_DOCUMENT = 'document';
|
||||
const DELETE_TYPE_COLLECTIONS = 'collections';
|
||||
const DELETE_TYPE_PROJECTS = 'projects';
|
||||
const DELETE_TYPE_FUNCTIONS = 'functions';
|
||||
const DELETE_TYPE_DEPLOYMENTS = 'deployments';
|
||||
const DELETE_TYPE_USERS = 'users';
|
||||
const DELETE_TYPE_TEAMS = 'teams';
|
||||
const DELETE_TYPE_EXECUTIONS = 'executions';
|
||||
const DELETE_TYPE_AUDIT = 'audit';
|
||||
const DELETE_TYPE_ABUSE = 'abuse';
|
||||
const DELETE_TYPE_USAGE = 'usage';
|
||||
const DELETE_TYPE_REALTIME = 'realtime';
|
||||
const DELETE_TYPE_BUCKETS = 'buckets';
|
||||
const DELETE_TYPE_INSTALLATIONS = 'installations';
|
||||
const DELETE_TYPE_RULES = 'rules';
|
||||
const DELETE_TYPE_SESSIONS = 'sessions';
|
||||
const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp';
|
||||
const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource';
|
||||
const DELETE_TYPE_SCHEDULES = 'schedules';
|
||||
const DELETE_TYPE_TOPIC = 'topic';
|
||||
const DELETE_TYPE_TARGET = 'target';
|
||||
const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets';
|
||||
const DELETE_TYPE_SESSION_TARGETS = 'session_targets';
|
||||
|
||||
// Message types
|
||||
const MESSAGE_SEND_TYPE_INTERNAL = 'internal';
|
||||
const MESSAGE_SEND_TYPE_EXTERNAL = 'external';
|
||||
// Mail Types
|
||||
const MAIL_TYPE_VERIFICATION = 'verification';
|
||||
const MAIL_TYPE_MAGIC_SESSION = 'magicSession';
|
||||
const MAIL_TYPE_RECOVERY = 'recovery';
|
||||
const MAIL_TYPE_INVITATION = 'invitation';
|
||||
const MAIL_TYPE_CERTIFICATE = 'certificate';
|
||||
// Auth Types
|
||||
const APP_AUTH_TYPE_SESSION = 'Session';
|
||||
const APP_AUTH_TYPE_JWT = 'JWT';
|
||||
const APP_AUTH_TYPE_KEY = 'Key';
|
||||
const APP_AUTH_TYPE_ADMIN = 'Admin';
|
||||
// Response related
|
||||
const MAX_OUTPUT_CHUNK_SIZE = 2 * 1024 * 1024; // 2MB
|
||||
// Function headers
|
||||
const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host'];
|
||||
const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length'];
|
||||
// Message types
|
||||
const MESSAGE_TYPE_EMAIL = 'email';
|
||||
const MESSAGE_TYPE_SMS = 'sms';
|
||||
const MESSAGE_TYPE_PUSH = 'push';
|
||||
// Usage metrics
|
||||
const METRIC_TEAMS = 'teams';
|
||||
const METRIC_USERS = 'users';
|
||||
const METRIC_MESSAGES = 'messages';
|
||||
const METRIC_SESSIONS = 'sessions';
|
||||
const METRIC_DATABASES = 'databases';
|
||||
const METRIC_COLLECTIONS = 'collections';
|
||||
const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections';
|
||||
const METRIC_DOCUMENTS = 'documents';
|
||||
const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents';
|
||||
const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents';
|
||||
const METRIC_BUCKETS = 'buckets';
|
||||
const METRIC_FILES = 'files';
|
||||
const METRIC_FILES_STORAGE = 'files.storage';
|
||||
const METRIC_BUCKET_ID_FILES = '{bucketInternalId}.files';
|
||||
const METRIC_BUCKET_ID_FILES_STORAGE = '{bucketInternalId}.files.storage';
|
||||
const METRIC_FUNCTIONS = 'functions';
|
||||
const METRIC_DEPLOYMENTS = 'deployments';
|
||||
const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage';
|
||||
const METRIC_BUILDS = 'builds';
|
||||
const METRIC_BUILDS_STORAGE = 'builds.storage';
|
||||
const METRIC_BUILDS_COMPUTE = 'builds.compute';
|
||||
const METRIC_FUNCTION_ID_BUILDS = '{functionInternalId}.builds';
|
||||
const METRIC_FUNCTION_ID_BUILDS_STORAGE = '{functionInternalId}.builds.storage';
|
||||
const METRIC_FUNCTION_ID_BUILDS_COMPUTE = '{functionInternalId}.builds.compute';
|
||||
const METRIC_FUNCTION_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments';
|
||||
const METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage';
|
||||
const METRIC_EXECUTIONS = 'executions';
|
||||
const METRIC_EXECUTIONS_COMPUTE = 'executions.compute';
|
||||
const METRIC_FUNCTION_ID_EXECUTIONS = '{functionInternalId}.executions';
|
||||
const METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE = '{functionInternalId}.executions.compute';
|
||||
const METRIC_NETWORK_REQUESTS = 'network.requests';
|
||||
const METRIC_NETWORK_INBOUND = 'network.inbound';
|
||||
const METRIC_NETWORK_OUTBOUND = 'network.outbound';
|
||||
398
app/init/database/filters.php
Normal file
398
app/init/database/filters.php
Normal file
|
|
@ -0,0 +1,398 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\OpenSSL\OpenSSL;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
|
||||
use Utopia\System\System;
|
||||
|
||||
Database::addFilter(
|
||||
'casting',
|
||||
function (mixed $value) {
|
||||
return json_encode(['value' => $value], JSON_PRESERVE_ZERO_FRACTION);
|
||||
},
|
||||
function (mixed $value) {
|
||||
if (is_null($value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return json_decode($value, true)['value'];
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'enum',
|
||||
function (mixed $value, Document $attribute) {
|
||||
if ($attribute->isSet('elements')) {
|
||||
$attribute->removeAttribute('elements');
|
||||
}
|
||||
|
||||
return $value;
|
||||
},
|
||||
function (mixed $value, Document $attribute) {
|
||||
$formatOptions = \json_decode($attribute->getAttribute('formatOptions', '[]'), true);
|
||||
if (isset($formatOptions['elements'])) {
|
||||
$attribute->setAttribute('elements', $formatOptions['elements']);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'range',
|
||||
function (mixed $value, Document $attribute) {
|
||||
if ($attribute->isSet('min')) {
|
||||
$attribute->removeAttribute('min');
|
||||
}
|
||||
if ($attribute->isSet('max')) {
|
||||
$attribute->removeAttribute('max');
|
||||
}
|
||||
|
||||
return $value;
|
||||
},
|
||||
function (mixed $value, Document $attribute) {
|
||||
$formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true);
|
||||
if (isset($formatOptions['min']) || isset($formatOptions['max'])) {
|
||||
$attribute
|
||||
->setAttribute('min', $formatOptions['min'])
|
||||
->setAttribute('max', $formatOptions['max'])
|
||||
;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryAttributes',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
$attributes = $database->find('attributes', [
|
||||
Query::equal('collectionInternalId', [$document->getInternalId()]),
|
||||
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
|
||||
Query::limit($database->getLimitForAttributes()),
|
||||
]);
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) {
|
||||
$options = $attribute->getAttribute('options');
|
||||
foreach ($options as $key => $value) {
|
||||
$attribute->setAttribute($key, $value);
|
||||
}
|
||||
$attribute->removeAttribute('options');
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryIndexes',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('indexes', [
|
||||
Query::equal('collectionInternalId', [$document->getInternalId()]),
|
||||
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
|
||||
Query::limit($database->getLimitForIndexes()),
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryPlatforms',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('platforms', [
|
||||
Query::equal('projectInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryKeys',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('keys', [
|
||||
Query::equal('projectInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryWebhooks',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('webhooks', [
|
||||
Query::equal('projectInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQuerySessions',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database->getAuthorization()->skip(fn () => $database->find('sessions', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryTokens',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database->getAuthorization()->skip(fn () => $database
|
||||
->find('tokens', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryChallenges',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database->getAuthorization()->skip(fn () => $database
|
||||
->find('challenges', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryAuthenticators',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database->getAuthorization()->skip(fn () => $database
|
||||
->find('authenticators', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryMemberships',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database->getAuthorization()->skip(fn () => $database
|
||||
->find('memberships', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryVariables',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('variables', [
|
||||
Query::equal('resourceInternalId', [$document->getInternalId()]),
|
||||
Query::equal('resourceType', ['function']),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'encrypt',
|
||||
function (mixed $value) {
|
||||
$key = System::getEnv('_APP_OPENSSL_KEY_V1');
|
||||
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
|
||||
$tag = null;
|
||||
|
||||
return json_encode([
|
||||
'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
|
||||
'method' => OpenSSL::CIPHER_AES_128_GCM,
|
||||
'iv' => \bin2hex($iv),
|
||||
'tag' => \bin2hex($tag ?? ''),
|
||||
'version' => '1',
|
||||
]);
|
||||
},
|
||||
function (mixed $value) {
|
||||
if (is_null($value)) {
|
||||
return;
|
||||
}
|
||||
$value = json_decode($value, true);
|
||||
$key = System::getEnv('_APP_OPENSSL_KEY_V' . $value['version']);
|
||||
|
||||
return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag']));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryProjectVariables',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('variables', [
|
||||
Query::equal('resourceType', ['project']),
|
||||
Query::limit(APP_LIMIT_SUBQUERY)
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'userSearch',
|
||||
function (mixed $value, Document $user) {
|
||||
$searchValues = [
|
||||
$user->getId(),
|
||||
$user->getAttribute('email', ''),
|
||||
$user->getAttribute('name', ''),
|
||||
$user->getAttribute('phone', '')
|
||||
];
|
||||
|
||||
foreach ($user->getAttribute('labels', []) as $label) {
|
||||
$searchValues[] = 'label:' . $label;
|
||||
}
|
||||
|
||||
$search = implode(' ', \array_filter($searchValues));
|
||||
|
||||
return $search;
|
||||
},
|
||||
function (mixed $value) {
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryTargets',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database->getAuthorization()->skip(fn () => $database
|
||||
->find('targets', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY)
|
||||
]));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryTopicTargets',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
$targetIds = $database->getAuthorization()->skip(fn () => \array_map(
|
||||
fn ($document) => $document->getAttribute('targetInternalId'),
|
||||
$database->find('subscribers', [
|
||||
Query::equal('topicInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY)
|
||||
])
|
||||
));
|
||||
if (\count($targetIds) > 0) {
|
||||
return $database->find('targets', [
|
||||
Query::equal('$internalId', $targetIds)
|
||||
]);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'providerSearch',
|
||||
function (mixed $value, Document $provider) {
|
||||
$searchValues = [
|
||||
$provider->getId(),
|
||||
$provider->getAttribute('name', ''),
|
||||
$provider->getAttribute('provider', ''),
|
||||
$provider->getAttribute('type', '')
|
||||
];
|
||||
|
||||
$search = \implode(' ', \array_filter($searchValues));
|
||||
|
||||
return $search;
|
||||
},
|
||||
function (mixed $value) {
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'topicSearch',
|
||||
function (mixed $value, Document $topic) {
|
||||
$searchValues = [
|
||||
$topic->getId(),
|
||||
$topic->getAttribute('name', ''),
|
||||
$topic->getAttribute('description', ''),
|
||||
];
|
||||
|
||||
$search = \implode(' ', \array_filter($searchValues));
|
||||
|
||||
return $search;
|
||||
},
|
||||
function (mixed $value) {
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'messageSearch',
|
||||
function (mixed $value, Document $message) {
|
||||
$searchValues = [
|
||||
$message->getId(),
|
||||
$message->getAttribute('description', ''),
|
||||
$message->getAttribute('status', ''),
|
||||
];
|
||||
|
||||
$data = \json_decode($message->getAttribute('data', []), true);
|
||||
$providerType = $message->getAttribute('providerType', '');
|
||||
|
||||
if ($providerType === MESSAGE_TYPE_EMAIL) {
|
||||
$searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]);
|
||||
} elseif ($providerType === MESSAGE_TYPE_SMS) {
|
||||
$searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]);
|
||||
} else {
|
||||
$searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]);
|
||||
}
|
||||
|
||||
$search = \implode(' ', \array_filter($searchValues));
|
||||
|
||||
return $search;
|
||||
},
|
||||
function (mixed $value) {
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
43
app/init/database/formats.php
Normal file
43
app/init/database/formats.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
use Utopia\Database\Validator\Structure;
|
||||
use Utopia\Http\Validator\IP;
|
||||
use Utopia\Http\Validator\Range;
|
||||
use Utopia\Http\Validator\URL;
|
||||
use Utopia\Http\Validator\WhiteList;
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () {
|
||||
return new Email();
|
||||
}, Database::VAR_STRING);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () {
|
||||
return new DatetimeValidator();
|
||||
}, Database::VAR_DATETIME);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) {
|
||||
$elements = $attribute['formatOptions']['elements'];
|
||||
return new WhiteList($elements, true);
|
||||
}, Database::VAR_STRING);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () {
|
||||
return new IP();
|
||||
}, Database::VAR_STRING);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () {
|
||||
return new URL();
|
||||
}, Database::VAR_STRING);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) {
|
||||
$min = $attribute['formatOptions']['min'] ?? -INF;
|
||||
$max = $attribute['formatOptions']['max'] ?? INF;
|
||||
return new Range($min, $max, Range::TYPE_INTEGER);
|
||||
}, Database::VAR_INTEGER);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) {
|
||||
$min = $attribute['formatOptions']['min'] ?? -INF;
|
||||
$max = $attribute['formatOptions']['max'] ?? INF;
|
||||
return new Range($min, $max, Range::TYPE_FLOAT);
|
||||
}, Database::VAR_FLOAT);
|
||||
23
app/init/locale.php
Normal file
23
app/init/locale.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Locale\Locale;
|
||||
|
||||
Locale::$exceptions = false;
|
||||
|
||||
$locales = Config::getParam('locale-codes', []);
|
||||
|
||||
foreach ($locales as $locale) {
|
||||
$code = $locale['code'];
|
||||
|
||||
$path = __DIR__ . '/../config/locale/translations/' . $code . '.json';
|
||||
|
||||
if (!\file_exists($path)) {
|
||||
$path = __DIR__ . '/../config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar`
|
||||
if (!\file_exists($path)) {
|
||||
$path = __DIR__ . '/../config/locale/translations/en.json'; // if none translation exists, use default from `en.json`
|
||||
}
|
||||
}
|
||||
|
||||
Locale::setLanguageFromJSON($code, $path);
|
||||
}
|
||||
798
app/init2.php
Normal file
798
app/init2.php
Normal file
|
|
@ -0,0 +1,798 @@
|
|||
<?php
|
||||
|
||||
global $http, $container;
|
||||
|
||||
use Ahc\Jwt\JWT;
|
||||
use Ahc\Jwt\JWTException;
|
||||
use Appwrite\Auth\Auth;
|
||||
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;
|
||||
use Appwrite\Event\Usage;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\GraphQL\Promises\Adapter\Swoole;
|
||||
use Appwrite\GraphQL\Schema;
|
||||
use Appwrite\Hooks\Hooks;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\OpenSSL\OpenSSL;
|
||||
use Appwrite\URL\URL;
|
||||
use Appwrite\Utopia\Queue\Connections;
|
||||
use MaxMind\Db\Reader;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\Database\PDOConfig;
|
||||
use Swoole\Database\PDOPool;
|
||||
use Swoole\Database\PDOProxy;
|
||||
use Swoole\Database\RedisConfig;
|
||||
use Swoole\Database\RedisPool;
|
||||
use Utopia\Cache\Adapter\Redis as RedisCache;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Adapter\MariaDB;
|
||||
use Utopia\Database\Adapter\MySQL;
|
||||
use Utopia\Database\Adapter\SQL;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
use Utopia\Database\Validator\Structure;
|
||||
use Utopia\DI\Dependency;
|
||||
use Utopia\Domains\Validator\PublicDomain;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Http\Http;
|
||||
use Utopia\Http\Request;
|
||||
use Utopia\Http\Response;
|
||||
use Utopia\Http\Validator\Hostname;
|
||||
use Utopia\Http\Validator\IP;
|
||||
use Utopia\Http\Validator\Range;
|
||||
use Utopia\Http\Validator\WhiteList;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Logger\Logger;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Pools\Pool;
|
||||
use Utopia\Queue;
|
||||
use Utopia\Queue\Connection;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\Storage\Device;
|
||||
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\VCS\Adapter\Git\GitHub as VcsGitHub;
|
||||
use Utopia\Cache\Adapter\None;
|
||||
|
||||
|
||||
function getAdapter($type, $scheme, $resource) {
|
||||
switch ($type) {
|
||||
case 'database':
|
||||
$adapter = match ($scheme) {
|
||||
'mariadb' => new MariaDB($resource),
|
||||
'mysql' => new MySQL($resource),
|
||||
default => null
|
||||
};
|
||||
|
||||
$adapter->setDatabase($scheme);
|
||||
break;
|
||||
case 'pubsub':
|
||||
$adapter = $resource();
|
||||
break;
|
||||
case 'queue':
|
||||
$adapter = match ($scheme) {
|
||||
//'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()),
|
||||
default => null
|
||||
};
|
||||
break;
|
||||
case 'cache':
|
||||
$adapter = match ($scheme) {
|
||||
'redis' => new RedisCache($resource),
|
||||
default => null
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation.");
|
||||
}
|
||||
|
||||
return $adapter;
|
||||
}
|
||||
|
||||
$global = new Registry();
|
||||
|
||||
$global->set('logger', function () {
|
||||
// Register error logger
|
||||
$providerName = System::getEnv('_APP_LOGGING_PROVIDER', '');
|
||||
$providerConfig = System::getEnv('_APP_LOGGING_CONFIG', '');
|
||||
|
||||
if (empty($providerName) || empty($providerConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Logger::hasProvider($providerName)) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled");
|
||||
}
|
||||
|
||||
$classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName);
|
||||
$adapter = new $classname($providerConfig);
|
||||
return new Logger($adapter);
|
||||
});
|
||||
|
||||
$global->set('geodb', function () {
|
||||
/**
|
||||
* @disregard P1009 Undefined type
|
||||
*/
|
||||
return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-02.mmdb');
|
||||
});
|
||||
|
||||
$global->set('pools', (function () {
|
||||
$fallbackForDB = 'db_main=' . URL::unparse([
|
||||
'scheme' => 'mariadb',
|
||||
'host' => System::getEnv('_APP_DB_HOST', 'mariadb'),
|
||||
'port' => System::getEnv('_APP_DB_PORT', '3306'),
|
||||
'user' => System::getEnv('_APP_DB_USER', ''),
|
||||
'pass' => System::getEnv('_APP_DB_PASS', ''),
|
||||
'path' => System::getEnv('_APP_DB_SCHEMA', ''),
|
||||
]);
|
||||
$fallbackForRedis = 'redis_main=' . URL::unparse([
|
||||
'scheme' => 'redis',
|
||||
'host' => System::getEnv('_APP_REDIS_HOST', 'redis'),
|
||||
'port' => System::getEnv('_APP_REDIS_PORT', '6379'),
|
||||
'user' => System::getEnv('_APP_REDIS_USER', ''),
|
||||
'pass' => System::getEnv('_APP_REDIS_PASS', ''),
|
||||
]);
|
||||
|
||||
$connections = [
|
||||
'console' => [
|
||||
'type' => 'database',
|
||||
'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB),
|
||||
'multiple' => false,
|
||||
'schemes' => ['mariadb', 'mysql'],
|
||||
],
|
||||
'database' => [
|
||||
'type' => 'database',
|
||||
'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB),
|
||||
'multiple' => true,
|
||||
'schemes' => ['mariadb', 'mysql'],
|
||||
],
|
||||
'queue' => [
|
||||
'type' => 'queue',
|
||||
'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis),
|
||||
'multiple' => false,
|
||||
'schemes' => ['redis'],
|
||||
],
|
||||
'pubsub' => [
|
||||
'type' => 'pubsub',
|
||||
'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis),
|
||||
'multiple' => false,
|
||||
'schemes' => ['redis'],
|
||||
],
|
||||
'cache' => [
|
||||
'type' => 'cache',
|
||||
'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis),
|
||||
'multiple' => true,
|
||||
'schemes' => ['redis'],
|
||||
],
|
||||
];
|
||||
|
||||
$pools = [];
|
||||
$poolSize = (int)System::getEnv('_APP_POOL_CLIENTS', 9000);
|
||||
$poolSize = 9000;
|
||||
|
||||
foreach ($connections as $key => $connection) {
|
||||
$dsns = $connection['dsns'] ?? '';
|
||||
$multipe = $connection['multiple'] ?? false;
|
||||
$schemes = $connection['schemes'] ?? [];
|
||||
$dsns = explode(',', $connection['dsns'] ?? '');
|
||||
foreach ($dsns as &$dsn) {
|
||||
$dsn = explode('=', $dsn);
|
||||
$name = ($multipe) ? $dsn[0] : 'main';
|
||||
$dsn = $dsn[1] ?? '';
|
||||
|
||||
if (empty($dsn)) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}");
|
||||
}
|
||||
|
||||
$dsn = new DSN($dsn);
|
||||
$dsnHost = $dsn->getHost();
|
||||
$dsnPort = $dsn->getPort();
|
||||
$dsnUser = $dsn->getUser();
|
||||
$dsnPass = $dsn->getPassword();
|
||||
$dsnScheme = $dsn->getScheme();
|
||||
$dsnDatabase = $dsn->getPath();
|
||||
|
||||
if (!in_array($dsnScheme, $schemes)) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Resource
|
||||
*
|
||||
* Creation could be reused accross connection types like database, cache, queue, etc.
|
||||
*
|
||||
* Resource assignment to an adapter will happen below.
|
||||
*/
|
||||
switch ($dsnScheme) {
|
||||
case 'mysql':
|
||||
case 'mariadb':
|
||||
$pool = new PDOPool((new PDOConfig)
|
||||
->withHost($dsnHost)
|
||||
->withPort($dsnPort)
|
||||
->withDbName($dsnDatabase)
|
||||
->withCharset('utf8mb4')
|
||||
->withUsername($dsnUser)
|
||||
->withPassword($dsnPass)
|
||||
->withOptions([
|
||||
// No need to set PDO::ATTR_ERRMODE it is overwitten in PDOProxy
|
||||
// PDO::ATTR_TIMEOUT => 3, // Seconds
|
||||
// PDO::ATTR_PERSISTENT => true,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => true,
|
||||
PDO::ATTR_STRINGIFY_FETCHES => true,
|
||||
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
|
||||
|
||||
]),
|
||||
$poolSize
|
||||
);
|
||||
break;
|
||||
case 'redis':
|
||||
$pool = new RedisPool((new RedisConfig)
|
||||
->withHost($dsnHost)
|
||||
->withPort((int)$dsnPort)
|
||||
->withAuth($dsnPass)
|
||||
, $poolSize);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid scheme");
|
||||
}
|
||||
|
||||
$pools['pools-' . $key . '-' . $name] = [
|
||||
'pool' => $pool,
|
||||
'dsn' => $dsn,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return function () use ($pools): array {
|
||||
return $pools;
|
||||
};
|
||||
})());
|
||||
|
||||
$mode = new Dependency();
|
||||
$mode
|
||||
->setName('mode')
|
||||
->inject('request')
|
||||
->setCallback(function (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));
|
||||
});
|
||||
$container->set($mode);
|
||||
|
||||
$user = new Dependency();
|
||||
$user
|
||||
->setName('user')
|
||||
->inject('mode')
|
||||
->inject('project')
|
||||
->inject('console')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForConsole')
|
||||
->inject('authorization')
|
||||
->setCallback(function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization) {
|
||||
$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') {
|
||||
$user = $dbForConsole->getDocument('users', Auth::$unique);
|
||||
} else {
|
||||
$user = $dbForProject->getDocument('users', Auth::$unique);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$user = $dbForConsole->getDocument('users', Auth::$unique);
|
||||
}
|
||||
|
||||
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([]);
|
||||
}
|
||||
|
||||
if (APP_MODE_ADMIN === $mode) {
|
||||
if ($user->find('teamId', $project->getAttribute('teamId'), 'memberships')) {
|
||||
$authorization->setDefaultStatus(false); // Cancel security segmentation for admin users.
|
||||
} else {
|
||||
$user = new Document([]);
|
||||
}
|
||||
}
|
||||
|
||||
$authJWT = $request->getHeader('x-appwrite-jwt', '');
|
||||
|
||||
if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication
|
||||
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
|
||||
|
||||
try {
|
||||
$payload = $jwt->decode($authJWT);
|
||||
} catch (JWTException $error) {
|
||||
throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage());
|
||||
}
|
||||
|
||||
$jwtUserId = $payload['userId'] ?? '';
|
||||
$jwtSessionId = $payload['sessionId'] ?? '';
|
||||
|
||||
if ($jwtUserId && $jwtSessionId) {
|
||||
$user = $dbForProject->getDocument('users', $jwtUserId);
|
||||
}
|
||||
|
||||
if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token
|
||||
$user = new Document([]);
|
||||
}
|
||||
}
|
||||
|
||||
// Adds logs to database queries
|
||||
$dbForProject->setMetadata('user', $user->getId());
|
||||
$dbForConsole->setMetadata('user', $user->getId());
|
||||
|
||||
return $user;
|
||||
});
|
||||
$container->set($user);
|
||||
|
||||
$console = new Dependency();
|
||||
$console
|
||||
->setName('console')
|
||||
->setCallback(function () {
|
||||
return new Document([
|
||||
'$id' => ID::custom('console'),
|
||||
'$internalId' => ID::custom('console'),
|
||||
'name' => 'Appwrite',
|
||||
'$collection' => ID::custom('projects'),
|
||||
'description' => 'Appwrite core engine',
|
||||
'logo' => '',
|
||||
'teamId' => -1,
|
||||
'webhooks' => [],
|
||||
'keys' => [],
|
||||
'platforms' => [
|
||||
[
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'name' => 'Localhost',
|
||||
'type' => Origin::CLIENT_TYPE_WEB,
|
||||
'hostname' => 'localhost',
|
||||
], // Current host is added on app init
|
||||
],
|
||||
'legalName' => '',
|
||||
'legalCountry' => '',
|
||||
'legalState' => '',
|
||||
'legalCity' => '',
|
||||
'legalAddress' => '',
|
||||
'legalTaxId' => '',
|
||||
'auths' => [
|
||||
'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled',
|
||||
'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user
|
||||
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds
|
||||
],
|
||||
'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [],
|
||||
'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [],
|
||||
'oAuthProviders' => [
|
||||
'githubEnabled' => true,
|
||||
'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''),
|
||||
'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '')
|
||||
],
|
||||
]);
|
||||
});
|
||||
$container->set($console);
|
||||
|
||||
$project = new Dependency();
|
||||
$project
|
||||
->setName('project')
|
||||
->inject('dbForConsole')
|
||||
->inject('request')
|
||||
->inject('console')
|
||||
->inject('authorization')
|
||||
->setCallback(function (Database $dbForConsole, Request $request, Document $console, Authorization $authorization) {
|
||||
$projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', ''));
|
||||
|
||||
if (empty($projectId) || $projectId === 'console') {
|
||||
return $console;
|
||||
}
|
||||
|
||||
$project = $authorization->skip(fn () => $dbForConsole->getDocument('projects', $projectId));
|
||||
|
||||
return $project;
|
||||
});
|
||||
$container->set($project);
|
||||
|
||||
$pools = new Dependency();
|
||||
$pools
|
||||
->setName('pools')
|
||||
->inject('registry')
|
||||
->setCallback(function (Registry $registry) {
|
||||
return $registry->get('pools');
|
||||
});
|
||||
$container->set($pools);
|
||||
|
||||
$dbForProject = new Dependency();
|
||||
$dbForProject
|
||||
->setName('dbForProject')
|
||||
->inject('pools')
|
||||
->inject('project')
|
||||
->inject('cache')
|
||||
->inject('dbForConsole')
|
||||
->inject('connections')
|
||||
->inject('authorization')
|
||||
->setCallback(function(array $pools, Document $project, Cache $cache, Database $dbForConsole, Connections $connections, Authorization $authorization) {
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
return $dbForConsole;
|
||||
}
|
||||
|
||||
$pool = $pools['pools-database-'.$project->getAttribute('database')]['pool'];
|
||||
$dsn = $pools['pools-database-'.$project->getAttribute('database')]['dsn'];
|
||||
|
||||
$connection = $pool->get();
|
||||
$connections->add($connection, $pool);
|
||||
$adapter = match ($dsn->getScheme()) {
|
||||
'mariadb' => new MariaDB($connection),
|
||||
'mysql' => new MySQL($connection),
|
||||
default => null
|
||||
};
|
||||
|
||||
$database = new Database($adapter, $cache);
|
||||
$database->setAuthorization($authorization);
|
||||
|
||||
$database
|
||||
->setNamespace('_' . $project->getInternalId())
|
||||
->setMetadata('host', \gethostname())
|
||||
->setMetadata('project', $project->getId())
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS);
|
||||
|
||||
return $database;
|
||||
});
|
||||
$container->set($dbForProject);
|
||||
|
||||
$dbForConsole = new Dependency();
|
||||
$dbForConsole
|
||||
->setName('dbForConsole')
|
||||
->inject('pools')
|
||||
->inject('cache')
|
||||
->inject('authorization')
|
||||
->inject('connections')
|
||||
->setCallback(function(array $pools, Cache $cache, Authorization $authorization, Connections $connections): Database {
|
||||
$pool = $pools['pools-console-main']['pool'];
|
||||
$dsn = $pools['pools-console-main']['dsn'];
|
||||
$connection = $pool->get();
|
||||
$connections->add($connection, $pool);
|
||||
|
||||
$adapter = match ($dsn->getScheme()) {
|
||||
'mariadb' => new MariaDB($connection),
|
||||
'mysql' => new MySQL($connection),
|
||||
default => null
|
||||
};
|
||||
|
||||
$adapter->setDatabase('appwrite');
|
||||
|
||||
$database = new Database($adapter, $cache);
|
||||
$database->setAuthorization($authorization);
|
||||
|
||||
$database
|
||||
->setNamespace('_console')
|
||||
->setMetadata('host', \gethostname())
|
||||
->setMetadata('project', 'console')
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS);
|
||||
|
||||
return $database;
|
||||
});
|
||||
$container->set($dbForConsole);
|
||||
|
||||
$cache = new Dependency();
|
||||
$cache
|
||||
->setName('cache')
|
||||
->setCallback(function (): Cache {
|
||||
return new Cache(new None());
|
||||
});
|
||||
$container->set($cache);
|
||||
|
||||
$authorization = new Dependency();
|
||||
$authorization
|
||||
->setName('authorization')
|
||||
->setCallback(function (): Authorization {
|
||||
return new Authorization();
|
||||
});
|
||||
$container->set($authorization);
|
||||
|
||||
$registry = new Dependency();
|
||||
$registry
|
||||
->setName('registry')
|
||||
->setCallback(function () use (&$global): Registry {
|
||||
return $global;
|
||||
});
|
||||
$container->set($registry);
|
||||
|
||||
$pools = new Dependency();
|
||||
$pools
|
||||
->setName('pools')
|
||||
->inject('registry')
|
||||
->setCallback(function (Registry $registry) {
|
||||
return $registry->get('pools');
|
||||
});
|
||||
$container->set($pools);
|
||||
|
||||
$logger = new Dependency();
|
||||
$logger
|
||||
->setName('logger')
|
||||
->inject('registry')
|
||||
->setCallback(function (Registry $registry) {
|
||||
return $registry->get('logger');
|
||||
});
|
||||
$container->set($logger);
|
||||
|
||||
$log = new Dependency();
|
||||
$log
|
||||
->setName('log')
|
||||
->setCallback(function () {
|
||||
return new Log();
|
||||
});
|
||||
$container->set($log);
|
||||
|
||||
$connections = new Dependency();
|
||||
$connections
|
||||
->setName('connections')
|
||||
->setCallback(function () {
|
||||
return new Connections();
|
||||
});
|
||||
$container->set($connections);
|
||||
|
||||
$locale = new Dependency();
|
||||
$locale
|
||||
->setName('locale')
|
||||
->setCallback(fn () => new Locale(System::getEnv('_APP_LOCALE', 'en')));
|
||||
$container->set($locale);
|
||||
|
||||
$localeCodes = new Dependency();
|
||||
$localeCodes
|
||||
->setName('localeCodes')
|
||||
->setCallback(fn () => array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])));
|
||||
$container->set($localeCodes);
|
||||
|
||||
$queue = new Dependency();
|
||||
$queue
|
||||
->setName('queue')
|
||||
->inject('pools')
|
||||
->inject('connections')
|
||||
->setCallback(function (array $pools, Connections $connections) {
|
||||
$pool = $pools['pools-queue-main']['pool'];
|
||||
$dsn = $pools['pools-queue-main']['dsn'];
|
||||
$connection = $pool->get();
|
||||
$connections->add($connection, $pool);
|
||||
|
||||
return new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort());
|
||||
});
|
||||
$container->set($queue);
|
||||
|
||||
$queueForMessaging = new Dependency();
|
||||
$queueForMessaging
|
||||
->setName('queueForMessaging')
|
||||
->inject('queue')
|
||||
->setCallback(function (Connection $queue) {
|
||||
return new Messaging($queue);
|
||||
});
|
||||
$container->set($queueForMessaging);
|
||||
|
||||
$queueForMails = new Dependency();
|
||||
$queueForMails
|
||||
->setName('queueForMails')
|
||||
->inject('queue')
|
||||
->setCallback(function (Connection $queue) {
|
||||
return new Mail($queue);
|
||||
});
|
||||
$container->set($queueForMails);
|
||||
|
||||
$queueForBuilds = new Dependency();
|
||||
$queueForBuilds
|
||||
->setName('queueForBuilds')
|
||||
->inject('queue')
|
||||
->setCallback(function (Connection $queue) {
|
||||
return new Build($queue);
|
||||
});
|
||||
$container->set($queueForBuilds);
|
||||
|
||||
$queueForDatabase = new Dependency();
|
||||
$queueForDatabase
|
||||
->setName('queueForDatabase')
|
||||
->inject('queue')
|
||||
->setCallback(function (Connection $queue) {
|
||||
return new EventDatabase($queue);
|
||||
});
|
||||
$container->set($queueForDatabase);
|
||||
|
||||
$queueForDeletes = new Dependency();
|
||||
$queueForDeletes
|
||||
->setName('queueForDeletes')
|
||||
->inject('queue')
|
||||
->setCallback(function (Connection $queue) {
|
||||
return new Delete($queue);
|
||||
});
|
||||
$container->set($queueForDeletes);
|
||||
|
||||
$queueForEvents = new Dependency();
|
||||
$queueForEvents
|
||||
->setName('queueForEvents')
|
||||
->inject('queue')
|
||||
->setCallback(function (Connection $queue) {
|
||||
return new Event($queue);
|
||||
});
|
||||
$container->set($queueForEvents);
|
||||
|
||||
$queueForAudits = new Dependency();
|
||||
$queueForAudits
|
||||
->setName('queueForAudits')
|
||||
->inject('queue')
|
||||
->setCallback(function (Connection $queue) {
|
||||
return new Audit($queue);
|
||||
});
|
||||
$container->set($queueForAudits);
|
||||
|
||||
$queueForFunctions = new Dependency();
|
||||
$queueForFunctions
|
||||
->setName('queueForFunctions')
|
||||
->inject('queue')
|
||||
->setCallback(function (Connection $queue) {
|
||||
return new Func($queue);
|
||||
});
|
||||
$container->set($queueForFunctions);
|
||||
|
||||
$queueForUsage = new Dependency();
|
||||
$queueForUsage
|
||||
->setName('queueForUsage')
|
||||
->inject('queue')
|
||||
->setCallback(function (Connection $queue) {
|
||||
return new Usage($queue);
|
||||
});
|
||||
$container->set($queueForUsage);
|
||||
|
||||
$queueForCertificates = new Dependency();
|
||||
$queueForCertificates
|
||||
->setName('queueForCertificates')
|
||||
->inject('queue')
|
||||
->setCallback(function (Connection $queue) {
|
||||
return new Certificate($queue);
|
||||
});
|
||||
$container->set($queueForCertificates);
|
||||
|
||||
$queueForMigrations = new Dependency();
|
||||
$queueForMigrations
|
||||
->setName('queueForMigrations')
|
||||
->inject('queue')
|
||||
->setCallback(function (Connection $queue) {
|
||||
return new Migration($queue);
|
||||
});
|
||||
$container->set($queueForMigrations);
|
||||
|
||||
$clients = new Dependency();
|
||||
$clients
|
||||
->setName('clients')
|
||||
->inject('request')
|
||||
->inject('console')
|
||||
->inject('project')
|
||||
->setCallback(function (Request $request, Document $console, Document $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) && isset($node['hostname']) && !empty($node['hostname']))
|
||||
)
|
||||
);
|
||||
|
||||
$clients = \array_unique(
|
||||
\array_merge(
|
||||
$clientsConsole,
|
||||
\array_map(
|
||||
fn ($node) => $node['hostname'],
|
||||
\array_filter(
|
||||
$project->getAttribute('platforms', []),
|
||||
fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname']))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return $clients;
|
||||
});
|
||||
$container->set($clients);
|
||||
|
||||
$geodb = new Dependency();
|
||||
$geodb
|
||||
->setName('geodb')
|
||||
->inject('registry')
|
||||
->setCallback(function (Registry $register) {
|
||||
return $register->get('geodb');
|
||||
});
|
||||
$container->set($geodb);
|
||||
Loading…
Reference in a new issue