Merge pull request #10937 from appwrite/fix-imagine-hostnames-2

Fix imagine hostnames 2
This commit is contained in:
Luke B. Silver 2025-12-11 19:05:12 +00:00 committed by GitHub
commit e4a7b1b103
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 60 additions and 74 deletions

View file

@ -6,8 +6,12 @@ use Utopia\System\System;
* Platform configuration
*/
return [
'domain' => System::getEnv('_APP_DOMAIN', 'localhost'),
'consoleDomain' => System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', 'localhost')),
'apiHostname' => System::getEnv('_APP_DOMAIN', 'localhost'),
'consoleHostname' => System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', 'localhost')),
'hostnames' => array_filter(array_unique([
System::getEnv('_APP_DOMAIN', 'localhost'),
System::getEnv('_APP_CONSOLE_DOMAIN', 'localhost'),
])),
'platformName' => APP_EMAIL_PLATFORM_NAME,
'logoUrl' => APP_EMAIL_LOGO_URL,
'accentColor' => APP_EMAIL_ACCENT_COLOR,

View file

@ -9,7 +9,7 @@ use Utopia\System\System;
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$platform = Config::getParam('platform', []);
$hostname = $platform['consoleDomain'] ?? '';
$hostname = $platform['consoleHostname'] ?? '';
$url = $protocol . '://' . $hostname;

View file

@ -1325,7 +1325,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
$host = $platform['consoleDomain'] ?? '';
$host = $platform['consoleHostname'] ?? '';
$redirectBase = $protocol . '://' . $host;
if ($protocol === 'https' && $port !== '443') {
$redirectBase .= ':' . $port;
@ -1978,7 +1978,7 @@ App::get('/v1/account/tokens/oauth2/:provider')
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
$host = $platform['consoleDomain'] ?? '';
$host = $platform['consoleHostname'] ?? '';
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$port = $request->getPort();
$redirectBase = $protocol . '://' . $host;
@ -2153,7 +2153,7 @@ App::post('/v1/account/tokens/magic-url')
if (empty($url)) {
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$host = $platform['consoleDomain'] ?? '';
$host = $platform['consoleHostname'] ?? '';
$port = $request->getPort();
$callbackBase = $protocol . '://' . $host;
if ($protocol === 'https' && $port !== '443') {

View file

@ -132,7 +132,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$commentStatus = $isAuthorized ? 'waiting' : 'failed';
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$hostname = $platform['consoleDomain'] ?? '';
$hostname = $platform['consoleHostname'] ?? '';
$authorizeUrl = $protocol . '://' . $hostname . "/console/git/authorize-contributor?projectId={$projectId}&installationId={$installationId}&repositoryId={$repositoryId}&providerPullRequestId={$providerPullRequestId}";
@ -555,7 +555,7 @@ App::get('/v1/vcs/github/authorize')
$appName = System::getEnv('_APP_VCS_GITHUB_APP_NAME');
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$hostname = $platform['consoleDomain'] ?? '';
$hostname = $platform['consoleHostname'] ?? '';
if (empty($appName)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'GitHub App name is not configured. Please configure VCS (Version Control System) variables in .env file.');
@ -614,7 +614,7 @@ App::get('/v1/vcs/github/callback')
$region = $project->getAttribute('region', 'default');
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$hostname = $platform['consoleDomain'] ?? '';
$hostname = $platform['consoleHostname'] ?? '';
$defaultState = [
'success' => $protocol . '://' . $hostname . "/console/project-$region-$projectId/settings/git-installations",

View file

@ -59,7 +59,7 @@ Config::setParam('domainVerification', false);
Config::setParam('cookieDomain', 'localhost');
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, array $domains)
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey)
{
$host = $request->getHostname() ?? '';
if (!empty($previewHostname)) {
@ -80,7 +80,8 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
$errorView = __DIR__ . '/../views/general/error.phtml';
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$url = $protocol . '://' . $platform['consoleDomain'];
$url = $protocol . '://' . $platform['consoleHostname'];
$platformHostnames = $platform['hostnames'] ?? [];
if ($rule->isEmpty()) {
$appDomainFunctionsFallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', '');
@ -101,7 +102,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
throw $exception;
}
if (!in_array($host, $domains)) {
if (!in_array($host, $platformHostnames)) {
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'Router protection does not allow accessing Appwrite over this domain. Please add it as custom domain to your project or disable _APP_OPTIONS_ROUTER_PROTECTION environment variable.', view: $errorView);
}
@ -269,7 +270,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
}
if (!$authorized) {
$url = $protocol . "://" . $platform['consoleDomain'];
$url = $protocol . "://" . $platform['consoleHostname'];
$response
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Pragma', 'no-cache')
@ -858,15 +859,15 @@ App::init()
->inject('devKey')
->inject('apiKey')
->inject('cors')
->inject('domains')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Func $queueForFunctions, Executor $executor, array $platform, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey, Cors $cors, array $domains) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Func $queueForFunctions, Executor $executor, array $platform, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey, Cors $cors) {
/*
* Appwrite Router
*/
$hostname = $request->getHostname() ?? '';
$platformHostnames = $platform['hostnames'] ?? [];
// Only run Router when external domain
if (!in_array($hostname, $domains) || !empty($previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $apiKey, $domains)) {
if (!in_array($hostname, $platformHostnames) || !empty($previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $apiKey)) {
$utopia->getRoute()?->label('router', true);
}
}
@ -1028,10 +1029,11 @@ App::init()
->inject('console')
->inject('dbForPlatform')
->inject('queueForCertificates')
->inject('domains')
->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $domains) {
->inject('platform')
->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform) {
$hostname = $request->getHostname();
$cache = Config::getParam('domains', []);
$cache = Config::getParam('hostnames', []);
$platformHostnames = $platform['hostnames'] ?? [];
// 1. Cache hit
if (array_key_exists($hostname, $cache)) {
@ -1042,7 +1044,7 @@ App::init()
$domain = new Domain(!empty($hostname) ? $hostname : '');
if (empty($domain->get()) || !$domain->isKnown() || $domain->isTest()) {
$cache[$domain->get()] = false;
Config::setParam('domains', $cache);
Config::setParam('hostnames', $cache);
Console::warning($domain->get() . ' is not a publicly accessible domain. Skipping SSL certificate generation.');
return;
}
@ -1053,7 +1055,7 @@ App::init()
}
// 3. Check if domain is a main domain
if (!in_array($domain->get(), $domains)) {
if (!in_array($domain->get(), $platformHostnames)) {
Console::warning($domain->get() . ' is not a main domain. Skipping SSL certificate generation.');
return;
}
@ -1114,7 +1116,7 @@ App::init()
Console::info('Certificate already exists');
} finally {
$cache[$domain->get()] = true;
Config::setParam('domains', $cache);
Config::setParam('hostnames', $cache);
Authorization::reset();
}
});
@ -1138,14 +1140,14 @@ App::options()
->inject('project')
->inject('devKey')
->inject('apiKey')
->inject('domains')
->inject('cors')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, Document $project, Document $devKey, ?Key $apiKey, array $domains, Cors $cors) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, Document $project, Document $devKey, ?Key $apiKey, Cors $cors) {
/*
* Appwrite Router
*/
$platformHostnames = $platform['hostnames'] ?? [];
// Only run Router when external domain
if (!in_array($request->getHostname(), $domains) || !empty($previewHostname)) {
if (!in_array($request->getHostname(), $platformHostnames) || !empty($previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $apiKey)) {
$utopia->getRoute()?->label('router', true);
}
@ -1448,9 +1450,9 @@ App::get('/robots.txt')
->inject('platform')
->inject('previewHostname')
->inject('apiKey')
->inject('domains')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, array $domains) {
if (in_array($request->getHostname(), $domains) || !empty($previewHostname)) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey) {
$platformHostnames = $platform['hostnames'] ?? [];
if (in_array($request->getHostname(), $platformHostnames) || !empty($previewHostname)) {
$template = new View(__DIR__ . '/../views/general/robots.phtml');
$response->text($template->render(false));
} else {
@ -1480,9 +1482,9 @@ App::get('/humans.txt')
->inject('platform')
->inject('previewHostname')
->inject('apiKey')
->inject('domains')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, array $domains) {
if (in_array($request->getHostname(), $domains) || !empty($previewHostname)) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey) {
$platformHostnames = $platform['hostnames'] ?? [];
if (in_array($request->getHostname(), $platformHostnames) || !empty($previewHostname)) {
$template = new View(__DIR__ . '/../views/general/humans.phtml');
$response->text($template->render(false));
} else {

View file

@ -161,14 +161,6 @@ App::setResource('queueForStatsResources', function (Publisher $publisher) {
return new StatsResources($publisher);
}, ['publisher']);
/**
* List of domains served by the application.
*/
App::setResource('domains', fn () => array_unique(array_filter([
...\explode(',', System::getEnv('_APP_DOMAIN', 'localhost')),
...\explode(',', System::getEnv('_APP_CONSOLE_DOMAIN', 'localhost'))
])));
/**
* Platform configuration
*/
@ -183,33 +175,16 @@ App::setResource('platform', function (Request $request) {
if ($request->getPort() === '80' && $protocol !== 'http') {
$port = ':80';
}
$platform['endpoint'] = "$protocol://{$platform['domain']}{$port}/v1";
$platform['endpoint'] = "$protocol://{$platform['apiHostname']}{$port}/v1";
return $platform;
}, ['request']);
/**
* Safe request origin used to construct urls
*/
App::setResource('origin', function (Request $request) {
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$port = '';
if ($request->getPort() === '443' && $protocol !== 'https') {
$port = ':443';
}
if ($request->getPort() === '80' && $protocol !== 'http') {
$port = ':80';
}
return "$protocol://{$request->getHostname()}{$port}";
}, ['request']);
/**
* List of allowed request hostnames for the request.
*/
App::setResource('allowedHostnames', function (array $domains, Document $project, Document $rule, Document $devKey, Request $request) {
$allowed = [...$domains];
App::setResource('allowedHostnames', function (array $platform, Document $project, Document $rule, Document $devKey, Request $request) {
$allowed = [...($platform['hostnames'] ?? [])];
/* Add platform configured hostnames */
if (!$project->isEmpty() && $project->getId() !== 'console') {
@ -230,7 +205,7 @@ App::setResource('allowedHostnames', function (array $domains, Document $project
}
return array_unique($allowed);
}, ['domains', 'project', 'rule', 'devKey', 'request']);
}, ['platform', 'project', 'rule', 'devKey', 'request']);
/**
* List of allowed request schemes for the request.

View file

@ -17,7 +17,7 @@ $buttons = [];
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$platform = Config::getParam('platform', []);
$hostname = $platform['consoleDomain'] ?? '';
$hostname = $platform['consoleHostname'] ?? '';
// TODO: remove this later
if (System::getEnv('_APP_ENV') === 'development') {
$hostname = 'localhost';

View file

@ -59,7 +59,7 @@ class Get extends Action
->param('type', '', new WhiteList(['rules']), 'Resource type.')
->inject('response')
->inject('dbForPlatform')
->inject('domains')
->inject('platform')
->callback($this->action(...));
}
@ -68,8 +68,9 @@ class Get extends Action
string $type,
Response $response,
Database $dbForPlatform,
array $domains
array $platform
) {
$domains = $platform['hostnames'] ?? [];
if ($type === 'rules') {
$sitesDomain = System::getEnv('_APP_DOMAIN_SITES', '');
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');

View file

@ -67,12 +67,13 @@ class Create extends Action
->inject('queueForCertificates')
->inject('queueForEvents')
->inject('dbForPlatform')
->inject('domains')
->inject('platform')
->callback($this->action(...));
}
public function action(string $domain, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, array $domains)
public function action(string $domain, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, array $platform)
{
$domains = $platform['hostnames'] ?? [];
$sitesDomain = System::getEnv('_APP_DOMAIN_SITES', '');
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');

View file

@ -72,12 +72,13 @@ class Create extends Action
->inject('queueForEvents')
->inject('dbForPlatform')
->inject('dbForProject')
->inject('domains')
->inject('platform')
->callback($this->action(...));
}
public function action(string $domain, string $functionId, string $branch, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject, array $domains)
public function action(string $domain, string $functionId, string $branch, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject, array $platform)
{
$domains = $platform['hostnames'] ?? [];
$sitesDomain = System::getEnv('_APP_DOMAIN_SITES', '');
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');

View file

@ -75,12 +75,13 @@ class Create extends Action
->inject('queueForEvents')
->inject('dbForPlatform')
->inject('dbForProject')
->inject('domains')
->inject('platform')
->callback($this->action(...));
}
public function action(string $domain, string $url, int $statusCode, string $resourceId, string $resourceType, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject, array $domains)
public function action(string $domain, string $url, int $statusCode, string $resourceId, string $resourceType, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject, array $platform)
{
$domains = $platform['hostnames'] ?? [];
$sitesDomain = System::getEnv('_APP_DOMAIN_SITES', '');
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');

View file

@ -72,12 +72,13 @@ class Create extends Action
->inject('queueForEvents')
->inject('dbForPlatform')
->inject('dbForProject')
->inject('domains')
->inject('platform')
->callback($this->action(...));
}
public function action(string $domain, string $siteId, string $branch, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject, array $domains)
public function action(string $domain, string $siteId, string $branch, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject, array $platform)
{
$domains = $platform['hostnames'] ?? [];
$sitesDomain = System::getEnv('_APP_DOMAIN_SITES', '');
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');

View file

@ -119,7 +119,7 @@ class Comment
$i = 0;
foreach ($projects as $projectId => $project) {
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$hostname = $this->platform['consoleDomain'] ?? '';
$hostname = $this->platform['consoleHostname'] ?? '';
$text .= "## {$project['name']}\n\n";
$text .= "Project ID: `{$projectId}`\n\n";
@ -233,7 +233,7 @@ class Comment
public function generatImage(string $pathLight, string $pathDark, string $alt, int $width): string
{
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$hostname = $this->platform['consoleDomain'] ?? '';
$hostname = $this->platform['consoleHostname'] ?? '';
$imageLight = $protocol . '://' . $hostname . $pathLight;
$imageDark = $protocol . '://' . $hostname . $pathDark;