2019-05-09 18:54:39 +12:00
< ? php
2020-06-29 05:31:21 +12:00
use Utopia\App ;
2019-05-09 18:54:39 +12:00
use Utopia\Exception ;
use Utopia\Validator\ArrayList ;
use Utopia\Validator\WhiteList ;
use Utopia\Validator\Range ;
use Utopia\Validator\Text ;
use Utopia\Validator\HexColor ;
use Utopia\Cache\Cache ;
use Utopia\Cache\Adapter\Filesystem ;
2019-05-12 03:30:53 +12:00
use Appwrite\ClamAV\Network ;
2020-03-25 06:56:32 +13:00
use Appwrite\Database\Database ;
2020-10-31 08:53:27 +13:00
use Appwrite\Database\Document ;
2020-03-25 06:56:32 +13:00
use Appwrite\Database\Validator\UID ;
2021-01-22 21:28:33 +13:00
use Utopia\Storage\Storage ;
use Utopia\Storage\Validator\File ;
use Utopia\Storage\Validator\FileSize ;
use Utopia\Storage\Validator\Upload ;
use Utopia\Storage\Compression\Algorithms\GZIP ;
2021-02-20 02:59:46 +13:00
use Utopia\Image\Image ;
2020-03-25 06:56:32 +13:00
use Appwrite\OpenSSL\OpenSSL ;
2020-10-31 08:53:27 +13:00
use Appwrite\Utopia\Response ;
2020-06-30 09:43:34 +12:00
use Utopia\Config\Config ;
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: post ( '/v1/storage/files' )
2020-02-01 11:34:07 +13:00
-> desc ( 'Create File' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'storage' ])
2020-02-01 11:34:07 +13:00
-> label ( 'scope' , 'files.write' )
2020-10-31 08:53:27 +13:00
-> label ( 'event' , 'storage.files.create' )
2020-02-01 11:34:07 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
-> label ( 'sdk.namespace' , 'storage' )
-> label ( 'sdk.method' , 'createFile' )
-> label ( 'sdk.description' , '/docs/references/storage/create-file.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.request.type' , 'multipart/form-data' )
2020-04-11 06:59:14 +12:00
-> label ( 'sdk.methodType' , 'upload' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_CREATED )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_FILE )
2020-09-11 02:40:14 +12:00
-> param ( 'file' , [], new File (), 'Binary file.' , 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.' )
-> 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.' )
2020-12-27 05:48:36 +13:00
-> inject ( 'request' )
-> inject ( 'response' )
-> inject ( 'projectDB' )
-> inject ( 'audits' )
-> inject ( 'usage' )
-> action ( function ( $file , $read , $write , $request , $response , $projectDB , $audits , $usage ) {
2020-10-30 02:07:56 +13:00
/** @var Utopia\Swoole\Request $request */
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 09:43:34 +12:00
/** @var Appwrite\Database\Database $projectDB */
2020-07-06 02:19:59 +12:00
/** @var Appwrite\Event\Event $audits */
2020-06-30 09:43:34 +12:00
/** @var Appwrite\Event\Event $usage */
$file = $request -> getFiles ( 'file' );
/*
2020-11-21 10:24:39 +13:00
* Validators
*/
2020-06-30 09:43:34 +12:00
//$fileType = new FileType(array(FileType::FILE_TYPE_PNG, FileType::FILE_TYPE_GIF, FileType::FILE_TYPE_JPEG));
$fileSize = new FileSize ( App :: getEnv ( '_APP_STORAGE_LIMIT' , 0 ));
$upload = new Upload ();
if ( empty ( $file )) {
throw new Exception ( 'No file sent' , 400 );
}
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
// Make sure we handle a single file and multiple files the same way
$file [ 'name' ] = ( \is_array ( $file [ 'name' ]) && isset ( $file [ 'name' ][ 0 ])) ? $file [ 'name' ][ 0 ] : $file [ 'name' ];
$file [ 'tmp_name' ] = ( \is_array ( $file [ 'tmp_name' ]) && isset ( $file [ 'tmp_name' ][ 0 ])) ? $file [ 'tmp_name' ][ 0 ] : $file [ 'tmp_name' ];
$file [ 'size' ] = ( \is_array ( $file [ 'size' ]) && isset ( $file [ 'size' ][ 0 ])) ? $file [ 'size' ][ 0 ] : $file [ 'size' ];
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
// Check if file type is allowed (feature for project settings?)
//if (!$fileType->isValid($file['tmp_name'])) {
//throw new Exception('File type not allowed', 400);
//}
2020-02-01 11:34:07 +13:00
2020-07-11 16:17:45 +12:00
if ( ! $fileSize -> isValid ( $file [ 'size' ])) { // Check if file size is exceeding allowed limit
2020-06-30 09:43:34 +12:00
throw new Exception ( 'File size not allowed' , 400 );
}
2020-02-01 11:34:07 +13:00
2020-07-15 09:20:46 +12:00
$device = Storage :: getDevice ( 'files' );
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
if ( ! $upload -> isValid ( $file [ 'tmp_name' ])) {
throw new Exception ( 'Invalid file' , 403 );
}
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
// Save to storage
$size = $device -> getFileSize ( $file [ 'tmp_name' ]);
$path = $device -> getPath ( \uniqid () . '.' . \pathinfo ( $file [ 'name' ], PATHINFO_EXTENSION ));
if ( ! $device -> upload ( $file [ 'tmp_name' ], $path )) { // TODO deprecate 'upload' and replace with 'move'
throw new Exception ( 'Failed moving file' , 500 );
}
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
$mimeType = $device -> getFileMimeType ( $path ); // Get mime-type before compression and encryption
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
if ( App :: getEnv ( '_APP_STORAGE_ANTIVIRUS' ) === 'enabled' ) { // Check if scans are enabled
2021-01-03 08:45:59 +13:00
$antiVirus = new Network ( App :: getEnv ( '_APP_STORAGE_ANTIVIRUS_HOST' , 'clamav' ),
( int ) App :: getEnv ( '_APP_STORAGE_ANTIVIRUS_PORT' , 3310 ));
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
if ( ! $antiVirus -> fileScan ( $path )) {
$device -> delete ( $path );
throw new Exception ( 'Invalid file' , 403 );
2020-02-01 11:34:07 +13:00
}
2020-06-30 09:43:34 +12:00
}
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
// Compression
$compressor = new GZIP ();
$data = $device -> read ( $path );
$data = $compressor -> compress ( $data );
$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 );
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
if ( ! $device -> write ( $path , $data )) {
throw new Exception ( 'Failed to save file' , 500 );
}
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
$sizeActual = $device -> getFileSize ( $path );
$file = $projectDB -> createDocument ([
'$collection' => Database :: SYSTEM_COLLECTION_FILES ,
'$permissions' => [
'read' => $read ,
'write' => $write ,
],
'dateCreated' => \time (),
'folderId' => '' ,
'name' => $file [ 'name' ],
'path' => $path ,
'signature' => $device -> getFileHash ( $path ),
'mimeType' => $mimeType ,
'sizeOriginal' => $size ,
'sizeActual' => $sizeActual ,
'algorithm' => $compressor -> getName (),
'token' => \bin2hex ( \random_bytes ( 64 )),
'comment' => '' ,
'fileOpenSSLVersion' => '1' ,
'fileOpenSSLCipher' => OpenSSL :: CIPHER_AES_128_GCM ,
'fileOpenSSLTag' => \bin2hex ( $tag ),
'fileOpenSSLIV' => \bin2hex ( $iv ),
]);
if ( false === $file ) {
throw new Exception ( 'Failed saving file to DB' , 500 );
}
2020-02-01 11:34:07 +13:00
2020-07-06 02:19:59 +12:00
$audits
2020-06-30 09:43:34 +12:00
-> setParam ( 'event' , 'storage.files.create' )
-> setParam ( 'resource' , 'storage/files/' . $file -> getId ())
;
$usage
-> setParam ( 'storage' , $sizeActual )
;
2020-10-31 21:42:41 +13:00
$response
-> setStatusCode ( Response :: STATUS_CODE_CREATED )
-> dynamic ( $file , Response :: MODEL_FILE )
;
2020-12-27 05:48:36 +13:00
});
2020-02-01 11:34:07 +13:00
2020-06-29 05:31:21 +12:00
App :: get ( '/v1/storage/files' )
2019-05-09 18:54:39 +12:00
-> desc ( 'List Files' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'storage' ])
2019-06-09 01:13:19 +12:00
-> label ( 'scope' , 'files.read' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'storage' )
2020-01-31 09:58:49 +13:00
-> label ( 'sdk.method' , 'listFiles' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/storage/list-files.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_FILE_LIST )
2020-09-11 02:40:14 +12:00
-> 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 )
2020-12-27 05:48:36 +13:00
-> inject ( 'response' )
-> inject ( 'projectDB' )
2020-06-30 09:43:34 +12:00
-> action ( function ( $search , $limit , $offset , $orderType , $response , $projectDB ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 09:43:34 +12:00
/** @var Appwrite\Database\Database $projectDB */
$results = $projectDB -> getCollection ([
'limit' => $limit ,
'offset' => $offset ,
'orderType' => $orderType ,
'search' => $search ,
'filters' => [
'$collection=' . Database :: SYSTEM_COLLECTION_FILES ,
],
]);
2020-10-31 08:53:27 +13:00
$response -> dynamic ( new Document ([
'sum' => $projectDB -> getSum (),
'files' => $results
]), Response :: MODEL_FILE_LIST );
2020-12-27 05:48:36 +13:00
});
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: get ( '/v1/storage/files/:fileId' )
2019-05-09 18:54:39 +12:00
-> desc ( 'Get File' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'storage' ])
2019-06-09 01:13:19 +12:00
-> label ( 'scope' , 'files.read' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'storage' )
2020-01-31 09:58:49 +13:00
-> label ( 'sdk.method' , 'getFile' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/storage/get-file.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_FILE )
2020-09-11 02:40:14 +12:00
-> param ( 'fileId' , '' , new UID (), 'File unique ID.' )
2020-12-27 05:48:36 +13:00
-> inject ( 'response' )
-> inject ( 'projectDB' )
2020-06-30 09:43:34 +12:00
-> action ( function ( $fileId , $response , $projectDB ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 09:43:34 +12:00
/** @var Appwrite\Database\Database $projectDB */
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
$file = $projectDB -> getDocument ( $fileId );
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if ( empty ( $file -> getId ()) || Database :: SYSTEM_COLLECTION_FILES != $file -> getCollection ()) {
throw new Exception ( 'File not found' , 404 );
2019-05-09 18:54:39 +12:00
}
2020-06-30 09:43:34 +12:00
2020-10-31 08:53:27 +13:00
$response -> dynamic ( $file , Response :: MODEL_FILE );
2020-12-27 05:48:36 +13:00
});
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: get ( '/v1/storage/files/:fileId/preview' )
2019-08-25 20:10:28 +12:00
-> desc ( 'Get File Preview' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'storage' ])
2019-06-09 01:13:19 +12:00
-> label ( 'scope' , 'files.read' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'storage' )
2020-01-31 09:58:49 +13:00
-> label ( 'sdk.method' , 'getFilePreview' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/storage/get-file-preview.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
2020-11-12 11:02:02 +13:00
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_IMAGE )
2020-04-11 06:59:14 +12:00
-> label ( 'sdk.methodType' , 'location' )
2020-09-11 02:40:14 +12:00
-> 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 )
-> param ( 'quality' , 100 , new Range ( 0 , 100 ), 'Preview image quality. Pass an integer between 0 to 100. Defaults to 100.' , 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 )
2020-12-27 05:48:36 +13:00
-> inject ( 'request' )
-> inject ( 'response' )
-> inject ( 'project' )
-> inject ( 'projectDB' )
2020-06-30 23:09:28 +12:00
-> action ( function ( $fileId , $width , $height , $quality , $background , $output , $request , $response , $project , $projectDB ) {
2020-10-30 02:07:56 +13:00
/** @var Utopia\Swoole\Request $request */
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 23:09:28 +12:00
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Database $projectDB */
2019-05-09 18:54:39 +12:00
2020-07-15 09:20:46 +12:00
$storage = 'files' ;
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( ! \extension_loaded ( 'imagick' )) {
throw new Exception ( 'Imagick extension is missing' , 500 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( ! Storage :: exists ( $storage )) {
throw new Exception ( 'No such storage device' , 400 );
}
2020-07-04 03:14:51 +12:00
if (( \strpos ( $request -> getAccept (), 'image/webp' ) === false ) && ( 'webp' == $output )) { // Fallback webp to jpeg when no browser support
2020-06-30 23:09:28 +12:00
$output = 'jpg' ;
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$inputs = Config :: getParam ( 'storage-inputs' );
$outputs = Config :: getParam ( 'storage-outputs' );
$fileLogos = Config :: getParam ( 'storage-logos' );
2020-06-30 16:32:36 +12:00
2020-06-30 23:09:28 +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 . $background . $storage . $output );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$file = $projectDB -> getDocument ( $fileId );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( empty ( $file -> getId ()) || Database :: SYSTEM_COLLECTION_FILES != $file -> getCollection ()) {
throw new Exception ( 'File not found' , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$path = $file -> getAttribute ( 'path' );
$type = \strtolower ( \pathinfo ( $path , PATHINFO_EXTENSION ));
$algorithm = $file -> getAttribute ( 'algorithm' );
$cipher = $file -> getAttribute ( 'fileOpenSSLCipher' );
$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 ;
2020-06-20 23:20:49 +12:00
$type = \strtolower ( \pathinfo ( $path , PATHINFO_EXTENSION ));
2020-06-30 23:09:28 +12:00
$key = \md5 ( $path . $width . $height . $quality . $background . $storage . $output );
}
2019-05-12 16:56:55 +12:00
2020-06-30 23:09:28 +12:00
$compressor = new GZIP ();
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 ( ! \file_exists ( $path )) {
throw new Exception ( 'File not found' , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +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 */ );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( $data ) {
$output = ( empty ( $output )) ? $type : $output ;
2019-05-09 18:54:39 +12:00
2020-07-03 15:11:16 +12:00
return $response
2020-07-03 21:11:19 +12:00
-> setContentType (( \array_key_exists ( $output , $outputs )) ? $outputs [ $output ] : $outputs [ 'jpg' ])
2020-06-30 23:09:28 +12:00
-> addHeader ( 'Expires' , $date )
-> addHeader ( 'X-Appwrite-Cache' , 'hit' )
-> send ( $data )
;
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$source = $device -> read ( $path );
if ( ! empty ( $cipher )) { // Decrypt
$source = OpenSSL :: decrypt (
$source ,
$file -> getAttribute ( 'fileOpenSSLCipher' ),
App :: getEnv ( '_APP_OPENSSL_KEY_V' . $file -> getAttribute ( 'fileOpenSSLVersion' )),
0 ,
\hex2bin ( $file -> getAttribute ( 'fileOpenSSLIV' )),
\hex2bin ( $file -> getAttribute ( 'fileOpenSSLTag' ))
);
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( ! empty ( $algorithm )) {
$source = $compressor -> decompress ( $source );
}
2019-05-09 18:54:39 +12:00
2021-02-20 02:59:46 +13:00
$image = new Image ( $source );
2019-05-09 18:54:39 +12:00
2021-02-20 02:59:46 +13:00
$image -> crop (( int ) $width , ( int ) $height );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( ! empty ( $background )) {
2021-02-20 02:59:46 +13:00
$image -> setBackground ( '#' . $background );
2020-06-30 23:09:28 +12:00
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$output = ( empty ( $output )) ? $type : $output ;
2019-05-09 18:54:39 +12:00
2021-02-20 02:59:46 +13:00
$data = $image -> output ( $output , $quality );
2020-07-03 15:11:16 +12:00
$cache -> save ( $key , $data );
2020-06-30 23:09:28 +12:00
$response
-> setContentType ( $outputs [ $output ])
-> addHeader ( 'Expires' , $date )
-> addHeader ( 'X-Appwrite-Cache' , 'miss' )
2020-07-03 15:11:16 +12:00
-> send ( $data )
2020-06-30 23:09:28 +12:00
;
2019-05-09 18:54:39 +12:00
2021-02-20 02:59:46 +13:00
unset ( $image );
2020-12-27 05:48:36 +13:00
});
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: get ( '/v1/storage/files/:fileId/download' )
2019-08-25 20:10:28 +12:00
-> desc ( 'Get File for Download' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'storage' ])
2019-06-09 01:13:19 +12:00
-> label ( 'scope' , 'files.read' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'storage' )
2020-01-31 09:58:49 +13:00
-> label ( 'sdk.method' , 'getFileDownload' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/storage/get-file-download.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
2020-11-12 18:12:25 +13:00
-> label ( 'sdk.response.type' , '*/*' )
2020-04-11 06:59:14 +12:00
-> label ( 'sdk.methodType' , 'location' )
2020-09-11 02:40:14 +12:00
-> param ( 'fileId' , '' , new UID (), 'File unique ID.' )
2020-12-27 05:48:36 +13:00
-> inject ( 'response' )
-> inject ( 'projectDB' )
2020-06-30 23:09:28 +12:00
-> action ( function ( $fileId , $response , $projectDB ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 23:09:28 +12:00
/** @var Appwrite\Database\Database $projectDB */
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$file = $projectDB -> getDocument ( $fileId );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( empty ( $file -> getId ()) || Database :: SYSTEM_COLLECTION_FILES != $file -> getCollection ()) {
throw new Exception ( 'File not found' , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$path = $file -> getAttribute ( 'path' , '' );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( ! \file_exists ( $path )) {
throw new Exception ( 'File not found in ' . $path , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$compressor = new GZIP ();
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
$source = $device -> read ( $path );
if ( ! empty ( $file -> getAttribute ( 'fileOpenSSLCipher' ))) { // Decrypt
$source = OpenSSL :: decrypt (
$source ,
$file -> getAttribute ( 'fileOpenSSLCipher' ),
App :: getEnv ( '_APP_OPENSSL_KEY_V' . $file -> getAttribute ( 'fileOpenSSLVersion' )),
0 ,
\hex2bin ( $file -> getAttribute ( 'fileOpenSSLIV' )),
\hex2bin ( $file -> getAttribute ( 'fileOpenSSLTag' ))
);
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$source = $compressor -> decompress ( $source );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
// Response
$response
-> setContentType ( $file -> getAttribute ( 'mimeType' ))
-> addHeader ( 'Content-Disposition' , 'attachment; 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 ())
-> send ( $source )
;
2020-12-27 05:48:36 +13:00
});
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: get ( '/v1/storage/files/:fileId/view' )
2019-08-25 20:10:28 +12:00
-> desc ( 'Get File for View' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'storage' ])
2019-06-09 01:13:19 +12:00
-> label ( 'scope' , 'files.read' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'storage' )
2020-01-31 09:58:49 +13:00
-> label ( 'sdk.method' , 'getFileView' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/storage/get-file-view.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
2020-11-12 18:12:25 +13:00
-> label ( 'sdk.response.type' , '*/*' )
2020-04-11 06:59:14 +12:00
-> label ( 'sdk.methodType' , 'location' )
2020-09-11 02:40:14 +12:00
-> param ( 'fileId' , '' , new UID (), 'File unique ID.' )
2020-12-27 05:48:36 +13:00
-> inject ( 'response' )
-> inject ( 'projectDB' )
2020-12-30 08:43:53 +13:00
-> action ( function ( $fileId , $response , $projectDB ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 23:09:28 +12:00
/** @var Appwrite\Database\Database $projectDB */
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$file = $projectDB -> getDocument ( $fileId );
$mimes = Config :: getParam ( 'storage-mimes' );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( empty ( $file -> getId ()) || Database :: SYSTEM_COLLECTION_FILES != $file -> getCollection ()) {
throw new Exception ( 'File not found' , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$path = $file -> getAttribute ( 'path' , '' );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( ! \file_exists ( $path )) {
throw new Exception ( 'File not found in ' . $path , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$compressor = new GZIP ();
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
$contentType = 'text/plain' ;
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( \in_array ( $file -> getAttribute ( 'mimeType' ), $mimes )) {
$contentType = $file -> getAttribute ( 'mimeType' );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$source = $device -> read ( $path );
if ( ! empty ( $file -> getAttribute ( 'fileOpenSSLCipher' ))) { // Decrypt
$source = OpenSSL :: decrypt (
$source ,
$file -> getAttribute ( 'fileOpenSSLCipher' ),
App :: getEnv ( '_APP_OPENSSL_KEY_V' . $file -> getAttribute ( 'fileOpenSSLVersion' )),
0 ,
\hex2bin ( $file -> getAttribute ( 'fileOpenSSLIV' )),
\hex2bin ( $file -> getAttribute ( 'fileOpenSSLTag' ))
);
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$output = $compressor -> decompress ( $source );
$fileName = $file -> getAttribute ( 'name' , '' );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
// Response
$response
-> setContentType ( $contentType )
-> addHeader ( 'Content-Security-Policy' , 'script-src none;' )
-> addHeader ( 'X-Content-Type-Options' , 'nosniff' )
-> addHeader ( 'Content-Disposition' , 'inline; filename="' . $fileName . '"' )
-> 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 ())
-> send ( $output )
;
2020-12-27 05:48:36 +13:00
});
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: put ( '/v1/storage/files/:fileId' )
2019-08-29 00:29:45 +12:00
-> desc ( 'Update File' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'storage' ])
2019-08-29 00:29:45 +12:00
-> label ( 'scope' , 'files.write' )
2020-10-31 08:53:27 +13:00
-> label ( 'event' , 'storage.files.update' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
2019-08-29 00:29:45 +12:00
-> label ( 'sdk.namespace' , 'storage' )
2020-01-31 09:58:49 +13:00
-> label ( 'sdk.method' , 'updateFile' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/storage/update-file.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_FILE )
2020-09-11 02:40:14 +12:00
-> 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.' )
2020-12-27 05:48:36 +13:00
-> inject ( 'response' )
-> inject ( 'projectDB' )
-> inject ( 'audits' )
2020-10-31 08:53:27 +13:00
-> action ( function ( $fileId , $read , $write , $response , $projectDB , $audits ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 23:09:28 +12:00
/** @var Appwrite\Database\Database $projectDB */
2020-07-06 02:19:59 +12:00
/** @var Appwrite\Event\Event $audits */
2019-08-29 00:29:45 +12:00
2020-06-30 23:09:28 +12:00
$file = $projectDB -> getDocument ( $fileId );
2019-08-29 00:29:45 +12:00
2020-06-30 23:09:28 +12:00
if ( empty ( $file -> getId ()) || Database :: SYSTEM_COLLECTION_FILES != $file -> getCollection ()) {
throw new Exception ( 'File not found' , 404 );
}
2019-08-29 00:29:45 +12:00
2020-06-30 23:09:28 +12:00
$file = $projectDB -> updateDocument ( \array_merge ( $file -> getArrayCopy (), [
'$permissions' => [
'read' => $read ,
'write' => $write ,
],
'folderId' => '' ,
]));
2019-08-29 00:29:45 +12:00
2020-06-30 23:09:28 +12:00
if ( false === $file ) {
throw new Exception ( 'Failed saving file to DB' , 500 );
}
2020-01-05 04:45:28 +13:00
2020-07-06 02:19:59 +12:00
$audits
2020-06-30 23:09:28 +12:00
-> setParam ( 'event' , 'storage.files.update' )
-> setParam ( 'resource' , 'storage/files/' . $file -> getId ())
;
2020-10-31 08:53:27 +13:00
$response -> dynamic ( $file , Response :: MODEL_FILE );
2020-12-27 05:48:36 +13:00
});
2019-08-29 00:29:45 +12:00
2020-06-29 05:31:21 +12:00
App :: delete ( '/v1/storage/files/:fileId' )
2019-05-09 18:54:39 +12:00
-> desc ( 'Delete File' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'storage' ])
2019-08-14 23:53:07 +12:00
-> label ( 'scope' , 'files.write' )
2020-10-31 08:53:27 +13:00
-> label ( 'event' , 'storage.files.delete' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'storage' )
2020-01-31 09:58:49 +13:00
-> label ( 'sdk.method' , 'deleteFile' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/storage/delete-file.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_NOCONTENT )
-> label ( 'sdk.response.model' , Response :: MODEL_NONE )
2020-09-11 02:40:14 +12:00
-> param ( 'fileId' , '' , new UID (), 'File unique ID.' )
2020-12-27 05:48:36 +13:00
-> inject ( 'response' )
-> inject ( 'projectDB' )
-> inject ( 'events' )
-> inject ( 'audits' )
-> inject ( 'usage' )
2020-12-07 11:14:57 +13:00
-> action ( function ( $fileId , $response , $projectDB , $events , $audits , $usage ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 23:09:28 +12:00
/** @var Appwrite\Database\Database $projectDB */
2020-12-07 11:14:57 +13:00
/** @var Appwrite\Event\Event $events */
2020-07-06 02:19:59 +12:00
/** @var Appwrite\Event\Event $audits */
2020-06-30 23:09:28 +12:00
/** @var Appwrite\Event\Event $usage */
$file = $projectDB -> getDocument ( $fileId );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( empty ( $file -> getId ()) || Database :: SYSTEM_COLLECTION_FILES != $file -> getCollection ()) {
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' , '' ))) {
if ( ! $projectDB -> deleteDocument ( $fileId )) {
throw new Exception ( 'Failed to remove file from DB' , 500 );
2019-05-09 18:54:39 +12:00
}
2020-06-30 23:09:28 +12:00
}
2020-10-31 08:53:27 +13:00
2020-07-06 02:19:59 +12:00
$audits
2020-06-30 23:09:28 +12:00
-> setParam ( 'event' , 'storage.files.delete' )
-> setParam ( 'resource' , 'storage/files/' . $file -> getId ())
;
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
2020-10-31 08:53:27 +13:00
-> setParam ( 'payload' , $response -> output ( $file , Response :: MODEL_FILE ))
;
2021-03-19 09:32:16 +13:00
$response -> dynamic ( new Document (), Response :: MODEL_NONE );
2020-12-27 05:48:36 +13:00
});
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
// App::get('/v1/storage/files/:fileId/scan')
2020-05-26 01:12:59 +12:00
// ->desc('Scan Storage')
2020-06-26 06:32:12 +12:00
// ->groups(['api', 'storage'])
2020-05-26 01:12:59 +12:00
// ->label('scope', 'god')
// ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
// ->label('sdk.namespace', 'storage')
// ->label('sdk.method', 'getFileScan')
// ->label('sdk.hide', true)
2020-09-11 02:40:14 +12:00
// ->param('fileId', '', new UID(), 'File unique ID.')
// ->param('storage', 'files', new WhiteList(['files']);})
2020-05-26 01:12:59 +12:00
// ->action(
// function ($fileId, $storage) use ($response, $request, $projectDB) {
// $file = $projectDB->getDocument($fileId);
// if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
// throw new Exception('File not found', 404);
// }
// $path = $file->getAttribute('path', '');
// if (!file_exists($path)) {
// throw new Exception('File not found in '.$path, 404);
// }
// $compressor = new GZIP();
// $device = Storage::getDevice($storage);
// $source = $device->read($path);
// if (!empty($file->getAttribute('fileOpenSSLCipher'))) { // Decrypt
// $source = OpenSSL::decrypt(
// $source,
// $file->getAttribute('fileOpenSSLCipher'),
2020-06-29 08:45:36 +12:00
// App::getEnv('_APP_OPENSSL_KEY_V'.$file->getAttribute('fileOpenSSLVersion')),
2020-05-26 01:12:59 +12:00
// 0,
// hex2bin($file->getAttribute('fileOpenSSLIV')),
// hex2bin($file->getAttribute('fileOpenSSLTag'))
// );
// }
// $source = $compressor->decompress($source);
// $antiVirus = new Network('clamav', 3310);
// //var_dump($antiVirus->ping());
// //var_dump($antiVirus->version());
// //var_dump($antiVirus->fileScan('/storage/uploads/app-1/5/9/f/e/59fecaed49645.pdf'));
// }
// );