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 ;
2021-05-03 20:28:31 +12:00
use Utopia\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 ;
2021-05-27 22:09:14 +12:00
use Utopia\Database\Query ;
2021-05-20 18:42:20 +12:00
use Utopia\Validator\Numeric ;
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' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
2020-02-01 11:34:07 +13:00
-> 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 )
2021-03-22 11:17:20 +13:00
-> 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 )
2020-12-27 05:48:36 +13:00
-> inject ( 'request' )
-> inject ( 'response' )
2021-05-03 20:28:31 +12:00
-> inject ( 'dbForInternal' )
2021-03-22 11:17:20 +13:00
-> inject ( 'user' )
2020-12-27 05:48:36 +13:00
-> inject ( 'audits' )
-> inject ( 'usage' )
2021-05-03 20:28:31 +12:00
-> action ( function ( $file , $read , $write , $request , $response , $dbForInternal , $user , $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 */
2021-05-03 20:28:31 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2021-03-22 11:17:20 +13:00
/** @var Appwrite\Database\Document $user */
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
2021-03-22 19:54:42 +13:00
if ( ! $device -> write ( $path , $data , $mimeType )) {
2020-06-30 09:43:34 +12:00
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 );
2021-05-03 20:28:31 +12:00
$file = $dbForInternal -> createDocument ( 'files' , new Document ([
'$read' => ( is_null ( $read ) && ! $user -> isEmpty ()) ? [ 'user:' . $user -> getId ()] : $read ? ? [], // By default set read permissions for user
'$write' => ( is_null ( $write ) && ! $user -> isEmpty ()) ? [ 'user:' . $user -> getId ()] : $write ? ? [], // By default set write permissions for user
2020-06-30 09:43:34 +12:00
'dateCreated' => \time (),
2021-05-03 20:28:31 +12:00
'bucketId' => '' ,
2020-06-30 09:43:34 +12:00
'name' => $file [ 'name' ],
'path' => $path ,
'signature' => $device -> getFileHash ( $path ),
'mimeType' => $mimeType ,
'sizeOriginal' => $size ,
'sizeActual' => $sizeActual ,
'algorithm' => $compressor -> getName (),
'comment' => '' ,
2021-05-03 20:28:31 +12:00
'openSSLVersion' => '1' ,
'openSSLCipher' => OpenSSL :: CIPHER_AES_128_GCM ,
'openSSLTag' => \bin2hex ( $tag ),
'openSSLIV' => \bin2hex ( $iv ),
]));
2020-06-30 09:43:34 +12: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 )
;
2021-05-27 22:09:14 +12:00
$response -> setStatusCode ( Response :: STATUS_CODE_CREATED );
$response -> dynamic2 ( $file , Response :: MODEL_FILE );
2020-10-31 21:42:41 +13:00
;
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' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
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' )
2021-05-03 20:28:31 +12:00
-> inject ( 'dbForInternal' )
-> action ( function ( $search , $limit , $offset , $orderType , $response , $dbForInternal ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2021-05-03 20:28:31 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2021-05-27 22:09:14 +12:00
$queries = ( $search ) ? [ new Query ( 'name' , Query :: TYPE_SEARCH , $search )] : [];
2021-05-03 20:28:31 +12:00
$response -> dynamic2 ( new Document ([
2021-05-27 22:09:14 +12:00
'files' => $dbForInternal -> find ( 'files' , $queries , $limit , $offset , [ '_id' ], [ $orderType ]),
'sum' => $dbForInternal -> count ( 'files' , $queries , APP_LIMIT_COUNT ),
2020-10-31 08:53:27 +13:00
]), 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' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
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' )
2021-05-03 20:28:31 +12:00
-> inject ( 'dbForInternal' )
-> action ( function ( $fileId , $response , $dbForInternal ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2021-05-03 20:28:31 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2019-05-09 18:54:39 +12:00
2021-05-03 20:28:31 +12:00
$file = $dbForInternal -> getDocument ( 'files' , $fileId );
2019-05-09 18:54:39 +12:00
2021-05-03 20:28:31 +12:00
if ( empty ( $file -> getId ())) {
2020-06-30 09:43:34 +12:00
throw new Exception ( 'File not found' , 404 );
2019-05-09 18:54:39 +12:00
}
2020-06-30 09:43:34 +12:00
2021-05-03 20:28:31 +12:00
$response -> dynamic2 ( $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' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
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 )
2021-04-04 20:40:03 +12:00
-> param ( 'borderWidth' , 0 , new Range ( 0 , 100 ), 'Preview image border in pixels. Pass an integer between 0 to 100. Defaults to 0.' , true )
2021-03-22 19:54:42 +13:00
-> param ( 'borderColor' , '' , new HexColor (), 'Preview image border color. Use a valid HEX color, no # is needed for prefix.' , true )
2021-04-04 20:40:03 +12:00
-> param ( 'borderRadius' , 0 , new Range ( 0 , 4000 ), 'Preview image border radius in pixels. Pass an integer between 0 to 4000.' , true )
2021-05-21 18:57:43 +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 )
2021-04-04 20:40:03 +12:00
-> param ( 'rotation' , 0 , new Range ( 0 , 360 ), 'Preview image rotation in degrees. Pass an integer between 0 and 360.' , true )
2020-09-11 02:40:14 +12:00
-> 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' )
2021-05-03 20:28:31 +12:00
-> inject ( 'dbForInternal' )
-> action ( function ( $fileId , $width , $height , $quality , $borderWidth , $borderColor , $borderRadius , $opacity , $rotation , $background , $output , $request , $response , $project , $dbForInternal ) {
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 */
2021-05-03 20:28:31 +12:00
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForInternal */
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
2021-04-08 22:57:30 +12:00
$key = \md5 ( $fileId . $width . $height . $quality . $borderWidth . $borderColor . $borderRadius . $opacity . $rotation . $background . $storage . $output );
2019-05-09 18:54:39 +12:00
2021-05-03 20:28:31 +12:00
$file = $dbForInternal -> getDocument ( 'files' , $fileId );
2019-05-09 18:54:39 +12:00
2021-05-03 20:28:31 +12:00
if ( empty ( $file -> getId ())) {
2020-06-30 23:09:28 +12:00
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' );
2021-05-03 20:28:31 +12:00
$cipher = $file -> getAttribute ( 'openSSLCipher' );
2020-06-30 23:09:28 +12:00
$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 ));
2021-04-08 22:57:30 +12:00
$key = \md5 ( $path . $width . $height . $quality . $borderWidth . $borderColor . $borderRadius . $opacity . $rotation . $background . $storage . $output );
2020-06-30 23:09:28 +12:00
}
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 ,
2021-05-03 20:28:31 +12:00
$file -> getAttribute ( 'openSSLCipher' ),
App :: getEnv ( '_APP_OPENSSL_KEY_V' . $file -> getAttribute ( 'openSSLVersion' )),
2020-06-30 23:09:28 +12:00
0 ,
2021-05-03 20:28:31 +12:00
\hex2bin ( $file -> getAttribute ( 'openSSLIV' )),
\hex2bin ( $file -> getAttribute ( 'openSSLTag' ))
2020-06-30 23:09:28 +12:00
);
}
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 );
2021-04-09 19:42:45 +12:00
2021-04-09 19:56:49 +12:00
if ( ! empty ( $opacity ) || $opacity == 0 ) {
2021-04-09 19:42:45 +12:00
$image -> setOpacity ( $opacity );
}
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
2021-04-09 19:04:46 +12:00
if ( ! empty ( $borderWidth ) ) {
$image -> setBorder ( $borderWidth , '#' . $borderColor );
}
2019-05-09 18:54:39 +12:00
2021-03-22 19:54:42 +13:00
if ( ! empty ( $borderRadius )) {
$image -> setBorderRadius ( $borderRadius );
}
if ( ! empty ( $rotation )) {
$image -> setRotation ( $rotation );
}
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' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
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' )
2021-05-03 20:28:31 +12:00
-> inject ( 'dbForInternal' )
-> action ( function ( $fileId , $response , $dbForInternal ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2021-05-03 20:28:31 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2019-05-09 18:54:39 +12:00
2021-05-03 20:28:31 +12:00
$file = $dbForInternal -> getDocument ( 'files' , $fileId );
2019-05-09 18:54:39 +12:00
2021-05-03 20:28:31 +12:00
if ( empty ( $file -> getId ())) {
2020-06-30 23:09:28 +12:00
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 );
2021-05-03 20:28:31 +12:00
if ( ! empty ( $file -> getAttribute ( 'openSSLCipher' ))) { // Decrypt
2020-06-30 23:09:28 +12:00
$source = OpenSSL :: decrypt (
$source ,
2021-05-03 20:28:31 +12:00
$file -> getAttribute ( 'openSSLCipher' ),
App :: getEnv ( '_APP_OPENSSL_KEY_V' . $file -> getAttribute ( 'openSSLVersion' )),
2020-06-30 23:09:28 +12:00
0 ,
2021-05-03 20:28:31 +12:00
\hex2bin ( $file -> getAttribute ( 'openSSLIV' )),
\hex2bin ( $file -> getAttribute ( 'openSSLTag' ))
2020-06-30 23:09:28 +12:00
);
}
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' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
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' )
2021-05-03 20:28:31 +12:00
-> inject ( 'dbForInternal' )
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 */
2021-05-03 20:28:31 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2019-05-09 18:54:39 +12:00
2021-05-03 20:28:31 +12:00
$file = $projectDB -> getDocument ( 'files' , $fileId );
2020-06-30 23:09:28 +12:00
$mimes = Config :: getParam ( 'storage-mimes' );
2019-05-09 18:54:39 +12:00
2021-05-03 20:28:31 +12:00
if ( empty ( $file -> getId ())) {
2020-06-30 23:09:28 +12:00
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 );
2021-05-03 20:28:31 +12:00
if ( ! empty ( $file -> getAttribute ( 'openSSLCipher' ))) { // Decrypt
2020-06-30 23:09:28 +12:00
$source = OpenSSL :: decrypt (
$source ,
2021-05-03 20:28:31 +12:00
$file -> getAttribute ( 'openSSLCipher' ),
App :: getEnv ( '_APP_OPENSSL_KEY_V' . $file -> getAttribute ( 'openSSLVersion' )),
2020-06-30 23:09:28 +12:00
0 ,
2021-05-03 20:28:31 +12:00
\hex2bin ( $file -> getAttribute ( 'openSSLIV' )),
\hex2bin ( $file -> getAttribute ( 'openSSLTag' ))
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 = $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' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
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' )
2021-05-03 20:28:31 +12:00
-> inject ( 'dbForInternal' )
2020-12-27 05:48:36 +13:00
-> inject ( 'audits' )
2021-05-03 20:28:31 +12:00
-> action ( function ( $fileId , $read , $write , $response , $dbForInternal , $audits ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2021-05-03 20:28:31 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2020-07-06 02:19:59 +12:00
/** @var Appwrite\Event\Event $audits */
2019-08-29 00:29:45 +12:00
2021-05-03 20:28:31 +12:00
$file = $dbForInternal -> getDocument ( 'files' , $fileId );
2019-08-29 00:29:45 +12:00
2021-05-03 20:28:31 +12:00
if ( empty ( $file -> getId ())) {
2020-06-30 23:09:28 +12:00
throw new Exception ( 'File not found' , 404 );
}
2019-08-29 00:29:45 +12:00
2021-05-03 20:28:31 +12:00
$file = $dbForInternal -> updateDocument ( 'files' , $fileId , new Document ( \array_merge ( $file -> getArrayCopy (), [
'$read' => $read ,
'$write' => $write ,
'bucketId' => '' ,
])));
2019-08-29 00:29:45 +12: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 ())
;
2021-05-03 20:28:31 +12:00
$response -> dynamic2 ( $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' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
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' )
2021-05-03 20:28:31 +12:00
-> inject ( 'dbForInternal' )
2020-12-27 05:48:36 +13:00
-> inject ( 'events' )
-> inject ( 'audits' )
-> inject ( 'usage' )
2021-05-03 20:28:31 +12:00
-> action ( function ( $fileId , $response , $dbForInternal , $events , $audits , $usage ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2021-05-03 20:28:31 +12:00
/** @var Utopia\Database\Database $dbForInternal */
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 */
2021-05-03 20:28:31 +12:00
$file = $dbForInternal -> getDocument ( 'files' , $fileId );
2019-05-09 18:54:39 +12:00
2021-05-03 20:28:31 +12:00
if ( empty ( $file -> getId ())) {
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
}
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
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 ();
2021-05-03 20:28:31 +12:00
});