Compare commits

..
Author SHA1 Message Date
Matej Bačo b4f5af8461 Fix: unlimited deployment size 2025-05-22 17:25:47 +02:00
Matej BačoandGitHub 4adea70684 Merge pull request #9863 from appwrite/feat-add-configurable-resource-size
Add configurable deployment and build size
2025-05-22 15:28:40 +02:00
Matej BačoandGitHub 9321f585f5 Update app/worker.php 2025-05-22 15:22:40 +02:00
Jake BarnbyandGitHub 91d729cc55 Merge pull request #9864 from appwrite/fix-generic-s3
Fix bucket not included in path
2025-05-22 13:09:53 +00:00
Jake Barnby bd01d12db5 Format 2025-05-23 00:51:31 +12:00
Jake Barnby d2d9032ede Fix bucket not included in path 2025-05-23 00:49:26 +12:00
Khushboo Verma b727b8c100 Add configurable deployment and build size 2025-05-22 18:01:34 +05:30
Christy JacobandGitHub 9df0045024 Merge pull request #9861 from appwrite/fix-cname-validation
Fix: CNAME validation
2025-05-22 14:15:27 +04:00
Christy JacobandGitHub 3c74818591 Merge pull request #9859 from appwrite/sites-certificates
fix: send deploymentResourceType in rules verification
2025-05-22 13:30:18 +04:00
Matej BačoandGitHub a734ae163a Merge pull request #9860 from appwrite/fix-error-page-for-development
Only load error page for development mode
2025-05-22 10:31:46 +02:00
Matej Bačo 67a5192705 Fix CNAME validation 2025-05-22 10:13:12 +02:00
Fabian Gruber 128dc1d946 fix: send deploymentResourceType in rules verification 2025-05-22 10:05:45 +02:00
11 changed files with 91 additions and 29 deletions
+37 -5
View File
@@ -214,11 +214,42 @@ App::patch('/v1/proxy/rules/:ruleId/verification')
throw new Exception(Exception::RULE_NOT_FOUND);
}
$validators = [];
$targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME', ''));
if ($targetCNAME->isKnown() && !$targetCNAME->isTest()) {
$validators[] = new DNS($targetCNAME->get(), DNS::RECORD_CNAME);
$targetCNAME = null;
switch ($rule->getAttribute('type', '')) {
case 'api':
// For example: fra.cloud.appwrite.io
$targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME', ''));
break;
case 'redirect':
// For example: appwrite.network
$targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_SITES', ''));
break;
case 'deployment':
switch ($rule->getAttribute('deploymentResourceType', '')) {
case 'function':
// For example: fra.appwrite.run
$targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_FUNCTIONS', ''));
break;
case 'site':
// For example: appwrite.network
$targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_SITES', ''));
break;
default:
break;
}
// no break
default:
break;
}
$validators = [];
if (!is_null($targetCNAME)) {
if ($targetCNAME->isKnown() && !$targetCNAME->isTest()) {
$validators[] = new DNS($targetCNAME->get(), DNS::RECORD_CNAME);
}
}
if ((new IP(IP::V4))->isValid(System::getEnv('_APP_DOMAIN_TARGET_A', ''))) {
$validators[] = new DNS(System::getEnv('_APP_DOMAIN_TARGET_A', ''), DNS::RECORD_A);
}
@@ -260,7 +291,8 @@ App::patch('/v1/proxy/rules/:ruleId/verification')
// Issue a TLS certificate when domain is verified
$queueForCertificates
->setDomain(new Document([
'domain' => $rule->getAttribute('domain')
'domain' => $rule->getAttribute('domain'),
'domainType' => $rule->getAttribute('deploymentResourceType', $rule->getAttribute('type')),
]))
->trigger();
+6 -6
View File
@@ -52,7 +52,6 @@ use Utopia\Storage\Device\S3;
use Utopia\Storage\Device\Wasabi;
use Utopia\Storage\Storage;
use Utopia\System\System;
use Utopia\Telemetry\Adapter as Telemetry;
use Utopia\Telemetry\Adapter\None as NoTelemetry;
use Utopia\Validator\Hostname;
use Utopia\Validator\WhiteList;
@@ -487,10 +486,9 @@ App::setResource('timelimit', function (\Redis $redis) {
};
}, ['redis']);
App::setResource('deviceForLocal', function (Telemetry $telemetry) {
App::setResource('deviceForLocal', function () {
return new Local();
}, ['telemetry']);
});
App::setResource('deviceForFiles', function ($project) {
return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
}, ['project']);
@@ -534,7 +532,8 @@ function getDevice(string $root, string $connection = ''): Device
switch ($device) {
case Storage::DEVICE_S3:
if (!empty($url)) {
return new S3($root, $accessKey, $accessSecret, $url, $region, $acl);
$bucketRoot = (!empty($bucket) ? $bucket . '/' : '') . \ltrim($root, '/');
return new S3($bucketRoot, $accessKey, $accessSecret, $url, $region, $acl);
} else {
return new AWS($root, $accessKey, $accessSecret, $bucket, $region, $acl);
}
@@ -566,7 +565,8 @@ function getDevice(string $root, string $connection = ''): Device
$s3Acl = 'private';
$s3EndpointUrl = System::getEnv('_APP_STORAGE_S3_ENDPOINT', '');
if (!empty($s3EndpointUrl)) {
return new S3($root, $s3AccessKey, $s3SecretKey, $s3EndpointUrl, $s3Region, $s3Acl);
$bucketRoot = (!empty($s3Bucket) ? $s3Bucket . '/' : '') . \ltrim($root, '/');
return new S3($bucketRoot, $s3AccessKey, $s3SecretKey, $s3EndpointUrl, $s3Region, $s3Acl);
} else {
return new AWS($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
}
+1
View File
@@ -240,6 +240,7 @@ Server::setResource('timelimit', function (\Redis $redis) {
Server::setResource('log', fn () => new Log());
Server::setResource('publisher', function (Group $pools) {
return new BrokerPool(publisher: $pools->get('publisher'));
}, ['pools']);
@@ -86,6 +86,7 @@ class Create extends Action
->inject('deviceForFunctions')
->inject('deviceForLocal')
->inject('queueForBuilds')
->inject('plan')
->callback([$this, 'action']);
}
@@ -102,7 +103,8 @@ class Create extends Action
Document $project,
Device $deviceForFunctions,
Device $deviceForLocal,
Build $queueForBuilds
Build $queueForBuilds,
array $plan
) {
$activate = \strval($activate) === 'true' || \strval($activate) === '1';
@@ -135,8 +137,14 @@ class Create extends Action
throw new Exception(Exception::STORAGE_FILE_EMPTY, 'No file sent');
}
$functionSizeLimit = (int) System::getEnv('_APP_COMPUTE_SIZE_LIMIT', '30000000');
if (isset($plan['deploymentSize'])) {
$functionSizeLimit = $plan['deploymentSize'] * 1000 * 1000;
}
$fileExt = new FileExt([FileExt::TYPE_GZIP]);
$fileSizeValidator = new FileSize(System::getEnv('_APP_COMPUTE_SIZE_LIMIT', '30000000'));
$fileSizeValidator = new FileSize($functionSizeLimit);
$upload = new Upload();
// Make sure we handle a single file and multiple files the same way
@@ -174,7 +182,7 @@ class Create extends Action
}
}
if (!$fileSizeValidator->isValid($fileSize)) { // Check if file size is exceeding allowed limit
if (!$fileSizeValidator->isValid($fileSize) && $functionSizeLimit !== 0) { // Check if file size is exceeding allowed limit
throw new Exception(Exception::STORAGE_INVALID_FILE_SIZE);
}
@@ -73,6 +73,7 @@ class Builds extends Action
->inject('deviceForFiles')
->inject('log')
->inject('executor')
->inject('plan')
->callback([$this, 'action']);
}
@@ -92,6 +93,7 @@ class Builds extends Action
* @param Device $deviceForFiles
* @param Log $log
* @param Executor $executor
* @param array $plan
* @return void
* @throws \Utopia\Database\Exception
*/
@@ -111,7 +113,8 @@ class Builds extends Action
callable $isResourceBlocked,
Device $deviceForFiles,
Log $log,
Executor $executor
Executor $executor,
array $plan
): void {
$payload = $message->getPayload() ?? [];
@@ -150,7 +153,8 @@ class Builds extends Action
$template,
$isResourceBlocked,
$log,
$executor
$executor,
$plan
);
break;
@@ -177,6 +181,7 @@ class Builds extends Action
* @param Document $template
* @param Log $log
* @param Executor $executor
* @param array $plan
* @return void
* @throws \Utopia\Database\Exception
*
@@ -200,7 +205,8 @@ class Builds extends Action
Document $template,
callable $isResourceBlocked,
Log $log,
Executor $executor
Executor $executor,
array $plan
): void {
$resourceKey = match ($resource->getCollection()) {
'functions' => 'functionId',
@@ -476,8 +482,12 @@ class Builds extends Action
$directorySize = $localDevice->getDirectorySize($tmpDirectory);
$sizeLimit = (int)System::getEnv('_APP_COMPUTE_SIZE_LIMIT', '30000000');
if ($directorySize > $sizeLimit) {
throw new \Exception('Repository directory size should be less than ' . number_format($sizeLimit / 1048576, 2) . ' MBs.');
if (isset($plan['deploymentSize'])) {
$sizeLimit = (int) $plan['deploymentSize'] * 1000 * 1000;
}
if ($directorySize > $sizeLimit && $sizeLimit !== 0) {
throw new \Exception('Repository directory size should be less than ' . number_format($sizeLimit / (1000 * 1000), 2) . ' MBs.');
}
Console::execute('find ' . \escapeshellarg($tmpDirectory) . ' -type d -name ".git" -exec rm -rf {} +', '', $stdout, $stderr);
@@ -803,8 +813,11 @@ class Builds extends Action
$durationEnd = \microtime(true);
$buildSizeLimit = (int)System::getEnv('_APP_COMPUTE_BUILD_SIZE_LIMIT', '2000000000');
if ($response['size'] > $buildSizeLimit) {
throw new \Exception('Build size should be less than ' . number_format($buildSizeLimit / 1048576, 2) . ' MBs.');
if (isset($plan['buildSize'])) {
$buildSizeLimit = $plan['buildSize'] * 1000 * 1000;
}
if ($response['size'] > $buildSizeLimit && $buildSizeLimit !== 0) {
throw new \Exception('Build size should be less than ' . number_format($buildSizeLimit / (1000 * 1000), 2) . ' MBs.');
}
/** Update the build document */
@@ -174,7 +174,7 @@ class Create extends Action
$queueForCertificates
->setDomain(new Document([
'domain' => $rule->getAttribute('domain'),
'domainType' => 'api',
'domainType' => $rule->getAttribute('deploymentResourceType', $rule->getAttribute('type')),
]))
->trigger();
}
@@ -192,7 +192,7 @@ class Create extends Action
$queueForCertificates
->setDomain(new Document([
'domain' => $rule->getAttribute('domain'),
'domainType' => 'function',
'domainType' => $rule->getAttribute('deploymentResourceType', $rule->getAttribute('type')),
]))
->trigger();
}
@@ -180,7 +180,7 @@ class Create extends Action
$queueForCertificates
->setDomain(new Document([
'domain' => $rule->getAttribute('domain'),
'domainType' => 'redirect',
'domainType' => $rule->getAttribute('deploymentResourceType', $rule->getAttribute('type')),
]))
->trigger();
}
@@ -192,7 +192,7 @@ class Create extends Action
$queueForCertificates
->setDomain(new Document([
'domain' => $rule->getAttribute('domain'),
'domainType' => 'site',
'domainType' => $rule->getAttribute('deploymentResourceType', $rule->getAttribute('type')),
]))
->trigger();
}
@@ -85,6 +85,7 @@ class Create extends Action
->inject('deviceForSites')
->inject('deviceForLocal')
->inject('queueForBuilds')
->inject('plan')
->callback([$this, 'action']);
}
@@ -103,7 +104,8 @@ class Create extends Action
Event $queueForEvents,
Device $deviceForSites,
Device $deviceForLocal,
Build $queueForBuilds
Build $queueForBuilds,
array $plan
) {
$activate = \strval($activate) === 'true' || \strval($activate) === '1';
@@ -136,8 +138,14 @@ class Create extends Action
throw new Exception(Exception::STORAGE_FILE_EMPTY, 'No file sent');
}
$siteSizeLimit = (int) System::getEnv('_APP_COMPUTE_SIZE_LIMIT', '30000000');
if (isset($plan['deploymentSize'])) {
$siteSizeLimit = $plan['deploymentSize'] * 1000 * 1000;
}
$fileExt = new FileExt([FileExt::TYPE_GZIP]);
$fileSizeValidator = new FileSize(System::getEnv('_APP_COMPUTE_SIZE_LIMIT', '30000000'));
$fileSizeValidator = new FileSize($siteSizeLimit);
$upload = new Upload();
// Make sure we handle a single file and multiple files the same way
@@ -175,7 +183,7 @@ class Create extends Action
}
}
if (!$fileSizeValidator->isValid($fileSize)) { // Check if file size is exceeding allowed limit
if (!$fileSizeValidator->isValid($fileSize) && $siteSizeLimit !== 0) { // Check if file size is exceeding allowed limit
throw new Exception(Exception::STORAGE_INVALID_FILE_SIZE);
}
@@ -96,7 +96,7 @@ class Certificates extends Action
$log->addTag('domain', $domain->get());
$domainType = $payload['domainType'] ?? null;
$domainType = $document->getAttribute('domainType');
$this->execute($domain, $domainType, $dbForPlatform, $queueForMails, $queueForEvents, $queueForWebhooks, $queueForFunctions, $queueForRealtime, $log, $certificates, $skipRenewCheck, $plan);
}