From 5ca51a9941676983f01393ab7d06394e8945b593 Mon Sep 17 00:00:00 2001 From: Einhornimmond Date: Sat, 18 Jan 2025 08:36:16 +0100 Subject: [PATCH] more schemas --- admin/src/config/index.js | 31 +++------- admin/src/config/schema.js | 71 +++++++++++++++++++++++ backend/src/config/index.ts | 21 +------ backend/src/config/schema.ts | 30 ++++++++++ config/common.schema.js | 102 +++++++++++++++++++++++++-------- frontend/src/config/index.js | 104 ++++++++++++++++++++++++++++++++-- frontend/src/config/schema.js | 78 ++++++++++++++++--------- frontend/vite.config.js | 1 + 8 files changed, 339 insertions(+), 99 deletions(-) create mode 100644 admin/src/config/schema.js create mode 100644 backend/src/config/schema.ts diff --git a/admin/src/config/index.js b/admin/src/config/index.js index 2c6302e29..2973be257 100644 --- a/admin/src/config/index.js +++ b/admin/src/config/index.js @@ -3,14 +3,8 @@ // Load Package Details for some default values const pkg = require('../../package') - -const constants = { - CONFIG_VERSION: { - DEFAULT: 'DEFAULT', - EXPECTED: 'v3.2024-08-06', - CURRENT: '', - }, -} +const schema = require('./schema') +const joi = require('joi') const version = { ADMIN_MODULE_PROTOCOL: process.env.ADMIN_MODULE_PROTOCOL ?? 'http', @@ -47,11 +41,11 @@ const environment = { // const COMMUNITY_URL = // COMMUNITY_HOST && URL_PROTOCOL ? URL_PROTOCOL + '://' + COMMUNITY_HOST : undefined const COMMUNITY_URL = process.env.COMMUNITY_URL ?? ADMIN_MODULE_URL -const WALLET_URL = process.env.WALLET_URL ?? COMMUNITY_URL ?? 'http://localhost' +const WALLET_URL = process.env.WALLET_URL ?? COMMUNITY_URL ?? 'http://0.0.0.0' const endpoints = { GRAPHQL_URI: process.env.GRAPHQL_URL ?? COMMUNITY_URL + (process.env.GRAPHQL_PATH ?? '/graphql'), - WALLET_AUTH_URL: WALLET_URL + (process.env.WALLET_AUTH_PATH ?? '/authenticate?token={token}'), + WALLET_AUTH_URL: WALLET_URL + (process.env.WALLET_AUTH_PATH ?? '/authenticate?token='), WALLET_LOGIN_URL: WALLET_URL + (process.env.WALLET_LOGIN_PATH ?? '/login'), } @@ -59,24 +53,15 @@ const debug = { DEBUG_DISABLE_AUTH: process.env.DEBUG_DISABLE_AUTH === 'true' ?? false, } -// Check config version -constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION ?? constants.CONFIG_VERSION.DEFAULT -if ( - ![constants.CONFIG_VERSION.EXPECTED, constants.CONFIG_VERSION.DEFAULT].includes( - constants.CONFIG_VERSION.CURRENT, - ) -) { - throw new Error( - `Fatal: Config Version incorrect - expected "${constants.CONFIG_VERSION.EXPECTED}" or "${constants.CONFIG_VERSION.DEFAULT}", but found "${constants.CONFIG_VERSION.CURRENT}"`, - ) -} - const CONFIG = { - ...constants, ...version, ...environment, ...endpoints, ...debug, } +// 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 new file mode 100644 index 000000000..202a5fd8b --- /dev/null +++ b/admin/src/config/schema.js @@ -0,0 +1,71 @@ +const commonSchema = require('../../../config/common.schema') +const Joi = require('joi') + +module.exports = commonSchema.keys({ + 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' }), + }) + .description( + 'Protocol for admin module hosting, has to be the same as for backend api url and admin to prevent mixed block errors' + ) + .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' }), + Joi.string() + .ip({ version: ['ipv4'] }) + .messages({ 'string.ip': 'Must be a valid IPv4 address' }), + Joi.string().domain().messages({ 'string.domain': 'Must be a valid domain' }), + ) + .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 + + ADMIN_MODULE_PORT: Joi.number() + .integer() + .min(1024) + .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 + + 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(), + + DEBUG_DISABLE_AUTH: Joi.boolean() + .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/backend/src/config/index.ts b/backend/src/config/index.ts index b65de4e37..7b8c7d709 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -15,13 +15,6 @@ const constants = { DB_VERSION: '0087-add_index_on_user_roles', DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', - // default log level on production should be info - LOG_LEVEL: process.env.LOG_LEVEL ?? 'info', - CONFIG_VERSION: { - DEFAULT: 'DEFAULT', - EXPECTED: 'v23.2024-04-04', - CURRENT: '', - }, } const server = { @@ -31,6 +24,8 @@ const server = { GRAPHIQL: process.env.GRAPHIQL === 'true' || false, GDT_API_URL: process.env.GDT_API_URL ?? 'https://gdt.gradido.net', PRODUCTION: process.env.NODE_ENV === 'production' || false, + // default log level on production should be info + LOG_LEVEL: process.env.LOG_LEVEL ?? 'info', } const database = { @@ -114,18 +109,6 @@ const webhook = { // This is needed by graphql-directive-auth process.env.APP_SECRET = server.JWT_SECRET -// Check config version -constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION ?? constants.CONFIG_VERSION.DEFAULT -if ( - ![constants.CONFIG_VERSION.EXPECTED, constants.CONFIG_VERSION.DEFAULT].includes( - constants.CONFIG_VERSION.CURRENT, - ) -) { - throw new Error( - `Fatal: Config Version incorrect - expected "${constants.CONFIG_VERSION.EXPECTED}" or "${constants.CONFIG_VERSION.DEFAULT}", but found "${constants.CONFIG_VERSION.CURRENT}"`, - ) -} - const federation = { FEDERATION_BACKEND_SEND_ON_API: process.env.FEDERATION_BACKEND_SEND_ON_API ?? '1_0', // ?? operator don't work here as expected diff --git a/backend/src/config/schema.ts b/backend/src/config/schema.ts new file mode 100644 index 000000000..9598e5b0d --- /dev/null +++ b/backend/src/config/schema.ts @@ -0,0 +1,30 @@ +const commonSchema = require('../../../config/common.schema') +const Joi = require('joi') + +module.exports = commonSchema.keys({ + PORT: Joi.number() + .integer() + .min(1024) + .max(49151) + .description('Port for hosting backend, default: 4000') + .default(4000) + .required(), + + // TODO: check format + JWT_SECRET: Joi.string() + .default('secret123') + .description('jwt secret for jwt tokens used for login') + .required(), + + // TODO: check format + JWT_EXPIRES_IN: Joi.string() + .default('10m') + .description('time for jwt token expire, auto logout') + .required(), + + GDT_API_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .default('https://gdt.gradido.net') + .required(), // TODO: only required if gdt is active + +}) \ No newline at end of file diff --git a/config/common.schema.js b/config/common.schema.js index a3debf455..e39c62f65 100644 --- a/config/common.schema.js +++ b/config/common.schema.js @@ -1,26 +1,28 @@ const Joi = require('joi') module.exports = Joi.object({ - browser_protocol: Joi.string() + BROWSER_PROTOCOL: Joi.string() .valid('http', 'https') .description( 'Protocol for all URLs in the browser, must be either http or https to prevent mixed content issues.', ) - .default('http'), + .default('http') + .require(), - community_url: Joi.string() + COMMUNITY_URL: Joi.string() .uri({ scheme: ['http', 'https'] }) - .when('browser_protocol', { + .when('BROWSER_PROTOCOL', { is: 'https', then: Joi.string().uri({ scheme: 'https' }), otherwise: Joi.string().uri({ scheme: 'http' }), }) - .description('The base URL of the community') - .default('http://0.0.0.0'), + .description('The base URL of the community, should have the same scheme like frontend, admin and backend api to prevent mixed contend issues.') + .default('http://0.0.0.0') + .require(), - external_backend_url: Joi.string() + GRAPHQL_URI: Joi.string() .uri({ scheme: ['http', 'https'] }) - .when('browser_protocol', { + .when('BROWSER_PROTOCOL', { is: 'https', then: Joi.string().uri({ scheme: 'https' }), otherwise: Joi.string().uri({ scheme: 'http' }), @@ -32,39 +34,89 @@ module.exports = Joi.object({ must use the same protocol as browser_protocol. `, ) - .default('http://0.0.0.0/graphql'), + .default('http://0.0.0.0/graphql') + .require(), - community_name: Joi.string() + COMMUNITY_NAME: Joi.string() .min(3) .max(100) .description('The name of the community') - .default('Gradido Entwicklung'), + .default('Gradido Entwicklung') + .require(), - community_description: Joi.string() + COMMUNITY_DESCRIPTION: Joi.string() .min(10) .max(300) .description('A short description of the community') - .default('Die lokale Entwicklungsumgebung von Gradido.'), + .default('Die lokale Entwicklungsumgebung von Gradido.') + .require(), - community_support_email: Joi.string() + COMMUNITY_SUPPORT_MAIL: Joi.string() .email() .description('The support email address for the community will be used in frontend and E-Mails') - .default('support@supportmail.com'), + .default('support@supportmail.com') + .require(), - community_location: Joi.string() + COMMUNITY_LOCATION: Joi.string() .pattern(/^[-+]?[0-9]{1,2}(\.[0-9]+)?,\s?[-+]?[0-9]{1,3}(\.[0-9]+)?$/) + // TODO: ask chatgpt for the correct way + .when('GMS_ACTIVE', { + is: true, + then: Joi.string().require(), + otherwise: Joi.string().optional() + }) .description('Geographical location of the community in "latitude, longitude" format') - .default('49.280377, 9.690151'), + .default('49.280377, 9.690151') + .require(), + + GRAPHIQL: Joi.boolean() + .description('Flag for enabling graphql playground for debugging.') + .default(false) + .require(), // todo: only allow true in development mode - gdt_active: Joi.boolean() - .description('Flag to indicate if gdt (Gradido Transform) service is used.') - .default(false), - - gms_active: Joi.boolean() + GMS_ACTIVE: Joi.boolean() .description('Flag to indicate if the GMS (Geographic Member Search) service is used.') - .default(false), + .default(false) + .require(), - humhub_active: Joi.boolean() + HUMHUB_ACTIVE: Joi.boolean() .description('Flag to indicate if the HumHub based Community Server is used.') - .default(false), + .default(false) + .require(), + + LOG_LEVEL: Joi.string() + .valid(['INFO', 'ERROR']) // TODO: lookup values + .description('set log level') + .default('INFO') + .required(), + + DB_HOST: Joi.string() + .uri({ scheme: ['http'] }) + .description("database host like 'localhost' or 'mariadb' in docker setup") + .default('localhost') + .required(), + +DB_PORT: Joi.number() + .integer() + .min(1024) + .max(49151) + .description('database port, default: 3306') + .default(3306) + .required(), + +// TODO: check allowed users for mariadb +DB_USER: Joi.string() + .description('database user name like root (default) or gradido') + .default('root') + .required(), + +DB_PASSWORD: Joi.string() + .description('database password') + .default('') + .required(), + +DB_DATABASE: Joi.string() + .description('database name like gradido_community') + .default('gradido_community') + .required(), }) \ No newline at end of file diff --git a/frontend/src/config/index.js b/frontend/src/config/index.js index c1c06e555..ade682bf3 100644 --- a/frontend/src/config/index.js +++ b/frontend/src/config/index.js @@ -1,13 +1,107 @@ -/* eslint-disable camelcase */ // ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env). // The whole contents is exposed to the client // Load Package Details for some default values -import schema from './schema' -import { validateAndExport } from '../../../config' +const pkg = require('../../package') +const schema = require('./schema') +const joi = require('joi') -const CONFIG = { - ...validateAndExport(schema), +const constants = { + DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 } +const version = { + FRONTEND_MODULE_PROTOCOL: process.env.FRONTEND_MODULE_PROTOCOL ?? 'http', + FRONTEND_MODULE_HOST: process.env.FRONTEND_MODULE_HOST ?? '0.0.0.0', + FRONTEND_MODULE_PORT: process.env.FRONTEND_MODULE_PORT ?? '3000', + APP_VERSION: pkg.version, + BUILD_COMMIT: process.env.BUILD_COMMIT ?? null, + // 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), +} + +let FRONTEND_MODULE_URL +// in case of hosting the frontend module with a nodejs-instance +if (process.env.FRONTEND_HOSTING === 'nodejs') { + FRONTEND_MODULE_URL = + version.FRONTEND_MODULE_PROTOCOL + + '://' + + 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 // + +// ':' + +// version.FRONTEND_MODULE_PORT + +const features = { + GMS_ACTIVE: process.env.GMS_ACTIVE ?? false, + HUMHUB_ACTIVE: process.env.HUMHUB_ACTIVE ?? false, +} + +const environment = { + NODE_ENV: process.env.NODE_ENV, + DEBUG: process.env.NODE_ENV !== 'production' ?? false, + PRODUCTION: process.env.NODE_ENV === 'production' ?? false, + DEFAULT_PUBLISHER_ID: process.env.DEFAULT_PUBLISHER_ID ?? 2896, +} + +// const COMMUNITY_HOST = process.env.COMMUNITY_HOST ?? 'localhost' +// const URL_PROTOCOL = process.env.URL_PROTOCOL ?? 'http' +const COMMUNITY_URL = process.env.COMMUNITY_URL ?? FRONTEND_MODULE_URL + +const endpoints = { + GRAPHQL_URI: process.env.GRAPHQL_URI ?? COMMUNITY_URL + (process.env.GRAPHQL_PATH ?? '/graphql'), + ADMIN_AUTH_URL: + process.env.ADMIN_AUTH_URL ?? + COMMUNITY_URL + (process.env.ADMIN_AUTH_PATH ?? '/admin/authenticate?token='), +} + +const community = { + COMMUNITY_NAME: process.env.COMMUNITY_NAME ?? 'Gradido Entwicklung', + COMMUNITY_URL: COMMUNITY_URL, + COMMUNITY_REGISTER_URL: COMMUNITY_URL + (process.env.COMMUNITY_REGISTER_PATH ?? '/register'), + COMMUNITY_DESCRIPTION: + process.env.COMMUNITY_DESCRIPTION ?? 'Die lokale Entwicklungsumgebung von Gradido.', + COMMUNITY_SUPPORT_MAIL: process.env.COMMUNITY_SUPPORT_MAIL ?? 'support@supportmail.com', + COMMUNITY_LOCATION: process.env.COMMUNITY_LOCATION ?? '49.280377, 9.690151', +} + +const meta = { + META_URL: process.env.META_URL ?? 'http://localhost', + META_TITLE_DE: process.env.META_TITLE_DE ?? 'Gradido – Dein Dankbarkeitskonto', + META_TITLE_EN: process.env.META_TITLE_EN ?? 'Gradido - Your gratitude account', + META_DESCRIPTION_DE: + process.env.META_DESCRIPTION_DE ?? + 'Dankbarkeit ist die Währung der neuen Zeit. Immer mehr Menschen entfalten ihr Potenzial und gestalten eine gute Zukunft für alle.', + META_DESCRIPTION_EN: + process.env.META_DESCRIPTION_EN ?? + 'Gratitude is the currency of the new age. More and more people are unleashing their potential and shaping a good future for all.', + META_KEYWORDS_DE: + process.env.META_KEYWORDS_DE ?? + 'Grundeinkommen, Währung, Dankbarkeit, Schenk-Ökonomie, Natürliche Ökonomie des Lebens, Ökonomie, Ökologie, Potenzialentfaltung, Schenken und Danken, Kreislauf des Lebens, Geldsystem', + META_KEYWORDS_EN: + process.env.META_KEYWORDS_EN ?? + 'Basic Income, Currency, Gratitude, Gift Economy, Natural Economy of Life, Economy, Ecology, Potential Development, Giving and Thanking, Cycle of Life, Monetary System', + META_AUTHOR: process.env.META_AUTHOR ?? 'Bernd Hückstädt - Gradido-Akademie', +} + +const CONFIG = { + ...constants, + ...version, + ...features, + ...environment, + ...endpoints, + ...community, + ...meta, +} + +// Check config +// TODO: use validate and construct error message including description +joi.attempt(CONFIG, schema) + module.exports = CONFIG diff --git a/frontend/src/config/schema.js b/frontend/src/config/schema.js index 6e2455b4e..b3c20b956 100644 --- a/frontend/src/config/schema.js +++ b/frontend/src/config/schema.js @@ -2,8 +2,25 @@ const commonSchema = require('../../../config/common.schema') const Joi = require('joi') module.exports = commonSchema.keys({ - // export default Joi.object({ - frontend_vite_host: Joi.alternatives() + FRONTEND_MODULE_PROTOCOL: Joi.string() + .valid('http', 'https') + .when('BROWSER_PROTOCOL', { + is: 'https', + then: Joi.string().uri({ scheme: 'https' }), + otherwise: Joi.string().uri({ scheme: 'http' }), + }) + .description( + 'Protocol for frontend module hosting, has to be the same as for backend api url and admin to prevent mixed block errors' + ) + .default('http') + .required(), + + FRONTEND_HOSTING: Joi.string() + .valid('nodejs') + .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' }), Joi.string() @@ -12,21 +29,20 @@ module.exports = commonSchema.keys({ Joi.string().domain().messages({ 'string.domain': 'Must be a valid domain' }), ) .description( - ` - Host (domain, IPv4, or localhost) for the frontend when running Vite as a standalone Node.js instance; - internally, nginx forward requests to this address. - `, + 'Host (domain, IPv4, or localhost) for the frontend, default is 0.0.0.0 for local hosting during develop', ) - .default('0.0.0.0'), + .default('0.0.0.0') + .required(), // required only if community_url isn't set or FRONTEND_HOSTING is nodejs - frontend_vite_port: Joi.number() + FRONTEND_MODULE_PORT: Joi.number() .integer() .min(1024) .max(49151) - .description('Port for hosting Frontend with Vite as a Node.js instance') - .default(3000), + .description('Port for hosting Frontend with Vite as a Node.js instance, default: 3000') + .default(3000) + .required(), // required only if FRONTEND_HOSTING is nodejs - admin_auth_url: Joi.string() + ADMIN_AUTH_URL: Joi.string() .uri({ scheme: ['http', 'https'] }) .when('browser_protocol', { is: 'https', @@ -36,44 +52,52 @@ module.exports = commonSchema.keys({ .description('Extern Url for admin-frontend') .default('http://0.0.0.0/admin/authenticate?token='), - meta_url: Joi.string() + META_URL: Joi.string() .uri({ scheme: ['http', 'https'] }) .description('The base URL for the meta tags.') - .default('http://localhost'), + .default('http://localhost') + .required(), - meta_title_de: Joi.string() + META_TITLE_DE: Joi.string() .description('Meta title in German.') - .default('Gradido – Dein Dankbarkeitskonto'), + .default('Gradido – Dein Dankbarkeitskonto') + .required(), - meta_title_en: Joi.string() + META_TITLE_EN: Joi.string() .description('Meta title in English.') - .default('Gradido - Your gratitude account'), + .default('Gradido - Your gratitude account') + .required(), - meta_description_de: Joi.string() + META_DESCRIPTION_DE: Joi.string() .description('Meta description in German.') .default( 'Dankbarkeit ist die Währung der neuen Zeit. Immer mehr Menschen entfalten ihr Potenzial und gestalten eine gute Zukunft für alle.', - ), + ) + .required(), - meta_description_en: Joi.string() + META_DESCRIPTION_EN: Joi.string() .description('Meta description in English.') .default( 'Gratitude is the currency of the new age. More and more people are unleashing their potential and shaping a good future for all.', - ), + ) + .required(), - meta_keywords_de: Joi.string() + META_KEYWORDS_DE: Joi.string() .description('Meta keywords in German.') .default( 'Grundeinkommen, Währung, Dankbarkeit, Schenk-Ökonomie, Natürliche Ökonomie des Lebens, Ökonomie, Ökologie, Potenzialentfaltung, Schenken und Danken, Kreislauf des Lebens, Geldsystem', - ), + ) + .required(), - meta_keywords_en: Joi.string() + META_KEYWORDS_EN: Joi.string() .description('Meta keywords in English.') .default( 'Basic Income, Currency, Gratitude, Gift Economy, Natural Economy of Life, Economy, Ecology, Potential Development, Giving and Thanking, Cycle of Life, Monetary System', - ), + ) + .required(), - meta_author: Joi.string() + META_AUTHOR: Joi.string() .description('The author for the meta tags.') - .default('Bernd Hückstädt - Gradido-Akademie'), + .default('Bernd Hückstädt - Gradido-Akademie') + .required(), }) diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 2516501c9..cb885dcec 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -22,6 +22,7 @@ export default defineConfig({ server: { host: CONFIG.FRONTEND_MODULE_HOST, // '0.0.0.0', port: CONFIG.FRONTEND_MODULE_PORT, // 3000, + https: CONFIG.FRONTEND_MODULE_PROTOCOL === 'https', }, resolve: { alias: {