From 436300439dbdc7c77a53547c33e5ad69fedf26d8 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Tue, 14 Jul 2020 23:33:52 +0300 Subject: [PATCH] Enabled code upload --- app/app.php | 4 ++ app/controllers/api/functions.php | 51 +++++++++++++++++-- app/controllers/api/storage.php | 6 +-- .../docs/examples/functions/create-tag.md | 2 +- app/sdks/console-web/src/sdk.js | 4 +- app/sdks/console-web/src/sdk.min.js | 2 +- app/sdks/console-web/types/index.d.ts | 4 +- app/views/console/functions/function.phtml | 29 +++++++++-- public/dist/scripts/app-all.js | 2 +- public/dist/scripts/app-dep.js | 2 +- public/scripts/dependencies/appwrite.js | 4 +- src/Appwrite/Storage/Validator/FileType.php | 4 ++ 12 files changed, 92 insertions(+), 22 deletions(-) diff --git a/app/app.php b/app/app.php index 41617dc06..904545f17 100644 --- a/app/app.php +++ b/app/app.php @@ -14,6 +14,8 @@ use Appwrite\Database\Database; use Appwrite\Database\Document; use Appwrite\Database\Validator\Authorization; use Appwrite\Network\Validator\Origin; +use Appwrite\Storage\Device\Local; +use Appwrite\Storage\Storage; Config::setParam('domainVerification', false); Config::setParam('cookieDomain', 'localhost'); @@ -87,6 +89,8 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo : '.'.$request->getHostname() ); + Storage::setDevice('local', new Local(APP_STORAGE_UPLOADS.'/app-'.$project->getId())); + /* * Security Headers * diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 5f95fa7d6..eda9e0d6b 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -2,6 +2,11 @@ use Appwrite\Database\Database; use Appwrite\Database\Validator\UID; +use Appwrite\Storage\Storage; +use Appwrite\Storage\Validator\File; +use Appwrite\Storage\Validator\FileSize; +use Appwrite\Storage\Validator\FileType; +use Appwrite\Storage\Validator\Upload; use Appwrite\Task\Validator\Cron; use Utopia\App; use Utopia\Response; @@ -215,13 +220,50 @@ App::post('/v1/functions/:functionId/tags') ->label('sdk.description', '/docs/references/functions/create-tag.md') ->param('functionId', '', function () { return new UID(); }, 'Function unique ID.') ->param('command', '', function () { return new Text('1028'); }, 'Code execution command.') - ->param('code', '', function () { return new Text(128); }, 'Code package. Use the '.APP_NAME.' code packager to create a deployable package file.') - ->action(function ($functionId, $command, $code, $response, $projectDB) { + // ->param('code', '', function () { return new Text(128); }, 'Code package. Use the '.APP_NAME.' code packager to create a deployable package file.') + ->param('code', [], function () { return new File(); }, 'Gzip file containing your code.', false) + ->action(function ($functionId, $command, $code, $request, $response, $projectDB) { $function = $projectDB->getDocument($functionId); if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) { throw new Exception('Function not found', 404); } + + $file = $request->getFiles('code'); + $device = Storage::getDevice('local'); + $fileType = new FileType([FileType::FILE_TYPE_GZIP]); + $fileSize = new FileSize(App::getEnv('_APP_STORAGE_LIMIT', 0)); + $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 + $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']; + + // Check if file type is allowed (feature for project settings?) + if (!$fileType->isValid($file['tmp_name'])) { + throw new Exception('File type not allowed', 400); + } + + if (!$fileSize->isValid($file['size'])) { // Check if file size is exceeding allowed limit + throw new Exception('File size not allowed', 400); + } + + if (!$upload->isValid($file['tmp_name'])) { + throw new Exception('Invalid file', 403); + } + + // 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); + } $tag = $projectDB->createDocument([ '$collection' => Database::SYSTEM_COLLECTION_TAGS, @@ -232,7 +274,8 @@ App::post('/v1/functions/:functionId/tags') 'dateCreated' => time(), 'functionId' => $function->getId(), 'command' => $command, - 'code' => $code, + 'codePath' => $path, + 'codeSize' => $size, ]); if (false === $tag) { @@ -243,7 +286,7 @@ App::post('/v1/functions/:functionId/tags') ->setStatusCode(Response::STATUS_CODE_CREATED) ->json($tag->getArrayCopy()) ; - }, ['response', 'projectDB']); + }, ['request', 'response', 'projectDB']); App::get('/v1/functions/:functionId/tags') ->groups(['api', 'functions']) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index cd7fe327e..b462ef140 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -23,10 +23,6 @@ use Appwrite\Resize\Resize; use Appwrite\OpenSSL\OpenSSL; use Utopia\Config\Config; -App::init(function ($project) { - Storage::setDevice('local', new Local(APP_STORAGE_UPLOADS.'/app-'.$project->getId())); -}, ['project'], 'storage'); - App::post('/v1/storage/files') ->desc('Create File') ->groups(['api', 'storage']) @@ -38,7 +34,7 @@ App::post('/v1/storage/files') ->label('sdk.description', '/docs/references/storage/create-file.md') ->label('sdk.consumes', 'multipart/form-data') ->label('sdk.methodType', 'upload') - ->param('file', [], function () { return new File(); }, 'Binary File.', false) + ->param('file', [], function () { return new File(); }, 'Binary file.', false) ->param('read', [], function () { return 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', [], function () { return 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.') ->action(function ($file, $read, $write, $request, $response, $user, $projectDB, $webhooks, $audits, $usage) { diff --git a/app/sdks/console-web/docs/examples/functions/create-tag.md b/app/sdks/console-web/docs/examples/functions/create-tag.md index 6f08f7a10..743517ff1 100644 --- a/app/sdks/console-web/docs/examples/functions/create-tag.md +++ b/app/sdks/console-web/docs/examples/functions/create-tag.md @@ -6,7 +6,7 @@ sdk .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; -let promise = sdk.functions.createTag('[FUNCTION_ID]', '[COMMAND]', '[CODE]'); +let promise = sdk.functions.createTag('[FUNCTION_ID]', '[COMMAND]', document.getElementById('uploader').files[0]); promise.then(function (response) { console.log(response); // Success diff --git a/app/sdks/console-web/src/sdk.js b/app/sdks/console-web/src/sdk.js index 3919a751a..b246db4cc 100644 --- a/app/sdks/console-web/src/sdk.js +++ b/app/sdks/console-web/src/sdk.js @@ -2149,7 +2149,7 @@ * * @param {string} functionId * @param {string} command - * @param {string} code + * @param {File} code * @throws {Error} * @return {Promise} */ @@ -2180,7 +2180,7 @@ return http .post(path, { - 'content-type': 'application/json', + 'content-type': 'multipart/form-data', }, payload); }, diff --git a/app/sdks/console-web/src/sdk.min.js b/app/sdks/console-web/src/sdk.min.js index dace36b3e..6501248b8 100644 --- a/app/sdks/console-web/src/sdk.min.js +++ b/app/sdks/console-web/src/sdk.min.js @@ -187,7 +187,7 @@ if(command===undefined){throw new Error('Missing required parameter: "command"') if(code===undefined){throw new Error('Missing required parameter: "code"')} let path='/functions/{functionId}/tags'.replace(new RegExp('{functionId}','g'),functionId);let payload={};if(command){payload.command=command} if(code){payload.code=code} -return http.post(path,{'content-type':'application/json',},payload)},getTag:function(functionId,tagId){if(functionId===undefined){throw new Error('Missing required parameter: "functionId"')} +return http.post(path,{'content-type':'multipart/form-data',},payload)},getTag:function(functionId,tagId){if(functionId===undefined){throw new Error('Missing required parameter: "functionId"')} if(tagId===undefined){throw new Error('Missing required parameter: "tagId"')} let path='/functions/{functionId}/tags/{tagId}'.replace(new RegExp('{functionId}','g'),functionId).replace(new RegExp('{tagId}','g'),tagId);let payload={};return http.get(path,{'content-type':'application/json',},payload)},deleteTag:function(functionId,tagId){if(functionId===undefined){throw new Error('Missing required parameter: "functionId"')} if(tagId===undefined){throw new Error('Missing required parameter: "tagId"')} diff --git a/app/sdks/console-web/types/index.d.ts b/app/sdks/console-web/types/index.d.ts index 8a8489928..b34f32c5b 100644 --- a/app/sdks/console-web/types/index.d.ts +++ b/app/sdks/console-web/types/index.d.ts @@ -739,11 +739,11 @@ declare namespace Appwrite { * * @param {string} functionId * @param {string} command - * @param {string} code + * @param {File} code * @throws {Error} * @return {Promise} */ - createTag(functionId: string, command: string, code: string): Promise; + createTag(functionId: string, command: string, code: File): Promise; /** * Get Tag diff --git a/app/views/console/functions/function.phtml b/app/views/console/functions/function.phtml index 31aceefbc..238a4a4ea 100644 --- a/app/views/console/functions/function.phtml +++ b/app/views/console/functions/function.phtml @@ -90,9 +90,32 @@ $events = array_keys($this->getParam('events', []));

Deploy a New Tag

-
-   -
+
+ + + + + + + + + + +
diff --git a/public/dist/scripts/app-all.js b/public/dist/scripts/app-all.js index dd2cb64d5..8d1d0c45a 100644 --- a/public/dist/scripts/app-all.js +++ b/public/dist/scripts/app-all.js @@ -201,7 +201,7 @@ if(command===undefined){throw new Error('Missing required parameter: "command"') if(code===undefined){throw new Error('Missing required parameter: "code"');} let path='/functions/{functionId}/tags'.replace(new RegExp('{functionId}','g'),functionId);let payload={};if(command){payload['command']=command;} if(code){payload['code']=code;} -return http.post(path,{'content-type':'application/json',},payload);},getTag:function(functionId,tagId){if(functionId===undefined){throw new Error('Missing required parameter: "functionId"');} +return http.post(path,{'content-type':'multipart/form-data',},payload);},getTag:function(functionId,tagId){if(functionId===undefined){throw new Error('Missing required parameter: "functionId"');} if(tagId===undefined){throw new Error('Missing required parameter: "tagId"');} let path='/functions/{functionId}/tags/{tagId}'.replace(new RegExp('{functionId}','g'),functionId).replace(new RegExp('{tagId}','g'),tagId);let payload={};return http.get(path,{'content-type':'application/json',},payload);},deleteTag:function(functionId,tagId){if(functionId===undefined){throw new Error('Missing required parameter: "functionId"');} if(tagId===undefined){throw new Error('Missing required parameter: "tagId"');} diff --git a/public/dist/scripts/app-dep.js b/public/dist/scripts/app-dep.js index 264c87384..264cc488d 100644 --- a/public/dist/scripts/app-dep.js +++ b/public/dist/scripts/app-dep.js @@ -201,7 +201,7 @@ if(command===undefined){throw new Error('Missing required parameter: "command"') if(code===undefined){throw new Error('Missing required parameter: "code"');} let path='/functions/{functionId}/tags'.replace(new RegExp('{functionId}','g'),functionId);let payload={};if(command){payload['command']=command;} if(code){payload['code']=code;} -return http.post(path,{'content-type':'application/json',},payload);},getTag:function(functionId,tagId){if(functionId===undefined){throw new Error('Missing required parameter: "functionId"');} +return http.post(path,{'content-type':'multipart/form-data',},payload);},getTag:function(functionId,tagId){if(functionId===undefined){throw new Error('Missing required parameter: "functionId"');} if(tagId===undefined){throw new Error('Missing required parameter: "tagId"');} let path='/functions/{functionId}/tags/{tagId}'.replace(new RegExp('{functionId}','g'),functionId).replace(new RegExp('{tagId}','g'),tagId);let payload={};return http.get(path,{'content-type':'application/json',},payload);},deleteTag:function(functionId,tagId){if(functionId===undefined){throw new Error('Missing required parameter: "functionId"');} if(tagId===undefined){throw new Error('Missing required parameter: "tagId"');} diff --git a/public/scripts/dependencies/appwrite.js b/public/scripts/dependencies/appwrite.js index 3919a751a..b246db4cc 100644 --- a/public/scripts/dependencies/appwrite.js +++ b/public/scripts/dependencies/appwrite.js @@ -2149,7 +2149,7 @@ * * @param {string} functionId * @param {string} command - * @param {string} code + * @param {File} code * @throws {Error} * @return {Promise} */ @@ -2180,7 +2180,7 @@ return http .post(path, { - 'content-type': 'application/json', + 'content-type': 'multipart/form-data', }, payload); }, diff --git a/src/Appwrite/Storage/Validator/FileType.php b/src/Appwrite/Storage/Validator/FileType.php index 164e59924..13aca61df 100644 --- a/src/Appwrite/Storage/Validator/FileType.php +++ b/src/Appwrite/Storage/Validator/FileType.php @@ -13,6 +13,7 @@ class FileType extends Validator const FILE_TYPE_JPEG = 'jpeg'; const FILE_TYPE_GIF = 'gif'; const FILE_TYPE_PNG = 'png'; + const FILE_TYPE_GZIP = 'gz'; /** * File Type Binaries. @@ -23,6 +24,7 @@ class FileType extends Validator self::FILE_TYPE_JPEG => "\xFF\xD8\xFF", self::FILE_TYPE_GIF => 'GIF', self::FILE_TYPE_PNG => "\x89\x50\x4e\x47\x0d\x0a", + self::FILE_TYPE_GZIP => "application/x-gzip", ); /** @@ -76,6 +78,8 @@ class FileType extends Validator $bytes = \fgets($handle, 8); + var_dump($bytes); + foreach ($this->whiteList as $key) { if (\strpos($bytes, $this->types[$key]) === 0) { \fclose($handle);