1
0
Fork 0
mirror of synced 2024-07-03 21:50:34 +12:00

Merge remote-tracking branch 'origin/0.16.x' into fix-ui-with-queries-v2

This commit is contained in:
Matej Bačo 2022-08-31 13:33:18 +00:00
commit a85a471d3b
19 changed files with 246 additions and 61 deletions

View file

@ -34,7 +34,8 @@ ENV PHP_REDIS_VERSION=5.3.7 \
PHP_SWOOLE_VERSION=v4.8.10 \ PHP_SWOOLE_VERSION=v4.8.10 \
PHP_IMAGICK_VERSION=3.7.0 \ PHP_IMAGICK_VERSION=3.7.0 \
PHP_YAML_VERSION=2.2.2 \ PHP_YAML_VERSION=2.2.2 \
PHP_MAXMINDDB_VERSION=v1.11.0 PHP_MAXMINDDB_VERSION=v1.11.0 \
PHP_ZSTD_VERSION="4504e4186e79b197cfcb75d4d09aa47ef7d92fe9 "
RUN \ RUN \
apk add --no-cache --virtual .deps \ apk add --no-cache --virtual .deps \
@ -50,7 +51,8 @@ RUN \
yaml-dev \ yaml-dev \
imagemagick \ imagemagick \
imagemagick-dev \ imagemagick-dev \
libmaxminddb-dev libmaxminddb-dev \
zstd-dev
RUN docker-php-ext-install sockets RUN docker-php-ext-install sockets
@ -123,6 +125,16 @@ RUN \
./configure && \ ./configure && \
make && make install make && make install
# Zstd Compression
FROM compile as zstd
RUN git clone --recursive -n https://github.com/kjdev/php-ext-zstd.git \
&& cd php-ext-zstd \
&& git checkout $PHP_ZSTD_VERSION \
&& phpize \
&& ./configure --with-libzstd \
&& make && make install
# Rust Extensions Compile Image # Rust Extensions Compile Image
FROM php:8.0.18-cli as rust_compile FROM php:8.0.18-cli as rust_compile
@ -293,6 +305,7 @@ COPY --from=yaml /usr/local/lib/php/extensions/no-debug-non-zts-20200930/yaml.so
COPY --from=maxmind /usr/local/lib/php/extensions/no-debug-non-zts-20200930/maxminddb.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/ COPY --from=maxmind /usr/local/lib/php/extensions/no-debug-non-zts-20200930/maxminddb.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/
COPY --from=mongodb /usr/local/lib/php/extensions/no-debug-non-zts-20200930/mongodb.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/ COPY --from=mongodb /usr/local/lib/php/extensions/no-debug-non-zts-20200930/mongodb.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/
COPY --from=scrypt /usr/local/lib/php/extensions/php-scrypt/target/libphp_scrypt.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/ COPY --from=scrypt /usr/local/lib/php/extensions/php-scrypt/target/libphp_scrypt.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/
COPY --from=zstd /usr/local/lib/php/extensions/no-debug-non-zts-20200930/zstd.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/
# Add Source Code # Add Source Code
COPY ./app /usr/src/code/app COPY ./app /usr/src/code/app
@ -350,6 +363,7 @@ RUN echo extension=imagick.so >> /usr/local/etc/php/conf.d/imagick.ini
RUN echo extension=yaml.so >> /usr/local/etc/php/conf.d/yaml.ini RUN echo extension=yaml.so >> /usr/local/etc/php/conf.d/yaml.ini
RUN echo extension=maxminddb.so >> /usr/local/etc/php/conf.d/maxminddb.ini RUN echo extension=maxminddb.so >> /usr/local/etc/php/conf.d/maxminddb.ini
RUN echo extension=libphp_scrypt.so >> /usr/local/etc/php/conf.d/libphp_scrypt.ini RUN echo extension=libphp_scrypt.so >> /usr/local/etc/php/conf.d/libphp_scrypt.ini
RUN echo extension=zstd.so >> /usr/local/etc/php/conf.d/zstd.ini
RUN if [ "$DEBUG" == "true" ]; then printf "zend_extension=yasd \nyasd.debug_mode=remote \nyasd.init_file=/usr/local/dev/yasd_init.php \nyasd.remote_port=9005 \nyasd.log_level=-1" >> /usr/local/etc/php/conf.d/yasd.ini; fi RUN if [ "$DEBUG" == "true" ]; then printf "zend_extension=yasd \nyasd.debug_mode=remote \nyasd.init_file=/usr/local/dev/yasd_init.php \nyasd.remote_port=9005 \nyasd.log_level=-1" >> /usr/local/etc/php/conf.d/yasd.ini; fi
RUN if [ "$DEBUG" == "true" ]; then echo "opcache.enable=0" >> /usr/local/etc/php/conf.d/appwrite.ini; fi RUN if [ "$DEBUG" == "true" ]; then echo "opcache.enable=0" >> /usr/local/etc/php/conf.d/appwrite.ini; fi

