mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge pull request #11564 from appwrite/feat/migrate-di-container
feat: migrate from static Http::setResource() to DI Container
This commit is contained in:
+34
-38
@@ -18,13 +18,15 @@ use Swoole\Timer;
|
||||
use Utopia\Cache\Adapter\Pool as CachePool;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Adapters\Generic;
|
||||
use Utopia\CLI\CLI;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Console;
|
||||
use Utopia\Database\Adapter\Pool as DatabasePool;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\DI\Dependency;
|
||||
use Utopia\DI\Container;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Platform\Service;
|
||||
@@ -47,7 +49,7 @@ require_once __DIR__ . '/controllers/general.php';
|
||||
global $register;
|
||||
|
||||
$platform = new Appwrite();
|
||||
$args = $platform->getEnv('argv');
|
||||
$args = $_SERVER['argv'] ?? [];
|
||||
|
||||
\array_shift($args);
|
||||
if (! isset($args[0])) {
|
||||
@@ -56,21 +58,15 @@ if (! isset($args[0])) {
|
||||
}
|
||||
|
||||
$taskName = $args[0];
|
||||
$container = new Container();
|
||||
$cli = new CLI(new Generic(), $_SERVER['argv'] ?? [], $container);
|
||||
|
||||
$platform->setCli($cli);
|
||||
$platform->init(Service::TYPE_TASK);
|
||||
$cli = $platform->getCli();
|
||||
|
||||
$setResource = function (string $name, callable $callback, array $injections = []) use ($cli) {
|
||||
$dependency = new Dependency();
|
||||
$dependency->setName($name)->setCallback($callback);
|
||||
foreach ($injections as $injection) {
|
||||
$dependency->inject($injection);
|
||||
}
|
||||
$cli->setResource($dependency);
|
||||
};
|
||||
$container->set('register', fn () => $register, []);
|
||||
|
||||
$setResource('register', fn () => $register, []);
|
||||
|
||||
$setResource('cache', function ($pools) {
|
||||
$container->set('cache', function ($pools) {
|
||||
$list = Config::getParam('pools-cache', []);
|
||||
$adapters = [];
|
||||
|
||||
@@ -81,18 +77,18 @@ $setResource('cache', function ($pools) {
|
||||
return new Cache(new Sharding($adapters));
|
||||
}, ['pools']);
|
||||
|
||||
$setResource('pools', function (Registry $register) {
|
||||
$container->set('pools', function (Registry $register) {
|
||||
return $register->get('pools');
|
||||
}, ['register']);
|
||||
|
||||
$setResource('authorization', function () {
|
||||
$container->set('authorization', function () {
|
||||
$authorization = new Authorization();
|
||||
$authorization->disable();
|
||||
|
||||
return $authorization;
|
||||
}, []);
|
||||
|
||||
$setResource('dbForPlatform', function ($pools, $cache, $authorization) {
|
||||
$container->set('dbForPlatform', function ($pools, $cache, $authorization) {
|
||||
$sleep = 3;
|
||||
$maxAttempts = 5;
|
||||
$attempts = 0;
|
||||
@@ -135,17 +131,17 @@ $setResource('dbForPlatform', function ($pools, $cache, $authorization) {
|
||||
return $dbForPlatform;
|
||||
}, ['pools', 'cache', 'authorization']);
|
||||
|
||||
$setResource('console', function () {
|
||||
$container->set('console', function () {
|
||||
return new Document(Config::getParam('console'));
|
||||
}, []);
|
||||
|
||||
$setResource(
|
||||
$container->set(
|
||||
'isResourceBlocked',
|
||||
fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false,
|
||||
[]
|
||||
);
|
||||
|
||||
$setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache, $authorization) {
|
||||
$container->set('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache, $authorization) {
|
||||
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
|
||||
|
||||
return function (Document $project) use ($pools, $dbForPlatform, $cache, $authorization, &$databases) {
|
||||
@@ -207,10 +203,10 @@ $setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $c
|
||||
};
|
||||
}, ['pools', 'dbForPlatform', 'cache', 'authorization']);
|
||||
|
||||
$setResource('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
|
||||
$container->set('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
|
||||
$database = null;
|
||||
|
||||
return function (?Document $project = null) use ($pools, $cache, $database, $authorization) {
|
||||
return function (?Document $project = null) use ($pools, $cache, &$database, $authorization) {
|
||||
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$database->setTenant($project->getSequence());
|
||||
return $database;
|
||||
@@ -235,41 +231,41 @@ $setResource('getLogsDB', function (Group $pools, Cache $cache, Authorization $a
|
||||
return $database;
|
||||
};
|
||||
}, ['pools', 'cache', 'authorization']);
|
||||
$setResource('publisher', function (Group $pools) {
|
||||
$container->set('publisher', function (Group $pools) {
|
||||
return new BrokerPool(publisher: $pools->get('publisher'));
|
||||
}, ['pools']);
|
||||
$setResource('publisherDatabases', function (BrokerPool $publisher) {
|
||||
$container->set('publisherDatabases', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
$setResource('publisherFunctions', function (BrokerPool $publisher) {
|
||||
$container->set('publisherFunctions', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
$setResource('publisherMigrations', function (BrokerPool $publisher) {
|
||||
$container->set('publisherMigrations', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
$setResource('publisherMessaging', function (BrokerPool $publisher) {
|
||||
$container->set('publisherMessaging', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
$setResource('usage', function () {
|
||||
$container->set('usage', function () {
|
||||
return new UsageContext();
|
||||
}, []);
|
||||
$setResource('publisherForUsage', fn (Publisher $publisher) => new UsagePublisher(
|
||||
$container->set('publisherForUsage', fn (Publisher $publisher) => new UsagePublisher(
|
||||
$publisher,
|
||||
new Queue(System::getEnv('_APP_STATS_USAGE_QUEUE_NAME', Event::STATS_USAGE_QUEUE_NAME))
|
||||
), ['publisher']);
|
||||
$setResource('queueForStatsResources', function (Publisher $publisher) {
|
||||
$container->set('queueForStatsResources', function (Publisher $publisher) {
|
||||
return new StatsResources($publisher);
|
||||
}, ['publisher']);
|
||||
$setResource('queueForFunctions', function (Publisher $publisher) {
|
||||
$container->set('queueForFunctions', function (Publisher $publisher) {
|
||||
return new Func($publisher);
|
||||
}, ['publisher']);
|
||||
$setResource('queueForDeletes', function (Publisher $publisher) {
|
||||
$container->set('queueForDeletes', function (Publisher $publisher) {
|
||||
return new Delete($publisher);
|
||||
}, ['publisher']);
|
||||
$setResource('queueForCertificates', function (Publisher $publisher) {
|
||||
$container->set('queueForCertificates', function (Publisher $publisher) {
|
||||
return new Certificate($publisher);
|
||||
}, ['publisher']);
|
||||
$setResource('logError', function (Registry $register) {
|
||||
$container->set('logError', function (Registry $register) {
|
||||
return function (Throwable $error, string $namespace, string $action) use ($register) {
|
||||
Console::error('[Error] Timestamp: ' . date('c', time()));
|
||||
Console::error('[Error] Type: ' . get_class($error));
|
||||
@@ -321,13 +317,13 @@ $setResource('logError', function (Registry $register) {
|
||||
};
|
||||
}, ['register']);
|
||||
|
||||
$setResource('executor', fn () => new Executor(), []);
|
||||
$container->set('executor', fn () => new Executor(), []);
|
||||
|
||||
$setResource('bus', function (Registry $register) use ($cli) {
|
||||
return $register->get('bus')->setResolver(fn (string $name) => $cli->getResource($name));
|
||||
$container->set('bus', function (Registry $register) use ($container) {
|
||||
return $register->get('bus')->setResolver(fn (string $name) => $container->get($name));
|
||||
}, ['register']);
|
||||
|
||||
$setResource('telemetry', fn () => new NoTelemetry(), []);
|
||||
$container->set('telemetry', fn () => new NoTelemetry(), []);
|
||||
|
||||
$exitCode = 0;
|
||||
|
||||
|
||||
@@ -752,11 +752,8 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
|
||||
}
|
||||
|
||||
if (\is_array($values)) {
|
||||
$count = 0;
|
||||
foreach ($values as $value) {
|
||||
$override = $count === 0;
|
||||
$response->addHeader($name, $value, override: $override);
|
||||
$count++;
|
||||
$response->addHeader($name, $value);
|
||||
}
|
||||
} else {
|
||||
$response->addHeader($name, $values);
|
||||
@@ -1474,7 +1471,9 @@ Http::error()
|
||||
try {
|
||||
$cors = $utopia->getResource('cors');
|
||||
foreach ($cors->headers($request->getOrigin()) as $name => $value) {
|
||||
$response->addHeader($name, $value, override: true);
|
||||
$response
|
||||
->removeHeader($name)
|
||||
->addHeader($name, $value);
|
||||
}
|
||||
} catch (Throwable) {
|
||||
// Degrade gracefully - error response without CORS is no worse than before.
|
||||
|
||||
+57
-54
@@ -1,16 +1,13 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/init.php';
|
||||
require_once __DIR__ . '/init/span.php';
|
||||
|
||||
global $register;
|
||||
$registerRequestResources = require __DIR__ . '/init/resources/request.php';
|
||||
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Swoole\Constant;
|
||||
use Swoole\Http\Request as SwooleRequest;
|
||||
use Swoole\Http\Response as SwooleResponse;
|
||||
use Swoole\Http\Server;
|
||||
use Swoole\Process;
|
||||
use Swoole\Table;
|
||||
use Swoole\Timer;
|
||||
@@ -29,6 +26,7 @@ use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Http\Adapter\Swoole\Server;
|
||||
use Utopia\Http\Files;
|
||||
use Utopia\Http\Http;
|
||||
use Utopia\Logger\Log;
|
||||
@@ -49,18 +47,35 @@ $certifiedDomains = new Table(100_000);
|
||||
$certifiedDomains->column('value', Table::TYPE_INT, 1);
|
||||
$certifiedDomains->create();
|
||||
|
||||
Http::setResource('riskyDomains', fn () => $riskyDomains);
|
||||
Http::setResource('certifiedDomains', fn () => $certifiedDomains);
|
||||
|
||||
$http = new Server(
|
||||
host: "0.0.0.0",
|
||||
port: System::getEnv('PORT', 80),
|
||||
mode: SWOOLE_PROCESS,
|
||||
);
|
||||
global $container;
|
||||
$container->set('riskyDomains', fn () => $riskyDomains);
|
||||
$container->set('certifiedDomains', fn () => $certifiedDomains);
|
||||
$container->set('pools', function ($register) {
|
||||
return $register->get('pools');
|
||||
}, ['register']);
|
||||
|
||||
$payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing
|
||||
$totalWorkers = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intval(System::getEnv('_APP_WORKER_PER_CORE', 6));
|
||||
|
||||
$swooleAdapter = new Server(
|
||||
host: "0.0.0.0",
|
||||
port: System::getEnv('PORT', 80),
|
||||
settings: [
|
||||
Constant::OPTION_WORKER_NUM => $totalWorkers,
|
||||
Constant::OPTION_DISPATCH_FUNC => dispatch(...),
|
||||
Constant::OPTION_DISPATCH_MODE => SWOOLE_DISPATCH_UIDMOD,
|
||||
Constant::OPTION_HTTP_COMPRESSION => false,
|
||||
Constant::OPTION_PACKAGE_MAX_LENGTH => $payloadSize,
|
||||
Constant::OPTION_OUTPUT_BUFFER_SIZE => $payloadSize,
|
||||
Constant::OPTION_TASK_WORKER_NUM => 1, // required for the task to fetch domains background
|
||||
],
|
||||
container: $container,
|
||||
);
|
||||
|
||||
$container->set('container', fn () => fn () => $swooleAdapter->getContainer());
|
||||
|
||||
$http = $swooleAdapter->getServer();
|
||||
|
||||
/**
|
||||
* Assigns HTTP requests to worker threads by analyzing its payload/content.
|
||||
*
|
||||
@@ -69,16 +84,16 @@ $totalWorkers = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intva
|
||||
* riskier tasks to a dedicated worker subset. Prefers idle workers, with fallback to random selection if necessary.
|
||||
* doc: https://openswoole.com/docs/modules/swoole-server/configuration#dispatch_func
|
||||
*
|
||||
* @param Server $server Swoole server instance.
|
||||
* @param \Swoole\Http\Server $server Swoole server instance.
|
||||
* @param int $fd client ID
|
||||
* @param int $type the type of data and its current state
|
||||
* @param string|null $data Request content for categorization.
|
||||
* @global int $totalThreads Total number of workers.
|
||||
* @return int Chosen worker ID for the request.
|
||||
*/
|
||||
function dispatch(Server $server, int $fd, int $type, $data = null): int
|
||||
function dispatch(\Swoole\Http\Server $server, int $fd, int $type, $data = null): int
|
||||
{
|
||||
$resolveWorkerId = function (Server $server, $data = null) {
|
||||
$resolveWorkerId = function (\Swoole\Http\Server $server, $data = null) {
|
||||
global $totalWorkers, $riskyDomains;
|
||||
|
||||
// If data is not set we can send request to any worker
|
||||
@@ -161,18 +176,6 @@ function dispatch(Server $server, int $fd, int $type, $data = null): int
|
||||
return $workerId;
|
||||
}
|
||||
|
||||
|
||||
$http
|
||||
->set([
|
||||
Constant::OPTION_WORKER_NUM => $totalWorkers,
|
||||
Constant::OPTION_DISPATCH_FUNC => dispatch(...),
|
||||
Constant::OPTION_DISPATCH_MODE => SWOOLE_DISPATCH_UIDMOD,
|
||||
Constant::OPTION_HTTP_COMPRESSION => false,
|
||||
Constant::OPTION_PACKAGE_MAX_LENGTH => $payloadSize,
|
||||
Constant::OPTION_OUTPUT_BUFFER_SIZE => $payloadSize,
|
||||
Constant::OPTION_TASK_WORKER_NUM => 1, // required for the task to fetch domains background
|
||||
]);
|
||||
|
||||
$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) {
|
||||
});
|
||||
|
||||
@@ -189,16 +192,14 @@ $http->on(Constant::EVENT_AFTER_RELOAD, function ($server) {
|
||||
Console::success('Reload completed...');
|
||||
});
|
||||
|
||||
Http::setResource('bus', function ($register, $utopia) {
|
||||
return $register->get('bus')->setResolver(fn (string $name) => $utopia->getResource($name));
|
||||
}, ['register', 'utopia']);
|
||||
$container->set('bus', function ($register) use ($swooleAdapter) {
|
||||
return $register->get('bus')->setResolver(fn (string $name) => $swooleAdapter->getContainer()->get($name));
|
||||
}, ['register']);
|
||||
|
||||
include __DIR__ . '/controllers/general.php';
|
||||
|
||||
function createDatabase(Http $app, string $resourceKey, string $dbName, array $collections, mixed $pools, ?callable $extraSetup = null): void
|
||||
{
|
||||
$max = 15;
|
||||
$sleep = 2;
|
||||
$max = 15;
|
||||
$sleep = 2;
|
||||
$attempts = 0;
|
||||
@@ -289,13 +290,13 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c
|
||||
Span::current()?->finish();
|
||||
}
|
||||
|
||||
$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $totalWorkers, $register) {
|
||||
$app = new Http('UTC');
|
||||
$http->on(Constant::EVENT_START, function ($http) use ($payloadSize, $totalWorkers, $swooleAdapter) {
|
||||
$app = new Http($swooleAdapter, 'UTC');
|
||||
|
||||
go(function () use ($register, $app) {
|
||||
$pools = $register->get('pools');
|
||||
/** @var \Utopia\Pools\Group $pools */
|
||||
Http::setResource('pools', fn () => $pools);
|
||||
/** @var \Utopia\Pools\Group $pools */
|
||||
$pools = $app->getResource('pools');
|
||||
|
||||
go(function () use ($app, $pools) {
|
||||
|
||||
/** @var array $collections */
|
||||
$collections = Config::getParam('collections', []);
|
||||
@@ -511,14 +512,11 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $tot
|
||||
});
|
||||
});
|
||||
|
||||
$http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) use ($register, $files) {
|
||||
$swooleAdapter->onRequest(function ($utopiaRequest, $utopiaResponse) use ($files, $swooleAdapter, $registerRequestResources) {
|
||||
Span::init('http.request');
|
||||
|
||||
Http::setResource('swooleRequest', fn () => $swooleRequest);
|
||||
Http::setResource('swooleResponse', fn () => $swooleResponse);
|
||||
|
||||
$request = new Request($swooleRequest);
|
||||
$response = new Response($swooleResponse);
|
||||
$request = new Request($utopiaRequest->getSwooleRequest());
|
||||
$response = new Response($utopiaResponse->getSwooleResponse());
|
||||
|
||||
Span::add('http.method', $request->getMethod());
|
||||
|
||||
@@ -534,13 +532,18 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
|
||||
return;
|
||||
}
|
||||
|
||||
$app = new Http('UTC');
|
||||
$requestContainer = $swooleAdapter->getContainer();
|
||||
$requestContainer->set('request', fn () => $request);
|
||||
$requestContainer->set('response', fn () => $response);
|
||||
|
||||
$app = new Http($swooleAdapter, 'UTC');
|
||||
$requestContainer->set('utopia', fn () => $app);
|
||||
|
||||
$registerRequestResources($requestContainer);
|
||||
|
||||
$app->setCompression(System::getEnv('_APP_COMPRESSION_ENABLED', 'enabled') === 'enabled');
|
||||
$app->setCompressionMinSize(intval(System::getEnv('_APP_COMPRESSION_MIN_SIZE_BYTES', '1024'))); // 1KB
|
||||
|
||||
$pools = $register->get('pools');
|
||||
Http::setResource('pools', fn () => $pools);
|
||||
|
||||
try {
|
||||
$authorization = $app->getResource('authorization');
|
||||
|
||||
@@ -624,6 +627,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
|
||||
}
|
||||
}
|
||||
|
||||
$swooleResponse = $utopiaResponse->getSwooleResponse();
|
||||
$swooleResponse->setStatusCode(500);
|
||||
|
||||
$output = ((Http::isDevelopment())) ? [
|
||||
@@ -647,11 +651,10 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
|
||||
});
|
||||
|
||||
// Fetch domains every `DOMAIN_SYNC_TIMER` seconds and update in the memory
|
||||
$http->on(Constant::EVENT_TASK, function () use ($register) {
|
||||
$http->on(Constant::EVENT_TASK, function () use ($swooleAdapter) {
|
||||
$lastSyncUpdate = null;
|
||||
$pools = $register->get('pools');
|
||||
Http::setResource('pools', fn () => $pools);
|
||||
$app = new Http('UTC');
|
||||
|
||||
$app = new Http($swooleAdapter, 'UTC');
|
||||
|
||||
/** @var Utopia\Database\Database $dbForPlatform */
|
||||
$dbForPlatform = $app->getResource('dbForPlatform');
|
||||
@@ -726,4 +729,4 @@ $http->on(Constant::EVENT_TASK, function () use ($register) {
|
||||
});
|
||||
});
|
||||
|
||||
$http->start();
|
||||
$swooleAdapter->start();
|
||||
|
||||
+2
-13
@@ -245,19 +245,8 @@ $register->set('pools', function () {
|
||||
$maxConnections = (int) System::getEnv('_APP_CONNECTIONS_MAX', 151);
|
||||
$instanceConnections = $maxConnections / (int) System::getEnv('_APP_POOL_CLIENTS', 14);
|
||||
|
||||
$multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled';
|
||||
|
||||
if ($multiprocessing) {
|
||||
$workerCount = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intval(System::getEnv('_APP_WORKER_PER_CORE', 6));
|
||||
} else {
|
||||
$workerCount = 1;
|
||||
}
|
||||
|
||||
if ($workerCount > $instanceConnections) {
|
||||
throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500);
|
||||
}
|
||||
|
||||
$poolSize = (int)($instanceConnections / $workerCount);
|
||||
$workerCount = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intval(System::getEnv('_APP_WORKER_PER_CORE', 6));
|
||||
$poolSize = max(1, (int)($instanceConnections / $workerCount));
|
||||
|
||||
foreach ($connections as $key => $connection) {
|
||||
$type = $connection['type'] ?? '';
|
||||
|
||||
+38
-1419
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,435 @@
|
||||
<?php
|
||||
|
||||
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\Realtime;
|
||||
use Appwrite\Event\Screenshot;
|
||||
use Appwrite\Event\Webhook;
|
||||
use Appwrite\Usage\Context;
|
||||
use Appwrite\Utopia\Database\Documents\User;
|
||||
use Utopia\Audit\Adapter\Database as AdapterDatabase;
|
||||
use Utopia\Audit\Audit as UtopiaAudit;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Console;
|
||||
use Utopia\Database\Adapter\Pool as DatabasePool;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\DI\Container;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Queue\Publisher;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\Storage\Device\Telemetry as TelemetryDevice;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Telemetry\Adapter as Telemetry;
|
||||
|
||||
/**
|
||||
* Register per-job resources on the given container.
|
||||
* These resources depend on the queue message or keep mutable state and
|
||||
* must be fresh for each worker job.
|
||||
*/
|
||||
return function (Container $container): void {
|
||||
$container->set('log', fn () => new Log(), []);
|
||||
|
||||
$container->set('usage', fn () => new Context(), []);
|
||||
|
||||
$container->set('authorization', function () {
|
||||
$authorization = new Authorization();
|
||||
$authorization->disable();
|
||||
|
||||
return $authorization;
|
||||
}, []);
|
||||
|
||||
$container->set('dbForPlatform', function (Cache $cache, Group $pools, Authorization $authorization) {
|
||||
$adapter = new DatabasePool($pools->get('console'));
|
||||
$dbForPlatform = new Database($adapter, $cache);
|
||||
|
||||
$dbForPlatform
|
||||
->setDatabase(APP_DATABASE)
|
||||
->setAuthorization($authorization)
|
||||
->setNamespace('_console')
|
||||
->setDocumentType('users', User::class);
|
||||
|
||||
return $dbForPlatform;
|
||||
}, ['cache', 'pools', 'authorization']);
|
||||
|
||||
$container->set('project', function ($message, Database $dbForPlatform) {
|
||||
$payload = $message->getPayload() ?? [];
|
||||
$project = new Document($payload['project'] ?? []);
|
||||
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
return $project;
|
||||
}
|
||||
|
||||
return $dbForPlatform->getDocument('projects', $project->getId());
|
||||
}, ['message', 'dbForPlatform']);
|
||||
|
||||
$container->set('dbForProject', function (Cache $cache, Group $pools, Document $project, Database $dbForPlatform, Authorization $authorization) {
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
return $dbForPlatform;
|
||||
}
|
||||
|
||||
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'));
|
||||
}
|
||||
|
||||
$adapter = new DatabasePool($pools->get($dsn->getHost()));
|
||||
$database = new Database($adapter, $cache);
|
||||
$database->setDocumentType('users', User::class);
|
||||
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant($project->getSequence())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
|
||||
$database
|
||||
->setDatabase(APP_DATABASE)
|
||||
->setAuthorization($authorization)
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
|
||||
|
||||
return $database;
|
||||
}, ['cache', 'pools', 'project', 'dbForPlatform', 'authorization']);
|
||||
|
||||
$container->set('getProjectDB', function (Group $pools, Database $dbForPlatform, Cache $cache, Authorization $authorization) {
|
||||
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
|
||||
|
||||
return function (Document $project) use ($pools, $dbForPlatform, $cache, $authorization, &$databases): Database {
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
return $dbForPlatform;
|
||||
}
|
||||
|
||||
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'));
|
||||
}
|
||||
|
||||
if (isset($databases[$dsn->getHost()])) {
|
||||
$database = $databases[$dsn->getHost()];
|
||||
$database->setAuthorization($authorization);
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant($project->getSequence())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
|
||||
return $database;
|
||||
}
|
||||
|
||||
$adapter = new DatabasePool($pools->get($dsn->getHost()));
|
||||
$database = new Database($adapter, $cache);
|
||||
|
||||
$databases[$dsn->getHost()] = $database;
|
||||
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant($project->getSequence())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
|
||||
$database
|
||||
->setDatabase(APP_DATABASE)
|
||||
->setAuthorization($authorization)
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
|
||||
|
||||
return $database;
|
||||
};
|
||||
}, ['pools', 'dbForPlatform', 'cache', 'authorization']);
|
||||
|
||||
$container->set('getDatabasesDB', function (Cache $cache, Registry $register, Document $project, Authorization $authorization) {
|
||||
return function (Document $database, ?Document $projectDocument = null) use ($cache, $register, $project, $authorization): Database {
|
||||
$projectDocument ??= $project;
|
||||
$databaseDSN = $database->getAttribute('database', $project->getAttribute('database', ''));
|
||||
$databaseType = $database->getAttribute('type', '');
|
||||
|
||||
// Backwards-compatibility: older or seeded legacy databases may not have a DSN stored
|
||||
// in the "database" attribute. In that case, fall back to the project's database DSN.
|
||||
if ($databaseDSN === '') {
|
||||
$databaseDSN = $projectDocument->getAttribute('database', '');
|
||||
}
|
||||
|
||||
try {
|
||||
$databaseDSN = new DSN($databaseDSN);
|
||||
} catch (\InvalidArgumentException) {
|
||||
$databaseDSN = new DSN('mysql://' . $databaseDSN);
|
||||
}
|
||||
|
||||
try {
|
||||
$dsn = new DSN($projectDocument->getAttribute('database'));
|
||||
} catch (\InvalidArgumentException) {
|
||||
// Temporary fallback until all projects use shared tables
|
||||
$dsn = new DSN('mysql://' . $projectDocument->getAttribute('database'));
|
||||
}
|
||||
|
||||
$pools = $register->get('pools');
|
||||
$pool = $pools->get($databaseDSN->getHost());
|
||||
|
||||
$adapter = new DatabasePool($pool);
|
||||
$database = new Database($adapter, $cache);
|
||||
$database
|
||||
->setDatabase(APP_DATABASE)
|
||||
->setAuthorization($authorization);
|
||||
$database->getAdapter()->setSupportForAttributes($databaseType !== DOCUMENTSDB);
|
||||
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
if (\in_array($dsn->getHost(), $sharedTables, true)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant((int) $projectDocument->getSequence())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $projectDocument->getSequence());
|
||||
}
|
||||
|
||||
$database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
|
||||
return $database;
|
||||
};
|
||||
}, ['cache', 'register', 'project', 'authorization']);
|
||||
|
||||
$container->set('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
|
||||
$database = null;
|
||||
|
||||
return function (?Document $project = null) use ($pools, $cache, $authorization, &$database) {
|
||||
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$database->setTenant($project->getSequence());
|
||||
|
||||
return $database;
|
||||
}
|
||||
|
||||
$adapter = new DatabasePool($pools->get('logs'));
|
||||
$database = new Database($adapter, $cache);
|
||||
|
||||
$database
|
||||
->setDatabase(APP_DATABASE)
|
||||
->setAuthorization($authorization)
|
||||
->setSharedTables(true)
|
||||
->setNamespace('logsV1')
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER)
|
||||
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES_WORKER);
|
||||
|
||||
if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$database->setTenant($project->getSequence());
|
||||
}
|
||||
|
||||
return $database;
|
||||
};
|
||||
}, ['pools', 'cache', 'authorization']);
|
||||
|
||||
$container->set('abuseRetention', function () {
|
||||
return \time() - (int) System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400); // 1 day
|
||||
}, []);
|
||||
|
||||
$container->set('auditRetention', function (Document $project) {
|
||||
if ($project->getId() === 'console') {
|
||||
return DateTime::addSeconds(new \DateTime(), -1 * (int) System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE', 15778800)); // 6 months
|
||||
}
|
||||
|
||||
return DateTime::addSeconds(new \DateTime(), -1 * (int) System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600)); // 14 days
|
||||
}, ['project']);
|
||||
|
||||
$container->set('executionRetention', function () {
|
||||
return DateTime::addSeconds(new \DateTime(), -1 * (int) System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600)); // 14 days
|
||||
}, []);
|
||||
|
||||
$container->set('queueForDatabase', function (Publisher $publisher) {
|
||||
return new EventDatabase($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
$container->set('queueForMessaging', function (Publisher $publisher) {
|
||||
return new Messaging($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
$container->set('queueForMails', function (Publisher $publisher) {
|
||||
return new Mail($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
$container->set('queueForBuilds', function (Publisher $publisher) {
|
||||
return new Build($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
$container->set('queueForScreenshots', function (Publisher $publisher) {
|
||||
return new Screenshot($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
$container->set('queueForDeletes', function (Publisher $publisher) {
|
||||
return new Delete($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
$container->set('queueForEvents', function (Publisher $publisher) {
|
||||
return new Event($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
$container->set('queueForAudits', function (Publisher $publisher) {
|
||||
return new Audit($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
$container->set('queueForWebhooks', function (Publisher $publisher) {
|
||||
return new Webhook($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
$container->set('queueForFunctions', function (Publisher $publisher) {
|
||||
return new Func($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
$container->set('queueForRealtime', function () {
|
||||
return new Realtime();
|
||||
}, []);
|
||||
|
||||
$container->set('queueForCertificates', function (Publisher $publisher) {
|
||||
return new Certificate($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
$container->set('queueForMigrations', function (Publisher $publisher) {
|
||||
return new Migration($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
$container->set('deviceForSites', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_SITES . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
$container->set('deviceForMigrations', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
$container->set('deviceForFunctions', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
$container->set('deviceForFiles', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
$container->set('deviceForBuilds', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
$container->set('deviceForCache', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
$container->set('logError', function (Registry $register, Document $project) {
|
||||
return function (Throwable $error, string $namespace, string $action, ?array $extras = null) use ($register, $project) {
|
||||
$logger = $register->get('logger');
|
||||
|
||||
if ($logger) {
|
||||
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||
|
||||
$log = new Log();
|
||||
$log->setNamespace($namespace);
|
||||
$log->setServer(System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname()));
|
||||
$log->setVersion($version);
|
||||
$log->setType(Log::TYPE_ERROR);
|
||||
$log->setMessage($error->getMessage());
|
||||
|
||||
$log->addTag('code', $error->getCode());
|
||||
$log->addTag('verboseType', \get_class($error));
|
||||
$log->addTag('projectId', $project->getId() ?? '');
|
||||
|
||||
$log->addExtra('file', $error->getFile());
|
||||
$log->addExtra('line', $error->getLine());
|
||||
$log->addExtra('trace', $error->getTraceAsString());
|
||||
|
||||
if ($error->getPrevious() !== null) {
|
||||
if ($error->getPrevious()->getMessage() != $error->getMessage()) {
|
||||
$log->addExtra('previousMessage', $error->getPrevious()->getMessage());
|
||||
}
|
||||
$log->addExtra('previousFile', $error->getPrevious()->getFile());
|
||||
$log->addExtra('previousLine', $error->getPrevious()->getLine());
|
||||
}
|
||||
|
||||
foreach (($extras ?? []) as $key => $value) {
|
||||
$log->addExtra($key, $value);
|
||||
}
|
||||
|
||||
$log->setAction($action);
|
||||
|
||||
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
|
||||
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
||||
|
||||
try {
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Error log pushed with status code: ' . $responseCode);
|
||||
} catch (Throwable $th) {
|
||||
Console::error('Error pushing log: ' . $th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
Console::warning("Failed: {$error->getMessage()}");
|
||||
Console::warning($error->getTraceAsString());
|
||||
|
||||
if ($error->getPrevious() !== null) {
|
||||
if ($error->getPrevious()->getMessage() != $error->getMessage()) {
|
||||
Console::warning("Previous Failed: {$error->getPrevious()->getMessage()}");
|
||||
}
|
||||
Console::warning("Previous File: {$error->getPrevious()->getFile()} Line: {$error->getPrevious()->getLine()}");
|
||||
}
|
||||
};
|
||||
}, ['register', 'project']);
|
||||
|
||||
$container->set('getAudit', function (Database $dbForPlatform, callable $getProjectDB) {
|
||||
return function (Document $project) use ($dbForPlatform, $getProjectDB) {
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
$adapter = new AdapterDatabase($dbForPlatform);
|
||||
|
||||
return new UtopiaAudit($adapter);
|
||||
}
|
||||
|
||||
$dbForProject = $getProjectDB($project);
|
||||
$adapter = new AdapterDatabase($dbForProject);
|
||||
|
||||
return new UtopiaAudit($adapter);
|
||||
};
|
||||
}, ['dbForPlatform', 'getProjectDB']);
|
||||
|
||||
$container->set('executionsRetentionCount', function (Document $project, array $plan) {
|
||||
if ($project->getId() === 'console' || empty($plan)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) ($plan['executionsRetentionCount'] ?? 100);
|
||||
}, ['project', 'plan']);
|
||||
};
|
||||
+20
-5
@@ -33,7 +33,9 @@ use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\DI\Container;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Http\Adapter\FPM\Server as HttpServer;
|
||||
use Utopia\Http\Http;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Pools\Group;
|
||||
@@ -48,6 +50,8 @@ use Utopia\WebSocket\Server;
|
||||
*/
|
||||
require_once __DIR__ . '/init.php';
|
||||
|
||||
$registerRequestResources ??= require __DIR__ . '/init/resources/request.php';
|
||||
|
||||
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
||||
|
||||
// Log uncaught exceptions in one line instead of relying on Swoole's full backtrace dump
|
||||
@@ -240,6 +244,11 @@ if (!function_exists('triggerStats')) {
|
||||
}
|
||||
}
|
||||
|
||||
global $container;
|
||||
$container->set('pools', function ($register) {
|
||||
return $register->get('pools');
|
||||
}, ['register']);
|
||||
|
||||
$realtime = getRealtime();
|
||||
|
||||
/**
|
||||
@@ -614,16 +623,22 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
||||
Console::error('Failed to restart pub/sub...');
|
||||
});
|
||||
|
||||
$server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $register, $stats, &$realtime) {
|
||||
$app = new Http('UTC');
|
||||
$server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $register, $stats, &$realtime, $registerRequestResources) {
|
||||
global $container;
|
||||
$request = new Request($request);
|
||||
$response = new Response(new SwooleResponse());
|
||||
|
||||
Console::info("Connection open (user: {$connection})");
|
||||
|
||||
Http::setResource('pools', fn () => $register->get('pools'));
|
||||
Http::setResource('request', fn () => $request);
|
||||
Http::setResource('response', fn () => $response);
|
||||
$connectionContainer = new Container($container);
|
||||
|
||||
$adapter = new HttpServer($connectionContainer);
|
||||
$app = new Http($adapter, 'UTC');
|
||||
$connectionContainer->set('utopia', fn () => $app);
|
||||
$connectionContainer->set('request', fn () => $request);
|
||||
$connectionContainer->set('response', fn () => $response);
|
||||
|
||||
$registerRequestResources($connectionContainer);
|
||||
|
||||
$project = null;
|
||||
$logUser = null;
|
||||
|
||||
+42
-530
@@ -1,564 +1,69 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/init.php';
|
||||
|
||||
$registerWorkerMessageResources = require __DIR__ . '/init/worker/message.php';
|
||||
use Appwrite\Certificates\LetsEncrypt;
|
||||
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\Publisher\Usage as UsagePublisher;
|
||||
use Appwrite\Event\Realtime;
|
||||
use Appwrite\Event\Screenshot;
|
||||
use Appwrite\Event\Webhook;
|
||||
use Appwrite\Platform\Appwrite;
|
||||
use Appwrite\Usage\Context;
|
||||
use Appwrite\Utopia\Database\Documents\User;
|
||||
use Executor\Executor;
|
||||
use Swoole\Runtime;
|
||||
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
|
||||
use Utopia\Audit\Adapter\Database as AdapterDatabase;
|
||||
use Utopia\Audit\Audit as UtopiaAudit;
|
||||
use Utopia\Cache\Adapter\Pool as CachePool;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Console;
|
||||
use Utopia\Database\Adapter\Pool as DatabasePool;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Logger\Logger;
|
||||
use Utopia\Platform\Service;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Queue\Adapter\Swoole;
|
||||
use Utopia\Queue\Broker\Pool as BrokerPool;
|
||||
use Utopia\Queue\Message;
|
||||
use Utopia\Queue\Publisher;
|
||||
use Utopia\Queue\Queue;
|
||||
use Utopia\Queue\Server;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\Storage\Device\Telemetry as TelemetryDevice;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Telemetry\Adapter as Telemetry;
|
||||
use Utopia\Telemetry\Adapter\None as NoTelemetry;
|
||||
|
||||
Runtime::enableCoroutine();
|
||||
require_once __DIR__ . '/init/span.php';
|
||||
|
||||
global $register;
|
||||
Server::setResource('register', fn () => $register);
|
||||
global $container;
|
||||
$container->set('pools', function ($register) {
|
||||
return $register->get('pools');
|
||||
}, ['register']);
|
||||
|
||||
Server::setResource('authorization', function () {
|
||||
$container->set('authorization', function () {
|
||||
$authorization = new Authorization();
|
||||
$authorization->disable();
|
||||
|
||||
return $authorization;
|
||||
}, []);
|
||||
|
||||
Server::setResource('dbForPlatform', function (Cache $cache, Registry $register, Authorization $authorization) {
|
||||
$pools = $register->get('pools');
|
||||
$adapter = new DatabasePool($pools->get('console'));
|
||||
$dbForPlatform = new Database($adapter, $cache);
|
||||
$container->set('project', fn () => new Document([]), []);
|
||||
|
||||
$dbForPlatform
|
||||
->setDatabase(APP_DATABASE)
|
||||
->setAuthorization($authorization)
|
||||
->setNamespace('_console')
|
||||
->setDocumentType('users', User::class);
|
||||
$container->set('log', fn () => new Log(), []);
|
||||
|
||||
return $dbForPlatform;
|
||||
}, ['cache', 'register', 'authorization']);
|
||||
|
||||
Server::setResource('project', function (Message $message, Database $dbForPlatform) {
|
||||
$payload = $message->getPayload() ?? [];
|
||||
$project = new Document($payload['project'] ?? []);
|
||||
|
||||
if ($project->getId() === 'console') {
|
||||
return $project;
|
||||
}
|
||||
|
||||
return $dbForPlatform->getDocument('projects', $project->getId());
|
||||
}, ['message', 'dbForPlatform']);
|
||||
|
||||
Server::setResource('dbForProject', function (Cache $cache, Registry $register, Message $message, Document $project, Database $dbForPlatform, Authorization $authorization) {
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
return $dbForPlatform;
|
||||
}
|
||||
|
||||
$pools = $register->get('pools');
|
||||
|
||||
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'));
|
||||
}
|
||||
|
||||
$adapter = new DatabasePool($pools->get($dsn->getHost()));
|
||||
$database = new Database($adapter, $cache);
|
||||
$database->setDocumentType('users', User::class);
|
||||
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant($project->getSequence())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
|
||||
$database
|
||||
->setDatabase(APP_DATABASE)
|
||||
->setAuthorization($authorization)
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
|
||||
|
||||
return $database;
|
||||
}, ['cache', 'register', 'message', 'project', 'dbForPlatform', 'authorization']);
|
||||
|
||||
Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache, Authorization $authorization) {
|
||||
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
|
||||
|
||||
return function (Document $project) use ($pools, $dbForPlatform, $cache, $authorization, &$databases): Database {
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
return $dbForPlatform;
|
||||
}
|
||||
|
||||
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'));
|
||||
}
|
||||
|
||||
if (isset($databases[$dsn->getHost()])) {
|
||||
$database = $databases[$dsn->getHost()];
|
||||
$database->setAuthorization($authorization);
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant($project->getSequence())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
|
||||
return $database;
|
||||
}
|
||||
|
||||
$adapter = new DatabasePool($pools->get($dsn->getHost()));
|
||||
$database = new Database($adapter, $cache);
|
||||
|
||||
$databases[$dsn->getHost()] = $database;
|
||||
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant($project->getSequence())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
|
||||
$database
|
||||
->setDatabase(APP_DATABASE)
|
||||
->setAuthorization($authorization)
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
|
||||
|
||||
return $database;
|
||||
};
|
||||
}, ['pools', 'dbForPlatform', 'cache', 'authorization']);
|
||||
|
||||
Server::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
|
||||
$database = null;
|
||||
|
||||
return function (?Document $project = null) use ($pools, $cache, $database, $authorization) {
|
||||
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$database->setTenant($project->getSequence());
|
||||
return $database;
|
||||
}
|
||||
|
||||
$adapter = new DatabasePool($pools->get('logs'));
|
||||
$database = new Database($adapter, $cache);
|
||||
|
||||
$database
|
||||
->setDatabase(APP_DATABASE)
|
||||
->setAuthorization($authorization)
|
||||
->setSharedTables(true)
|
||||
->setNamespace('logsV1')
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER)
|
||||
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES_WORKER);
|
||||
|
||||
if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$database->setTenant($project->getSequence());
|
||||
}
|
||||
|
||||
return $database;
|
||||
};
|
||||
}, ['pools', 'cache', 'authorization']);
|
||||
|
||||
Server::setResource('getDatabasesDB', function (Cache $cache, Registry $register, Document $project, Authorization $authorization) {
|
||||
return function (Document $database, ?Document $projectDocument = null) use ($cache, $register, $project, $authorization): Database {
|
||||
$projectDocument ??= $project;
|
||||
$databaseDSN = $database->getAttribute('database', $project->getAttribute('database', ''));
|
||||
$databaseType = $database->getAttribute('type', '');
|
||||
|
||||
// Backwards‑compatibility: older or seeded legacy databases may not have a DSN stored
|
||||
// in the "database" attribute. In that case, fall back to the project's database DSN.
|
||||
if ($databaseDSN === '') {
|
||||
$databaseDSN = $projectDocument->getAttribute('database', '');
|
||||
}
|
||||
|
||||
try {
|
||||
$databaseDSN = new DSN($databaseDSN);
|
||||
} catch (\InvalidArgumentException) {
|
||||
$databaseDSN = new DSN('mysql://'.$databaseDSN);
|
||||
}
|
||||
|
||||
try {
|
||||
$dsn = new DSN($projectDocument->getAttribute('database'));
|
||||
} catch (\InvalidArgumentException) {
|
||||
// Temporary fallback until all projects use shared tables
|
||||
$dsn = new DSN('mysql://' . $projectDocument->getAttribute('database'));
|
||||
}
|
||||
|
||||
$pools = $register->get('pools');
|
||||
$pool = $pools->get($databaseDSN->getHost());
|
||||
|
||||
$adapter = new DatabasePool($pool);
|
||||
$database = new Database($adapter, $cache);
|
||||
$database
|
||||
->setDatabase(APP_DATABASE)
|
||||
->setAuthorization($authorization);
|
||||
$database->getAdapter()->setSupportForAttributes($databaseType !== DOCUMENTSDB);
|
||||
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
if (\in_array($dsn->getHost(), $sharedTables, true)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant((int) $projectDocument->getSequence())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $projectDocument->getSequence());
|
||||
}
|
||||
|
||||
$database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
|
||||
return $database;
|
||||
};
|
||||
}, ['cache', 'register', 'project', 'authorization']);
|
||||
|
||||
Server::setResource('abuseRetention', function () {
|
||||
return time() - (int) System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400); // 1 day
|
||||
});
|
||||
|
||||
Server::setResource('auditRetention', function (Document $project) {
|
||||
if ($project->getId() === 'console') {
|
||||
return DateTime::addSeconds(new \DateTime(), -1 * (int) System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE', 15778800)); // 6 months
|
||||
}
|
||||
|
||||
return DateTime::addSeconds(new \DateTime(), -1 * (int) System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600)); // 14 days
|
||||
}, ['project']);
|
||||
|
||||
Server::setResource('executionRetention', function () {
|
||||
return DateTime::addSeconds(new \DateTime(), -1 * (int) System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600)); // 14 days
|
||||
});
|
||||
|
||||
Server::setResource('cache', function (Registry $register) {
|
||||
$pools = $register->get('pools');
|
||||
$list = Config::getParam('pools-cache', []);
|
||||
$adapters = [];
|
||||
|
||||
foreach ($list as $value) {
|
||||
$adapters[] = new CachePool($pools->get($value));
|
||||
}
|
||||
|
||||
return new Cache(new Sharding($adapters));
|
||||
}, ['register']);
|
||||
|
||||
Server::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;
|
||||
});
|
||||
|
||||
Server::setResource('timelimit', function (\Redis $redis) {
|
||||
return function (string $key, int $limit, int $time) use ($redis) {
|
||||
return new TimeLimitRedis($key, $limit, $time, $redis);
|
||||
};
|
||||
}, ['redis']);
|
||||
|
||||
Server::setResource('log', fn () => new Log());
|
||||
|
||||
Server::setResource('publisher', function (Group $pools) {
|
||||
return new BrokerPool(publisher: $pools->get('publisher'));
|
||||
}, ['pools']);
|
||||
|
||||
Server::setResource('publisherDatabases', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('publisherFunctions', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('publisherMigrations', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('publisherMessaging', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('consumer', function (Group $pools) {
|
||||
$container->set('consumer', function (Group $pools) {
|
||||
return new BrokerPool(consumer: $pools->get('consumer'));
|
||||
}, ['pools']);
|
||||
|
||||
Server::setResource('consumerDatabases', function (BrokerPool $consumer) {
|
||||
$container->set('consumerDatabases', function (BrokerPool $consumer) {
|
||||
return $consumer;
|
||||
}, ['consumer']);
|
||||
|
||||
Server::setResource('consumerMigrations', function (BrokerPool $consumer) {
|
||||
$container->set('consumerMigrations', function (BrokerPool $consumer) {
|
||||
return $consumer;
|
||||
}, ['consumer']);
|
||||
|
||||
Server::setResource('consumerStatsUsage', function (BrokerPool $consumer) {
|
||||
$container->set('consumerStatsUsage', function (BrokerPool $consumer) {
|
||||
return $consumer;
|
||||
}, ['consumer']);
|
||||
|
||||
Server::setResource('usage', function () {
|
||||
return new Context();
|
||||
}, []);
|
||||
Server::setResource('publisherForUsage', fn (Publisher $publisher) => new UsagePublisher(
|
||||
$publisher,
|
||||
new Queue(System::getEnv('_APP_STATS_USAGE_QUEUE_NAME', Event::STATS_USAGE_QUEUE_NAME))
|
||||
), ['publisher']);
|
||||
|
||||
Server::setResource('queueForDatabase', function (Publisher $publisher) {
|
||||
return new EventDatabase($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForMessaging', function (Publisher $publisher) {
|
||||
return new Messaging($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForMails', function (Publisher $publisher) {
|
||||
return new Mail($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForBuilds', function (Publisher $publisher) {
|
||||
return new Build($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForScreenshots', function (Publisher $publisher) {
|
||||
return new Screenshot($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForDeletes', function (Publisher $publisher) {
|
||||
return new Delete($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForEvents', function (Publisher $publisher) {
|
||||
return new Event($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForAudits', function (Publisher $publisher) {
|
||||
return new Audit($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForWebhooks', function (Publisher $publisher) {
|
||||
return new Webhook($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForFunctions', function (Publisher $publisher) {
|
||||
return new Func($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForRealtime', function () {
|
||||
return new Realtime();
|
||||
}, []);
|
||||
|
||||
Server::setResource('queueForCertificates', function (Publisher $publisher) {
|
||||
return new Certificate($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForMigrations', function (Publisher $publisher) {
|
||||
return new Migration($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('logger', function (Registry $register) {
|
||||
return $register->get('logger');
|
||||
}, ['register']);
|
||||
|
||||
Server::setResource('pools', function (Registry $register) {
|
||||
return $register->get('pools');
|
||||
}, ['register']);
|
||||
|
||||
Server::setResource('telemetry', fn () => new NoTelemetry());
|
||||
|
||||
Server::setResource('deviceForSites', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_SITES . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
Server::setResource('deviceForMigrations', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
Server::setResource('deviceForFunctions', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
Server::setResource('deviceForFiles', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
Server::setResource('deviceForBuilds', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
Server::setResource('deviceForCache', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
Server::setResource(
|
||||
'isResourceBlocked',
|
||||
fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false
|
||||
);
|
||||
|
||||
Server::setResource('plan', function (array $plan = []) {
|
||||
return [];
|
||||
});
|
||||
|
||||
Server::setResource('certificates', function () {
|
||||
$container->set('certificates', function () {
|
||||
$email = System::getEnv('_APP_EMAIL_CERTIFICATES', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'));
|
||||
if (empty($email)) {
|
||||
throw new Exception('You must set a valid security email address (_APP_EMAIL_CERTIFICATES) to issue a LetsEncrypt SSL certificate.');
|
||||
}
|
||||
|
||||
return new LetsEncrypt($email);
|
||||
});
|
||||
}, []);
|
||||
|
||||
Server::setResource('logError', function (Registry $register, Document $project) {
|
||||
return function (Throwable $error, string $namespace, string $action, ?array $extras = null) use ($register, $project) {
|
||||
$logger = $register->get('logger');
|
||||
|
||||
if ($logger) {
|
||||
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||
|
||||
$log = new Log();
|
||||
$log->setNamespace($namespace);
|
||||
$log->setServer(System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname()));
|
||||
$log->setVersion($version);
|
||||
$log->setType(Log::TYPE_ERROR);
|
||||
$log->setMessage($error->getMessage());
|
||||
|
||||
$log->addTag('code', $error->getCode());
|
||||
$log->addTag('verboseType', get_class($error));
|
||||
$log->addTag('projectId', $project->getId() ?? '');
|
||||
|
||||
$log->addExtra('file', $error->getFile());
|
||||
$log->addExtra('line', $error->getLine());
|
||||
$log->addExtra('trace', $error->getTraceAsString());
|
||||
|
||||
if ($error->getPrevious() !== null) {
|
||||
if ($error->getPrevious()->getMessage() != $error->getMessage()) {
|
||||
$log->addExtra('previousMessage', $error->getPrevious()->getMessage());
|
||||
}
|
||||
$log->addExtra('previousFile', $error->getPrevious()->getFile());
|
||||
$log->addExtra('previousLine', $error->getPrevious()->getLine());
|
||||
}
|
||||
|
||||
foreach (($extras ?? []) as $key => $value) {
|
||||
$log->addExtra($key, $value);
|
||||
}
|
||||
|
||||
$log->setAction($action);
|
||||
|
||||
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
|
||||
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
||||
|
||||
try {
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Error log pushed with status code: ' . $responseCode);
|
||||
} catch (Throwable $th) {
|
||||
Console::error('Error pushing log: ' . $th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
Console::warning("Failed: {$error->getMessage()}");
|
||||
Console::warning($error->getTraceAsString());
|
||||
|
||||
if ($error->getPrevious() !== null) {
|
||||
if ($error->getPrevious()->getMessage() != $error->getMessage()) {
|
||||
Console::warning("Previous Failed: {$error->getPrevious()->getMessage()}");
|
||||
}
|
||||
Console::warning("Previous File: {$error->getPrevious()->getFile()} Line: {$error->getPrevious()->getLine()}");
|
||||
}
|
||||
};
|
||||
}, ['register', 'project']);
|
||||
|
||||
Server::setResource('executor', fn () => new Executor());
|
||||
|
||||
Server::setResource('getAudit', function (Database $dbForPlatform, callable $getProjectDB) {
|
||||
return function (Document $project) use ($dbForPlatform, $getProjectDB) {
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
$adapter = new AdapterDatabase($dbForPlatform);
|
||||
|
||||
return new UtopiaAudit($adapter);
|
||||
}
|
||||
|
||||
$dbForProject = $getProjectDB($project);
|
||||
$adapter = new AdapterDatabase($dbForProject);
|
||||
|
||||
return new UtopiaAudit($adapter);
|
||||
};
|
||||
}, ['dbForPlatform', 'getProjectDB']);
|
||||
|
||||
Server::setResource('executionsRetentionCount', function (Document $project, array $plan) {
|
||||
if ($project->getId() === 'console' || empty($plan)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) ($plan['executionsRetentionCount'] ?? 100);
|
||||
}, ['project', 'plan']);
|
||||
|
||||
$pools = $register->get('pools');
|
||||
$platform = new Appwrite();
|
||||
$args = $platform->getEnv('argv');
|
||||
$args = $_SERVER['argv'] ?? [];
|
||||
|
||||
if (! isset($args[1])) {
|
||||
Console::error('Missing worker name');
|
||||
@@ -574,38 +79,45 @@ if (\str_starts_with($workerName, 'databases')) {
|
||||
$queueName = System::getEnv('_APP_QUEUE_NAME', 'v1-' . strtolower($workerName));
|
||||
}
|
||||
|
||||
/** @var \Utopia\Pools\Group $pools */
|
||||
$pools = $container->get('pools');
|
||||
|
||||
$adapter = new Swoole(
|
||||
$pools->get('consumer')->pop()->getResource(),
|
||||
System::getEnv('_APP_WORKERS_NUM', 1),
|
||||
$queueName
|
||||
);
|
||||
|
||||
$worker = new Server($adapter, $container);
|
||||
|
||||
try {
|
||||
/**
|
||||
* Any worker can be configured with the following env vars:
|
||||
* - _APP_WORKERS_NUM The total number of worker processes
|
||||
* - _APP_WORKER_PER_CORE The number of worker processes per core (ignored if _APP_WORKERS_NUM is set)
|
||||
* - _APP_QUEUE_NAME The name of the queue to read for database events
|
||||
*/
|
||||
$worker->init()->action(function () use ($worker, $registerWorkerMessageResources) {
|
||||
$registerWorkerMessageResources($worker->getContainer());
|
||||
});
|
||||
|
||||
$container->set('bus', function ($register) use ($worker) {
|
||||
return $register->get('bus')->setResolver(
|
||||
fn (string $name) => $worker->getContainer()->get($name)
|
||||
);
|
||||
}, ['register']);
|
||||
|
||||
$platform->setWorker($worker);
|
||||
$platform->init(Service::TYPE_WORKER, [
|
||||
'workersNum' => System::getEnv('_APP_WORKERS_NUM', 1),
|
||||
'connection' => $pools->get('consumer')->pop()->getResource(),
|
||||
'workerName' => strtolower($workerName) ?? null,
|
||||
'queueName' => $queueName,
|
||||
'workerName' => strtolower($workerName),
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
Console::error($e->getMessage() . ', File: ' . $e->getFile() . ', Line: ' . $e->getLine());
|
||||
Console::exit(1);
|
||||
}
|
||||
|
||||
$worker = $platform->getWorker();
|
||||
|
||||
Server::setResource('bus', function ($register) use ($worker) {
|
||||
return $register->get('bus')->setResolver(fn (string $name) => $worker->getResource($name));
|
||||
}, ['register']);
|
||||
|
||||
$worker
|
||||
->error()
|
||||
->inject('error')
|
||||
->inject('logger')
|
||||
->inject('log')
|
||||
->inject('pools')
|
||||
->inject('project')
|
||||
->inject('authorization')
|
||||
->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project, Authorization $authorization) use ($queueName) {
|
||||
->action(function (Throwable $error, ?Logger $logger, Log $log, Document $project, Authorization $authorization) use ($queueName) {
|
||||
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||
|
||||
if ($logger) {
|
||||
|
||||
+7
-8
@@ -52,34 +52,34 @@
|
||||
"appwrite/php-runtimes": "0.19.*",
|
||||
"appwrite/php-clamav": "2.0.*",
|
||||
"utopia-php/abuse": "1.2.*",
|
||||
"utopia-php/agents": "1.2.*",
|
||||
"utopia-php/analytics": "0.15.*",
|
||||
"utopia-php/audit": "2.2.*",
|
||||
"utopia-php/auth": "0.5.*",
|
||||
"utopia-php/cache": "1.0.*",
|
||||
"utopia-php/cli": "0.22.*",
|
||||
"utopia-php/cli": "0.23.*",
|
||||
"utopia-php/compression": "0.1.*",
|
||||
"utopia-php/config": "1.*",
|
||||
"utopia-php/console": "0.1.*",
|
||||
"utopia-php/database": "5.*",
|
||||
"utopia-php/agents": "1.*",
|
||||
"utopia-php/detector": "0.2.*",
|
||||
"utopia-php/domains": "1.*",
|
||||
"utopia-php/emails": "0.6.*",
|
||||
"utopia-php/dns": "1.6.*",
|
||||
"utopia-php/dsn": "0.2.1",
|
||||
"utopia-php/framework": "0.33.*",
|
||||
"utopia-php/framework": "0.34.*",
|
||||
"utopia-php/fetch": "0.5.*",
|
||||
"utopia-php/image": "0.8.*",
|
||||
"utopia-php/locale": "0.8.*",
|
||||
"utopia-php/logger": "0.6.*",
|
||||
"utopia-php/messaging": "0.22.*",
|
||||
"utopia-php/migration": "1.9.*",
|
||||
"utopia-php/platform": "0.7.*",
|
||||
"utopia-php/platform": "0.12.*",
|
||||
"utopia-php/pools": "1.*",
|
||||
"utopia-php/span": "1.1.*",
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
"utopia-php/queue": "0.15.*",
|
||||
"utopia-php/servers": "0.2.5",
|
||||
"utopia-php/queue": "0.17.*",
|
||||
"utopia-php/servers": "0.3.*",
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/storage": "1.0.*",
|
||||
"utopia-php/system": "0.10.*",
|
||||
@@ -94,8 +94,7 @@
|
||||
"spomky-labs/otphp": "11.*",
|
||||
"webonyx/graphql-php": "14.11.*",
|
||||
"league/csv": "9.14.*",
|
||||
"enshrined/svg-sanitize": "0.22.*",
|
||||
"utopia-php/di": "0.1.0"
|
||||
"enshrined/svg-sanitize": "0.22.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-fileinfo": "*",
|
||||
|
||||
Generated
+130
-67
@@ -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": "4fe91e67f343fbe6deac1fdc7eda949f",
|
||||
"content-hash": "e9c38bbebc60849e70e3640aaa4422cd",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
@@ -3403,16 +3403,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/agents",
|
||||
"version": "1.3.0",
|
||||
"version": "1.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/agents.git",
|
||||
"reference": "06064fd9fb19b77ae45a12ec7bcbc17670912c30"
|
||||
"reference": "052227953678a30ecc4b5467401fcb0b2386471e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/agents/zipball/06064fd9fb19b77ae45a12ec7bcbc17670912c30",
|
||||
"reference": "06064fd9fb19b77ae45a12ec7bcbc17670912c30",
|
||||
"url": "https://api.github.com/repos/utopia-php/agents/zipball/052227953678a30ecc4b5467401fcb0b2386471e",
|
||||
"reference": "052227953678a30ecc4b5467401fcb0b2386471e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3450,9 +3450,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/agents/issues",
|
||||
"source": "https://github.com/utopia-php/agents/tree/1.3.0"
|
||||
"source": "https://github.com/utopia-php/agents/tree/1.2.1"
|
||||
},
|
||||
"time": "2026-03-26T03:51:11+00:00"
|
||||
"time": "2026-02-24T06:03:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/analytics",
|
||||
@@ -3658,21 +3658,21 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/cli",
|
||||
"version": "0.22.0",
|
||||
"version": "0.23.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/cli.git",
|
||||
"reference": "a7ac387ee626fd27075a87e836fb72c5be38add4"
|
||||
"reference": "8d1955b8bc4dc631f45d7c7df689ed7b63f70621"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/cli/zipball/a7ac387ee626fd27075a87e836fb72c5be38add4",
|
||||
"reference": "a7ac387ee626fd27075a87e836fb72c5be38add4",
|
||||
"url": "https://api.github.com/repos/utopia-php/cli/zipball/8d1955b8bc4dc631f45d7c7df689ed7b63f70621",
|
||||
"reference": "8d1955b8bc4dc631f45d7c7df689ed7b63f70621",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.4",
|
||||
"utopia-php/servers": "0.2.*"
|
||||
"utopia-php/servers": "0.3.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "1.2.*",
|
||||
@@ -3703,9 +3703,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/cli/issues",
|
||||
"source": "https://github.com/utopia-php/cli/tree/0.22.0"
|
||||
"source": "https://github.com/utopia-php/cli/tree/0.23.1"
|
||||
},
|
||||
"time": "2025-10-21T10:42:45+00:00"
|
||||
"time": "2026-04-05T15:27:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/compression",
|
||||
@@ -3954,25 +3954,26 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/di",
|
||||
"version": "0.1.0",
|
||||
"version": "0.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/di.git",
|
||||
"reference": "22490c95f7ac3898ed1c33f1b1b5dd577305ee31"
|
||||
"reference": "07025d721ed5d9be27932e8e640acf1467fc4b9d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/di/zipball/22490c95f7ac3898ed1c33f1b1b5dd577305ee31",
|
||||
"reference": "22490c95f7ac3898ed1c33f1b1b5dd577305ee31",
|
||||
"url": "https://api.github.com/repos/utopia-php/di/zipball/07025d721ed5d9be27932e8e640acf1467fc4b9d",
|
||||
"reference": "07025d721ed5d9be27932e8e640acf1467fc4b9d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2"
|
||||
"php": ">=8.2",
|
||||
"psr/container": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "^1.2",
|
||||
"laravel/pint": "^1.27",
|
||||
"phpbench/phpbench": "^1.2",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "^9.5.25",
|
||||
"swoole/ide-helper": "4.8.3"
|
||||
},
|
||||
@@ -3989,16 +3990,18 @@
|
||||
],
|
||||
"description": "A simple and lite library for managing dependency injections",
|
||||
"keywords": [
|
||||
"framework",
|
||||
"http",
|
||||
"PSR-11",
|
||||
"container",
|
||||
"dependency-injection",
|
||||
"di",
|
||||
"php",
|
||||
"upf"
|
||||
"utopia"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/di/issues",
|
||||
"source": "https://github.com/utopia-php/di/tree/0.1.0"
|
||||
"source": "https://github.com/utopia-php/di/tree/0.3.2"
|
||||
},
|
||||
"time": "2024-08-08T14:35:19+00:00"
|
||||
"time": "2026-03-21T07:42:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/dns",
|
||||
@@ -4268,30 +4271,34 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/framework",
|
||||
"version": "0.33.41",
|
||||
"version": "0.34.17",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/http.git",
|
||||
"reference": "0f3bf2377c867e547c929c3733b8224afee6ef06"
|
||||
"reference": "d3e4143b8b06d9823d0c29a06dacefa5a1b93677"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/http/zipball/0f3bf2377c867e547c929c3733b8224afee6ef06",
|
||||
"reference": "0f3bf2377c867e547c929c3733b8224afee6ef06",
|
||||
"url": "https://api.github.com/repos/utopia-php/http/zipball/d3e4143b8b06d9823d0c29a06dacefa5a1b93677",
|
||||
"reference": "d3e4143b8b06d9823d0c29a06dacefa5a1b93677",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.3",
|
||||
"ext-swoole": "*",
|
||||
"php": ">=8.2",
|
||||
"utopia-php/compression": "0.1.*",
|
||||
"utopia-php/di": "0.3.*",
|
||||
"utopia-php/servers": "0.3.*",
|
||||
"utopia-php/telemetry": "0.2.*",
|
||||
"utopia-php/validators": "0.2.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/instantiator": "^1.5",
|
||||
"laravel/pint": "1.*",
|
||||
"phpbench/phpbench": "1.*",
|
||||
"phpbench/phpbench": "^1.2",
|
||||
"phpstan/phpstan": "1.*",
|
||||
"phpunit/phpunit": "9.*",
|
||||
"swoole/ide-helper": "^6.0"
|
||||
"phpunit/phpunit": "^9.5.25",
|
||||
"swoole/ide-helper": "4.8.3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@@ -4303,17 +4310,72 @@
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "A simple, light and advanced PHP framework",
|
||||
"description": "A simple, light and advanced PHP HTTP framework",
|
||||
"keywords": [
|
||||
"framework",
|
||||
"http",
|
||||
"php",
|
||||
"upf"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/http/issues",
|
||||
"source": "https://github.com/utopia-php/http/tree/0.33.41"
|
||||
"source": "https://github.com/utopia-php/http/tree/0.34.17"
|
||||
},
|
||||
"time": "2026-02-24T12:01:28+00:00"
|
||||
"time": "2026-04-06T04:40:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/http",
|
||||
"version": "0.34.16",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/http.git",
|
||||
"reference": "2b4021ba3f9d476264ce9fd6703d6c79de9add7f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/http/zipball/2b4021ba3f9d476264ce9fd6703d6c79de9add7f",
|
||||
"reference": "2b4021ba3f9d476264ce9fd6703d6c79de9add7f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-swoole": "*",
|
||||
"php": ">=8.2",
|
||||
"utopia-php/compression": "0.1.*",
|
||||
"utopia-php/di": "0.3.*",
|
||||
"utopia-php/servers": "0.3.*",
|
||||
"utopia-php/telemetry": "0.2.*",
|
||||
"utopia-php/validators": "0.2.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/instantiator": "^1.5",
|
||||
"laravel/pint": "1.*",
|
||||
"phpbench/phpbench": "^1.2",
|
||||
"phpstan/phpstan": "1.*",
|
||||
"phpunit/phpunit": "^9.5.25",
|
||||
"swoole/ide-helper": "4.8.3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Utopia\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "A simple, light and advanced PHP HTTP framework",
|
||||
"keywords": [
|
||||
"framework",
|
||||
"http",
|
||||
"php",
|
||||
"upf"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/http/issues",
|
||||
"source": "https://github.com/utopia-php/http/tree/0.34.16"
|
||||
},
|
||||
"time": "2026-03-20T10:39:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/image",
|
||||
@@ -4634,30 +4696,30 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/platform",
|
||||
"version": "0.7.16",
|
||||
"version": "0.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/platform.git",
|
||||
"reference": "34e67e4b80b5741c380071fe765fbc12a132de4f"
|
||||
"reference": "068ee46228f0c3972e6b569f2c86b6c80fe583d8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/platform/zipball/34e67e4b80b5741c380071fe765fbc12a132de4f",
|
||||
"reference": "34e67e4b80b5741c380071fe765fbc12a132de4f",
|
||||
"url": "https://api.github.com/repos/utopia-php/platform/zipball/068ee46228f0c3972e6b569f2c86b6c80fe583d8",
|
||||
"reference": "068ee46228f0c3972e6b569f2c86b6c80fe583d8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-redis": "*",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/cli": "0.22.*",
|
||||
"utopia-php/framework": "0.33.*",
|
||||
"utopia-php/queue": "0.15.*"
|
||||
"php": ">=8.1",
|
||||
"utopia-php/cli": "0.23.*",
|
||||
"utopia-php/http": "0.34.*",
|
||||
"utopia-php/queue": "0.17.*",
|
||||
"utopia-php/servers": "0.3.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "1.*",
|
||||
"phpstan/phpstan": "2.*",
|
||||
"phpunit/phpunit": "9.*"
|
||||
"laravel/pint": "1.2.*",
|
||||
"phpunit/phpunit": "^9.3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@@ -4679,9 +4741,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/platform/issues",
|
||||
"source": "https://github.com/utopia-php/platform/tree/0.7.16"
|
||||
"source": "https://github.com/utopia-php/platform/tree/0.12.0"
|
||||
},
|
||||
"time": "2026-02-11T06:36:48+00:00"
|
||||
"time": "2026-03-31T14:44:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/pools",
|
||||
@@ -4791,32 +4853,33 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/queue",
|
||||
"version": "0.15.6",
|
||||
"version": "0.17.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/queue.git",
|
||||
"reference": "08e361d69610f371382b344c369eef355ca414b4"
|
||||
"reference": "0fbc7d7312f5cf76ec112513fb93317000901f5f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/queue/zipball/08e361d69610f371382b344c369eef355ca414b4",
|
||||
"reference": "08e361d69610f371382b344c369eef355ca414b4",
|
||||
"url": "https://api.github.com/repos/utopia-php/queue/zipball/0fbc7d7312f5cf76ec112513fb93317000901f5f",
|
||||
"reference": "0fbc7d7312f5cf76ec112513fb93317000901f5f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.3",
|
||||
"php-amqplib/php-amqplib": "^3.7",
|
||||
"utopia-php/di": "0.3.*",
|
||||
"utopia-php/fetch": "0.5.*",
|
||||
"utopia-php/pools": "1.*",
|
||||
"utopia-php/servers": "0.2.*",
|
||||
"utopia-php/servers": "0.3.*",
|
||||
"utopia-php/telemetry": "0.2.*",
|
||||
"utopia-php/validators": "0.2.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-redis": "*",
|
||||
"laravel/pint": "^0.2.3",
|
||||
"laravel/pint": "^1.0",
|
||||
"phpstan/phpstan": "^1.8",
|
||||
"phpunit/phpunit": "^9.5.5",
|
||||
"phpunit/phpunit": "^11.0",
|
||||
"swoole/ide-helper": "4.8.8",
|
||||
"workerman/workerman": "^4.0"
|
||||
},
|
||||
@@ -4851,9 +4914,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/queue/issues",
|
||||
"source": "https://github.com/utopia-php/queue/tree/0.15.6"
|
||||
"source": "https://github.com/utopia-php/queue/tree/0.17.0"
|
||||
},
|
||||
"time": "2026-02-23T13:03:51+00:00"
|
||||
"time": "2026-03-23T16:21:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/registry",
|
||||
@@ -4909,21 +4972,21 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/servers",
|
||||
"version": "0.2.5",
|
||||
"version": "0.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/servers.git",
|
||||
"reference": "4770e879a90685af4ba14e7e5d95d0a17c7fdf03"
|
||||
"reference": "235be31200df9437fc96a1c270ffef4c64fafe52"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/servers/zipball/4770e879a90685af4ba14e7e5d95d0a17c7fdf03",
|
||||
"reference": "4770e879a90685af4ba14e7e5d95d0a17c7fdf03",
|
||||
"url": "https://api.github.com/repos/utopia-php/servers/zipball/235be31200df9437fc96a1c270ffef4c64fafe52",
|
||||
"reference": "235be31200df9437fc96a1c270ffef4c64fafe52",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"utopia-php/di": "0.1.*",
|
||||
"php": ">=8.2",
|
||||
"utopia-php/di": "0.3.*",
|
||||
"utopia-php/validators": "0.*"
|
||||
},
|
||||
"require-dev": {
|
||||
@@ -4957,9 +5020,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/servers/issues",
|
||||
"source": "https://github.com/utopia-php/servers/tree/0.2.5"
|
||||
"source": "https://github.com/utopia-php/servers/tree/0.3.0"
|
||||
},
|
||||
"time": "2026-02-10T04:21:53+00:00"
|
||||
"time": "2026-03-13T11:31:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/span",
|
||||
|
||||
@@ -26,9 +26,9 @@ class Resolvers
|
||||
): callable {
|
||||
return static fn ($type, $args, $context, $info) => new Swoole(
|
||||
function (callable $resolve, callable $reject) use ($utopia, $route, $args) {
|
||||
$utopia = $utopia->getResource('utopia:graphql', true);
|
||||
$request = $utopia->getResource('request', true);
|
||||
$response = $utopia->getResource('response', true);
|
||||
$utopia = $utopia->getResource('utopia:graphql');
|
||||
$request = $utopia->getResource('request');
|
||||
$response = $utopia->getResource('response');
|
||||
|
||||
$path = $route->getPath();
|
||||
foreach ($args as $key => $value) {
|
||||
@@ -93,9 +93,9 @@ class Resolvers
|
||||
): callable {
|
||||
return static fn ($type, $args, $context, $info) => new Swoole(
|
||||
function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $args) {
|
||||
$utopia = $utopia->getResource('utopia:graphql', true);
|
||||
$request = $utopia->getResource('request', true);
|
||||
$response = $utopia->getResource('response', true);
|
||||
$utopia = $utopia->getResource('utopia:graphql');
|
||||
$request = $utopia->getResource('request');
|
||||
$response = $utopia->getResource('response');
|
||||
|
||||
$request->setMethod('GET');
|
||||
$request->setURI($url($databaseId, $collectionId, $args));
|
||||
@@ -124,9 +124,9 @@ class Resolvers
|
||||
): callable {
|
||||
return static fn ($type, $args, $context, $info) => new Swoole(
|
||||
function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $params, $args) {
|
||||
$utopia = $utopia->getResource('utopia:graphql', true);
|
||||
$request = $utopia->getResource('request', true);
|
||||
$response = $utopia->getResource('response', true);
|
||||
$utopia = $utopia->getResource('utopia:graphql');
|
||||
$request = $utopia->getResource('request');
|
||||
$response = $utopia->getResource('response');
|
||||
|
||||
$request->setMethod('GET');
|
||||
$request->setURI($url($databaseId, $collectionId, $args));
|
||||
@@ -160,9 +160,9 @@ class Resolvers
|
||||
): callable {
|
||||
return static fn ($type, $args, $context, $info) => new Swoole(
|
||||
function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $params, $args) {
|
||||
$utopia = $utopia->getResource('utopia:graphql', true);
|
||||
$request = $utopia->getResource('request', true);
|
||||
$response = $utopia->getResource('response', true);
|
||||
$utopia = $utopia->getResource('utopia:graphql');
|
||||
$request = $utopia->getResource('request');
|
||||
$response = $utopia->getResource('response');
|
||||
|
||||
$request->setMethod('POST');
|
||||
$request->setURI($url($databaseId, $collectionId, $args));
|
||||
@@ -192,9 +192,9 @@ class Resolvers
|
||||
): callable {
|
||||
return static fn ($type, $args, $context, $info) => new Swoole(
|
||||
function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $params, $args) {
|
||||
$utopia = $utopia->getResource('utopia:graphql', true);
|
||||
$request = $utopia->getResource('request', true);
|
||||
$response = $utopia->getResource('response', true);
|
||||
$utopia = $utopia->getResource('utopia:graphql');
|
||||
$request = $utopia->getResource('request');
|
||||
$response = $utopia->getResource('response');
|
||||
|
||||
$request->setMethod('PATCH');
|
||||
$request->setURI($url($databaseId, $collectionId, $args));
|
||||
@@ -222,9 +222,9 @@ class Resolvers
|
||||
): callable {
|
||||
return static fn ($type, $args, $context, $info) => new Swoole(
|
||||
function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $args) {
|
||||
$utopia = $utopia->getResource('utopia:graphql', true);
|
||||
$request = $utopia->getResource('request', true);
|
||||
$response = $utopia->getResource('response', true);
|
||||
$utopia = $utopia->getResource('utopia:graphql');
|
||||
$request = $utopia->getResource('request');
|
||||
$response = $utopia->getResource('response');
|
||||
|
||||
$request->setMethod('DELETE');
|
||||
$request->setURI($url($databaseId, $collectionId, $args));
|
||||
@@ -266,7 +266,7 @@ class Resolvers
|
||||
try {
|
||||
$route = $utopia->match($request, fresh: true);
|
||||
|
||||
$utopia->execute($route, $request, $response);
|
||||
$utopia->execute($route, $request);
|
||||
} catch (\Throwable $e) {
|
||||
if ($beforeReject) {
|
||||
$e = $beforeReject($e);
|
||||
|
||||
@@ -32,10 +32,6 @@ class Schema
|
||||
array $urls,
|
||||
array $params,
|
||||
): GQLSchema {
|
||||
Http::setResource('utopia:graphql', static function () use ($utopia) {
|
||||
return $utopia;
|
||||
});
|
||||
|
||||
if (!empty(self::$schema)) {
|
||||
return self::$schema;
|
||||
}
|
||||
|
||||
@@ -145,9 +145,20 @@ class Server
|
||||
$paths = $this->paths;
|
||||
$state = $this->state;
|
||||
|
||||
Http::setResource('installerState', fn () => $state);
|
||||
Http::setResource('installerConfig', fn () => $config);
|
||||
Http::setResource('installerPaths', fn () => $paths);
|
||||
$adapter = new class ($host, $port, ['worker_num' => 1]) extends SwooleAdapter {
|
||||
public function getNativeServer(): SwooleServer
|
||||
{
|
||||
return $this->server;
|
||||
}
|
||||
};
|
||||
|
||||
$nativeServer = $adapter->getNativeServer();
|
||||
|
||||
$container = $adapter->getContainer();
|
||||
$container->set('installerState', fn () => $state);
|
||||
$container->set('installerConfig', fn () => $config);
|
||||
$container->set('installerPaths', fn () => $paths);
|
||||
$container->set('swooleServer', fn () => $nativeServer);
|
||||
|
||||
// Register routes via Utopia Platform
|
||||
$platform = new Installer();
|
||||
@@ -160,17 +171,6 @@ class Server
|
||||
->inject('response')
|
||||
->action($errorHandler->action(...));
|
||||
|
||||
$adapter = new class ($host, $port, ['worker_num' => 1]) extends SwooleAdapter {
|
||||
public function getNativeServer(): SwooleServer
|
||||
{
|
||||
return $this->server;
|
||||
}
|
||||
};
|
||||
|
||||
$nativeServer = $adapter->getNativeServer();
|
||||
|
||||
Http::setResource('swooleServer', fn () => $nativeServer);
|
||||
|
||||
$nativeServer->on('start', function () use ($nativeServer, $port, $readyFile) {
|
||||
\Swoole\Process::signal(SIGTERM, fn () => $nativeServer->shutdown());
|
||||
\Swoole\Process::signal(SIGINT, fn () => $nativeServer->shutdown());
|
||||
@@ -180,7 +180,7 @@ class Server
|
||||
}
|
||||
});
|
||||
|
||||
$adapter->onRequest(function (Request $request, Response $response) use ($files) {
|
||||
$adapter->onRequest(function (Request $request, Response $response) use ($adapter, $files) {
|
||||
// Serve static files from memory
|
||||
$uri = $request->getURI();
|
||||
if ($files->isFileLoaded($uri)) {
|
||||
@@ -190,7 +190,7 @@ class Server
|
||||
return;
|
||||
}
|
||||
|
||||
$app = new Http('UTC');
|
||||
$app = new Http($adapter, 'UTC');
|
||||
$app->run($request, $response);
|
||||
});
|
||||
|
||||
|
||||
@@ -182,19 +182,33 @@ class Update extends Action
|
||||
$dbForDatabases = $getDatabasesDB($databaseDoc);
|
||||
|
||||
try {
|
||||
$dbForDatabases->withTransaction(function () use ($dbForDatabases, $dbForProject, $transactionState, $queueForDeletes, $transactionId, &$transaction, &$operations, &$totalOperations, &$databaseOperations, &$currentDocumentId, $authorization) {
|
||||
$authorization->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([
|
||||
'status' => 'committing',
|
||||
])));
|
||||
$transaction = $authorization->skip(fn () => $dbForProject->updateDocument(
|
||||
'transactions',
|
||||
$transactionId,
|
||||
new Document(['status' => 'committing'])
|
||||
));
|
||||
|
||||
$operations = $authorization->skip(fn () => $dbForProject->find('transactionLogs', [
|
||||
Query::equal('transactionInternalId', [$transaction->getSequence()]),
|
||||
Query::orderAsc(),
|
||||
Query::limit(PHP_INT_MAX),
|
||||
]));
|
||||
$operations = $authorization->skip(fn () => $dbForProject->find('transactionLogs', [
|
||||
Query::equal('transactionInternalId', [$transaction->getSequence()]),
|
||||
Query::orderAsc(),
|
||||
Query::limit(PHP_INT_MAX),
|
||||
]));
|
||||
|
||||
$collections = [];
|
||||
foreach ($operations as $operation) {
|
||||
$databaseInternalId = $operation['databaseInternalId'];
|
||||
$collectionInternalId = $operation['collectionInternalId'];
|
||||
$collectionId = "database_{$databaseInternalId}_collection_{$collectionInternalId}";
|
||||
|
||||
if (!isset($collections[$collectionId])) {
|
||||
$collections[$collectionId] = $authorization->skip(
|
||||
fn () => $dbForProject->getCollection($collectionId)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$dbForDatabases->withTransaction(function () use ($dbForDatabases, $transactionState, &$operations, &$totalOperations, &$databaseOperations, &$currentDocumentId, $collections) {
|
||||
$state = [];
|
||||
$collections = [];
|
||||
|
||||
foreach ($operations as $operation) {
|
||||
$databaseInternalId = $operation['databaseInternalId'];
|
||||
@@ -210,11 +224,6 @@ class Update extends Action
|
||||
$data = $data->getArrayCopy();
|
||||
}
|
||||
|
||||
if (!isset($collections[$collectionId])) {
|
||||
$collections[$collectionId] = $authorization->skip(
|
||||
fn () => $dbForProject->getCollection($collectionId)
|
||||
);
|
||||
}
|
||||
$collection = $collections[$collectionId];
|
||||
|
||||
if (\is_array($data) && !empty($data)) {
|
||||
@@ -276,16 +285,17 @@ class Update extends Action
|
||||
}
|
||||
}
|
||||
|
||||
$transaction = $authorization->skip(fn () => $dbForProject->updateDocument(
|
||||
'transactions',
|
||||
$transactionId,
|
||||
new Document(['status' => 'committed'])
|
||||
));
|
||||
|
||||
$queueForDeletes
|
||||
->setType(DELETE_TYPE_DOCUMENT)
|
||||
->setDocument($transaction);
|
||||
});
|
||||
|
||||
$transaction = $authorization->skip(fn () => $dbForProject->updateDocument(
|
||||
'transactions',
|
||||
$transactionId,
|
||||
new Document(['status' => 'committed'])
|
||||
));
|
||||
|
||||
$queueForDeletes
|
||||
->setType(DELETE_TYPE_DOCUMENT)
|
||||
->setDocument($transaction);
|
||||
} catch (NotFoundException $e) {
|
||||
$authorization->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([
|
||||
'status' => 'failed',
|
||||
|
||||
@@ -9,7 +9,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Http\Http;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\Validator\Text;
|
||||
@@ -32,6 +31,7 @@ class Migrate extends Action
|
||||
->inject('getProjectDB')
|
||||
->inject('register')
|
||||
->inject('authorization')
|
||||
->inject('console')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
@@ -48,7 +48,8 @@ class Migrate extends Action
|
||||
Database $dbForPlatform,
|
||||
callable $getProjectDB,
|
||||
Registry $register,
|
||||
Authorization $authorization
|
||||
Authorization $authorization,
|
||||
Document $console
|
||||
): void {
|
||||
|
||||
if (!\array_key_exists($version, Migration::$versions)) {
|
||||
@@ -125,8 +126,6 @@ class Migrate extends Action
|
||||
Console::log('Migrated ' . ++$count . '/' . $total . ' projects...');
|
||||
});
|
||||
|
||||
$console = (new Http('UTC'))->getResource('console');
|
||||
|
||||
try {
|
||||
$migration
|
||||
->setProject($console, $getProjectDB($console), $dbForPlatform, $authorization, $getProjectDB);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Appwrite\Network\Validator\Redirect;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Specification\Format\OpenAPI3;
|
||||
@@ -18,6 +19,9 @@ use Utopia\Config\Config;
|
||||
use Utopia\Console;
|
||||
use Utopia\Database\Adapter\MySQL;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\DI\Container;
|
||||
use Utopia\Http\Adapter\FPM\Server as FPMServer;
|
||||
use Utopia\Http\Http;
|
||||
use Utopia\Http\Request as UtopiaRequest;
|
||||
use Utopia\Http\Response as UtopiaResponse;
|
||||
@@ -336,11 +340,17 @@ class Specs extends Action
|
||||
|
||||
$mocks = ($mode === 'mocks');
|
||||
|
||||
// Mock dependencies
|
||||
Http::setResource('request', fn () => $this->getRequest());
|
||||
Http::setResource('response', fn () => $response);
|
||||
Http::setResource('dbForPlatform', fn () => new Database(new MySQL(''), new Cache(new None())));
|
||||
Http::setResource('dbForProject', fn () => new Database(new MySQL(''), new Cache(new None())));
|
||||
// Mock dependencies needed by param validator injections in route definitions
|
||||
$specsContainer = new Container();
|
||||
$specsContainer->set('request', fn () => $this->getRequest());
|
||||
$specsContainer->set('response', fn () => $response);
|
||||
$specsContainer->set('dbForPlatform', fn () => new Database(new MySQL(''), new Cache(new None())));
|
||||
$specsContainer->set('dbForProject', fn () => new Database(new MySQL(''), new Cache(new None())));
|
||||
$specsContainer->set('redirectValidator', fn () => new Redirect([], []));
|
||||
$specsContainer->set('project', fn () => new Document([]));
|
||||
$specsContainer->set('passwordsDictionary', fn () => []);
|
||||
$specsContainer->set('localeCodes', fn () => \array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])));
|
||||
$specsContainer->set('plan', fn () => []);
|
||||
|
||||
$platforms = static::getPlatforms();
|
||||
$authCounts = $this->getAuthCounts();
|
||||
@@ -438,7 +448,7 @@ class Specs extends Action
|
||||
}
|
||||
|
||||
$arguments = [
|
||||
new Http('UTC'),
|
||||
new Http(new FPMServer($specsContainer), 'UTC'),
|
||||
$services,
|
||||
$routes,
|
||||
$models,
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
|
||||
namespace Appwrite\Promises;
|
||||
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\Coroutine\Channel;
|
||||
use Utopia\DI\Container;
|
||||
|
||||
class Swoole extends Promise
|
||||
{
|
||||
private const REQUEST_CONTAINER_CONTEXT_KEY = '__utopia_http_request_container';
|
||||
|
||||
public function __construct(?callable $executor = null)
|
||||
{
|
||||
parent::__construct($executor);
|
||||
@@ -16,7 +20,14 @@ class Swoole extends Promise
|
||||
callable $resolve,
|
||||
callable $reject
|
||||
): void {
|
||||
\go(function () use ($executor, $resolve, $reject) {
|
||||
$parentContainer = (Coroutine::getCid() !== -1)
|
||||
? (Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY] ?? null)
|
||||
: null;
|
||||
|
||||
\go(function () use ($executor, $resolve, $reject, $parentContainer) {
|
||||
if ($parentContainer !== null) {
|
||||
Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY] = new Container($parentContainer);
|
||||
}
|
||||
try {
|
||||
$executor($resolve, $reject);
|
||||
} catch (\Throwable $exception) {
|
||||
|
||||
@@ -9,7 +9,7 @@ class MockPublisher implements Publisher
|
||||
{
|
||||
private array $events = [];
|
||||
|
||||
public function enqueue(Queue $queue, array $payload): bool
|
||||
public function enqueue(Queue $queue, array $payload, bool $priority = false): bool
|
||||
{
|
||||
if (!isset($this->events[$queue->name])) {
|
||||
$this->events[$queue->name] = [];
|
||||
|
||||
Reference in New Issue
Block a user