diff --git a/.env b/.env index ad973f24f9..29861820c2 100644 --- a/.env +++ b/.env @@ -26,7 +26,7 @@ _APP_DNS=172.16.238.100 # CoreDNS _APP_DOMAIN=appwrite.test _APP_CONSOLE_DOMAIN=localhost _APP_DOMAIN_FUNCTIONS=functions.localhost -_APP_DOMAIN_SITES=sites.localhost +_APP_DOMAIN_SITES=sites.localhost,branded.localhost _APP_DOMAIN_TARGET_CNAME=cname.localhost _APP_DOMAIN_TARGET_A=203.0.0.1 _APP_DOMAIN_TARGET_AAAA=::1 diff --git a/app/config/platform.php b/app/config/platform.php index 913390ae5e..19a7a4986a 100644 --- a/app/config/platform.php +++ b/app/config/platform.php @@ -2,6 +2,13 @@ use Utopia\System\System; +// For now, take first domain as primary (for previews) +// Later-on this can become platform-specific with new env var (appwrite=this,imagine=that) +$sitePreviewDomain = System::getEnv('_APP_DOMAIN_SITES', ''); +if (\str_contains($sitePreviewDomain, ',')) { + $sitePreviewDomain = explode(',', $sitePreviewDomain)[0]; +} + /** * Platform configuration */ @@ -23,5 +30,5 @@ return [ 'privacyUrl' => APP_EMAIL_PRIVACY_URL, 'websiteUrl' => 'https://' . APP_DOMAIN, 'emailSenderName' => APP_EMAIL_PLATFORM_NAME, - 'sitePreviewDomain' => System::getEnv('_APP_DOMAIN_SITES', ''), + 'sitePreviewDomain' => $sitePreviewDomain, ]; diff --git a/app/controllers/general.php b/app/controllers/general.php index 40bcdf4ea3..09bb1a5ff8 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -85,22 +85,32 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw $platformHostnames = $platform['hostnames'] ?? []; if ($rule->isEmpty()) { - $appDomainFunctionsFallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', ''); - $appDomainFunctions = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - $appDomainSites = System::getEnv('_APP_DOMAIN_SITES', ''); - if (!empty($appDomainFunctionsFallback) && \str_ends_with($host, $appDomainFunctionsFallback)) { - $appDomainFunctions = $appDomainFunctionsFallback; + $denyDomains = []; + $denyEnvVars = [ + System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', ''), + System::getEnv('_APP_DOMAIN_FUNCTIONS', ''), + System::getEnv('_APP_DOMAIN_SITES', ''), + ]; + foreach ($denyEnvVars as $denyEnvVar) { + foreach (\explode(',', $denyEnvVar) as $denyDomain) { + if (empty($denyDomain)) { + continue; + } + $denyDomains[] = $denyDomain; + } } - if ($host === $appDomainFunctions || $host === $appDomainSites) { - throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.', view: $errorView); - } + foreach ($denyDomains as $denyDomain) { + if ($host === $denyDomain) { + throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.', view: $errorView); + } - if (\str_ends_with($host, $appDomainFunctions) || \str_ends_with($host, $appDomainSites)) { - $exception = new AppwriteException(AppwriteException::RULE_NOT_FOUND, 'This domain is not connected to any Appwrite resources. Visit domains tab under function/site settings to configure it.', view: $errorView); + if (\str_ends_with($host, $denyDomain)) { + $exception = new AppwriteException(AppwriteException::RULE_NOT_FOUND, 'This domain is not connected to any Appwrite resources. Visit domains tab under function/site settings to configure it.', view: $errorView); - $exception->addCTA('Start with this domain', $url . '/console'); - throw $exception; + $exception->addCTA('Start with this domain', $url . '/console'); + throw $exception; + } } if (!in_array($host, $platformHostnames)) { @@ -1094,19 +1104,28 @@ App::init() // 5. Create new rule $owner = ''; - $fallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', ''); - $funcDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - $siteDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - if (!empty($fallback) && \str_ends_with($domain->get(), $fallback)) { - $funcDomain = $fallback; + // Mark owner as Appwrite if its appwirte-owned domain + $appwriteDomains = []; + $appwriteDomainEnvs = [ + System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', ''), + System::getEnv('_APP_DOMAIN_FUNCTIONS', ''), + System::getEnv('_APP_DOMAIN_SITES', ''), + ]; + foreach ($appwriteDomainEnvs as $appwriteDomainEnv) { + foreach (\explode(',', $appwriteDomainEnv) as $appwriteDomain) { + if (empty($appwriteDomain)) { + continue; + } + $appwriteDomains[] = $appwriteDomain; + } } - if ( - (!empty($funcDomain) && \str_ends_with($domain->get(), $funcDomain)) || - (!empty($siteDomain) && \str_ends_with($domain->get(), $siteDomain)) - ) { - $owner = 'Appwrite'; + foreach ($appwriteDomains as $appwriteDomain) { + if (\str_ends_with($domain->get(), $appwriteDomain)) { + $owner = 'Appwrite'; + break; + } } $ruleId = $isMd5 ? md5($domain->get()) : ID::unique(); diff --git a/app/http.php b/app/http.php index 5d08c53eee..9fc0ea0950 100644 --- a/app/http.php +++ b/app/http.php @@ -591,9 +591,33 @@ $http->on(Constant::EVENT_TASK, function () use ($register, $domains) { $sum = count($results); foreach ($results as $document) { $domain = $document->getAttribute('domain'); - if (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS')) || str_ends_with($domain, System::getEnv('_APP_DOMAIN_SITES'))) { + + $denyDomains = []; + $denyEnvVars = [ + System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', ''), + System::getEnv('_APP_DOMAIN_FUNCTIONS', ''), + System::getEnv('_APP_DOMAIN_SITES', ''), + ]; + foreach ($denyEnvVars as $denyEnvVar) { + foreach (\explode(',', $denyEnvVar) as $denyDomain) { + if (empty($denyDomain)) { + continue; + } + $denyDomains[] = $denyDomain; + } + } + + $isDenyDomain = false; + foreach ($denyDomains as $denyDomain) { + if (str_ends_with($domain, $denyDomain)) { + $isDenyDomain = true; + } + } + + if ($isDenyDomain) { continue; } + $domains->set(md5($domain), ['value' => 1]); } $latestDocument = !empty(array_key_last($results)) ? $results[array_key_last($results)] : null; diff --git a/src/Appwrite/Platform/Modules/Console/Http/Resources/Get.php b/src/Appwrite/Platform/Modules/Console/Http/Resources/Get.php index 1468bf71ac..ae2317e880 100644 --- a/src/Appwrite/Platform/Modules/Console/Http/Resources/Get.php +++ b/src/Appwrite/Platform/Modules/Console/Http/Resources/Get.php @@ -74,36 +74,41 @@ class Get extends Action ) { $domains = $platform['hostnames'] ?? []; if ($type === 'rules') { - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - + $deniedDomains = [...$domains]; $restrictions = []; - if (!empty($sitesDomain)) { + + $sitesDomains = System::getEnv('_APP_DOMAIN_SITES', ''); + foreach (\explode(',', $sitesDomains) as $sitesDomain) { + if (empty($sitesDomain)) { + continue; + } + + $deniedDomains[] = $sitesDomain; + // Ensure site domains are exactly 1 subdomain, and dont start with reserved prefix $domainLevel = \count(\explode('.', $sitesDomain)); $restrictions[] = DomainValidator::createRestriction($sitesDomain, $domainLevel + 1, ['commit-', 'branch-']); } - if (!empty($functionsDomain)) { + + $functionsDomains = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + foreach (\explode(',', $functionsDomains) as $functionsDomain) { + if (empty($sitesDomain)) { + continue; + } + + $deniedDomains[] = $functionsDomain; + // Ensure function domains are exactly 1 subdomain $domainLevel = \count(\explode('.', $functionsDomain)); $restrictions[] = DomainValidator::createRestriction($functionsDomain, $domainLevel + 1); } + $validator = new DomainValidator($restrictions); if (!$validator->isValid($value)) { throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please use a different domain.'); } - $deniedDomains = [...$domains]; - - if (!empty($sitesDomain)) { - $deniedDomains[] = $sitesDomain; - } - - if (!empty($functionsDomain)) { - $deniedDomains[] = $functionsDomain; - } - $denyListDomains = System::getEnv('_APP_CUSTOM_DOMAIN_DENY_LIST', ''); $denyListDomains = \array_map('trim', explode(',', $denyListDomains)); foreach ($denyListDomains as $denyListDomain) { diff --git a/src/Appwrite/Platform/Modules/Proxy/Action.php b/src/Appwrite/Platform/Modules/Proxy/Action.php index c3fa535a5c..913d9e6a8d 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Action.php +++ b/src/Appwrite/Platform/Modules/Proxy/Action.php @@ -31,26 +31,42 @@ class Action extends PlatformAction protected function validateDomainRestrictions(string $domain, array $platform): void { $domains = $platform['hostnames'] ?? []; - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + $deniedDomains = [...$domains]; $restrictions = []; - if (!empty($sitesDomain)) { + + $sitesDomains = System::getEnv('_APP_DOMAIN_SITES', ''); + foreach (\explode(',', $sitesDomains) as $sitesDomain) { + if (empty($sitesDomain)) { + continue; + } + + $deniedDomains[] = $sitesDomain; + + // Ensure site domains are exactly 1 subdomain, and dont start with reserved prefix $domainLevel = \count(\explode('.', $sitesDomain)); $restrictions[] = ValidatorDomain::createRestriction($sitesDomain, $domainLevel + 1, ['commit-', 'branch-']); } - if (!empty($functionsDomain)) { + + $functionsDomains = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + foreach (\explode(',', $functionsDomains) as $functionsDomain) { + if (empty($sitesDomain)) { + continue; + } + + $deniedDomains[] = $functionsDomain; + + // Ensure function domains are exactly 1 subdomain $domainLevel = \count(\explode('.', $functionsDomain)); $restrictions[] = ValidatorDomain::createRestriction($functionsDomain, $domainLevel + 1); } + $validator = new ValidatorDomain($restrictions); if (!$validator->isValid($domain)) { throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please use a different domain.'); } - $deniedDomains = [...$domains]; - if (!empty($sitesDomain)) { $deniedDomains[] = $sitesDomain; } @@ -117,31 +133,40 @@ class Action extends PlatformAction } } - $targetCNAME = null; + $targetCNAMEs = []; $ruleType = $rule->getAttribute('type', ''); $resourceType = $rule->getAttribute('deploymentResourceType', ''); // Ensures different target based on rule's type, as configured by env variables if ($resourceType === 'function') { // For example: fra.appwrite.run - $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_FUNCTIONS', '')); + foreach (\explode(',', System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) as $targetCNAME) { + $targetCNAMEs[] = new Domain($targetCNAME); + } } elseif ($resourceType === 'site') { // For example: appwrite.network - $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_SITES', '')); + foreach (\explode(',', System::getEnv('_APP_DOMAIN_SITES', '')) as $targetCNAME) { + $targetCNAMEs[] = new Domain($targetCNAME); + } } elseif ($ruleType === 'api') { // For example: fra.cloud.appwrite.io - $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')); + $targetCNAMEs[] = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')); } elseif ($ruleType === 'redirect') { // Shouldn't be needed, because redirect should always have resourceTyp too, but just in case we default to sites // For example: appwrite.network - $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_SITES', '')); + $targetCNAMEs[] = new Domain(System::getEnv('_APP_DOMAIN_SITES', '')); } $validators = []; $mainValidator = null; // Validator to use for error description - if (!is_null($targetCNAME)) { - $validator = new $dnsValidatorClass($targetCNAME->get(), Record::TYPE_CNAME, $dnsServers); + if (\count($targetCNAMEs) > 0) { + $cnameValidators = []; + foreach ($targetCNAMEs as $targetCNAME) { + $cnameValidators[] = new $dnsValidatorClass($targetCNAME->get(), Record::TYPE_CNAME, $dnsServers); + } + + $validator = new AnyOf($cnameValidators); $validators[] = $validator; if (\is_null($mainValidator)) { @@ -185,4 +210,30 @@ class Action extends PlatformAction throw new Exception(Exception::RULE_VERIFICATION_FAILED, $mainValidator->getDescription()); } } + + protected function isAppwriteOwned(string $domain): bool + { + $appwriteDomains = []; + $appwriteDomainEnvs = [ + System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', ''), + System::getEnv('_APP_DOMAIN_FUNCTIONS', ''), + System::getEnv('_APP_DOMAIN_SITES', ''), + ]; + foreach ($appwriteDomainEnvs as $appwriteDomainEnv) { + foreach (\explode(',', $appwriteDomainEnv) as $appwriteDomain) { + if (empty($appwriteDomain)) { + continue; + } + $appwriteDomains[] = $appwriteDomain; + } + } + + foreach ($appwriteDomains as $appwriteDomain) { + if (\str_ends_with($domain, $appwriteDomain)) { + return true; + } + } + + return false; + } } diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php index 95ea8dd8cf..86b780bde0 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php @@ -74,18 +74,12 @@ class Create extends Action { $this->validateDomainRestrictions($domain, $platform); - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - // TODO: (@Meldiron) Remove after 1.7.x migration $ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain) : ID::unique(); $status = RULE_STATUS_CREATED; $owner = ''; - if ( - ($functionsDomain != '' && \str_ends_with($domain, $functionsDomain)) || - ($sitesDomain != '' && \str_ends_with($domain, $sitesDomain)) - ) { + if ($this->isAppwriteOwned($domain)) { $status = RULE_STATUS_VERIFIED; $owner = 'Appwrite'; } diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php index ea0fb69050..5837d80630 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php @@ -79,9 +79,6 @@ class Create extends Action { $this->validateDomainRestrictions($domain, $platform); - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { throw new Exception(Exception::RULE_RESOURCE_NOT_FOUND); @@ -94,10 +91,7 @@ class Create extends Action $status = RULE_STATUS_CREATED; $owner = ''; - if ( - ($functionsDomain != '' && \str_ends_with($domain, $functionsDomain)) || - ($sitesDomain != '' && \str_ends_with($domain, $sitesDomain)) - ) { + if ($this->isAppwriteOwned($domain)) { $status = RULE_STATUS_VERIFIED; $owner = 'Appwrite'; } diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php index f21374b49a..e1dd3de108 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php @@ -82,9 +82,6 @@ class Create extends Action { $this->validateDomainRestrictions($domain, $platform); - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - $collection = match ($resourceType) { 'site' => 'sites', 'function' => 'functions' @@ -99,10 +96,7 @@ class Create extends Action $status = RULE_STATUS_CREATED; $owner = ''; - if ( - ($functionsDomain != '' && \str_ends_with($domain, $functionsDomain)) || - ($sitesDomain != '' && \str_ends_with($domain, $sitesDomain)) - ) { + if ($this->isAppwriteOwned($domain)) { $status = RULE_STATUS_VERIFIED; $owner = 'Appwrite'; } diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php index 26bd453eb3..20829c1b91 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php @@ -79,9 +79,6 @@ class Create extends Action { $this->validateDomainRestrictions($domain, $platform); - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - $site = $dbForProject->getDocument('sites', $siteId); if ($site->isEmpty()) { throw new Exception(Exception::RULE_RESOURCE_NOT_FOUND); @@ -94,10 +91,7 @@ class Create extends Action $status = RULE_STATUS_CREATED; $owner = ''; - if ( - ($functionsDomain != '' && \str_ends_with($domain, $functionsDomain)) || - ($sitesDomain != '' && \str_ends_with($domain, $sitesDomain)) - ) { + if ($this->isAppwriteOwned($domain)) { $status = RULE_STATUS_VERIFIED; $owner = 'Appwrite'; } diff --git a/tests/e2e/Services/Projects/ProjectsCustomServerTest.php b/tests/e2e/Services/Projects/ProjectsCustomServerTest.php index b2cf57ddc4..313a4d53be 100644 --- a/tests/e2e/Services/Projects/ProjectsCustomServerTest.php +++ b/tests/e2e/Services/Projects/ProjectsCustomServerTest.php @@ -50,7 +50,7 @@ class ProjectsCustomServerTest extends Scope $this->assertEquals(204, $response['headers']['status-code']); - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + $functionsDomain = \explode(',', System::getEnv('_APP_DOMAIN_FUNCTIONS', ''))[0]; $response = $this->client->call(Client::METHOD_POST, '/proxy/rules/api', $headers, [ 'domain' => $functionsDomain, @@ -59,7 +59,7 @@ class ProjectsCustomServerTest extends Scope $this->assertEquals(400, $response['headers']['status-code']); - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); + $sitesDomain = \explode(',', System::getEnv('_APP_DOMAIN_SITES', ''))[0]; $response = $this->client->call(Client::METHOD_POST, '/proxy/rules/api', $headers, [ 'domain' => $sitesDomain, diff --git a/tests/e2e/Services/Proxy/ProxyCustomServerTest.php b/tests/e2e/Services/Proxy/ProxyCustomServerTest.php index 1830adbae3..0fe333b0c0 100644 --- a/tests/e2e/Services/Proxy/ProxyCustomServerTest.php +++ b/tests/e2e/Services/Proxy/ProxyCustomServerTest.php @@ -112,7 +112,8 @@ class ProxyCustomServerTest extends Scope $this->assertEquals(201, $rule['headers']['status-code']); $this->cleanupRule($rule['body']['$id']); - $domain = \uniqid() . '-vcs.' . System::getEnv('_APP_DOMAIN_SITES', ''); + $sitesDomain = \explode(',', System::getEnv('_APP_DOMAIN_SITES', ''))[0]; + $domain = \uniqid() . '-vcs.' . $sitesDomain; $rule = $this->createSiteRule('commit-' . $domain, $siteId); $this->assertEquals(400, $rule['headers']['status-code']); @@ -393,7 +394,8 @@ class ProxyCustomServerTest extends Scope $this->cleanupRule($rule['body']['$id']); // Create site appwrite-network domain - $domain = \uniqid() . '-cname-api.' . System::getEnv('_APP_DOMAIN_SITES'); + $sitesDomain = \explode(',', System::getEnv('_APP_DOMAIN_SITES', ''))[0]; + $domain = \uniqid() . '-cname-api.' . $sitesDomain; $rule = $this->createAPIRule($domain); $this->assertEquals(201, $rule['headers']['status-code']); diff --git a/tests/e2e/Services/Sites/SitesBase.php b/tests/e2e/Services/Sites/SitesBase.php index 7eb5d9699c..e7af09d1d9 100644 --- a/tests/e2e/Services/Sites/SitesBase.php +++ b/tests/e2e/Services/Sites/SitesBase.php @@ -368,12 +368,13 @@ trait SitesBase protected function setupSiteDomain(string $siteId, string $subdomain = ''): string { + $sitesDomain = \explode(',', System::getEnv('_APP_DOMAIN_SITES', ''))[0]; $subdomain = $subdomain ? $subdomain : ID::unique(); $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/site', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'domain' => $subdomain . '.' . System::getEnv('_APP_DOMAIN_SITES', ''), + 'domain' => $subdomain . '.' . $sitesDomain, 'siteId' => $siteId, ]); diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index ff4d8dd5e1..27294a9c3b 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -1810,11 +1810,12 @@ class SitesCustomServerTest extends Scope $siteId2 = $site2['body']['$id']; + $sitesDomain = \explode(',', System::getEnv('_APP_DOMAIN_SITES', ''))[0]; $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/site', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'domain' => $subdomain . '.' . System::getEnv('_APP_DOMAIN_SITES', ''), + 'domain' => $subdomain . '.' . $sitesDomain, 'siteId' => $siteId2, ]);