mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-react-native-platform
This commit is contained in:
@@ -11,11 +11,8 @@ use Utopia\Validator;
|
||||
*/
|
||||
class Headers extends Validator
|
||||
{
|
||||
protected bool $allowEmpty;
|
||||
|
||||
public function __construct(bool $allowEmpty = true)
|
||||
public function __construct(protected bool $allowEmpty = true, protected int $maxKeys = 100, protected int $maxSize = 16384)
|
||||
{
|
||||
$this->allowEmpty = $allowEmpty;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,7 +24,7 @@ class Headers extends Validator
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Invalid header format. Header keys can only contain alphanumeric characters, underscores, and hyphens. Header keys cannot start with "x-appwrite-" prefix.';
|
||||
return 'Invalid headers: Alphanumeric characters or hyphens only, cannot start with "x-appwrite", maximum ' . $this->maxKeys . ' keys, and total size ' . $this->maxSize . '.';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,34 +44,41 @@ class Headers extends Validator
|
||||
return false;
|
||||
}
|
||||
|
||||
if (\is_array($value)) {
|
||||
foreach ($value as $key => $val) {
|
||||
$length = \strlen($key);
|
||||
// Reject non-string keys
|
||||
if (!\is_string($key) || $length === 0) {
|
||||
return false;
|
||||
}
|
||||
if(\count($value) > $this->maxKeys) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check first and last character
|
||||
if (!ctype_alnum($key[0]) || !ctype_alnum($key[$length - 1])) {
|
||||
return false;
|
||||
}
|
||||
$size = 0;
|
||||
foreach ($value as $key => $val) {
|
||||
$length = \strlen($key);
|
||||
// Reject non-string keys
|
||||
if (!\is_string($key) || $length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check middle characters
|
||||
for ($i = 1; $i < $length - 1; $i++) {
|
||||
if (!ctype_alnum($key[$i]) && $key[$i] !== '-') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$size += $length + \strlen($val);
|
||||
if($size >= $this->maxSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for x-appwrite- prefix
|
||||
if (str_starts_with($key, 'x-appwrite-')) {
|
||||
// Check first and last character
|
||||
if (!ctype_alnum($key[0]) || !ctype_alnum($key[$length - 1])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check middle characters
|
||||
for ($i = 1; $i < $length - 1; $i++) {
|
||||
if (!ctype_alnum($key[$i]) && $key[$i] !== '-') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
// Check for x-appwrite- prefix
|
||||
if (str_starts_with($key, 'x-appwrite-')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -244,13 +244,7 @@ class Builds extends Action
|
||||
$tmpTemplateDirectory .= '/';
|
||||
}
|
||||
|
||||
$directorySize = $localDevice->getDirectorySize($tmpTemplateDirectory);
|
||||
$functionsSizeLimit = (int)System::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000');
|
||||
if ($directorySize > $functionsSizeLimit) {
|
||||
throw new \Exception('Repository directory size should be less than ' . number_format($functionsSizeLimit / 1048576, 2) . ' MBs.');
|
||||
}
|
||||
|
||||
$tarParamDirectory = \escapeshellarg('/tmp/builds/' . $buildId . '-template' . (empty($templateRootDirectory) ? '' : '/' . $templateRootDirectory));
|
||||
$tarParamDirectory = \escapeshellarg($tmpTemplateDirectory . (empty($templateRootDirectory) ? '' : '/' . $templateRootDirectory));
|
||||
Console::execute('tar --exclude code.tar.gz -czf ' . \escapeshellarg($tmpPathFile) . ' -C ' . \escapeshellcmd($tarParamDirectory) . ' .', '', $stdout, $stderr); // TODO: Replace escapeshellcmd with escapeshellarg if we find a way that doesnt break syntax
|
||||
|
||||
$source = $deviceForFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
|
||||
@@ -262,8 +256,9 @@ class Builds extends Action
|
||||
|
||||
Console::execute('rm -rf ' . \escapeshellarg($tmpTemplateDirectory), '', $stdout, $stderr);
|
||||
|
||||
$directorySize = $deviceForFunctions->getFileSize($source);
|
||||
$build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source));
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment->setAttribute('path', $source));
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment->setAttribute('path', $source)->setAttribute('size', $directorySize));
|
||||
}
|
||||
} elseif ($isNewBuild && $isVcsEnabled) {
|
||||
// VCS and VCS+Temaplte
|
||||
@@ -516,6 +511,8 @@ class Builds extends Action
|
||||
return;
|
||||
}
|
||||
|
||||
$isCanceled = false;
|
||||
|
||||
Co::join([
|
||||
Co\go(function () use ($executor, &$response, $project, $deployment, $source, $function, $runtime, $vars, $command, $cpus, $memory, &$err) {
|
||||
try {
|
||||
@@ -540,12 +537,16 @@ class Builds extends Action
|
||||
$err = $error;
|
||||
}
|
||||
}),
|
||||
Co\go(function () use ($executor, $project, $deployment, &$response, &$build, $dbForProject, $allEvents, &$err) {
|
||||
Co\go(function () use ($executor, $project, $deployment, &$response, &$build, $dbForProject, $allEvents, &$err, &$isCanceled) {
|
||||
try {
|
||||
$executor->getLogs(
|
||||
deploymentId: $deployment->getId(),
|
||||
projectId: $project->getId(),
|
||||
callback: function ($logs) use (&$response, &$err, &$build, $dbForProject, $allEvents, $project) {
|
||||
callback: function ($logs) use (&$response, &$err, &$build, $dbForProject, $allEvents, $project, &$isCanceled) {
|
||||
if($isCanceled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have response or error from concurrent coroutine, we already have latest logs
|
||||
if ($response === null && $err === null) {
|
||||
$build = $dbForProject->getDocument('builds', $build->getId());
|
||||
@@ -554,6 +555,12 @@ class Builds extends Action
|
||||
throw new \Exception('Build not found', 404);
|
||||
}
|
||||
|
||||
if ($build->getAttribute('status') === 'canceled') {
|
||||
$isCanceled = true;
|
||||
Console::info('Ignoring realtime logs because build has been canceled');
|
||||
return;
|
||||
}
|
||||
|
||||
$logs = \mb_substr($logs, 0, null, 'UTF-8'); // Get only valid UTF8 part - removes leftover half-multibytes causing SQL errors
|
||||
|
||||
$build = $build->setAttribute('logs', $build->getAttribute('logs', '') . $logs);
|
||||
@@ -586,11 +593,12 @@ class Builds extends Action
|
||||
}),
|
||||
]);
|
||||
|
||||
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
|
||||
Console::info('Build has been canceled');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($err) {
|
||||
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
|
||||
Console::info('Build has been canceled');
|
||||
return;
|
||||
}
|
||||
throw $err;
|
||||
}
|
||||
|
||||
@@ -611,6 +619,8 @@ class Builds extends Action
|
||||
$build->setAttribute('size', $response['size']);
|
||||
$build->setAttribute('logs', $response['output']);
|
||||
|
||||
$build = $dbForProject->updateDocument('builds', $buildId, $build);
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$this->runGitAction('ready', $github, $providerCommitHash, $owner, $repositoryName, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole);
|
||||
}
|
||||
@@ -652,12 +662,12 @@ class Builds extends Action
|
||||
$build->setAttribute('status', 'failed');
|
||||
$build->setAttribute('logs', $th->getMessage());
|
||||
|
||||
$build = $dbForProject->updateDocument('builds', $buildId, $build);
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$this->runGitAction('failed', $github, $providerCommitHash, $owner, $repositoryName, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole);
|
||||
}
|
||||
} finally {
|
||||
$build = $dbForProject->updateDocument('builds', $buildId, $build);
|
||||
|
||||
/**
|
||||
* Send realtime Event
|
||||
*/
|
||||
|
||||
@@ -7,7 +7,7 @@ use Appwrite\Extend\Exception;
|
||||
use Executor\Executor;
|
||||
use Throwable;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\Abuse\Adapters\Database as AbuseDatabase;
|
||||
use Utopia\Abuse\Adapters\Database\TimeLimit;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Cache\Adapter\Filesystem;
|
||||
use Utopia\Cache\Cache;
|
||||
@@ -493,7 +493,7 @@ class Deletes extends Action
|
||||
$projectCollectionIds = [
|
||||
...\array_keys(Config::getParam('collections', [])['projects']),
|
||||
Audit::COLLECTION,
|
||||
AbuseDatabase::COLLECTION,
|
||||
TimeLimit::COLLECTION,
|
||||
];
|
||||
|
||||
$limit = \count($projectCollectionIds) + 25;
|
||||
@@ -686,7 +686,7 @@ class Deletes extends Action
|
||||
{
|
||||
$projectId = $project->getId();
|
||||
$dbForProject = $getProjectDB($project);
|
||||
$timeLimit = new AbuseDatabase("", 0, 1, $dbForProject);
|
||||
$timeLimit = new TimeLimit("", 0, 1, $dbForProject);
|
||||
$abuse = new Abuse($timeLimit);
|
||||
|
||||
try {
|
||||
|
||||
@@ -33,9 +33,17 @@ class V16 extends Filter
|
||||
|
||||
protected function parseDeployment(array $content)
|
||||
{
|
||||
$content['buildStderr'] = '';
|
||||
$content['buildStdout'] = $content['buildLogs'];
|
||||
unset($content['buildLogs']);
|
||||
if(isset($content['buildLogs'])) {
|
||||
$content['buildStderr'] = '';
|
||||
$content['buildStdout'] = $content['buildLogs'];
|
||||
unset($content['buildLogs']);
|
||||
}
|
||||
|
||||
if(isset($content['buildSize'])) {
|
||||
$content['size'] += + $content['buildSize'] ?? 0;
|
||||
unset($content['buildSize']);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,12 @@ class Deployment extends Model
|
||||
'default' => 0,
|
||||
'example' => 128,
|
||||
])
|
||||
->addRule('buildSize', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'The build output size in bytes.',
|
||||
'default' => 0,
|
||||
'example' => 128,
|
||||
])
|
||||
->addRule('buildId', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'The current build ID.',
|
||||
|
||||
Reference in New Issue
Block a user