diff --git a/src/fireedge/genPotFile.js b/src/fireedge/genPotFile.js new file mode 100644 index 0000000000..e274759488 --- /dev/null +++ b/src/fireedge/genPotFile.js @@ -0,0 +1,30 @@ +const { createReadStream, generateFile } = require('fireedge-genpotfile'); +const constants = require('./src/utils/constants'); +const clientConstants = require('./src/public/constants'); + +const testFolder = './src/public'; +const exportFile = 'test.po'; +const definitions = { ...constants, ...clientConstants }; + +// function Tr() +const optsFunc = { + regex: /Tr(\("|\('|\()[a-zA-Z0-9_ ]*("\)|'\)|\))/g, + removeStart: /Tr(\()/g, + removeEnd: /(\))/g, + regexTextCaptureIndex: 0, + definitions +}; + +// React component +const optsComponent = { + regex: //g, + removeStart: //g, + regexTextCaptureIndex: 0, + definitions +}; + +createReadStream(testFolder, optsFunc); +createReadStream(testFolder, optsComponent); + +generateFile(exportFile); diff --git a/src/fireedge/package.json b/src/fireedge/package.json index 2ee0efcb3b..0c17b1e941 100644 --- a/src/fireedge/package.json +++ b/src/fireedge/package.json @@ -4,16 +4,15 @@ "description": "Opennebula FireEdge", "main": "dist/index.js", "scripts": { - "build": "webpack --mode=production && concurrently \"npm run copy_static_assets\" \"npm run build:css\"", - "build-node": "webpack --mode=production --env=node", - "build-front": "webpack --mode=production --env=front && concurrently \"npm run copy_static_assets\" \"npm run build:css\"", - "build:css": "node-sass src/public/scss/main.scss dist/public/app.css --output-style compressed", - "dev": "npm run copy_static_assets && concurrently \"npm run build:css\" \"nodemon --inspect dist\" \"webpack --mode=development --session=false --watch\"", - "dev:front": "npm run copy_static_assets && concurrently \"npm run build:css\" \"nodemon --inspect dist\" \"webpack --mode=development --session=false --env=front --watch\"", - + "build": "webpack --mode=production --env.node --env.front --env.ssr && concurrently \"npm run copy_static_assets\"", + "build-node": "webpack --mode=production --env.node --env.ssr", + "build-front": "npm run copy_static_assets && concurrently \"webpack --mode=production --env.front\"", + "dev": "npm run copy_static_assets && concurrently \"nodemon --inspect dist\" \"webpack --mode=development --env.node --env.front --env.ssr --env.hotreload --watch\"", + "dev-front": "npm run copy_static_assets && concurrently \"nodemon --inspect dist\" \"webpack --mode=development --env.node --env.hotreload\" \"webpack --mode=development --env.front --env.hotreload --watch\"" , "start": "node dist/index", "cypress:open": "cypress open", "cypress:run": "cypress run --headless --browser chrome --spec \"cypress/integration/**/*.spec.js\"", + "genPot": "node genPotFile.js", "copy_static_assets": "node copyStaticAssets.js" }, "keywords": [ @@ -42,6 +41,7 @@ "concurrently": "^5.2.0", "cors": "^2.8.5", "express": "^4.17.1", + "fireedge-genpotfile": "^1.0.0", "fs-extra": "^9.0.1", "helmet": "^3.23.3", "http": "0.0.1-security", @@ -49,7 +49,7 @@ "jsonschema": "^1.2.6", "jsonwebtoken": "^8.5.1", "jwt-simple": "^0.5.6", - "moment": "^2.27.0", + "luxon": "^1.25.0", "morgan": "^1.10.0", "node-pre-gyp": "^0.15.0", "one-datatable": "sbbarragan/material-table#develop", @@ -101,11 +101,17 @@ "jloboescalona-eslint-config": "^1.1.0", "node-sass": "^4.12.0", "nodemon": "^1.18.10", + "once": "^1.4.0", "react-addons-test-utils": "^15.6.2", + "readdirp": "^3.4.0", + "split": "^1.0.1", + "stream-combiner": "^0.2.2", "style-loader": "^1.0.0", + "through2": "^4.0.2", "webpack": "^4.43.0", "webpack-cli": "^3.3.7", "webpack-livereload-plugin": "^2.3.0", - "webpack-node-externals": "^1.7.2" + "webpack-node-externals": "^1.7.2", + "xgettext-regex": "^0.3.0" } } diff --git a/src/fireedge/src/index.js b/src/fireedge/src/index.js index 10758de6f0..5178be1dca 100644 --- a/src/fireedge/src/index.js +++ b/src/fireedge/src/index.js @@ -22,7 +22,6 @@ const path = require('path'); const socketIO = require('socket.io'); const cors = require('cors'); const { - existsSync, accessSync, constants: fsConstants, createWriteStream, @@ -36,6 +35,7 @@ const { defaultConfigLogFile, defaultTypeLog } = require('./utils/constants/defaults'); +const { validateServerIsSecure, getCert, getKey } = require('./utils/server'); const { entrypoint404, entrypointApi, @@ -50,10 +50,6 @@ const appConfig = getConfig(); const port = appConfig.PORT || 3000; const userLog = appConfig.LOG || 'dev'; -// ssl -const key = `${__dirname}/../cert/key.pem`; -const cert = `${__dirname}/../cert/cert.pem`; - let log = morgan('dev'); if (userLog === defaultTypeLog) { let logPath = `${defaultConfigLogPath}`; @@ -96,16 +92,15 @@ app.use('/', entrypointApp); // html for react app frontend app.get('*', entrypoint404); // server certificates -const appServer = - existsSync && key && cert && existsSync(key) && existsSync(cert) - ? secureServer( - { - key: readFileSync(key, 'utf8'), - cert: readFileSync(cert, 'utf8') - }, - app - ) - : unsecureServer(app); +const appServer = validateServerIsSecure() + ? secureServer( + { + key: readFileSync(getKey(), 'utf8'), + cert: readFileSync(getCert(), 'utf8') + }, + app + ) + : unsecureServer(app); // connect to websocket const io = socketIO.listen(appServer); diff --git a/src/fireedge/src/public/front-app.js b/src/fireedge/src/public/front-app.js index 2711d19f9a..99f6667f7f 100644 --- a/src/fireedge/src/public/front-app.js +++ b/src/fireedge/src/public/front-app.js @@ -21,16 +21,22 @@ import root from 'window-or-global'; import rootReducer from 'client/reducers'; import App from 'client/app'; +// eslint-disable-next-line no-underscore-dangle const preloadedState = root.__PRELOADED_STATE__; +// eslint-disable-next-line no-underscore-dangle delete root.__PRELOADED_STATE__; const store = createStore( rootReducer(), preloadedState, + // eslint-disable-next-line no-underscore-dangle root.__REDUX_DEVTOOLS_EXTENSION__ && root.__REDUX_DEVTOOLS_EXTENSION__() ); -document.getElementById('preloadState').remove(); +const element = document.getElementById('preloadState'); +if (element) { + element.remove(); +} hydrate(, document.getElementById('root')); diff --git a/src/fireedge/src/public/reducers/index.js b/src/fireedge/src/public/reducers/index.js index 7b9f84d0cc..65b8729647 100644 --- a/src/fireedge/src/public/reducers/index.js +++ b/src/fireedge/src/public/reducers/index.js @@ -17,13 +17,11 @@ const { combineReducers } = require('redux'); const Opennebula = require('./opennebula'); const Zendesk = require('./zendesk'); const General = require('./general'); -const GetConfigSystem = require('./system'); const Authenticated = require('./auth'); -const rootReducers = config => +const rootReducers = () => combineReducers({ Authenticated, - System: GetConfigSystem(config), Opennebula, Zendesk, General diff --git a/src/fireedge/src/public/reducers/system.js b/src/fireedge/src/public/reducers/system.js deleted file mode 100644 index 0bd7c672bf..0000000000 --- a/src/fireedge/src/public/reducers/system.js +++ /dev/null @@ -1,84 +0,0 @@ -/* Copyright 2002-2019, 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. */ -/* -------------------------------------------------------------------------- */ - -let initial = {}; - -const GetConfigSystem = config => { - let data = {}; - if (config) { - const systemData = config(); - if ( - systemData?.SYSTEM_DATA?.NO_AUTH && - Array.isArray(systemData?.SYSTEM_DATA?.NO_AUTH) - ) { - const dataNoAuth = systemData.SYSTEM_DATA.NO_AUTH; - const render = (finder, configData, result = {}) => { - const rtn = result; - const allowedTypes = ['string', 'number', 'boolean']; - if (finder && Array.isArray(finder)) { - finder.forEach(find => { - const entriesFind = Object.entries(find); - const [key, value] = entriesFind && entriesFind[0]; - const findKey = key && key === '0' ? find : key; - const findValue = key && key !== '0' && value; - if (findKey in configData && configData[findKey] !== null) { - const typeElement = typeof configData[findKey]; - if (allowedTypes.includes(typeElement)) { - rtn[findKey] = configData[findKey]; - } else if (Array.isArray(configData[findKey])) { - const xxx = configData[findKey].reduce( - (accValue, currentValue) => { - const validate = render( - findValue, - currentValue, - rtn[findKey] - ); - if (Object.entries(validate).length > 0) { - accValue.push(validate); - } - return accValue; - }, - [] - ); - rtn[findKey] = xxx; - } else { - rtn[findKey] = render( - findValue, - configData[findKey], - rtn[findKey] - ); - } - } - if (configData[findKey] === null) { - rtn[findKey] = null; - } - }); - } - return rtn; - }; - data = render(dataNoAuth, systemData); - } - initial = { ...initial, ...data }; - } - - return (state = initial, action) => { - switch (action.type) { - default: - return state; - } - }; -}; - -module.exports = GetConfigSystem; diff --git a/src/fireedge/src/routes/api/routes/auth/functions.js b/src/fireedge/src/routes/api/routes/auth/functions.js index 4defcb8e75..b59ee4833f 100644 --- a/src/fireedge/src/routes/api/routes/auth/functions.js +++ b/src/fireedge/src/routes/api/routes/auth/functions.js @@ -12,7 +12,7 @@ /* See the License for the specific language governing permissions and */ /* limitations under the License. */ /* -------------------------------------------------------------------------- */ -const moment = require('moment'); +const { DateTime } = require('luxon'); const { Map } = require('immutable'); const { httpMethod, @@ -105,10 +105,11 @@ const setRes = newRes => { const setDates = () => { const limitToken = appConfig.LIMIT_TOKEN; const { MIN, MAX } = limitToken; - now = moment(); - nowUnix = now.unix(); - nowWithDays = moment().add(extended ? MAX : MIN, 'days'); - relativeTime = nowWithDays.diff(now, 'seconds'); + now = DateTime.local(); + nowUnix = now.toSeconds(); + nowWithDays = now.plus({"days": extended ? MAX : MIN}); + const diff = nowWithDays.diff(now, 'seconds'); + relativeTime = diff.seconds; }; const connectOpennebula = () => nodeConnect(user, pass); @@ -149,7 +150,7 @@ const genJWT = informationUser => { if (informationUser && informationUser.ID && informationUser.PASSWORD) { const { ID: id } = informationUser; const dataJWT = { id, user, token: opennebulaToken }; - const jwt = createToken(dataJWT, nowUnix, nowWithDays.format('X')); + const jwt = createToken(dataJWT, nowUnix, nowWithDays.toSeconds()); if (jwt) { if (!global.users) { global.users = {}; diff --git a/src/fireedge/src/routes/entrypoints/App.js b/src/fireedge/src/routes/entrypoints/App.js index 6dfb59402e..e3a18fde18 100644 --- a/src/fireedge/src/routes/entrypoints/App.js +++ b/src/fireedge/src/routes/entrypoints/App.js @@ -21,13 +21,16 @@ const { createStore, compose, applyMiddleware } = require('redux'); const thunk = require('redux-thunk').default; const { ServerStyleSheets } = require('@material-ui/core/styles'); const rootReducer = require('../../public/reducers'); -const App = require('../../public/app').default; const { getConfig } = require('../../utils/yml'); const { includeMAPSJSbyHTML, includeJSbyHTML, includeCSSbyHTML } = require('../../utils'); +const { + defaultIP, + defaultPortHotReload +} = require('../../utils/constants/defaults'); const router = express.Router(); @@ -35,21 +38,30 @@ router.get('*', (req, res) => { const context = {}; const pathPublic = `${__dirname}/public`; - const composeEnhancer = - // eslint-disable-next-line no-underscore-dangle - (root && root.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose; - - const store = createStore( - rootReducer(getConfig), - composeEnhancer(applyMiddleware(thunk)) - ); - - const sheets = new ServerStyleSheets(); - const component = renderToString( - sheets.collect() - ); - const css = sheets.toString(); + let store = ''; + let storeRender = ''; + let component = ''; + let css = ''; + if (process.env.ssr) { + const composeEnhancer = + // eslint-disable-next-line no-underscore-dangle + (root && root.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose; + store = createStore( + rootReducer(getConfig), + composeEnhancer(applyMiddleware(thunk)) + ); + storeRender = ``; + // eslint-disable-next-line global-require + const App = require('../../public/app').default; + const sheets = new ServerStyleSheets(); + component = renderToString( + sheets.collect() + ); + css = ``; + } const html = ` @@ -57,17 +69,17 @@ router.get('*', (req, res) => { - + ${css} ${includeCSSbyHTML(pathPublic)} + ${ + process.env.hotreload + ? `` + : '' + }
${component}
- + ${storeRender} ${includeJSbyHTML(pathPublic) + includeMAPSJSbyHTML(pathPublic)} diff --git a/src/fireedge/src/routes/entrypoints/middlewares/api/index.js b/src/fireedge/src/routes/entrypoints/middlewares/api/index.js index b213a9c222..ef8f22ef9c 100644 --- a/src/fireedge/src/routes/entrypoints/middlewares/api/index.js +++ b/src/fireedge/src/routes/entrypoints/middlewares/api/index.js @@ -59,8 +59,7 @@ const validateResource = (req, res, next) => { idUserOpennebula = session.iss; userOpennebula = session.aud; passOpennebula = session.jti; - - if (!process.env.session) { + if (process.env.ssr) { if ( global && global.users && diff --git a/src/fireedge/src/utils/constants/defaults.js b/src/fireedge/src/utils/constants/defaults.js index 005302a149..6bd072ce3a 100644 --- a/src/fireedge/src/utils/constants/defaults.js +++ b/src/fireedge/src/utils/constants/defaults.js @@ -61,6 +61,9 @@ const defaults = { defaultMethodUserUpdate: 'user.update', defaultMethodUserInfo: 'user.info', defaultLang: 'en_US', + defaultIP: defaultIp, + defaultProtocolHotReload: 'http', + defaultPortHotReload: '3001', translations: { en_US: 'English', ca: 'Catalan', diff --git a/src/fireedge/src/utils/jwt.js b/src/fireedge/src/utils/jwt.js index 5001122b79..def14ccf61 100644 --- a/src/fireedge/src/utils/jwt.js +++ b/src/fireedge/src/utils/jwt.js @@ -14,7 +14,7 @@ /* -------------------------------------------------------------------------- */ const jwt = require('jwt-simple'); -const moment = require('moment'); +const { DateTime } = require('luxon'); const { messageTerminal } = require('./general'); const { getConfig } = require('./yml'); @@ -50,6 +50,7 @@ const validateAuth = req => { const token = authorization.replace(removeBearer, ''); try { const payload = jwt.decode(token, tokenSecret); + const now = DateTime.local(); if ( payload && 'iss' in payload && @@ -57,7 +58,7 @@ const validateAuth = req => { 'jti' in payload && 'iat' in payload && 'exp' in payload && - payload.exp >= moment().unix() + payload.exp >= now.toSeconds() ) { const { iss, aud, jti, iat, exp } = payload; rtn = { diff --git a/src/fireedge/src/utils/server.js b/src/fireedge/src/utils/server.js index 3c73d8779a..20ac989a3d 100644 --- a/src/fireedge/src/utils/server.js +++ b/src/fireedge/src/utils/server.js @@ -13,8 +13,21 @@ /* limitations under the License. */ /* -------------------------------------------------------------------------- */ const { Map } = require('immutable'); +const { existsSync } = require('fs-extra'); const { internalServerError } = require('./constants/http-codes'); +let cert = ''; +let key = ''; + +const validateServerIsSecure = () => { + cert = `${__dirname}/../cert/cert.pem`; + key = `${__dirname}/../cert/key.pem`; + return existsSync && key && cert && existsSync(key) && existsSync(cert); +}; + +const getCert = () => cert; +const getKey = () => key; + const httpResponse = (response, data, message) => { let rtn = Map(internalServerError).toObject(); rtn.data = data; @@ -31,5 +44,8 @@ const httpResponse = (response, data, message) => { }; module.exports = { - httpResponse + httpResponse, + validateServerIsSecure, + getCert, + getKey }; diff --git a/src/fireedge/webpack.config.js b/src/fireedge/webpack.config.js index b54f35b1de..eb2f9a6480 100644 --- a/src/fireedge/webpack.config.js +++ b/src/fireedge/webpack.config.js @@ -15,10 +15,15 @@ const webpack = require('webpack'); const nodeExternals = require('webpack-node-externals'); +const LiveReloadPlugin = require('webpack-livereload-plugin'); const path = require('path'); +const { defaultProtocolHotReload } = require('./src/utils/server'); + const { defaultWebpackMode, - defaultWebpackDevTool + defaultWebpackDevTool, + defaultIP, + defaultPortHotReload } = require('./src/utils/constants/defaults'); const js = { @@ -102,32 +107,40 @@ const clientConfig = { }; module.exports = (env, argv) => { + const build = []; + const systemVars = {}; + const plugins = []; + if (argv && argv.mode !== defaultWebpackMode) { [clientConfig.mode, serverConfig.mode] = Array(2).fill('production'); [clientConfig.devtool, serverConfig.devtool] = Array(2).fill(''); - } else if (argv && argv.session && argv.session === 'false') { - const pluginProcessEnv = [ - new webpack.DefinePlugin({ - 'process.env': { - session: JSON.stringify(argv.session) - } - }) - ]; - clientConfig.plugins = pluginProcessEnv; - serverConfig.plugins = pluginProcessEnv; } - let build = []; + if (env) { - switch (env) { - case 'front': - build.push(clientConfig); - break; - case 'node': - build.push(serverConfig); - break; + if (env.ssr) { + systemVars['process.env.ssr'] = true; + } + if (env.hotreload) { + systemVars['process.env.hotreload'] = true; + plugins.push( + new LiveReloadPlugin({ + port: parseInt(defaultPortHotReload, 10), + host: defaultIP, + protocol: defaultProtocolHotReload + }) + ); + } + + plugins.push(new webpack.DefinePlugin(systemVars)); + clientConfig.plugins = plugins; + serverConfig.plugins = plugins; + + if (env.front) { + build.push(clientConfig); + } + if (env.node) { + build.push(serverConfig); } - } else { - build = [serverConfig, clientConfig]; } return build; };