1
0
Fork 0
mirror of synced 2024-06-02 19:04:49 +12:00
appwrite/app/controllers/api/storage.php

1017 lines
46 KiB
PHP
Raw Normal View History

2019-05-09 18:54:39 +12:00
<?php
2021-07-07 22:07:11 +12:00
use Appwrite\ClamAV\Network;
use Appwrite\Database\Validator\UID;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Utopia\Response;
2021-07-12 22:23:01 +12:00
use Swoole\HTTP\Response as SwooleResponse;
2020-06-29 05:31:21 +12:00
use Utopia\App;
2019-05-09 18:54:39 +12:00
use Utopia\Cache\Adapter\Filesystem;
2021-07-07 22:07:11 +12:00
use Utopia\Cache\Cache;
use Utopia\Config\Config;
2021-05-03 20:28:31 +12:00
use Utopia\Database\Document;
2021-07-07 22:07:11 +12:00
use Utopia\Database\Query;
use Utopia\Exception;
use Utopia\Image\Image;
use Utopia\Storage\Compression\Algorithms\GZIP;
2021-01-22 21:28:33 +13:00
use Utopia\Storage\Storage;
use Utopia\Storage\Validator\File;
2021-07-07 22:07:11 +12:00
use Utopia\Storage\Validator\FileExt;
2021-01-22 21:28:33 +13:00
use Utopia\Storage\Validator\FileSize;
use Utopia\Storage\Validator\Upload;
2021-07-07 22:07:11 +12:00
use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean;
use Utopia\Validator\HexColor;
2021-06-14 23:59:47 +12:00
use Utopia\Validator\Integer;
2021-07-07 22:07:11 +12:00
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
2019-05-09 18:54:39 +12:00
2021-06-14 23:32:09 +12:00
App::post('/v1/storage/buckets')
->desc('Create storage bucket')
->groups(['api', 'storage'])
->label('scope', 'buckets.write')
->label('event', 'storage.buckets.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
2021-06-14 23:32:09 +12:00
->label('sdk.namespace', 'storage')
->label('sdk.method', 'createBucket')
->label('sdk.description', '/docs/references/storage/create-bucket.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_BUCKET)
2021-06-14 23:59:47 +12:00
->param('name', '', new Text(128), 'Bucket name', false)
->param('read', [], new ArrayList(new Text(64)), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
->param('write', [], new ArrayList(new Text(64)), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
2021-07-07 22:07:11 +12:00
->param('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0), new Integer(), 'Maximum file size allowed in bytes. Maximum allowed value is ' . App::getEnv('_APP_STORAGE_LIMIT', 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)
2021-06-18 19:34:52 +12:00
->param('allowedFileExtensions', [], new ArrayList(new Text(64)), 'Allowed file extensions', true)
->param('enabled', true, new Boolean(), 'Is bucket enabled?', true)
->param('adapter', 'local', new WhiteList(['local']), 'Storage adapter.', true)
2021-06-18 20:35:14 +12:00
->param('encryption', true, new Boolean(), 'Is encryption enabled? For file size above ' . Storage::human(APP_LIMIT_ENCRYPTION) . ' encryption is skipped even if it\'s enabled', true)
->param('antiVirus', true, new Boolean(), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS) . ' AntiVirus scanning is skipped even if it\'s enabled', true)
2021-06-14 23:32:09 +12:00
->inject('response')
2021-06-15 20:34:49 +12:00
->inject('dbForInternal')
2021-06-14 23:32:09 +12:00
->inject('audits')
2021-06-16 21:59:47 +12:00
->action(function ($name, $read, $write, $maximumFileSize, $allowedFileExtensions, $enabled, $adapter, $encryption, $antiVirus, $response, $dbForInternal, $audits) {
2021-06-14 23:59:47 +12:00
/** @var Appwrite\Utopia\Response $response */
2021-06-15 20:34:49 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2021-06-14 23:59:47 +12:00
/** @var Appwrite\Event\Event $audits */
$data = $dbForInternal->createDocument('buckets', new Document([
2021-06-16 17:31:07 +12:00
'$collection' => 'buckets',
'dateCreated' => \time(),
'dateUpdated' => \time(),
'name' => $name,
'maximumFileSize' => $maximumFileSize,
'allowedFileExtensions' => $allowedFileExtensions,
'enabled' => $enabled,
'adapter' => $adapter,
'encryption' => $encryption,
'antiVirus' => $antiVirus,
'$read' => $read,
'$write' => $write,
]));
2021-06-14 23:59:47 +12:00
$audits
->setParam('event', 'storage.buckets.create')
->setParam('resource', 'storage/buckets/' . $data->getId())
2021-06-14 23:59:47 +12:00
->setParam('data', $data->getArrayCopy())
;
2021-06-15 20:34:49 +12:00
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic2($data, Response::MODEL_BUCKET);
2021-06-14 23:32:09 +12:00
});
2021-06-15 19:23:22 +12:00
App::get('/v1/storage/buckets')
->desc('List buckets')
->groups(['api', 'storage'])
->label('scope', 'buckets.read')
2021-06-16 17:43:59 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
2021-06-15 19:23:22 +12:00
->label('sdk.namespace', 'storage')
->label('sdk.method', 'listBuckets')
->label('sdk.description', '/docs/references/storage/list-buckets.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_BUCKET_LIST)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
2021-06-15 20:37:23 +12:00
->inject('dbForInternal')
->action(function ($search, $limit, $offset, $orderType, $response, $dbForInternal) {
2021-06-15 19:23:22 +12:00
/** @var Appwrite\Utopia\Response $response */
2021-06-15 20:37:23 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2021-06-15 19:23:22 +12:00
2021-06-15 20:37:23 +12:00
$queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, $search)] : [];
2021-06-15 19:23:22 +12:00
2021-06-15 20:37:23 +12:00
$response->dynamic2(new Document([
'buckets' => $dbForInternal->find('buckets', $queries, $limit, $offset, ['_id'], [$orderType]),
'sum' => $dbForInternal->count('buckets', $queries, APP_LIMIT_COUNT),
2021-06-15 19:23:22 +12:00
]), Response::MODEL_BUCKET_LIST);
});
2021-06-15 19:48:59 +12:00
App::get('/v1/storage/buckets/:bucketId')
->desc('Get Bucket')
->groups(['api', 'storage'])
->label('scope', 'buckets.read')
2021-06-16 17:43:59 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
2021-06-15 19:48:59 +12:00
->label('sdk.namespace', 'storage')
->label('sdk.method', 'getBucket')
->label('sdk.description', '/docs/references/storage/get-bucket.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_BUCKET)
->param('bucketId', '', new UID(), 'Bucket unique ID.')
->inject('response')
2021-06-15 20:39:36 +12:00
->inject('dbForInternal')
->action(function ($bucketId, $response, $dbForInternal) {
2021-06-15 19:48:59 +12:00
/** @var Appwrite\Utopia\Response $response */
2021-06-15 20:39:36 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2021-06-15 19:48:59 +12:00
2021-06-15 20:39:36 +12:00
$bucket = $dbForInternal->getDocument('buckets', $bucketId);
2021-06-15 19:48:59 +12:00
if ($bucket->isEmpty()) {
2021-06-15 19:48:59 +12:00
throw new Exception('Bucket not found', 404);
}
2021-06-15 20:39:36 +12:00
$response->dynamic2($bucket, Response::MODEL_BUCKET);
2021-06-15 19:48:59 +12:00
});
2021-06-16 21:59:47 +12:00
App::put('/v1/storage/buckets/:bucketId')
2021-06-16 22:03:44 +12:00
->desc('Update Bucket')
2021-06-16 21:59:47 +12:00
->groups(['api', 'storage'])
2021-06-16 22:03:44 +12:00
->label('scope', 'buckets.write')
2021-06-17 19:49:49 +12:00
->label('event', 'storage.buckets.update')
2021-06-16 21:59:47 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'storage')
2021-06-16 22:03:44 +12:00
->label('sdk.method', 'updateBucket')
->label('sdk.description', '/docs/references/storage/update-bucket.md')
2021-06-16 21:59:47 +12:00
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_BUCKET)
->param('bucketId', '', new UID(), 'Bucket unique ID.')
->param('name', null, new Text(128), 'Bucket name', false)
->param('read', null, new ArrayList(new Text(64)), 'An array of strings with read permissions. By default inherits the existing read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
->param('write', null, new ArrayList(new Text(64)), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
2021-06-18 19:48:57 +12:00
->param('maximumFileSize', null, new Integer(), 'Maximum file size allowed in bytes. Maximum allowed value is ' . App::getEnv('_APP_STORAGE_LIMIT', 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)
2021-06-18 19:34:52 +12:00
->param('allowedFileExtensions', [], new ArrayList(new Text(64)), 'Allowed file extensions', true)
2021-06-16 21:59:47 +12:00
->param('enabled', true, new Boolean(), 'Is bucket enabled?', true)
2021-06-18 20:35:14 +12:00
->param('encryption', true, new Boolean(), 'Is encryption enabled? For file size above ' . Storage::human(APP_LIMIT_ENCRYPTION) . ' encryption is skipped even if it\'s enabled', true)
->param('antiVirus', true, new Boolean(), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS) . ' AntiVirus scanning is skipped even if it\'s enabled', true)
2021-06-16 21:59:47 +12:00
->inject('response')
->inject('dbForInternal')
->inject('audits')
2021-06-17 20:01:19 +12:00
->action(function ($bucketId, $name, $read, $write, $maximumFileSize, $allowedFileExtensions, $enabled, $encryption, $antiVirus, $response, $dbForInternal, $audits) {
2021-06-16 21:59:47 +12:00
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Appwrite\Event\Event $audits */
$bucket = $dbForInternal->getDocument('buckets', $bucketId);
if ($bucket->isEmpty()) {
2021-06-16 21:59:47 +12:00
throw new Exception('Bucket not found', 404);
}
2021-07-07 22:07:11 +12:00
$read??=$bucket->getAttribute('$read', []); // By default inherit read permissions
$write??=$bucket->getAttribute('$write', []); // By default inherit write permissions
2021-06-16 21:59:47 +12:00
$bucket = $dbForInternal->updateDocument('buckets', $bucket->getId(), $bucket
2021-07-07 22:07:11 +12:00
->setAttribute('name', $name)
->setAttribute('$read', $read)
->setAttribute('$write', $write)
->setAttribute('maximumFileSize', $maximumFileSize)
->setAttribute('allowedFileExtensions', $allowedFileExtensions)
->setAttribute('enabled', $enabled)
->setAttribute('encryption', $encryption)
->setAttribute('antiVirus', $antiVirus)
2021-06-16 21:59:47 +12:00
);
$audits
->setParam('event', 'storage.buckets.update')
->setParam('resource', 'storage/buckets/' . $bucket->getId())
->setParam('data', $bucket->getArrayCopy())
;
$response->dynamic2($bucket, Response::MODEL_BUCKET);
});
2021-06-16 22:17:14 +12:00
App::delete('/v1/storage/buckets/:bucketId')
->desc('Delete Bucket')
->groups(['api', 'storage'])
->label('scope', 'buckets.write')
2021-06-17 19:49:49 +12:00
->label('event', 'storage.buckets.delete')
2021-06-16 22:17:14 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'deleteBucket')
->label('sdk.description', '/docs/references/storage/delete-bucket.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('bucketId', '', new UID(), 'Bucket unique ID.')
->inject('response')
->inject('dbForInternal')
->inject('audits')
->inject('deletes')
2021-06-17 20:23:21 +12:00
->inject('events')
->action(function ($bucketId, $response, $dbForInternal, $audits, $deletes, $events) {
2021-06-16 22:17:14 +12:00
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $deletes */
2021-06-17 20:23:21 +12:00
/** @var Appwrite\Event\Event $events */
2021-06-16 22:17:14 +12:00
$bucket = $dbForInternal->getDocument('buckets', $bucketId);
if ($bucket->isEmpty()) {
2021-06-16 22:17:14 +12:00
throw new Exception('Bucket not found', 404);
}
$deletes
->setParam('type', DELETE_TYPE_DOCUMENT)
->setParam('document', $bucket)
;
2021-07-07 22:07:11 +12:00
if (!$dbForInternal->deleteDocument('buckets', $bucketId)) {
2021-06-16 22:17:14 +12:00
throw new Exception('Failed to remove project from DB', 500);
}
2021-06-17 20:23:21 +12:00
$events
->setParam('eventData', $response->output2($bucket, Response::MODEL_BUCKET))
;
2021-06-16 22:17:14 +12:00
$audits
->setParam('event', 'storage.buckets.delete')
->setParam('resource', 'storage/buckets/' . $bucket->getId())
->setParam('data', $bucket->getArrayCopy())
;
$response->noContent();
});
2021-06-17 22:10:58 +12:00
App::post('/v1/storage/buckets/:bucketId/files')
2021-07-07 22:07:11 +12:00
->alias('/v1/storage/files', ['bucketId' => 'default'])
2021-06-17 22:10:58 +12:00
->desc('Create File')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('event', 'storage.files.create')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'createFile')
->label('sdk.description', '/docs/references/storage/create-file.md')
->label('sdk.request.type', 'multipart/form-data')
->label('sdk.methodType', 'upload')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FILE)
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('file', [], new File(), 'Binary file.', false)
->param('read', null, new ArrayList(new Text(64)), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
->param('write', null, new ArrayList(new Text(64)), 'An array of strings with write permissions. By default only the current user is granted with write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
->inject('request')
->inject('response')
->inject('dbForInternal')
->inject('user')
->inject('audits')
->inject('usage')
->action(function ($bucketId, $file, $read, $write, $request, $response, $dbForInternal, $user, $audits, $usage) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $usage */
$bucket = $dbForInternal->getDocument('buckets', $bucketId);
2021-06-17 22:10:58 +12:00
2021-07-07 22:07:11 +12:00
if ($bucket->isEmpty()) {
throw new Exception('Bucket not found', 404);
2021-06-17 22:10:58 +12:00
}
$file = $request->getFiles('file');
2021-07-08 23:26:11 +12:00
/**
* Validators
*/
2021-06-23 00:12:46 +12:00
$allowedFileExtensions = $bucket->getAttribute('allowedFileExtensions', []);
$fileExt = new FileExt($allowedFileExtensions);
$maximumFileSize = $bucket->getAttribute('maximumFileSize', 0);
2021-07-07 22:07:11 +12:00
if ($maximumFileSize > (int) App::getEnv('_APP_STORAGE_LIMIT', 0)) {
2021-06-28 17:52:45 +12:00
throw new Exception('Error bucket maximum file size is larger than _APP_STORAGE_LIMIT', 500);
}
$fileSize = new FileSize($maximumFileSize);
2021-06-17 22:10:58 +12:00
$upload = new Upload();
if (empty($file)) {
throw new Exception('No file sent', 400);
}
// Make sure we handle a single file and multiple files the same way
2021-07-07 22:07:11 +12:00
$fileName = (\is_array($file['name']) && isset($file['name'][0])) ? $file['name'][0] : $file['name'];
$fileTmpName = (\is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name'];
$size = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
$contentRange = $request->getHeader('content-range');
2021-07-13 19:58:16 +12:00
$fileId = $dbForInternal->getId();
2021-07-07 22:07:11 +12:00
$chunk = 1;
$chunks = 1;
if (!empty($contentRange)) {
2021-07-15 21:40:41 +12:00
$start = $request->getContentRangeStart();
$end = $request->getContentRangeEnd();
$size = $request->getContentRangeSize();
2021-07-07 22:07:11 +12:00
2021-07-15 21:40:41 +12:00
$fileId = $request->getHeader('x-appwrite-file-id', $fileId);
if(empty($start) || empty($end) || empty($size)) {
2021-07-07 22:07:11 +12:00
throw new Exception('Invalid content-range header', 400);
}
if ($end == $size) {
2021-07-13 19:58:16 +12:00
//if it's a last chunks the chunk size might differ, so we set the $chunks and $chunk to notify it's last chunk
2021-07-07 22:07:11 +12:00
$chunks = $chunk = -1;
} else {
2021-07-13 19:58:16 +12:00
// Calculate total number of chunks based on the chunk size i.e ($rangeEnd - $rangeStart)
2021-07-07 22:07:11 +12:00
$chunks = (int) ceil($size / ($end + 1 - $start));
$chunk = (int) ($start / ($end + 1 - $start));
}
}
2021-06-17 22:10:58 +12:00
// Check if file type is allowed (feature for project settings?)
2021-07-07 22:07:11 +12:00
if (!empty($allowedFileExtensions) && !$fileExt->isValid($fileName)) {
2021-06-23 00:12:46 +12:00
throw new Exception('File extension not allowed', 400);
}
2021-06-17 22:10:58 +12:00
2021-07-07 22:07:11 +12:00
if (!$fileSize->isValid($size)) { // Check if file size is exceeding allowed limit
2021-06-17 22:10:58 +12:00
throw new Exception('File size not allowed', 400);
}
$device = Storage::getDevice('files');
2021-07-07 22:07:11 +12:00
if (!$upload->isValid($fileTmpName)) {
2021-06-17 22:10:58 +12:00
throw new Exception('Invalid file', 403);
}
// Save to storage
2021-07-07 22:07:11 +12:00
$size = $size ?? $device->getFileSize($fileTmpName);
2021-07-13 19:58:16 +12:00
$path = $device->getPath($fileId . '.' . \pathinfo($fileName, PATHINFO_EXTENSION));
2021-07-08 23:26:11 +12:00
$path = str_ireplace($device->getRoot(), $device->getRoot() . DIRECTORY_SEPARATOR . $bucket->getId(), $path);
2021-06-17 22:10:58 +12:00
2021-07-13 19:58:16 +12:00
$file = $dbForInternal->getDocument('files', $fileId);
2021-06-17 22:10:58 +12:00
2021-07-07 22:07:11 +12:00
if (!$file->isEmpty()) {
$chunks = $file->getAttribute('totalChunks', 1);
if ($chunk == -1) {
$chunk = $chunks - 1;
2021-06-17 22:10:58 +12:00
}
}
2021-07-13 19:58:16 +12:00
$chunksUploaded = $device->upload($fileTmpName, $path, $chunk, $chunks);
if (empty($chunksUploaded)) {
2021-07-07 22:07:11 +12:00
throw new Exception('Failed uploading file', 500);
2021-06-17 22:10:58 +12:00
}
2021-07-13 20:16:24 +12:00
$read = (is_null($read) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $read ?? [];
$write = (is_null($write) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $write ?? [];
2021-07-13 19:58:16 +12:00
if ($chunksUploaded == $chunks) {
2021-07-07 22:07:11 +12:00
if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled' && $bucket->getAttribute('antiVirus', true) && $size <= APP_LIMIT_ANTIVIRUS) {
$antiVirus = new Network(App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
2021-07-13 20:16:24 +12:00
(int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310));
2021-07-07 22:07:11 +12:00
if (!$antiVirus->fileScan($path)) {
$device->delete($path);
throw new Exception('Invalid file', 403);
}
}
2021-07-13 20:16:24 +12:00
$mimeType = $device->getFileMimeType($path); // Get mime-type before compression and encryption
$data = '';
2021-07-07 22:07:11 +12:00
// Compression
2021-07-13 20:16:24 +12:00
if ($size <= APP_LIMIT_COMPRESSION) {
2021-07-08 23:26:11 +12:00
$data = $device->read($path);
2021-07-13 20:16:24 +12:00
$compressor = new GZIP();
$data = $compressor->compress($data);
}
if ($bucket->getAttribute('encryption', true) && $size <= APP_LIMIT_ENCRYPTION) {
if(empty($data)) {
$data = $device->read($path);
2021-07-08 23:26:11 +12:00
}
2021-07-13 20:16:24 +12:00
$key = App::getEnv('_APP_OPENSSL_KEY_V1');
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag);
}
if(!empty($data)) {
2021-07-08 23:26:11 +12:00
if (!$device->write($path, $data, $mimeType)) {
throw new Exception('Failed to save file', 500);
}
2021-07-07 22:07:11 +12:00
}
$sizeActual = $device->getFileSize($path);
$algorithm = empty($compressor) ? '' : $compressor->getName();
$fileHash = $device->getFileHash($path);
if ($bucket->getAttribute('encryption', true) && $size <= APP_LIMIT_ENCRYPTION) {
$openSSLVersion = '1';
$openSSLCipher = OpenSSL::CIPHER_AES_128_GCM;
$openSSLTag = \bin2hex($tag);
$openSSLIV = \bin2hex($iv);
}
if ($file->isEmpty()) {
2021-07-13 19:58:16 +12:00
$file = $dbForInternal->createDocument('files', new Document([
2021-07-07 22:07:11 +12:00
'$read' => $read,
'$write' => $write,
'dateCreated' => \time(),
'bucketId' => $bucket->getId(),
'name' => $fileName,
'path' => $path,
'signature' => $fileHash,
'mimeType' => $mimeType,
'sizeOriginal' => $size,
'sizeActual' => $sizeActual,
'algorithm' => $algorithm,
'comment' => '',
2021-07-13 19:58:16 +12:00
'chunksTotal' => $chunks,
'chunksUploaded' => $chunksUploaded,
2021-07-07 22:07:11 +12:00
'openSSLVersion' => $openSSLVersion,
'openSSLCipher' => $openSSLCipher,
'openSSLTag' => $openSSLTag,
'openSSLIV' => $openSSLIV,
2021-07-13 19:58:16 +12:00
]));
2021-07-07 22:07:11 +12:00
} else {
2021-07-13 19:58:16 +12:00
$file = $dbForInternal->updateDocument('files', $fileId, $file
2021-07-07 22:07:11 +12:00
->setAttribute('$read', $read)
->setAttribute('$write', $write)
->setAttribute('signature', $fileHash)
->setAttribute('mimeType', $mimeType)
->setAttribute('sizeActual', $sizeActual)
->setAttribute('algorithm', $algorithm)
->setAttribute('openSSLVersion', $openSSLVersion)
->setAttribute('openSSLCipher', $openSSLCipher)
->setAttribute('openSSLTag', $openSSLTag)
->setAttribute('openSSLIV', $openSSLIV)
);
}
} else {
if ($file->isEmpty()) {
2021-07-13 19:58:16 +12:00
$file = $dbForInternal->createDocument('files', new Document([
'$id' => $fileId,
2021-07-13 20:16:24 +12:00
'$read' => $read,
'$write' => $write,
2021-07-07 22:07:11 +12:00
'dateCreated' => \time(),
'bucketId' => $bucket->getId(),
'name' => $fileName,
'path' => $path,
'signature' => '',
'mimeType' => '',
'sizeOriginal' => $size,
'sizeActual' => 0,
'algorithm' => '',
'comment' => '',
2021-07-13 19:58:16 +12:00
'chunksTotal' => $chunks,
'chunksUploaded' => $chunksUploaded,
]));
2021-07-07 22:07:11 +12:00
} else {
2021-07-13 19:58:16 +12:00
$file = $dbForInternal->updateDocument('files', $fileId, $file
->setAttribute('chunksUploaded', $chunksUploaded)
2021-07-07 22:07:11 +12:00
);
}
}
2021-06-17 22:10:58 +12:00
$audits
->setParam('event', 'storage.files.create')
2021-07-07 22:07:11 +12:00
->setParam('resource', 'storage/files/' . $file->getId())
2021-06-17 22:10:58 +12:00
;
2021-07-07 22:07:11 +12:00
if (!empty($sizeActual)) {
$usage
->setParam('storage', $sizeActual)
;
}
2021-06-17 22:10:58 +12:00
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic2($file, Response::MODEL_FILE);
});
2021-06-18 21:24:16 +12:00
App::get('/v1/storage/buckets/:bucketId/files')
->alias('/v1/storage/files', ['bucketId' => 'default'])
2021-06-18 21:24:16 +12:00
->desc('List Files')
->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'listFiles')
->label('sdk.description', '/docs/references/storage/list-files.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FILE_LIST)
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForInternal')
->action(function ($bucketId, $search, $limit, $offset, $orderType, $response, $dbForInternal) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
2021-06-20 22:55:24 +12:00
$bucket = $dbForInternal->getDocument('buckets', $bucketId);
2021-07-07 22:07:11 +12:00
if ($bucket->isEmpty()) {
throw new Exception('Bucket not found', 404);
2021-06-20 22:55:24 +12:00
}
2021-06-18 21:24:16 +12:00
$queries = [new Query('bucketId', Query::TYPE_EQUAL, [$bucketId])];
2021-07-07 22:07:11 +12:00
if ($search) {
2021-06-18 21:24:16 +12:00
$queries[] = [new Query('name', Query::TYPE_SEARCH, [$search])];
}
$response->dynamic2(new Document([
'files' => $dbForInternal->find('files', $queries, $limit, $offset, ['_id'], [$orderType]),
'sum' => $dbForInternal->count('files', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_FILE_LIST);
});
2021-06-18 21:33:00 +12:00
App::get('/v1/storage/buckets/:bucketId/files/:fileId')
->alias('/v1/storage/files/:fileId', ['bucketId' => 'default'])
2021-06-18 21:33:00 +12:00
->desc('Get File')
->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'getFile')
->label('sdk.description', '/docs/references/storage/get-file.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FILE)
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File unique ID.')
->inject('response')
->inject('dbForInternal')
->action(function ($bucketId, $fileId, $response, $dbForInternal) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
$bucket = $dbForInternal->getDocument('buckets', $bucketId);
2021-06-20 22:55:24 +12:00
2021-07-07 22:07:11 +12:00
if ($bucket->isEmpty()) {
throw new Exception('Bucket not found', 404);
2021-06-20 22:55:24 +12:00
}
2021-06-18 21:33:00 +12:00
$file = $dbForInternal->getDocument('files', $fileId);
2021-07-07 22:07:11 +12:00
if ($file->isEmpty() || $file->getAttribute('bucketId') != $bucketId) {
2021-06-18 21:33:00 +12:00
throw new Exception('File not found', 404);
}
$response->dynamic2($file, Response::MODEL_FILE);
});
App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
->alias('/v1/storage/files/:fileId/preview', ['bucketId' => 'default'])
->desc('Get File Preview')
->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'getFilePreview')
->label('sdk.description', '/docs/references/storage/get-file-preview.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE)
->label('sdk.methodType', 'location')
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File unique ID')
->param('width', 0, new Range(0, 4000), 'Resize preview image width, Pass an integer between 0 to 4000.', true)
->param('height', 0, new Range(0, 4000), 'Resize preview image height, Pass an integer between 0 to 4000.', true)
2021-07-05 00:37:00 +12:00
->param('gravity', Image::GRAVITY_CENTER, new WhiteList(Image::getGravityTypes()), 'Image crop gravity. Can be one of ' . implode(",", Image::getGravityTypes()), true)
->param('quality', 100, new Range(0, 100), 'Preview image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->param('borderWidth', 0, new Range(0, 100), 'Preview image border in pixels. Pass an integer between 0 to 100. Defaults to 0.', true)
->param('borderColor', '', new HexColor(), 'Preview image border color. Use a valid HEX color, no # is needed for prefix.', true)
->param('borderRadius', 0, new Range(0, 4000), 'Preview image border radius in pixels. Pass an integer between 0 to 4000.', true)
2021-07-07 22:07:11 +12:00
->param('opacity', 1, new Range(0, 1, Range::TYPE_FLOAT), 'Preview image opacity. Only works with images having an alpha channel (like png). Pass a number between 0 to 1.', true)
->param('rotation', 0, new Range(0, 360), 'Preview image rotation in degrees. Pass an integer between 0 and 360.', true)
->param('background', '', new HexColor(), 'Preview image background color. Only works with transparent images (png). Use a valid HEX color, no # is needed for prefix.', true)
->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true)
->inject('request')
->inject('response')
->inject('project')
->inject('dbForInternal')
->action(function ($bucketId, $fileId, $width, $height, $gravity, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $request, $response, $project, $dbForInternal) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForInternal */
$storage = 'files';
if (!\extension_loaded('imagick')) {
throw new Exception('Imagick extension is missing', 500);
}
if (!Storage::exists($storage)) {
throw new Exception('No such storage device', 400);
}
2021-06-21 20:05:11 +12:00
$bucket = $dbForInternal->getDocument('buckets', $bucketId);
2021-07-07 22:07:11 +12:00
if ($bucket->isEmpty()) {
throw new Exception('Bucket not found', 404);
2021-06-21 20:05:11 +12:00
}
if ((\strpos($request->getAccept(), 'image/webp') === false) && ('webp' == $output)) { // Fallback webp to jpeg when no browser support
$output = 'jpg';
}
$inputs = Config::getParam('storage-inputs');
$outputs = Config::getParam('storage-outputs');
$fileLogos = Config::getParam('storage-logos');
2021-07-07 22:07:11 +12:00
$date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT'; // 45 days cache
$key = \md5($fileId . $width . $height . $quality . $borderWidth . $borderColor . $borderRadius . $opacity . $rotation . $background . $storage . $output);
$file = $dbForInternal->getDocument('files', $fileId);
2021-06-20 23:25:48 +12:00
if ($file->isEmpty() || $file->getAttribute('bucketId') != $bucketId) {
throw new Exception('File not found', 404);
}
$path = $file->getAttribute('path');
$type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION));
$algorithm = $file->getAttribute('algorithm');
$cipher = $file->getAttribute('openSSLCipher');
$mime = $file->getAttribute('mimeType');
if (!\in_array($mime, $inputs)) {
$path = (\array_key_exists($mime, $fileLogos)) ? $fileLogos[$mime] : $fileLogos['default'];
$algorithm = null;
$cipher = null;
$background = (empty($background)) ? 'eceff1' : $background;
$type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION));
2021-07-07 22:07:11 +12:00
$key = \md5($path . $width . $height . $quality . $borderWidth . $borderColor . $borderRadius . $opacity . $rotation . $background . $storage . $output);
}
$compressor = new GZIP();
$device = Storage::getDevice('files');
if (!\file_exists($path)) {
throw new Exception('File not found', 404);
}
2021-07-07 22:07:11 +12:00
$cache = new Cache(new Filesystem(APP_STORAGE_CACHE . '/app-' . $project->getId())); // Limit file number or size
$data = $cache->load($key, 60 * 60 * 24 * 30 * 3/* 3 months */);
if ($data) {
$output = (empty($output)) ? $type : $output;
return $response
->setContentType((\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg'])
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'hit')
->send($data)
;
}
$source = $device->read($path);
if (!empty($cipher)) { // Decrypt
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('openSSLCipher'),
2021-07-07 22:07:11 +12:00
App::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
0,
\hex2bin($file->getAttribute('openSSLIV')),
\hex2bin($file->getAttribute('openSSLTag'))
);
}
if (!empty($algorithm)) {
$source = $compressor->decompress($source);
}
$image = new Image($source);
$image->crop((int) $width, (int) $height, $gravity);
2021-07-07 22:07:11 +12:00
if (!empty($opacity) || $opacity == 0) {
$image->setOpacity($opacity);
}
if (!empty($background)) {
2021-07-07 22:07:11 +12:00
$image->setBackground('#' . $background);
}
2021-07-07 22:07:11 +12:00
if (!empty($borderWidth)) {
$image->setBorder($borderWidth, '#' . $borderColor);
}
if (!empty($borderRadius)) {
$image->setBorderRadius($borderRadius);
}
if (!empty($rotation)) {
$image->setRotation($rotation);
}
$output = (empty($output)) ? $type : $output;
$data = $image->output($output, $quality);
$cache->save($key, $data);
$response
->setContentType($outputs[$output])
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'miss')
->send($data)
;
unset($image);
});
App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
->alias('/v1/storage/files/:fileId/download', ['bucketId' => 'default'])
->desc('Get File for Download')
->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'getFileDownload')
->label('sdk.description', '/docs/references/storage/get-file-download.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', '*/*')
->label('sdk.methodType', 'location')
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File unique ID.')
->inject('response')
->inject('dbForInternal')
2021-07-13 19:49:45 +12:00
->action(function ($bucketId, $fileId, $response, $dbForInternal) {
2021-07-12 22:23:01 +12:00
/** @var Utopia\Swoole\Request $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
$bucket = $dbForInternal->getDocument('buckets', $bucketId);
2021-07-07 22:07:11 +12:00
if ($bucket->isEmpty()) {
throw new Exception('Bucket not found', 404);
}
$file = $dbForInternal->getDocument('files', $fileId);
2021-06-20 23:25:48 +12:00
if ($file->isEmpty() || $file->getAttribute('bucketId') != $bucketId) {
throw new Exception('File not found', 404);
}
$path = $file->getAttribute('path', '');
2021-07-12 22:23:01 +12:00
$device = Storage::getDevice('files');
if (!$device->exists($path)) {
2021-07-07 22:07:11 +12:00
throw new Exception('File not found in ' . $path, 404);
}
2021-07-13 19:49:19 +12:00
$response
->setContentType($file->getAttribute('mimeType'))
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('X-Peak', \memory_get_peak_usage())
->addHeader('Content-Disposition', 'attachment; filename="' . $file->getAttribute('name', '') . '"')
;
2021-07-12 22:23:01 +12:00
if ($device->getFileSize($path) > APP_LIMIT_COMPRESSION) {
$response->addHeader('Content-Length', $device->getFileSize($path));
2021-07-13 19:49:19 +12:00
2021-07-12 22:23:01 +12:00
$handle = fopen($path, 'rb');
while(!feof($handle)) {
$response->chunk(@fread($handle,APP_CHUNK_SIZE), feof($handle));
}
fclose($handle);
2021-07-13 19:49:19 +12:00
return;
}
$source = $device->read($path);
if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('openSSLCipher'),
App::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
0,
\hex2bin($file->getAttribute('openSSLIV')),
\hex2bin($file->getAttribute('openSSLTag'))
);
}
if (!empty($file->getAttribute('algorithm', ''))) {
$compressor = new GZIP();
$source = $compressor->decompress($source);
2021-06-22 20:36:44 +12:00
}
2021-07-13 19:49:19 +12:00
$response->send($source);
});
App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
->alias('/v1/storage/files/:fileId/view', ['bucketId' => 'default'])
->desc('Get File for View')
->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'getFileView')
->label('sdk.description', '/docs/references/storage/get-file-view.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', '*/*')
->label('sdk.methodType', 'location')
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File unique ID.')
->inject('response')
->inject('dbForInternal')
->action(function ($bucketId, $fileId, $response, $dbForInternal) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
$bucket = $dbForInternal->getDocument('buckets', $bucketId);
2021-07-07 22:07:11 +12:00
if ($bucket->isEmpty()) {
throw new Exception('Bucket not found', 404);
}
2021-07-07 22:07:11 +12:00
$file = $dbForInternal->getDocument('files', $fileId);
$mimes = Config::getParam('storage-mimes');
2021-06-20 23:25:48 +12:00
if ($file->isEmpty() || $file->getAttribute('bucketId') != $bucketId) {
throw new Exception('File not found', 404);
}
$path = $file->getAttribute('path', '');
if (!\file_exists($path)) {
2021-07-07 22:07:11 +12:00
throw new Exception('File not found in ' . $path, 404);
}
$compressor = new GZIP();
$device = Storage::getDevice('files');
$contentType = 'text/plain';
if (\in_array($file->getAttribute('mimeType'), $mimes)) {
$contentType = $file->getAttribute('mimeType');
}
2021-07-13 19:49:19 +12:00
$response
->setContentType($contentType)
->addHeader('Content-Security-Policy', 'script-src none;')
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('X-Peak', \memory_get_peak_usage())
;
2021-07-13 19:49:19 +12:00
if ($device->getFileSize($path) > APP_LIMIT_COMPRESSION) { //Compression and chunking cannot both work together
2021-07-12 22:23:01 +12:00
$response
->addHeader('Content-Length',$device->getFileSize($path))
;
$handle = fopen($path, 'rb');
while(!feof($handle)) {
$response->chunk(@fread($handle,APP_CHUNK_SIZE), feof($handle));
}
fclose($handle);
2021-07-13 19:49:19 +12:00
return;
}
2021-07-12 22:23:01 +12:00
2021-07-13 19:49:19 +12:00
$source = $device->read($path);
if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('openSSLCipher'),
App::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
0,
\hex2bin($file->getAttribute('openSSLIV')),
\hex2bin($file->getAttribute('openSSLTag'))
);
2021-07-12 22:23:01 +12:00
}
2021-07-13 19:49:19 +12:00
$output = $compressor->decompress($source);
// Response
$response
->send($output)
;
});
App::put('/v1/storage/buckets/:bucketId/files/:fileId')
->alias('/v1/storage/files/:fileId', ['bucketId' => 'default'])
->desc('Update File')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('event', 'storage.files.update')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'updateFile')
->label('sdk.description', '/docs/references/storage/update-file.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FILE)
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File unique ID.')
->param('read', [], new ArrayList(new Text(64)), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('write', [], new ArrayList(new Text(64)), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->inject('response')
->inject('dbForInternal')
->inject('audits')
->action(function ($bucketId, $fileId, $read, $write, $response, $dbForInternal, $audits) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Appwrite\Event\Event $audits */
$bucket = $dbForInternal->getDocument('buckets', $bucketId);
2021-07-07 22:07:11 +12:00
if ($bucket->isEmpty()) {
throw new Exception('Bucket not found', 404);
}
$file = $dbForInternal->getDocument('files', $fileId);
2021-06-20 23:25:48 +12:00
if ($file->isEmpty() || $file->getAttribute('bucketId') != $bucketId) {
throw new Exception('File not found', 404);
}
$file = $dbForInternal->updateDocument('files', $fileId, $file
->setAttribute('$read', $read)
->setAttribute('$write', $write)
);
$audits
->setParam('event', 'storage.files.update')
2021-07-07 22:07:11 +12:00
->setParam('resource', 'storage/files/' . $file->getId())
;
$response->dynamic2($file, Response::MODEL_FILE);
});
App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
->alias('/v1/storage/files/:fileId', ['bucketId' => 'default'])
->desc('Delete File')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('event', 'storage.files.delete')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'deleteFile')
->label('sdk.description', '/docs/references/storage/delete-file.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File unique ID.')
->inject('response')
->inject('dbForInternal')
->inject('events')
->inject('audits')
->inject('usage')
->action(function ($bucketId, $fileId, $response, $dbForInternal, $events, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $usage */
2021-07-07 22:07:11 +12:00
$bucket = $dbForInternal->getDocument('buckets', $bucketId);
2021-07-07 22:07:11 +12:00
if ($bucket->isEmpty()) {
throw new Exception('Bucket not found', 404);
}
2021-05-03 20:28:31 +12:00
$file = $dbForInternal->getDocument('files', $fileId);
2019-05-09 18:54:39 +12:00
if ($file->isEmpty() || $file->getAttribute('bucketId') != $bucketId) {
2020-06-30 23:09:28 +12:00
throw new Exception('File not found', 404);
}
2019-05-09 18:54:39 +12:00
2020-07-15 09:20:46 +12:00
$device = Storage::getDevice('files');
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ($device->delete($file->getAttribute('path', ''))) {
2021-05-03 20:28:31 +12:00
if (!$dbForInternal->deleteDocument('files', $fileId)) {
2020-06-30 23:09:28 +12:00
throw new Exception('Failed to remove file from DB', 500);
2019-05-09 18:54:39 +12:00
}
2021-07-08 23:26:11 +12:00
} else {
throw new Exception('Failed to delete file from device', 500);
2020-06-30 23:09:28 +12:00
}
2021-07-07 22:07:11 +12:00
2020-07-06 02:19:59 +12:00
$audits
2020-06-30 23:09:28 +12:00
->setParam('event', 'storage.files.delete')
2021-07-07 22:07:11 +12:00
->setParam('resource', 'storage/files/' . $file->getId())
2020-06-30 23:09:28 +12:00
;
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$usage
->setParam('storage', $file->getAttribute('size', 0) * -1)
;
2019-05-09 18:54:39 +12:00
2020-12-07 11:14:57 +13:00
$events
2021-05-03 20:28:31 +12:00
->setParam('eventData', $response->output2($file, Response::MODEL_FILE))
2020-10-31 08:53:27 +13:00
;
2020-06-30 23:09:28 +12:00
$response->noContent();
});