diff --git a/src/fireedge/src/server/routes/api/files/files.js b/src/fireedge/src/server/routes/api/files/files.js new file mode 100644 index 0000000000..2af2d48a58 --- /dev/null +++ b/src/fireedge/src/server/routes/api/files/files.js @@ -0,0 +1,98 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ + +const { from: fromData, httpMethod } = require('server/utils/constants/defaults') +const { show, list, upload, update, deleteFile } = require('./functions') +const { GET, POST, PUT, DELETE } = httpMethod + +const publicRoutes = { + [GET]: { + null: { + action: show, + params: { + file: { + from: fromData.query, + name: 'file' + }, + token: { + from: fromData.query, + name: 'token' + } + } + } + } +} + +const privateRoutes = { + [GET]: { + null: { + action: list, + params: { + app: { + from: fromData.query, + name: 'app' + } + } + } + }, + [POST]: { + null: { + action: upload, + params: { + app: { + from: fromData.query, + name: 'app' + }, + files: { + from: 'files', + name: 'files' + } + } + } + }, + [PUT]: { + null: { + action: update, + params: { + name: { + from: fromData.query, + name: 'name' + }, + files: { + from: 'files', + name: 'files' + } + } + } + }, + [DELETE]: { + null: { + action: deleteFile, + params: { + file: { + from: fromData.query, + name: 'file' + } + } + } + } +} + +const fileApi = { + publicRoutes, + privateRoutes +} +module.exports = fileApi diff --git a/src/fireedge/src/server/routes/api/files/functions.js b/src/fireedge/src/server/routes/api/files/functions.js new file mode 100644 index 0000000000..116de27ca3 --- /dev/null +++ b/src/fireedge/src/server/routes/api/files/functions.js @@ -0,0 +1,330 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ +const { resolve, extname, parse, sep } = require('path') +const { global } = require('window-or-global') +const { jwtDecode } = require('server/utils/jwt') +const { + existsSync, + mkdirsSync, + moveSync +} = require('fs-extra') + +const { + defaultEmptyFunction +} = require('server/utils/constants/defaults') + +const { + ok, + internalServerError, + badRequest +} = require('server/utils/constants/http-codes') +const { Actions: ActionUser } = require('server/utils/constants/commands/user') +const { httpResponse, checkValidApp, getFiles, existsFile, removeFile } = require('server/utils/server') + +const httpBadRequest = httpResponse(badRequest, '', '') +const groupAdministrator = ['0', '1'] + +/** + * Check if user is a administrator. + * + * @param {*} oneConnection - one connection function + * @param {*} id - user ID + * @param {*} success - callback success + * @param {*} error - callback error + */ +const checkUserAdmin = ( + oneConnection = defaultEmptyFunction, + id = '', + success = defaultEmptyFunction, + error = defaultEmptyFunction +) => { + if ( + typeof oneConnection === 'function' && + id && + typeof success === 'function' && + typeof error === 'function' + ) { + oneConnection( + ActionUser.USER_INFO, + [parseInt(id, 10)], + (err, value) => { + if (!err && value && value.USER && value.USER.GROUPS && value.USER.GROUPS.ID) { + let admin = false + const groups = Array.isArray(value.USER.GROUPS.ID) ? value.USER.GROUPS.ID : [value.USER.GROUPS.ID] + for (const group of groups) { + if (groupAdministrator.includes(group)) { + admin = true + break + } + } + success(admin) + } else { + error(err) + } + }, + false + ) + } else { + error() + } +} + +/** + * Upload File. + * + * @param {object} res - response http + * @param {Function} next - express stepper + * @param {string} params - data response http + * @param {object} userData - user of http request + */ +const upload = (res = {}, next = defaultEmptyFunction, params = {}, userData = {}) => { + let rtn = httpBadRequest + if ( + global.paths.CPI && + params && + params.app && + checkValidApp(params.app) && + params.files && + userData && + userData.id + ) { + const pathUserData = `${params.app}/${userData.id}` + const pathUser = `${global.paths.CPI}/${pathUserData}` + if (!existsSync(pathUser)) { + mkdirsSync(pathUser) + } + let method = ok + let message = '' + const data = [] + for (const file of params.files) { + if (file && file.originalname && file.path && file.filename) { + const extFile = extname(file.originalname) + try { + const filenameApi = `${pathUserData}/${file.filename}${extFile}` + const filename = `${pathUser}/${file.filename}${extFile}` + moveSync(file.path, filename) + data.push(filenameApi) + } catch (error) { + method = internalServerError + message = error && error.message + break + } + } + } + rtn = httpResponse(method, data.length ? data : '', message) + } + res.locals.httpCode = rtn + next() +} + +/** + * List files by user. + * + * @param {object} res - response http + * @param {Function} next - express stepper + * @param {string} params - data response http + * @param {object} userData - user of http request + * @param {Function} oneConnection - one connection XMLRPC + */ +const list = (res = {}, next = defaultEmptyFunction, params = {}, userData = {}, oneConnection = defaultEmptyFunction) => { + const { user, password, id } = userData + const rtn = httpBadRequest + if ( + params && + params.app && + checkValidApp(params.app) && + user && + password && + id + ) { + const oneConnect = oneConnection(user, password) + checkUserAdmin( + oneConnect, + id, + (admin = false) => { + let data = [] + let pathUserData = `${params.app}/${id}` + if (admin) { + pathUserData = `${params.app}` + } + const pathUser = `${global.paths.CPI}/${pathUserData}` + data = getFiles(pathUser, true).map( + file => file.replace(`${global.paths.CPI}/`, '') + ) + res.locals.httpCode = httpResponse(ok, data) + next() + }, + () => { + res.locals.httpCode = internalServerError + next() + } + ) + } else { + res.locals.httpCode = rtn + next() + } +} + +/** + * Show file. + * + * @param {object} res - response http + * @param {Function} next - express stepper + * @param {string} params - data response http + * @param {object} userData - user of http request + */ +const show = (res = {}, next = defaultEmptyFunction, params = {}, userData = {}) => { + const rtn = httpBadRequest + const { file, token } = params + if (token && file && jwtDecode(token)) { + if (file) { + const pathFile = `${global.paths.CPI}/${file}` + existsFile( + pathFile, + () => { + res.locals.httpCode = httpResponse(ok, '', '', resolve(pathFile)) + next() + }, + () => { + res.locals.httpCode = httpResponse(internalServerError, '', '') + next() + } + ) + } + } else { + res.locals.httpCode = rtn + next() + } +} + +/** + * Check if user is a file owner. + * + * @param {string} file - filename + * @param {number} id - user id + * @returns {boolean} - if user is the file owner + */ +const checkFile = (file = '', id = '') => { + let rtn = false + if (file) { + const parsedFile = parse(file) + if (parsedFile && parsedFile.dir) { + const splitParsedFile = parsedFile.dir.split(sep) + if (Array.isArray(splitParsedFile) && checkValidApp(splitParsedFile[0]) && splitParsedFile[1] === id) { + rtn = true + } + } + } + return rtn +} + +/** + * Delete File. + * + * @param {object} res - response http + * @param {Function} next - express stepper + * @param {string} params - data response http + * @param {object} userData - user of http request + */ +const deleteFile = (res = {}, next = defaultEmptyFunction, params = {}, userData = {}) => { + const rtn = httpBadRequest + if ( + global.paths.CPI && + params && + params.file && + userData && + userData.id && + checkFile(params.file, userData.id) + ) { + const pathFile = `${global.paths.CPI}/${params.file}` + existsFile( + pathFile, + () => { + res.locals.httpCode = httpResponse(removeFile(pathFile) ? ok : internalServerError, '', '') + next() + }, + () => { + res.locals.httpCode = httpResponse(internalServerError, '', '') + next() + } + ) + } else { + res.locals.httpCode = rtn + next() + } +} + +/** + * Update File. + * + * @param {object} res - response http + * @param {Function} next - express stepper + * @param {string} params - data response http + * @param {object} userData - user of http request + */ +const update = (res = {}, next = defaultEmptyFunction, params = {}, userData = {}) => { + const rtn = httpBadRequest + if ( + global.paths.CPI && + params && + params.name && + params.files && + userData && + userData.id && + checkFile(params.name, userData.id) + ) { + const nameFile = params.name + const pathFile = `${global.paths.CPI}/${nameFile}` + existsFile( + pathFile, + () => { + let method = ok + let data = '' + let message = '' + for (const file of params.files) { + if (file && file.originalname && file.path && file.filename) { + try { + moveSync(file.path, pathFile, { overwrite: true }) + data = nameFile + } catch (error) { + method = internalServerError + message = error && error.message + break + } + } + } + res.locals.httpCode = httpResponse(method, data.length ? data : '', message) + next() + }, + () => { + res.locals.httpCode = httpResponse(internalServerError, '', '') + next() + } + ) + } else { + res.locals.httpCode = rtn + next() + } +} + +const functionRoutes = { + upload, + deleteFile, + update, + show, + list +} +module.exports = functionRoutes diff --git a/src/fireedge/src/server/routes/api/files/index.js b/src/fireedge/src/server/routes/api/files/index.js new file mode 100644 index 0000000000..84edacfa45 --- /dev/null +++ b/src/fireedge/src/server/routes/api/files/index.js @@ -0,0 +1,91 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ + +const { addFunctionAsRoute, setFunctionRoute } = require('server/utils/server') +const { privateRoutes: filePrivateRoutes, publicRoutes: filePublicRoutes } = require('./files') +const { FILES } = require('./string-routes') + +const privateRoutes = [] +const publicRoutes = [] + +/** + * Set private routes. + * + * @param {object} routes - object of routes + * @param {string} path - principal route + * @param {Function} action - function of route + */ +const setPrivateRoutes = (routes = {}, path = '', action = () => undefined) => { + if (Object.keys(routes).length > 0 && routes.constructor === Object) { + Object.keys(routes).forEach((route) => { + privateRoutes.push( + setFunctionRoute(route, path, + (req, res, next, connection, userId, user) => { + action(req, res, next, routes[route], user, connection) + } + ) + ) + }) + } +} + +/** + * Set public routes. + * + * @param {object} routes - object of routes + * @param {string} path - principal route + * @param {Function} action - function of route + */ +const setPublicRoutes = (routes = {}, path = '', action = () => undefined) => { + if (Object.keys(routes).length > 0 && routes.constructor === Object) { + Object.keys(routes).forEach((route) => { + publicRoutes.push( + setFunctionRoute(route, path, + (req, res, next, connection, userId, user) => { + action(req, res, next, routes[route], user, connection) + } + ) + ) + }) + } +} + +/** + * Add routes. + * + * @returns {Array} routes + */ +const generatePrivateRoutes = () => { + setPrivateRoutes(filePrivateRoutes, FILES, addFunctionAsRoute) + return privateRoutes +} + +/** + * Add routes. + * + * @returns {Array} routes + */ +const generatePublicRoutes = () => { + setPublicRoutes(filePublicRoutes, FILES, addFunctionAsRoute) + return publicRoutes +} + +const functionRoutes = { + private: generatePrivateRoutes(), + public: generatePublicRoutes() +} + +module.exports = functionRoutes diff --git a/src/fireedge/src/server/routes/api/files/string-routes.js b/src/fireedge/src/server/routes/api/files/string-routes.js new file mode 100644 index 0000000000..aa56d6f9e9 --- /dev/null +++ b/src/fireedge/src/server/routes/api/files/string-routes.js @@ -0,0 +1,23 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ + +const FILES = 'files' + +const Actions = { + FILES +} + +module.exports = Actions diff --git a/src/fireedge/src/server/routes/api/provision/functions.js b/src/fireedge/src/server/routes/api/provision/functions.js index 0ba59bd794..db90f7afa1 100644 --- a/src/fireedge/src/server/routes/api/provision/functions.js +++ b/src/fireedge/src/server/routes/api/provision/functions.js @@ -22,7 +22,6 @@ const events = require('events') const { Document, scalarOptions, stringify } = require('yaml') const { writeFileSync, - removeSync, readdirSync, statSync, existsSync, @@ -149,21 +148,6 @@ const createYMLContent = (content = '') => { return rtn } -/** - * Delete file. - * - * @param {string} path - the path for delete - */ -const removeFile = (path = '') => { - if (path) { - try { - removeSync(path, { force: true }) - } catch (error) { - messageTerminal(defaultError(error && error.message)) - } - } -} - /** * Rename folder. * @@ -308,7 +292,6 @@ const functionRoutes = { createYMLContent, createTemporalFile, createFolderWithFiles, - removeFile, renameFolder, moveToFolder, findRecursiveFolder, diff --git a/src/fireedge/src/server/routes/api/provision/provider-functions.js b/src/fireedge/src/server/routes/api/provision/provider-functions.js index 4de71db862..481006b966 100644 --- a/src/fireedge/src/server/routes/api/provision/provider-functions.js +++ b/src/fireedge/src/server/routes/api/provision/provider-functions.js @@ -34,12 +34,12 @@ const { parsePostData, getFilesbyEXT, existsFile, - executeCommand + executeCommand, + removeFile } = require('server/utils/server') const { createTemporalFile, createYMLContent, - removeFile, getEndpoint, getSpecificConfig } = require('./functions') diff --git a/src/fireedge/src/server/routes/api/provision/provision-functions.js b/src/fireedge/src/server/routes/api/provision/provision-functions.js index a1c0ff31eb..e0c4629fe2 100644 --- a/src/fireedge/src/server/routes/api/provision/provision-functions.js +++ b/src/fireedge/src/server/routes/api/provision/provision-functions.js @@ -37,7 +37,8 @@ const { getDirectories, getFilesbyEXT, executeCommand, - executeCommandAsync + executeCommandAsync, + removeFile } = require('server/utils/server') const { checkEmptyObject } = require('server/utils/general') const { @@ -50,7 +51,6 @@ const { createTemporalFile, createFolderWithFiles, createYMLContent, - removeFile, renameFolder, moveToFolder, findRecursiveFolder, diff --git a/src/fireedge/src/server/routes/api/provision/provision_template-functions.js b/src/fireedge/src/server/routes/api/provision/provision_template-functions.js index a64a914fff..29376d1fe3 100644 --- a/src/fireedge/src/server/routes/api/provision/provision_template-functions.js +++ b/src/fireedge/src/server/routes/api/provision/provision_template-functions.js @@ -27,12 +27,12 @@ const { const { httpResponse, parsePostData, - executeCommand + executeCommand, + removeFile } = require('server/utils/server') const { createYMLContent, createTemporalFile, - removeFile, getEndpoint, getSpecificConfig } = require('./functions') diff --git a/src/fireedge/src/server/routes/entrypoints/Api.js b/src/fireedge/src/server/routes/entrypoints/Api.js index 364c11eb3a..c8c0fcea02 100644 --- a/src/fireedge/src/server/routes/entrypoints/Api.js +++ b/src/fireedge/src/server/routes/entrypoints/Api.js @@ -92,21 +92,25 @@ router.all( } const zoneData = getDataZone(zone, defaultOpennebulaZones) if (zoneData) { + const user = getUserOpennebula() + const password = getPassOpennebula() + const userId = getIdUserOpennebula() const { rpc } = zoneData + /** * Instance of connection to opennebula. * * @param {string} user - opennegula user - * @param {string} pass - opennebula pass + * @param {string} password - opennebula pass * @returns {Function} opennebula executer calls to XMLRPC */ const connectOpennebula = ( - user = getUserOpennebula(), - pass = getPassOpennebula() - ) => opennebulaConnect(user, pass, rpc) + user, + password + ) => opennebulaConnect(user, password, rpc) const { resource } = req.params - const routeFunction = checkIfIsARouteFunction(resource, httpMethod) + const routeFunction = checkIfIsARouteFunction(resource, httpMethod, !!userId.length) res.locals.httpCode = httpResponse(methodNotAllowed) const dataSources = { @@ -126,14 +130,13 @@ router.all( ) req.serverDataSource = dataSources if (valRouteFunction) { - const userIdOpennebula = getIdUserOpennebula() valRouteFunction( req, res, next, connectOpennebula, - userIdOpennebula, - { id: userIdOpennebula, user: getUserOpennebula(), password: getPassOpennebula() } + userId, + { id: userId, user, password } ) } else { next() @@ -190,7 +193,6 @@ router.all( } //* worker thread */ - const user = getUserOpennebula() const paramsCommand = getOpennebulaMethod(dataSources) let workerPath = [__dirname] if (env && env.NODE_ENV === defaultWebpackMode) { @@ -213,7 +215,7 @@ router.all( { globalState: (global && global.paths) || {}, user, - password: getPassOpennebula(), + password, rpc, command, paramsCommand @@ -231,7 +233,11 @@ router.all( (req, res) => { clearStates() const { httpCode } = res.locals - res.status(httpCode.id).json(httpCode) + if (httpCode.file) { + res.sendFile(httpCode.file) + } else { + res.status(httpCode.id).json(httpCode) + } } ) diff --git a/src/fireedge/src/server/utils/constants/defaults.js b/src/fireedge/src/server/utils/constants/defaults.js index a1630b3106..d09ef13480 100644 --- a/src/fireedge/src/server/utils/constants/defaults.js +++ b/src/fireedge/src/server/utils/constants/defaults.js @@ -66,6 +66,7 @@ const defaults = { defaultFilesRoutes: [ '2fa', 'auth', + 'files', 'oneflow', 'support', 'vcenter', diff --git a/src/fireedge/src/server/utils/index.js b/src/fireedge/src/server/utils/index.js index d10c6346fd..6246d20774 100644 --- a/src/fireedge/src/server/utils/index.js +++ b/src/fireedge/src/server/utils/index.js @@ -89,13 +89,14 @@ const checkMethodRouteFunction = (routeFunction, httpMethod = '') => { * * @param {string} route - route * @param {string} httpMethod - http method + * @param {boolean} authenticated - user authenticated * @returns {object} route function */ -const checkIfIsARouteFunction = (route, httpMethod) => { +const checkIfIsARouteFunction = (route, httpMethod, authenticated) => { let rtn = false if (route && route.length) { const { private: functionPrivate, public: functionPublic } = functionRoutes - const functions = [...functionPrivate, ...functionPublic] + const functions = authenticated ? functionPrivate : functionPublic /** * Finder command. * @@ -110,6 +111,7 @@ const checkIfIsARouteFunction = (route, httpMethod) => { rtnCommand.httpMethod === httpMethod && rtnCommand.action && typeof rtnCommand.action === 'function' + const find = functions.find(finderCommand) if (find) { rtn = find diff --git a/src/fireedge/src/server/utils/jwt.js b/src/fireedge/src/server/utils/jwt.js index ffb6c90b50..0cf787152f 100644 --- a/src/fireedge/src/server/utils/jwt.js +++ b/src/fireedge/src/server/utils/jwt.js @@ -35,7 +35,7 @@ const createJWT = ( exp = '' ) => { let rtn = null - if (iss && aud && jti && iat && exp && global && global.paths && global.paths.FIREEDGE_KEY) { + if (iss && aud && jti && iat && exp) { const payload = { iss, aud, @@ -43,11 +43,45 @@ const createJWT = ( iat, exp } + rtn = jwtEncode(payload) + } + return rtn +} + +/** + * Encode JWT. + * + * @param {object} payload - data object + * @returns {object} - jwt or null + */ +const jwtEncode = (payload = {}) => { + let rtn = null + if (global && global.paths && global.paths.FIREEDGE_KEY) { rtn = jwt.encode(payload, global.paths.FIREEDGE_KEY) } return rtn } +/** + * Decode JWT. + * + * @param {string} token - token JWT + * @returns {object} data JWT + */ +const jwtDecode = (token = '') => { + if (global && global.paths && global.paths.FIREEDGE_KEY) { + try { + return jwt.decode(token, global.paths.FIREEDGE_KEY) + } catch (messageError) { + messageTerminal({ + color: 'red', + message: 'invalid: %s', + error: messageError + }) + } + } +} + /** * Validate auth (JWT). * @@ -60,10 +94,9 @@ const validateAuth = (req = {}) => { const authorization = req.headers.authorization const removeBearer = /^Bearer /i const token = authorization.replace(removeBearer, '') - const fireedgeKey = global && global.paths && global.paths.FIREEDGE_KEY - if (token && fireedgeKey) { + if (token) { try { - const payload = jwt.decode(token, fireedgeKey) + const payload = jwtDecode(token) if ( payload && 'iss' in payload && @@ -84,7 +117,7 @@ const validateAuth = (req = {}) => { } catch (error) { } } else { - const messageError = (!token && 'jwt') || (!fireedgeKey && 'fireedge_key') + const messageError = token || (global && global.paths && global.paths.FIREEDGE_KEY) if (messageError) { messageTerminal({ color: 'red', @@ -117,6 +150,7 @@ const check2Fa = (secret = '', token = '') => { } module.exports = { + jwtDecode, createJWT, validateAuth, check2Fa diff --git a/src/fireedge/src/server/utils/server.js b/src/fireedge/src/server/utils/server.js index d9b1a4e0c2..6783ea93b8 100644 --- a/src/fireedge/src/server/utils/server.js +++ b/src/fireedge/src/server/utils/server.js @@ -21,13 +21,14 @@ const { global } = require('window-or-global') const { resolve } = require('path') // eslint-disable-next-line node/no-deprecated-api const { createCipheriv, createCipher, createDecipheriv, createDecipher, createHash } = require('crypto') -const { existsSync, readFileSync, createWriteStream, readdirSync, statSync } = require('fs-extra') +const { existsSync, readFileSync, createWriteStream, readdirSync, statSync, removeSync } = require('fs-extra') const { internalServerError } = require('./constants/http-codes') const { messageTerminal } = require('server/utils/general') const { validateAuth } = require('server/utils/jwt') const { writeInLogger } = require('server/utils/logger') const { spawnSync, spawn } = require('child_process') const { + defaultApps, from: fromData, defaultAppName, defaultConfigFile, @@ -209,9 +210,10 @@ const getKey = () => key * @param {object} response - response http * @param {string} data - data for response http * @param {string} message - message + * @param {string} file - file * @returns {object} {data, message, id} */ -const httpResponse = (response = null, data = '', message = '') => { +const httpResponse = (response = null, data = '', message = '', file = '') => { let rtn = Map(internalServerError).toObject() rtn.data = data if (response) { @@ -223,6 +225,11 @@ const httpResponse = (response = null, data = '', message = '') => { if (message) { rtn.message = message } + if (file) { + rtn.message && delete rtn.message + rtn.data && delete rtn.data + rtn.file = file + } return rtn } @@ -816,6 +823,32 @@ const executeCommand = (command = '', resource = '', prependCommand = '', option } return rtn } +/** + * Check app name. + * + * @param {string} appName - app name + * @returns {object} app + */ +const checkValidApp = (appName = '') => defaultApps[appName] + +/** + * Delete file. + * + * @param {string} path - the path for delete + * @returns {boolean} flag if file is deleted + */ +const removeFile = (path = '') => { + let rtn = false + if (path) { + try { + removeSync(path, { force: true }) + rtn = true + } catch (error) { + messageTerminal(defaultError(error && error.message)) + } + } + return rtn +} /** * Run Asynchronous commands for CLI. @@ -892,5 +925,7 @@ module.exports = { getFiles, getFilesbyEXT, executeCommand, - executeCommandAsync + executeCommandAsync, + checkValidApp, + removeFile }