View file

@ -2882,6 +2882,16 @@ $collections = [
'required' => true, 'required' => true,
'array' => true, 'array' => true,
], ],
[
'$id' => 'compression',
'type' => Database::VAR_STRING,
'signed' => true,
'size' => 10,
'format' => '',
'filters' => [],
'required' => true,
'array' => false,
],
[ [
'$id' => ID::custom('encryption'), '$id' => ID::custom('encryption'),
'type' => Database::VAR_BOOLEAN, 'type' => Database::VAR_BOOLEAN,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -6,7 +6,6 @@ use Appwrite\Event\Delete;
use Appwrite\Event\Event; use Appwrite\Event\Event;
use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\OpenSSL\OpenSSL; use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Usage\Stats;
use Appwrite\Utopia\Response; use Appwrite\Utopia\Response;
use Utopia\App; use Utopia\App;
use Utopia\Config\Config; use Utopia\Config\Config;
@ -29,8 +28,8 @@ use Appwrite\Utopia\Database\Validator\Queries\Buckets;
use Appwrite\Utopia\Database\Validator\Queries\Files; use Appwrite\Utopia\Database\Validator\Queries\Files;
use Utopia\Image\Image; use Utopia\Image\Image;
use Utopia\Storage\Compression\Algorithms\GZIP; use Utopia\Storage\Compression\Algorithms\GZIP;
use Utopia\Storage\Compression\Algorithms\Zstd;
use Utopia\Storage\Device; use Utopia\Storage\Device;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage; use Utopia\Storage\Storage;
use Utopia\Storage\Validator\File; use Utopia\Storage\Validator\File;
use Utopia\Storage\Validator\FileExt; use Utopia\Storage\Validator\FileExt;
@ -39,7 +38,6 @@ use Utopia\Storage\Validator\Upload;
use Utopia\Validator\ArrayList; use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean; use Utopia\Validator\Boolean;
use Utopia\Validator\HexColor; use Utopia\Validator\HexColor;
use Utopia\Validator\Integer;
use Utopia\Validator\Range; use Utopia\Validator\Range;
use Utopia\Validator\Text; use Utopia\Validator\Text;
use Utopia\Validator\WhiteList; use Utopia\Validator\WhiteList;
@ -66,12 +64,13 @@ App::post('/v1/storage/buckets')
->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true) ->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true)
->param('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0), new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self-hosted setups you can change the max limit by changing the `_APP_STORAGE_LIMIT` environment variable. [Learn more about storage environment variables](docs/environment-variables#storage)', true) ->param('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0), new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self-hosted setups you can change the max limit by changing the `_APP_STORAGE_LIMIT` environment variable. [Learn more about storage environment variables](docs/environment-variables#storage)', true)
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true) ->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
->param('compression', 'none', new WhiteList([COMPRESSION_TYPE_NONE, COMPRESSION_TYPE_GZIP, COMPRESSION_TYPE_ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . COMPRESSION_TYPE_NONE . ', [' . COMPRESSION_TYPE_GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . COMPRESSION_TYPE_ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true)
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true) ->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true)
->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true) ->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true)
->inject('response') ->inject('response')
->inject('dbForProject') ->inject('dbForProject')
->inject('events') ->inject('events')
->action(function (string $bucketId, string $name, ?array $permissions, bool $fileSecurity, bool $enabled, int $maximumFileSize, array $allowedFileExtensions, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Event $events) { ->action(function (string $bucketId, string $name, ?array $permissions, bool $fileSecurity, bool $enabled, int $maximumFileSize, array $allowedFileExtensions, string $compression, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Event $events) {
$bucketId = $bucketId === 'unique()' ? ID::unique() : $bucketId; $bucketId = $bucketId === 'unique()' ? ID::unique() : $bucketId;
@ -120,6 +119,7 @@ App::post('/v1/storage/buckets')
'allowedFileExtensions' => $allowedFileExtensions, 'allowedFileExtensions' => $allowedFileExtensions,
'fileSecurity' => $fileSecurity, 'fileSecurity' => $fileSecurity,
'enabled' => $enabled, 'enabled' => $enabled,
'compression' => $compression,
'encryption' => $encryption, 'encryption' => $encryption,
'antivirus' => $antivirus, 'antivirus' => $antivirus,
'search' => implode(' ', [$bucketId, $name]), 'search' => implode(' ', [$bucketId, $name]),
@ -234,12 +234,13 @@ App::put('/v1/storage/buckets/:bucketId')
->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true) ->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true)
->param('maximumFileSize', null, new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self hosted version you can change the limit by changing _APP_STORAGE_LIMIT environment variable. [Learn more about storage environment variables](docs/environment-variables#storage)', true) ->param('maximumFileSize', null, new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self hosted version you can change the limit by changing _APP_STORAGE_LIMIT environment variable. [Learn more about storage environment variables](docs/environment-variables#storage)', true)
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true) ->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
->param('compression', 'none', new WhiteList([COMPRESSION_TYPE_NONE, COMPRESSION_TYPE_GZIP, COMPRESSION_TYPE_ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . COMPRESSION_TYPE_NONE . ', [' . COMPRESSION_TYPE_GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . COMPRESSION_TYPE_ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true)
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true) ->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true)
->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true) ->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true)
->inject('response') ->inject('response')
->inject('dbForProject') ->inject('dbForProject')
->inject('events') ->inject('events')
->action(function (string $bucketId, string $name, ?array $permissions, bool $fileSecurity, bool $enabled, ?int $maximumFileSize, array $allowedFileExtensions, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Event $events) { ->action(function (string $bucketId, string $name, ?array $permissions, bool $fileSecurity, bool $enabled, ?int $maximumFileSize, array $allowedFileExtensions, string $compression, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Event $events) {
$bucket = $dbForProject->getDocument('buckets', $bucketId); $bucket = $dbForProject->getDocument('buckets', $bucketId);
if ($bucket->isEmpty()) { if ($bucket->isEmpty()) {
@ -268,6 +269,7 @@ App::put('/v1/storage/buckets/:bucketId')
->setAttribute('fileSecurity', $fileSecurity) ->setAttribute('fileSecurity', $fileSecurity)
->setAttribute('enabled', $enabled) ->setAttribute('enabled', $enabled)
->setAttribute('encryption', $encryption) ->setAttribute('encryption', $encryption)
->setAttribute('compression', $compression)
->setAttribute('antivirus', $antivirus)); ->setAttribute('antivirus', $antivirus));
$events $events
@ -502,9 +504,18 @@ App::post('/v1/storage/buckets/:bucketId/files')
$mimeType = $deviceFiles->getFileMimeType($path); // Get mime-type before compression and encryption $mimeType = $deviceFiles->getFileMimeType($path); // Get mime-type before compression and encryption
$data = ''; $data = '';
// Compression // Compression
if ($fileSize <= APP_STORAGE_READ_BUFFER) { $algorithm = $bucket->getAttribute('compression', 'none');
if ($fileSize <= APP_STORAGE_READ_BUFFER && $algorithm != 'none') {
$data = $deviceFiles->read($path); $data = $deviceFiles->read($path);
switch ($algorithm) {
case 'zstd':
$compressor = new Zstd();
break;
case 'gzip':
default:
$compressor = new GZIP(); $compressor = new GZIP();
break;
}
$data = $compressor->compress($data); $data = $compressor->compress($data);
} }
@ -524,8 +535,6 @@ App::post('/v1/storage/buckets/:bucketId/files')
} }
$sizeActual = $deviceFiles->getFileSize($path); $sizeActual = $deviceFiles->getFileSize($path);
$algorithm = empty($compressor) ? '' : $compressor->getName();
$fileHash = $deviceFiles->getFileHash($path); $fileHash = $deviceFiles->getFileHash($path);
if ($bucket->getAttribute('encryption', true) && $fileSize <= APP_STORAGE_READ_BUFFER) { if ($bucket->getAttribute('encryption', true) && $fileSize <= APP_STORAGE_READ_BUFFER) {
@ -840,7 +849,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$path = $file->getAttribute('path'); $path = $file->getAttribute('path');
$type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION)); $type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION));
$algorithm = $file->getAttribute('algorithm'); $algorithm = $file->getAttribute('algorithm', 'none');
$cipher = $file->getAttribute('openSSLCipher'); $cipher = $file->getAttribute('openSSLCipher');
$mime = $file->getAttribute('mimeType'); $mime = $file->getAttribute('mimeType');
if (!\in_array($mime, $inputs) || $file->getAttribute('sizeActual') > (int) App::getEnv('_APP_STORAGE_PREVIEW_LIMIT', 20000000)) { if (!\in_array($mime, $inputs) || $file->getAttribute('sizeActual') > (int) App::getEnv('_APP_STORAGE_PREVIEW_LIMIT', 20000000)) {
@ -851,16 +860,13 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$path = $fileLogos['default_image']; $path = $fileLogos['default_image'];
} }
$algorithm = null; $algorithm = 'none';
$cipher = null; $cipher = null;
$background = (empty($background)) ? 'eceff1' : $background; $background = (empty($background)) ? 'eceff1' : $background;
$type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION)); $type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION));
$deviceFiles = $deviceLocal; $deviceFiles = $deviceLocal;
} }
$compressor = new GZIP();
if (!$deviceFiles->exists($path)) { if (!$deviceFiles->exists($path)) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
} }
@ -885,8 +891,15 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
); );
} }
if (!empty($algorithm)) { switch ($algorithm) {
case 'zstd':
$compressor = new Zstd();
$source = $compressor->decompress($source); $source = $compressor->decompress($source);
break;
case 'gzip':
$compressor = new GZIP();
$source = $compressor->decompress($source);
break;
} }
$image = new Image($source); $image = new Image($source);
@ -1021,12 +1034,21 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
); );
} }
if (!empty($file->getAttribute('algorithm', ''))) { switch ($file->getAttribute('algorithm', 'none')) {
case 'zstd':
if (empty($source)) {
$source = $deviceFiles->read($path);
}
$compressor = new Zstd();
$source = $compressor->decompress($source);
break;
case 'gzip':
if (empty($source)) { if (empty($source)) {
$source = $deviceFiles->read($path); $source = $deviceFiles->read($path);
} }
$compressor = new GZIP(); $compressor = new GZIP();
$source = $compressor->decompress($source); $source = $compressor->decompress($source);
break;
} }
if (!empty($source)) { if (!empty($source)) {
@ -1162,12 +1184,21 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
); );
} }
if (!empty($file->getAttribute('algorithm', ''))) { switch ($file->getAttribute('algorithm', 'none')) {
case 'zstd':
if (empty($source)) {
$source = $deviceFiles->read($path);
}
$compressor = new Zstd();
$source = $compressor->decompress($source);
break;
case 'gzip':
if (empty($source)) { if (empty($source)) {
$source = $deviceFiles->read($path); $source = $deviceFiles->read($path);
} }
$compressor = new GZIP(); $compressor = new GZIP();
$source = $compressor->decompress($source); $source = $compressor->decompress($source);
break;
} }
if (!empty($source)) { if (!empty($source)) {

View file

@ -169,6 +169,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
'maximumFileSize' => (int) App::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB 'maximumFileSize' => (int) App::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB
'allowedFileExtensions' => [], 'allowedFileExtensions' => [],
'enabled' => true, 'enabled' => true,
'compression' => 'gzip',
'encryption' => true, 'encryption' => true,
'antivirus' => true, 'antivirus' => true,
'fileSecurity' => true, 'fileSecurity' => true,

View file

@ -151,6 +151,10 @@ const DELETE_TYPE_BUCKETS = 'buckets';
const DELETE_TYPE_SESSIONS = 'sessions'; const DELETE_TYPE_SESSIONS = 'sessions';
const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp'; const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp';
const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource'; const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource';
// Compression type
const COMPRESSION_TYPE_NONE = 'none';
const COMPRESSION_TYPE_GZIP = 'gzip';
const COMPRESSION_TYPE_ZSTD = 'zstd';
// Mail Types // Mail Types
const MAIL_TYPE_VERIFICATION = 'verification'; const MAIL_TYPE_VERIFICATION = 'verification';
const MAIL_TYPE_MAGIC_SESSION = 'magicSession'; const MAIL_TYPE_MAGIC_SESSION = 'magicSession';

View file

@ -414,6 +414,13 @@ $fileUpdatePermissions = $this->getParam('fileUpdatePermissions', null);
<label for="bucket-maximum-file-size">Maximum File Size (bytes) <span class="tooltip small" data-tooltip="Limit file size allowed in the bucket."><i class="icon-info-circled"></i></span></label> <label for="bucket-maximum-file-size">Maximum File Size (bytes) <span class="tooltip small" data-tooltip="Limit file size allowed in the bucket."><i class="icon-info-circled"></i></span></label>
<input name="maximumFileSize" id="bucket-maximum-file-size" type="number" autocomplete="off" data-ls-bind="{{project-bucket.maximumFileSize}}" min="1" data-cast-to="integer" /> <input name="maximumFileSize" id="bucket-maximum-file-size" type="number" autocomplete="off" data-ls-bind="{{project-bucket.maximumFileSize}}" min="1" data-cast-to="integer" />
<label for="compression">Compression</label>
<select id="compression" data-ls-bind="{{project-bucket.compression}}" name="compression">
<option value="none">None</option>
<option value="gzip">Gzip</option>
<option value="zstd">Zstd</option>
</select>
<div class="margin-bottom"> <div class="margin-bottom">
<input name="enabled" type="hidden" data-forms-switch data-cast-to="boolean" data-ls-bind="{{project-bucket.enabled}}" /> &nbsp; Enabled <span class="tooltip" data-tooltip="Mark whether bucket is enabled"><i class="icon-info-circled"></i></span> <input name="enabled" type="hidden" data-forms-switch data-cast-to="boolean" data-ls-bind="{{project-bucket.enabled}}" /> &nbsp; Enabled <span class="tooltip" data-tooltip="Mark whether bucket is enabled"><i class="icon-info-circled"></i></span>
</div> </div>

View file

@ -57,7 +57,7 @@
"utopia-php/preloader": "0.2.*", "utopia-php/preloader": "0.2.*",
"utopia-php/domains": "1.1.*", "utopia-php/domains": "1.1.*",
"utopia-php/swoole": "0.3.*", "utopia-php/swoole": "0.3.*",
"utopia-php/storage": "0.9.*", "utopia-php/storage": "0.11.*",
"utopia-php/websocket": "0.1.0", "utopia-php/websocket": "0.1.0",
"utopia-php/image": "0.5.*", "utopia-php/image": "0.5.*",
"utopia-php/orchestration": "0.6.*", "utopia-php/orchestration": "0.6.*",

27
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "039de21eff3a27955696a9f6f645c548", "content-hash": "7c41d4bcb13c34e72039e3de7e84f732",
"packages": [ "packages": [
{ {
"name": "adhocore/jwt", "name": "adhocore/jwt",
@ -2556,19 +2556,22 @@
}, },
{ {
"name": "utopia-php/storage", "name": "utopia-php/storage",
"version": "0.9.0", "version": "0.11.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/utopia-php/storage.git", "url": "https://github.com/utopia-php/storage.git",
"reference": "c7912481a56e17cc86358fa8de57309de5e88ef7" "reference": "59802cf281d1976560cf6e353f250a9b870efddc"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/c7912481a56e17cc86358fa8de57309de5e88ef7", "url": "https://api.github.com/repos/utopia-php/storage/zipball/59802cf281d1976560cf6e353f250a9b870efddc",
"reference": "c7912481a56e17cc86358fa8de57309de5e88ef7", "reference": "59802cf281d1976560cf6e353f250a9b870efddc",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-fileinfo": "*",
"ext-zlib": "*",
"ext-zstd": "*",
"php": ">=8.0", "php": ">=8.0",
"utopia-php/framework": "0.*.*" "utopia-php/framework": "0.*.*"
}, },
@ -2602,9 +2605,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/utopia-php/storage/issues", "issues": "https://github.com/utopia-php/storage/issues",
"source": "https://github.com/utopia-php/storage/tree/0.9.0" "source": "https://github.com/utopia-php/storage/tree/0.11.0"
}, },
"time": "2022-05-19T11:05:45+00:00" "time": "2022-08-31T09:17:31+00:00"
}, },
{ {
"name": "utopia-php/swoole", "name": "utopia-php/swoole",
@ -2841,12 +2844,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/appwrite/sdk-generator.git", "url": "https://github.com/appwrite/sdk-generator.git",
"reference": "6597948263e88f73dbdd5c70259dd54aff2dfcf8" "reference": "532d4d15ec8f11539972f2848c2e230c49b24f65"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/6597948263e88f73dbdd5c70259dd54aff2dfcf8", "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/532d4d15ec8f11539972f2848c2e230c49b24f65",
"reference": "6597948263e88f73dbdd5c70259dd54aff2dfcf8", "reference": "532d4d15ec8f11539972f2848c2e230c49b24f65",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2883,7 +2886,7 @@
"issues": "https://github.com/appwrite/sdk-generator/issues", "issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/feat-new-headers" "source": "https://github.com/appwrite/sdk-generator/tree/feat-new-headers"
}, },
"time": "2022-08-30T18:29:13+00:00" "time": "2022-08-20T07:42:55+00:00"
}, },
{ {
"name": "doctrine/instantiator", "name": "doctrine/instantiator",
@ -5383,5 +5386,5 @@
"platform-overrides": { "platform-overrides": {
"php": "8.0" "php": "8.0"
}, },
"plugin-api-version": "2.2.0" "plugin-api-version": "2.3.0"
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -522,7 +522,7 @@ return new Promise(function(resolve,reject){let request=new XMLHttpRequest(),key
request.onload=function(){if(4===request.readyState&&399>=request.status){let data=request.response;let contentType=this.getResponseHeader('content-type');contentType=contentType.substring(0,contentType.indexOf(';'));switch(contentType){case'application/json':data=JSON.parse(data);break;} request.onload=function(){if(4===request.readyState&&399>=request.status){let data=request.response;let contentType=this.getResponseHeader('content-type');contentType=contentType.substring(0,contentType.indexOf(';'));switch(contentType){case'application/json':data=JSON.parse(data);break;}
resolve(data);}else{reject(new Error(request.statusText));}};if(progress){request.addEventListener('progress',progress);request.upload.addEventListener('progress',progress,false);} resolve(data);}else{reject(new Error(request.statusText));}};if(progress){request.addEventListener('progress',progress);request.upload.addEventListener('progress',progress,false);}
request.onerror=function(){reject(new Error("Network Error"));};request.send(params);})};return{'get':function(path,headers={},params={}){return call('GET',path+((params.length>0)?'?'+buildQuery(params):''),headers,{});},'post':function(path,headers={},params={},progress=null){return call('POST',path,headers,params,progress);},'put':function(path,headers={},params={},progress=null){return call('PUT',headers,params,progress);},'patch':function(path,headers={},params={},progress=null){return call('PATCH',path,headers,params,progress);},'delete':function(path,headers={},params={},progress=null){return call('DELETE',path,headers,params,progress);},'addGlobalParam':addGlobalParam,'addGlobalHeader':addGlobalHeader}}(window.document);let analytics={create:function(id,source,activity,url){return http.post('/analytics',{'content-type':'application/json'},{id:id,source:source,activity:activity,url:url,version:env.VERSION,setup:env.SETUP});},};return{analytics:analytics,};},true);})(window);(function(window){"use strict";window.ls.container.set('console',function(window){var client=new Appwrite.Client();var endpoint=window.location.origin+'/v1';client.setEndpoint(endpoint).setProject('console').setLocale(APP_ENV.LOCALE);return{client:client,account:new Appwrite.Account(client),avatars:new Appwrite.Avatars(client),databases:new Appwrite.Databases(client),functions:new Appwrite.Functions(client),health:new Appwrite.Health(client),locale:new Appwrite.Locale(client),projects:new Appwrite.Projects(client),storage:new Appwrite.Storage(client),teams:new Appwrite.Teams(client),users:new Appwrite.Users(client)}},true);})(window);(function(window){"use strict";window.ls.container.set('date',function(){function format(format,datetime){if(!datetime){return null;} request.onerror=function(){reject(new Error("Network Error"));};request.send(params);})};return{'get':function(path,headers={},params={}){return call('GET',path+((params.length>0)?'?'+buildQuery(params):''),headers,{});},'post':function(path,headers={},params={},progress=null){return call('POST',path,headers,params,progress);},'put':function(path,headers={},params={},progress=null){return call('PUT',headers,params,progress);},'patch':function(path,headers={},params={},progress=null){return call('PATCH',path,headers,params,progress);},'delete':function(path,headers={},params={},progress=null){return call('DELETE',path,headers,params,progress);},'addGlobalParam':addGlobalParam,'addGlobalHeader':addGlobalHeader}}(window.document);let analytics={create:function(id,source,activity,url){return http.post('/analytics',{'content-type':'application/json'},{id:id,source:source,activity:activity,url:url,version:env.VERSION,setup:env.SETUP});},};return{analytics:analytics,};},true);})(window);(function(window){"use strict";window.ls.container.set('console',function(window){var client=new Appwrite.Client();var endpoint=window.location.origin+'/v1';client.setEndpoint(endpoint).setProject('console').setLocale(APP_ENV.LOCALE);return{client:client,account:new Appwrite.Account(client),avatars:new Appwrite.Avatars(client),databases:new Appwrite.Databases(client),functions:new Appwrite.Functions(client),health:new Appwrite.Health(client),locale:new Appwrite.Locale(client),projects:new Appwrite.Projects(client),storage:new Appwrite.Storage(client),teams:new Appwrite.Teams(client),users:new Appwrite.Users(client)}},true);})(window);(function(window){"use strict";window.ls.container.set('date',function(){function format(format,datetime){if(!datetime){return null;}
return new Intl.DateTimeFormat('en-US',{timeZone:'UTC',hourCycle:'h24',...format}).format(new Date(datetime));} return new Intl.DateTimeFormat(navigator.languages,{hourCycle:'h24',...format}).format(new Date(datetime));}
return{format:format,}}(),true);})(window);(function(window){"use strict";window.ls.container.set('env',function(){return APP_ENV;},true);})(window);(function(window){"use strict";window.ls.container.set('form',function(){function cast(value,from,to,){if(value&&Array.isArray(value)&&to!=='array'){value=value.map(element=>cast(element,from,to));return value;} return{format:format,}}(),true);})(window);(function(window){"use strict";window.ls.container.set('env',function(){return APP_ENV;},true);})(window);(function(window){"use strict";window.ls.container.set('form',function(){function cast(value,from,to,){if(value&&Array.isArray(value)&&to!=='array'){value=value.map(element=>cast(element,from,to));return value;}
switch(to){case'int':case'integer':value=parseInt(value);break;case'numeric':value=Number(value);break;case'float':value=parseFloat(value);break;case'string':value=value.toString();if(value.length===0){value=null;} switch(to){case'int':case'integer':value=parseInt(value);break;case'numeric':value=Number(value);break;case'float':value=parseFloat(value);break;case'string':value=value.toString();if(value.length===0){value=null;}
break;case'json':value=(value)?JSON.parse(value):[];break;case'array':if(value&&value.constructor&&value.constructor===Array){break;} break;case'json':value=(value)?JSON.parse(value):[];break;case'array':if(value&&value.constructor&&value.constructor===Array){break;}

View file

@ -93,7 +93,10 @@
mode: '', mode: '',
}; };
this.headers = { this.headers = {
'x-sdk-version': 'appwrite:web:6.0.0', 'x-sdk-name': 'Console',
'x-sdk-platform': 'console',
'x-sdk-language': 'web',
'x-sdk-version': '6.0.0',
'X-Appwrite-Response-Format': '0.15.0', 'X-Appwrite-Response-Format': '0.15.0',
}; };
this.realtime = { this.realtime = {
@ -5125,12 +5128,13 @@
* @param {boolean} enabled * @param {boolean} enabled
* @param {number} maximumFileSize * @param {number} maximumFileSize
* @param {string[]} allowedFileExtensions * @param {string[]} allowedFileExtensions
* @param {string} compression
* @param {boolean} encryption * @param {boolean} encryption
* @param {boolean} antivirus * @param {boolean} antivirus
* @throws {AppwriteException} * @throws {AppwriteException}
* @returns {Promise} * @returns {Promise}
*/ */
createBucket(bucketId, name, fileSecurity, permissions, enabled, maximumFileSize, allowedFileExtensions, encryption, antivirus) { createBucket(bucketId, name, fileSecurity, permissions, enabled, maximumFileSize, allowedFileExtensions, compression, encryption, antivirus) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
if (typeof bucketId === 'undefined') { if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"'); throw new AppwriteException('Missing required parameter: "bucketId"');
@ -5164,6 +5168,9 @@
if (typeof allowedFileExtensions !== 'undefined') { if (typeof allowedFileExtensions !== 'undefined') {
payload['allowedFileExtensions'] = allowedFileExtensions; payload['allowedFileExtensions'] = allowedFileExtensions;
} }
if (typeof compression !== 'undefined') {
payload['compression'] = compression;
}
if (typeof encryption !== 'undefined') { if (typeof encryption !== 'undefined') {
payload['encryption'] = encryption; payload['encryption'] = encryption;
} }
@ -5211,12 +5218,13 @@
* @param {boolean} enabled * @param {boolean} enabled
* @param {number} maximumFileSize * @param {number} maximumFileSize
* @param {string[]} allowedFileExtensions * @param {string[]} allowedFileExtensions
* @param {string} compression
* @param {boolean} encryption * @param {boolean} encryption
* @param {boolean} antivirus * @param {boolean} antivirus
* @throws {AppwriteException} * @throws {AppwriteException}
* @returns {Promise} * @returns {Promise}
*/ */
updateBucket(bucketId, name, fileSecurity, permissions, enabled, maximumFileSize, allowedFileExtensions, encryption, antivirus) { updateBucket(bucketId, name, fileSecurity, permissions, enabled, maximumFileSize, allowedFileExtensions, compression, encryption, antivirus) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
if (typeof bucketId === 'undefined') { if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"'); throw new AppwriteException('Missing required parameter: "bucketId"');
@ -5247,6 +5255,9 @@
if (typeof allowedFileExtensions !== 'undefined') { if (typeof allowedFileExtensions !== 'undefined') {
payload['allowedFileExtensions'] = allowedFileExtensions; payload['allowedFileExtensions'] = allowedFileExtensions;
} }
if (typeof compression !== 'undefined') {
payload['compression'] = compression;
}
if (typeof encryption !== 'undefined') { if (typeof encryption !== 'undefined') {
payload['encryption'] = encryption; payload['encryption'] = encryption;
} }

View file

@ -66,6 +66,13 @@ class Bucket extends Model
'example' => ['jpg', 'png'], 'example' => ['jpg', 'png'],
'array' => true 'array' => true
]) ])
->addRule('compression', [
'type' => self::TYPE_STRING,
'description' => 'Compression algorithm choosen for compression. Will be one of ' . COMPRESSION_TYPE_NONE . ', [' . COMPRESSION_TYPE_GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . COMPRESSION_TYPE_ZSTD . '](https://en.wikipedia.org/wiki/Zstd).',
'default' => [],
'example' => 'gzip',
'array' => false
])
->addRule('encryption', [ ->addRule('encryption', [
'type' => self::TYPE_BOOLEAN, 'type' => self::TYPE_BOOLEAN,
'description' => 'Bucket is encrypted.', 'description' => 'Bucket is encrypted.',

View file

@ -243,6 +243,55 @@ trait StorageBase
return ['bucketId' => $bucketId, 'fileId' => $file['body']['$id'], 'largeFileId' => $largeFile['body']['$id'], 'largeBucketId' => $bucket2['body']['$id']]; return ['bucketId' => $bucketId, 'fileId' => $file['body']['$id'], 'largeFileId' => $largeFile['body']['$id'], 'largeBucketId' => $bucket2['body']['$id']];
} }
public function testCreateBucketFileZstdCompression(): array
{
$bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'bucketId' => ID::unique(),
'name' => 'Test Bucket',
'fileSecurity' => true,
'maximumFileSize' => 2000000, //2MB
'allowedFileExtensions' => ["jpg", "png"],
'compression' => 'zstd',
'permissions' => [
Permission::read(Role::any()),
Permission::create(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
]);
$this->assertEquals(201, $bucket['headers']['status-code']);
$this->assertNotEmpty($bucket['body']['$id']);
$this->assertEquals('zstd', $bucket['body']['compression']);
$bucketId = $bucket['body']['$id'];
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'fileId' => ID::unique(),
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'),
'permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
]);
$this->assertEquals(201, $file['headers']['status-code']);
$this->assertNotEmpty($file['body']['$id']);
$this->assertEquals(true, DateTime::isValid($file['body']['$createdAt']));
$this->assertEquals('logo.png', $file['body']['name']);
$this->assertEquals('image/png', $file['body']['mimeType']);
$this->assertEquals(47218, $file['body']['sizeOriginal']);
$this->assertTrue(md5_file(realpath(__DIR__ . '/../../../resources/logo.png')) != $file['body']['signature']); // should validate that the file is encrypted
return ['bucketId' => $bucketId];
}
/** /**
* @depends testCreateBucketFile * @depends testCreateBucketFile
*/ */
@ -598,6 +647,50 @@ trait StorageBase
return $data; return $data;
} }
/**
* @depends testCreateBucketFileZstdCompression
*/
public function testFilePreviewZstdCompression(array $data): array
{
$bucketId = $data['bucketId'];
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'fileId' => ID::unique(),
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'),
'permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
]);
$this->assertEquals(201, $file['headers']['status-code']);
$this->assertNotEmpty($file['body']['$id']);
$fileId = $file['body']['$id'];
//get image preview
$file3 = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/preview', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'width' => 300,
'height' => 100,
'borderRadius' => '50',
'opacity' => '0.5',
'output' => 'png',
'rotation' => '45',
]);
$this->assertEquals(200, $file3['headers']['status-code']);
$this->assertEquals('image/png', $file3['headers']['content-type']);
$this->assertNotEmpty($file3['body']);
return $data;
}
/** /**
* @depends testCreateBucketFile * @depends testCreateBucketFile
*/ */