UNPKG

@frangoteam/fuxa

Version:

Web-based Process Visualization (SCADA/HMI/Dashboard) software

378 lines (358 loc) 18.1 kB
/** * 'api/resources': Diagnose API to GET resources: images */ const fs = require('fs'); const path = require('path'); var express = require("express"); const authJwt = require('../jwt-helper'); const Report = require('../../runtime/jobs/report'); const fontkit = require('fontkit'); const os = require('os'); const { resolveWithin } = require('../path-helper'); var runtime; var secureFnc; var checkGroupsFnc; module.exports = { init: function (_runtime, _secureFnc, _checkGroupsFnc) { runtime = _runtime; secureFnc = _secureFnc; checkGroupsFnc = _checkGroupsFnc; }, app: function () { var resourcesApp = express(); resourcesApp.use(function (req, res, next) { if (!runtime.project) { res.status(404).end(); } else { next(); } }); /** * GET Server images folder content */ resourcesApp.get('/api/resources/images', secureFnc, function (req, res) { const permission = checkGroupsFnc(req); if (res.statusCode === 403) { runtime.logger.error("api get resources/images: Tocken Expired"); } else if (!authJwt.haveAdminPermission(permission)) { res.status(401).json({ error: "unauthorized_error", message: "Unauthorized!" }); runtime.logger.error("api get resources/images: Unauthorized!"); } else { try { var result = { ...req.query, ...{ groups: [] } }; var resourcesDirs = getDirectories(runtime.settings.imagesFileDir); for (var i = 0; i < resourcesDirs.length; i++) { var group = { name: resourcesDirs[i], items: [] }; var dirPath = path.resolve(runtime.settings.imagesFileDir, resourcesDirs[i]); var wwwSubDir = path.join('_images', resourcesDirs[i]); var files = getFiles(dirPath, ['.jpg', '.jpeg', '.png', '.gif', '.svg', '.mp4', '.webm', '.ogg', '.ogv']); for (var x = 0; x < files.length; x++) { var filename = files[x].replace(/\.[^\/.]+$/, ''); group.items.push({ path: path.join(wwwSubDir, files[x]).split(path.sep).join(path.posix.sep), name: filename }); } result.groups.push(group); } res.json(result); } catch (err) { if (err.code) { res.status(400).json({ error: err.code, message: err.message }); } else { res.status(400).json({ error: "unexpected_error", message: err.toString() }); } runtime.logger.error("api get resources/images: " + err.message); } } }); /** * GET Server resources folder content */ resourcesApp.get('/api/resources/resources', secureFnc, function (req, res) { try { const resourcesFilter = { fonts: ['ttf'] }; const wwwSubDir = '_resources'; const result = { ...req.query, ...{ groups: [] } }; const group = { name: wwwSubDir, items: [] }; var files = getFiles(runtime.settings.resourcesFileDir, ['.jpg', '.jpeg', '.png', '.gif', '.svg', '.pdf', '.ttf', '.mp4', '.webm', '.ogg', '.ogv']); for (var x = 0; x < files.length; x++) { const fileName = files[x]; const filePath = path.join(wwwSubDir, files[x]).split(path.sep).join(path.posix.sep); var fileLabel; if (resourcesFilter.fonts.some(suffix => fileName.endsWith(suffix))) { const font = fontkit.openSync(filePath); fileLabel = font.fullName; } group.items.push({ path: filePath, name: fileName, label: fileLabel }); } result.groups.push(group); res.json(result); } catch (err) { if (err.code) { res.status(400).json({ error: err.code, message: err.message }); } else { res.status(400).json({ error: "unexpected_error", message: err.toString() }); } runtime.logger.error("api get resources/resources: " + err.message); } }); /** * POST remove resource file */ resourcesApp.post('/api/resources/remove', secureFnc, function (req, res) { const permission = checkGroupsFnc(req); if (res.statusCode === 403) { runtime.logger.error("api post device: Tocken Expired"); } else if (!authJwt.haveAdminPermission(permission)) { res.status(401).json({ error: "unauthorized_error", message: "Unauthorized!" }); runtime.logger.error("api post remove resource: Unauthorized"); } else { try { const resolvedFile = resolveWithin(runtime.settings.resourcesFileDir, req.body.file); if (!resolvedFile) { res.status(400).json({ error: "invalid_path", message: "Invalid resource path." }); return; } const filePath = resolvedFile.resolvedTarget; if (fs.existsSync(filePath)) { fs.unlinkSync(filePath); } runtime.logger.info(`resources '${filePath}' deleted!`, true); res.end(); } catch (err) { if (err && err.code) { res.status(400).json({ error: err.code, message: err.message }); runtime.logger.error("api remove resource: " + err.message); } else { res.status(400).json({ error: "unexpected_error", message: err }); runtime.logger.error("api remove resource: " + err); } } } }); /** * GET svg/canvas rendered and converted to image */ resourcesApp.get('/api/resources/generateImage', secureFnc, function (req, res) { const permission = checkGroupsFnc(req); if (res.statusCode === 403) { runtime.logger.error("api get resources/generateImage: Tocken Expired"); } else if (!authJwt.haveAdminPermission(permission)) { res.status(401).json({ error: "unauthorized_error", message: "Unauthorized!" }); runtime.logger.error("api get resources/generateImage: Unauthorized!"); } else { try { var query = JSON.parse(req.query.param); const report = Report.create(null, runtime); report.getChartImage(query).then((content) => { res.end(content.toString('base64')); }).catch(function (err) { if (err.code) { res.status(400).json({ error: err.code, message: err.message }); } else { res.status(400).json({ error: "unexpected_error", message: err.toString() }); } runtime.logger.error("createImage: " + err.message); }); } catch (err) { if (err.code) { res.status(400).json({ error: err.code, message: err.message }); } else { res.status(400).json({ error: "unexpected_error", message: err.toString() }); } runtime.logger.error("api get resources/generateImage: " + err.message); } } }); /** * GET Templates * Take from resources storage and reply */ resourcesApp.get("/api/resources/templates", secureFnc, function (req, res) { const permission = checkGroupsFnc(req); if (res.statusCode === 403) { runtime.logger.error("api get templates: Tocken Expired"); } else if (!authJwt.haveAdminPermission(permission)) { res.status(401).json({ error: "unauthorized_error", message: "Unauthorized!" }); runtime.logger.error("api get templates: Unauthorized!"); } else { runtime.resourcesMgr.getTemplates(req.query).then(result => { // res.header("Access-Control-Allow-Origin", "*"); // res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); if (result) { result.forEach(template => { template.content = JSON.parse(template.content); }); res.json(result); } else { res.end(); } }).catch(function (err) { if (err.code) { res.status(400).json({ error: err.code, message: err.message }); } else { res.status(400).json({ error: "unexpected_error", message: err.toString() }); } runtime.logger.error("api get templates: " + err.message); }); } }); /** * POST template */ resourcesApp.post('/api/resources/template', secureFnc, function (req, res) { const permission = checkGroupsFnc(req); if (res.statusCode === 403) { runtime.logger.error("api post device: Tocken Expired"); } else if (!authJwt.haveAdminPermission(permission)) { res.status(401).json({ error: "unauthorized_error", message: "Unauthorized!" }); runtime.logger.error("api post template: Unauthorized"); } else { runtime.resourcesMgr.setTemplate(req.body.template).then((data) => { res.end(); }).catch(function (err) { if (err.code) { res.status(400).json({ error: err.code, message: err.message }); } else { res.status(400).json({ error: "unexpected_error", message: err.toString() }); } runtime.logger.error("api post template: " + err.message); }); } }); /** * DELETE template */ resourcesApp.delete("/api/resources/templates", secureFnc, function (req, res, next) { const permission = checkGroupsFnc(req); if (res.statusCode === 403) { runtime.logger.error("api delete templates: Tocken Expired"); } else if (!authJwt.haveAdminPermission(permission)) { res.status(401).json({ error: "unauthorized_error", message: "Unauthorized!" }); runtime.logger.error("api delete templates: Unauthorized"); } else { runtime.resourcesMgr.removeTemplates(req.query.templates).then((data) => { res.end(); }).catch(function (err) { if (err.code) { res.status(400).json({ error: err.code, message: err.message }); } else { res.status(400).json({ error: "unexpected_error", message: err.toString() }); } runtime.logger.error("api delete templates: " + err.message); }); } }); /** * GET Server widgets folder content */ resourcesApp.get('/api/resources/widgets', secureFnc, function (req, res) { const permission = checkGroupsFnc(req); if (res.statusCode === 403) { runtime.logger.error("api get resources/widgets: Tocken Expired"); } else if (!authJwt.haveAdminPermission(permission)) { res.status(401).json({ error: "unauthorized_error", message: "Unauthorized!" }); runtime.logger.error("api get resources/widgets: Unauthorized!"); } else { try { var result = { ...req.query, ...{ groups: [] } }; var resourcesDirs = getDirectories(runtime.settings.widgetsFileDir); for (var i = 0; i < resourcesDirs.length; i++) { var group = { name: resourcesDirs[i], items: [] }; var dirPath = path.resolve(runtime.settings.widgetsFileDir, resourcesDirs[i]); var wwwSubDir = path.join('_widgets', resourcesDirs[i]); var files = getFilesRecursive(dirPath, ['.svg']); for (var x = 0; x < files.length; x++) { var filename = files[x]; group.items.push({ path: path.join(wwwSubDir, files[x]).split(path.sep).join(path.posix.sep), name: filename }); } result.groups.push(group); } res.json(result); } catch (err) { if (err.code) { res.status(400).json({ error: err.code, message: err.message }); } else { res.status(400).json({ error: "unexpected_error", message: err.toString() }); } runtime.logger.error("api get resources/widgets: " + err.message); } } }); /** * POST Remove Server widget item */ resourcesApp.post('/api/resources/removeWidget', secureFnc, function (req, res) { const permission = checkGroupsFnc(req); if (res.statusCode === 403) { runtime.logger.error("api resources/removeWidget: Tocken Expired"); } else if (!authJwt.haveAdminPermission(permission)) { runtime.logger.error("api resources/removeWidget: Unauthorized!"); return res.status(401).json({ error: "unauthorized_error", message: "Unauthorized!" }); } try { let relPath = req.body?.path; if (!relPath || typeof relPath !== 'string') { return res.status(400).json({ error: "invalid_path", message: "Missing or invalid widget path." }); } let basePath = path.resolve(runtime.settings.appDir); if (process.versions.electron) { basePath = process.env.userDir || path.join(os.homedir(), '.fuxa'); } const resolvedWidget = resolveWithin(basePath, relPath); if (!resolvedWidget) { runtime.logger.error("api resources/widgets: security_violation " + relPath); return res.status(403).json({ error: 'security_violation', message: 'Invalid path' }); } const fullPath = resolvedWidget.resolvedTarget; if (!fs.existsSync(fullPath)) { return res.status(404).json({ error: "not_found", message: "Widget file not found." }); } try { fs.unlinkSync(fullPath); res.json({ success: true, path: relPath }); } catch (err) { runtime.logger.error("api removeWidget: " + err.message); res.status(500).json({ error: "delete_failed", message: err.message }); } } catch (err) { if (err.code) { res.status(400).json({ error: err.code, message: err.message }); } else { res.status(400).json({ error: "unexpected_error", message: err.toString() }); } runtime.logger.error("api resources/removeWidget: " + err.message); } }); return resourcesApp; } } function getDirectories(pathDir) { const directoriesInDIrectory = fs.readdirSync(pathDir, { withFileTypes: true }) .filter((item) => item.isDirectory()) .map((item) => item.name); return directoriesInDIrectory; } function getFiles(pathDir, extensions) { const filesInDIrectory = fs.readdirSync(pathDir) .filter((item) => extensions.indexOf(path.extname(item).toLowerCase()) !== -1); return filesInDIrectory; } function getFilesRecursive(pathDir, extensions, baseDir = pathDir) { const entries = fs.readdirSync(pathDir, { withFileTypes: true }); const files = []; for (const entry of entries) { const entryPath = path.join(pathDir, entry.name); if (entry.isDirectory()) { files.push(...getFilesRecursive(entryPath, extensions, baseDir)); } else if (extensions.indexOf(path.extname(entry.name).toLowerCase()) !== -1) { files.push(path.relative(baseDir, entryPath)); } } return files; }