From 5061c83f0c419e44528c77a1bf9cc496842c461d Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sat, 18 Jan 2025 18:24:19 +0100 Subject: [PATCH] copy new setup from frontend to admin --- admin/Dockerfile | 2 +- admin/package.json | 1 + admin/src/config/index.js | 10 +-- admin/src/config/schema.js | 91 ++++++++++++++++------- admin/vite.config.js | 135 ++++++++++++++++++++++------------ admin/yarn.lock | 40 ++++++++++ docker-compose.override.yml | 2 +- docker-compose.yml | 4 +- frontend/src/config/index.js | 6 +- frontend/src/config/schema.js | 10 +-- 10 files changed, 209 insertions(+), 92 deletions(-) diff --git a/admin/Dockerfile b/admin/Dockerfile index a01903cc3..2c90774bb 100644 --- a/admin/Dockerfile +++ b/admin/Dockerfile @@ -11,7 +11,7 @@ ENV BUILD_DATE="1970-01-01T00:00:00.00Z" ## We cannot do $(npm run version).${BUILD_NUMBER} here so we default to 0.0.0.0 ENV BUILD_VERSION="0.0.0.0" ## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000 -ENV BUILD_COMMIT="0000000" +ENV BUILD_COMMIT_SHORT="0000000" ## SET NODE_ENV ARG NODE_ENV="production" ## App relevant Envs diff --git a/admin/package.json b/admin/package.json index efa0a0b4e..0068f5efd 100644 --- a/admin/package.json +++ b/admin/package.json @@ -74,6 +74,7 @@ "eslint-plugin-prettier": "5.2.1", "eslint-plugin-promise": "^5.1.1", "eslint-plugin-vue": "8.7.1", + "joi": "^17.13.3", "jsdom": "^25.0.0", "mock-apollo-client": "^1.2.1", "postcss": "^8.4.8", diff --git a/admin/src/config/index.js b/admin/src/config/index.js index 2973be257..b73657219 100644 --- a/admin/src/config/index.js +++ b/admin/src/config/index.js @@ -3,15 +3,13 @@ // Load Package Details for some default values const pkg = require('../../package') -const schema = require('./schema') -const joi = require('joi') const version = { ADMIN_MODULE_PROTOCOL: process.env.ADMIN_MODULE_PROTOCOL ?? 'http', ADMIN_MODULE_HOST: process.env.ADMIN_MODULE_HOST ?? '0.0.0.0', ADMIN_MODULE_PORT: process.env.ADMIN_MODULE_PORT ?? '8080', APP_VERSION: pkg.version, - BUILD_COMMIT: process.env.BUILD_COMMIT ?? null, + BUILD_COMMIT: process.env.BUILD_COMMIT ?? undefined, // self reference of `version.BUILD_COMMIT` is not possible at this point, hence the duplicate code BUILD_COMMIT_SHORT: (process.env.BUILD_COMMIT ?? '0000000').slice(0, 7), } @@ -58,10 +56,8 @@ const CONFIG = { ...environment, ...endpoints, ...debug, + ADMIN_MODULE_URL, + COMMUNITY_URL, } -// Check config version -// TODO: use validate and construct error message including description -joi.attempt(CONFIG, schema) - module.exports = CONFIG diff --git a/admin/src/config/schema.js b/admin/src/config/schema.js index 202a5fd8b..c992eddc7 100644 --- a/admin/src/config/schema.js +++ b/admin/src/config/schema.js @@ -1,25 +1,59 @@ -const commonSchema = require('../../../config/common.schema') +const { + browserUrls, + APP_VERSION, + BUILD_COMMIT, + BUILD_COMMIT_SHORT, + COMMUNITY_URL, + DEBUG, + GRAPHQL_URI, + NODE_ENV, + PRODUCTION, +} = require('../../../config/common.schema') // from '../../../config/common.schema' const Joi = require('joi') -module.exports = commonSchema.keys({ +module.exports = Joi.object({ + browserUrls, + APP_VERSION, + BUILD_COMMIT, + BUILD_COMMIT_SHORT, + COMMUNITY_URL, + DEBUG, + GRAPHQL_URI, + NODE_ENV, + PRODUCTION, + + ADMIN_HOSTING: Joi.string() + .valid('nodejs', 'nginx') + .description('set to `nodejs` if admin is hosted by vite with a own nodejs instance') + .optional(), + + ADMIN_MODULE_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .when('COMMUNITY_URL', { + is: Joi.exist(), + then: Joi.optional(), // not required if COMMUNITY_URL is provided + otherwise: Joi.required(), // required if COMMUNITY_URL is missing + }) + .description("Base Url for reaching admin in browser, only needed if COMMUNITY_URL wasn't set") + .optional(), // optional in general, but conditionally required + ADMIN_MODULE_PROTOCOL: Joi.string() - .valid('http', 'https') - .when('BROWSER_PROTOCOL', { - is: 'https', - then: Joi.string().uri({ scheme: 'https' }), - otherwise: Joi.string().uri({ scheme: 'http' }), + .when('ADMIN_HOSTING', { + is: Joi.valid('nodejs'), + then: Joi.valid('http').required(), + otherwise: Joi.valid('http', 'https').required(), }) .description( - 'Protocol for admin module hosting, has to be the same as for backend api url and admin to prevent mixed block errors' + ` + Protocol for admin module hosting + - it has to be the same as for backend api url and frontend to prevent mixed block errors, + - if admin is served with nodejs: + is have to be http or setup must be updated to include a ssl certificate + `, ) .default('http') .required(), - ADMIN_HOSTING: Joi.string() - .valid('nodejs') - .description('set to `nodejs` if admin is hosted by vite with a own nodejs instance') - .optional(), - ADMIN_MODULE_HOST: Joi.alternatives() .try( Joi.string().valid('localhost').messages({ 'any.invalid': 'Must be localhost' }), @@ -28,11 +62,20 @@ module.exports = commonSchema.keys({ .messages({ 'string.ip': 'Must be a valid IPv4 address' }), Joi.string().domain().messages({ 'string.domain': 'Must be a valid domain' }), ) + .when('ADMIN_HOSTING', { + is: 'nodejs', + then: Joi.required(), + otherwise: Joi.optional(), + }) + .when('COMMUNITY_URL', { + is: null, + then: Joi.required(), + otherwise: Joi.optional(), + }) .description( 'Host (domain, IPv4, or localhost) for the admin, default is 0.0.0.0 for local hosting during develop', ) - .default('0.0.0.0') - .required(), // required only if community_url isn't set or ADMIN_HOSTING is nodejs + .default('0.0.0.0'), ADMIN_MODULE_PORT: Joi.number() .integer() @@ -40,26 +83,20 @@ module.exports = commonSchema.keys({ .max(49151) .description('Port for hosting Admin with Vite as a Node.js instance, default: 8080') .default(8080) - .required(), // required only if ADMIN_HOSTING is nodejs + .when('ADMIN_HOSTING', { + is: 'nodejs', + then: Joi.required(), + otherwise: Joi.optional(), + }), WALLET_AUTH_URL: Joi.string() .uri({ scheme: ['http', 'https'] }) - .when('browser_protocol', { - is: 'https', - then: Joi.string().uri({ scheme: 'https' }), - otherwise: Joi.string().uri({ scheme: 'http' }), - }) .description('Extern Url from wallet-frontend for forwarding from admin') .default('http://0.0.0.0/authenticate?token=') .required(), WALLET_LOGIN_URL: Joi.string() .uri({ scheme: ['http', 'https'] }) - .when('browser_protocol', { - is: 'https', - then: Joi.string().uri({ scheme: 'https' }), - otherwise: Joi.string().uri({ scheme: 'http' }), - }) .description('Extern Url from wallet-frontend for forwarding after logout') .default('http://0.0.0.0/login') .required(), @@ -68,4 +105,4 @@ module.exports = commonSchema.keys({ .description('Flag for disable authorization during development') .default(false) .optional(), // true is only allowed in not-production setup -}) \ No newline at end of file +}) diff --git a/admin/vite.config.js b/admin/vite.config.js index 40c51a167..db889d4d2 100644 --- a/admin/vite.config.js +++ b/admin/vite.config.js @@ -6,6 +6,7 @@ import Components from 'unplugin-vue-components/vite' import IconsResolve from 'unplugin-icons/resolver' import { BootstrapVueNextResolver } from 'bootstrap-vue-next' import EnvironmentPlugin from 'vite-plugin-environment' +import schema from './src/config/schema' import dotenv from 'dotenv' @@ -15,55 +16,93 @@ const CONFIG = require('./src/config') const path = require('path') -export default defineConfig({ - base: '/admin/', - server: { - host: CONFIG.ADMIN_MODULE_HOST, // '0.0.0.0', - port: CONFIG.ADMIN_MODULE_PORT, // 8080, - }, - resolve: { - alias: { - '@': path.resolve(__dirname, './src'), - assets: path.join(__dirname, 'src/assets'), - }, - extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'], - }, +export default defineConfig(({ command }) => { + if (command === 'serve') { + CONFIG.ADMIN_HOSTING = 'nodejs' + } else { + CONFIG.ADMIN_HOSTING = 'nginx' + } + // Check config + const configDataForValidation = { + ...CONFIG, + // make sure that all urls used in browser have the same protocol to prevent mixed content errors + browserUrls: [ + CONFIG.WALLET_AUTH_URL, + CONFIG.COMMUNITY_URL, + CONFIG.WALLET_LOGIN_URL, + CONFIG.GRAPHQL_URI, + CONFIG.ADMIN_MODULE_URL, + ], + } - plugins: [ - vue({ - template: { - compilerOptions: { - compatConfig: { - MODE: 2, + const { error } = schema.validate(configDataForValidation, { stack: true }) + const schemaJson = schema.describe() + if (error) { + error.details.forEach((err) => { + const key = err.context.key + const value = err.context.value + const description = schemaJson.keys[key] + ? schema.describe().keys[key].flags.description + : 'No description available' + if (configDataForValidation[key] === undefined) { + throw new Error(`Environment Variable '${key}' is missing. ${description}`) + } else { + throw new Error( + `Error on Environment Variable ${key} with value = ${value}: ${err.message}. ${description}`, + ) + } + }) + } + + return { + base: '/admin/', + server: { + host: CONFIG.ADMIN_MODULE_HOST, // '0.0.0.0', + port: CONFIG.ADMIN_MODULE_PORT, // 8080, + }, + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + assets: path.join(__dirname, 'src/assets'), + }, + extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'], + }, + + plugins: [ + vue({ + template: { + compilerOptions: { + compatConfig: { + MODE: 2, + }, }, }, - }, - }), - Components({ - resolvers: [IconsResolve(), BootstrapVueNextResolver()], - dts: true, - }), - Icons({ - compiler: 'vue3', - }), - EnvironmentPlugin({ - BUILD_COMMIT: null, - PORT: CONFIG.ADMIN_MODULE_PORT, // null, - COMMUNITY_HOST: CONFIG.ADMIN_MODULE_HOST, // null, - URL_PROTOCOL: CONFIG.ADMIN_MODULE_PROTOCOL, // null, - WALLET_URL: CONFIG.WALLET_AUTH_URL, // null, - GRAPHQL_URL: CONFIG.GRAPHQL_URI, // null, - GRAPHQL_PATH: process.env.GRAPHQL_PATH ?? '/graphql', // null, - WALLET_AUTH_PATH: CONFIG.WALLET_AUTH_URL, // null, - WALLET_LOGIN_PATH: CONFIG.WALLET_LOGIN_URL, // null, - DEBUG_DISABLE_AUTH: CONFIG.DEBUG_DISABLE_AUTH, // null, - // CONFIG_VERSION: CONFIG.CONFIG_VERSION, // null, - }), - commonjs(), - ], - build: { - outDir: path.resolve(__dirname, './build'), - chunkSizeWarningLimit: 1600, - }, - publicDir: '/admin', + }), + Components({ + resolvers: [IconsResolve(), BootstrapVueNextResolver()], + dts: true, + }), + Icons({ + compiler: 'vue3', + }), + EnvironmentPlugin({ + BUILD_COMMIT: null, + PORT: CONFIG.ADMIN_MODULE_PORT, // null, + COMMUNITY_HOST: CONFIG.ADMIN_MODULE_HOST, // null, + URL_PROTOCOL: CONFIG.ADMIN_MODULE_PROTOCOL, // null, + WALLET_URL: CONFIG.WALLET_AUTH_URL, // null, + GRAPHQL_URL: CONFIG.GRAPHQL_URI, // null, + GRAPHQL_PATH: process.env.GRAPHQL_PATH ?? '/graphql', // null, + WALLET_AUTH_PATH: CONFIG.WALLET_AUTH_URL, // null, + WALLET_LOGIN_PATH: CONFIG.WALLET_LOGIN_URL, // null, + DEBUG_DISABLE_AUTH: CONFIG.DEBUG_DISABLE_AUTH, // null, + }), + commonjs(), + ], + build: { + outDir: path.resolve(__dirname, './build'), + chunkSizeWarningLimit: 1600, + }, + publicDir: '/admin', + } }) diff --git a/admin/yarn.lock b/admin/yarn.lock index 03e984ff8..7a0644174 100644 --- a/admin/yarn.lock +++ b/admin/yarn.lock @@ -1072,6 +1072,18 @@ resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== +"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0": + version "9.3.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" + integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== + +"@hapi/topo@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@humanwhocodes/config-array@^0.11.14": version "0.11.14" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" @@ -1471,6 +1483,23 @@ resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== +"@sideway/address@^4.1.5": + version "4.1.5" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" + integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + "@types/estree@1.0.6", "@types/estree@^1.0.0": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" @@ -4712,6 +4741,17 @@ jest-canvas-mock@~2.5.2: cssfontparser "^1.2.1" moo-color "^1.0.2" +joi@^17.13.3: + version "17.13.3" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.13.3.tgz#0f5cc1169c999b30d344366d384b12d92558bcec" + integrity sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA== + dependencies: + "@hapi/hoek" "^9.3.0" + "@hapi/topo" "^5.1.0" + "@sideway/address" "^4.1.5" + "@sideway/formula" "^3.0.1" + "@sideway/pinpoint" "^2.0.0" + js-beautify@^1.14.9: version "1.15.1" resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.15.1.tgz#4695afb508c324e1084ee0b952a102023fc65b64" diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 3f967f229..a15555ea1 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -27,7 +27,7 @@ services: build: target: development environment: - - NODE_ENV="development" + - NODE_ENV=development # - DEBUG=true volumes: # This makes sure the docker container has its own node modules. diff --git a/docker-compose.yml b/docker-compose.yml index 0ab610f48..e35ff1baf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -54,7 +54,9 @@ services: # - BUILD_DATE="1970-01-01T00:00:00.00Z" # - BUILD_VERSION="0.0.0.0" # - BUILD_COMMIT="0000000" - - NODE_ENV="production" + - NODE_ENV=production + volumes: + - ./config:/config # env_file: # - ./.env # - ./admin/.env diff --git a/frontend/src/config/index.js b/frontend/src/config/index.js index 2e469a33c..1ace61a74 100644 --- a/frontend/src/config/index.js +++ b/frontend/src/config/index.js @@ -18,8 +18,7 @@ const version = { BUILD_COMMIT_SHORT: (process.env.BUILD_COMMIT ?? '0000000').slice(0, 7), } -// in case of hosting the frontend module with a nginx -let FRONTEND_MODULE_URL = version.FRONTEND_MODULE_PROTOCOL + '://' + version.FRONTEND_MODULE_HOST +let FRONTEND_MODULE_URL // in case of hosting the frontend module with a nodejs-instance if (process.env.FRONTEND_HOSTING === 'nodejs') { @@ -29,6 +28,9 @@ if (process.env.FRONTEND_HOSTING === 'nodejs') { version.FRONTEND_MODULE_HOST + ':' + version.FRONTEND_MODULE_PORT +} else { + // in case of hosting the frontend module with a nginx + FRONTEND_MODULE_URL = version.FRONTEND_MODULE_PROTOCOL + '://' + version.FRONTEND_MODULE_HOST } // const FRONTEND_MODULE_URI = version.FRONTEND_MODULE_PROTOCOL + '://' + version.FRONTEND_MODULE_HOST // + diff --git a/frontend/src/config/schema.js b/frontend/src/config/schema.js index ac762562b..a840a8510 100644 --- a/frontend/src/config/schema.js +++ b/frontend/src/config/schema.js @@ -49,6 +49,11 @@ module.exports = Joi.object({ .description('URL for Register a new Account in frontend.') .required(), + FRONTEND_HOSTING: Joi.string() + .valid('nodejs', 'nginx') + .description('set to `nodejs` if frontend is hosted by vite with a own nodejs instance') + .optional(), + FRONTEND_MODULE_URL: Joi.string() .uri({ scheme: ['http', 'https'] }) .when('COMMUNITY_URL', { @@ -78,11 +83,6 @@ module.exports = Joi.object({ .default('http') .required(), - FRONTEND_HOSTING: Joi.string() - .valid('nodejs', 'nginx') - .description('set to `nodejs` if frontend is hosted by vite with a own nodejs instance') - .optional(), - FRONTEND_MODULE_HOST: Joi.alternatives() .try( Joi.string().valid('localhost').messages({ 'any.invalid': 'Must be localhost' }),