experiment with joi

This commit is contained in:
einhornimmond 2025-01-17 07:30:14 +01:00
parent f1d9dabbb3
commit 27c67a063f
9 changed files with 280 additions and 113 deletions

View File

@ -64,7 +64,6 @@ const dltConnector = {
const community = {
COMMUNITY_NAME: process.env.COMMUNITY_NAME ?? 'Gradido Entwicklung',
COMMUNITY_URL,
COMMUNITY_REGISTER_URL: COMMUNITY_URL + (process.env.COMMUNITY_REGISTER_PATH ?? '/register'),
COMMUNITY_REDEEM_URL: COMMUNITY_URL + (process.env.COMMUNITY_REDEEM_PATH ?? '/redeem/{code}'),
COMMUNITY_REDEEM_CONTRIBUTION_URL:
COMMUNITY_URL + (process.env.COMMUNITY_REDEEM_CONTRIBUTION_PATH ?? '/redeem/CL-{code}'),

70
config/common.schema.js Normal file
View File

@ -0,0 +1,70 @@
const Joi = require('joi')
module.exports = Joi.object({
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'),
community_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('The base URL of the community')
.default('http://0.0.0.0'),
external_backend_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(
`
The external URL of the backend service,
accessible from outside the server (e.g., via Nginx or the server's public URL),
must use the same protocol as browser_protocol.
`,
)
.default('http://0.0.0.0/graphql'),
community_name: Joi.string()
.min(3)
.max(100)
.description('The name of the community')
.default('Gradido Entwicklung'),
community_description: Joi.string()
.min(10)
.max(300)
.description('A short description of the community')
.default('Die lokale Entwicklungsumgebung von Gradido.'),
community_support_email: Joi.string()
.email()
.description('The support email address for the community will be used in frontend and E-Mails')
.default('support@supportmail.com'),
community_location: Joi.string()
.pattern(/^[-+]?[0-9]{1,2}(\.[0-9]+)?,\s?[-+]?[0-9]{1,3}(\.[0-9]+)?$/)
.description('Geographical location of the community in "latitude, longitude" format')
.default('49.280377, 9.690151'),
gdt_active: Joi.boolean()
.description('Flag to indicate if gdt (Gradido Transform) service is used.')
.default(false),
gms_active: Joi.boolean()
.description('Flag to indicate if the GMS (Geographic Member Search) service is used.')
.default(false),
humhub_active: Joi.boolean()
.description('Flag to indicate if the HumHub based Community Server is used.')
.default(false),
})

78
config/index.js Normal file
View File

@ -0,0 +1,78 @@
const joi = require('joi')
const pkg = require('../package')
// Function to map and transform environment variables to match the schema
function mapEnvVariablesToSchema(schema) {
const mappedEnvVariables = {}
// Iterate over all schema keys
for (const [key, value] of Object.entries(schema.describe().keys)) {
const lowerKey = key.toLowerCase() // Convert key to lowercase to match environment variable keys
// Check if the environment variable exists and map it
const envValue = process.env[lowerKey]
if (envValue) {
// If the value is 'true' or 'false', convert it to a boolean
if (envValue === 'true' || envValue === 'false') {
mappedEnvVariables[lowerKey] = envValue === 'true'
}
// If the value is a number (check if it's a valid number), convert it
else if (!isNaN(envValue)) {
mappedEnvVariables[lowerKey] = Number(envValue) // Convert string to number
}
// For all other cases, keep the value as it is
else {
mappedEnvVariables[lowerKey] = envValue
}
} else {
// If no environment variable exists, use the default from the schema (if defined)
if (value.flags) {
let defaultValue = null
if (typeof value.flags.default === 'function') {
defaultValue = value.flags.default(schema)
} else {
defaultValue = value.flags.default
}
mappedEnvVariables[lowerKey] = defaultValue !== undefined ? defaultValue : null
// console.log({ key, value: mappedEnvVariables[lowerKey] })
}
}
}
return mappedEnvVariables
}
const constants = {
DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
}
const version = {
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),
}
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,
}
export function validateAndExport(schema) {
const envMappedToSchema = mapEnvVariablesToSchema(schema)
// validate env against schema
joi.attempt(envMappedToSchema, schema)
const finalEnvVariables = {}
for (const [key, value] of Object.entries(envMappedToSchema)) {
const upperKey = key.toUpperCase() // Convert key back to uppercase
finalEnvVariables[upperKey] = value
}
return {
...constants,
...version,
...environment,
...finalEnvVariables
}
}

View File

@ -87,7 +87,8 @@
"eslint-plugin-promise": "^5.1.1",
"eslint-plugin-vitest": "^0.5.4",
"eslint-plugin-vue": "8.7.1",
"jsdom": "^25.0.0",
"joi": "^17.13.3",
"jsdom": "^25.0.0",
"mock-apollo-client": "^1.2.1",
"postcss": "^8.4.8",
"postcss-html": "^1.3.0",

View File

@ -0,0 +1,79 @@
const commonSchema = require('../../../config/common.schema')
const Joi = require('joi')
module.exports = commonSchema.keys({
// export default Joi.object({
frontend_vite_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 frontend when running Vite as a standalone Node.js instance;
internally, nginx forwards requests to this address.
`,
)
.default('0.0.0.0'),
frontend_vite_port: Joi.number()
.integer()
.min(1024)
.max(49151)
.description('Port for hosting Frontend with Vite as a Node.js instance')
.default(3000),
admin_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 for admin-frontend')
.default('http://0.0.0.0/admin/authenticate?token='),
meta_url: Joi.string()
.uri({ scheme: ['http', 'https'] })
.description('The base URL for the meta tags.')
.default('http://localhost'),
meta_title_de: Joi.string()
.description('Meta title in German.')
.default('Gradido Dein Dankbarkeitskonto'),
meta_title_en: Joi.string()
.description('Meta title in English.')
.default('Gradido - Your gratitude account'),
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.',
),
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.',
),
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',
),
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',
),
meta_author: Joi.string()
.description('The author for the meta tags.')
.default('Bernd Hückstädt - Gradido-Akademie'),
})

View File

@ -1,118 +1,13 @@
/* 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
const pkg = require('../../package')
const constants = {
DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
CONFIG_VERSION: {
DEFAULT: 'DEFAULT',
EXPECTED: 'v7.2024-08-06',
CURRENT: '',
},
}
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={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',
}
// 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}"`,
)
}
import schema from './config.schema'
import { validateAndExport } from '../../../config'
const CONFIG = {
...constants,
...version,
...features,
...environment,
...endpoints,
...community,
...meta,
...validateAndExport(schema),
}
module.exports = CONFIG

View File

@ -292,7 +292,7 @@ const getCommunityStatistics = async () => {
}
const admin = () => {
window.location.assign(CONFIG.ADMIN_AUTH_URL.replace('{token}', store.state.token))
window.location.assign(CONFIG.ADMIN_AUTH_URL + store.state.token)
store.dispatch('logout') // logout without redirect
}

View File

@ -15,6 +15,8 @@ dotenv.config() // load env vars from .env
const CONFIG = require('./src/config')
console.log(CONFIG)
// https://vitejs.dev/config/
export default defineConfig({
server: {
@ -76,7 +78,7 @@ export default defineConfig({
URL_PROTOCOL: null,
COMMUNITY_URL: null,
GRAPHQL_PATH: null,
GRAPHQL_URI: CONFIG.GRAPHQL_URI, // null,
EXTERNAL_BACKEND_URL: CONFIG.EXTERNAL_BACKEND_URL, // null,
ADMIN_AUTH_PATH: CONFIG.ADMIN_AUTH_PATH ?? null, // it is the only env without exported default
ADMIN_AUTH_URL: CONFIG.ADMIN_AUTH_URL, // null,
COMMUNITY_NAME: null,
@ -107,5 +109,8 @@ export default defineConfig({
build: {
outDir: path.resolve(__dirname, './build'),
chunkSizeWarningLimit: 1600,
rollupOptions: {
external: ['joi'],
},
},
})

View File

@ -1073,6 +1073,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"
@ -1433,6 +1445,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==
"@sindresorhus/merge-streams@^2.1.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz#719df7fb41766bc143369eaa0dd56d8dc87c9958"
@ -4638,6 +4667,17 @@ jiti@^2.3.0, jiti@^2.4.0, jiti@^2.4.1:
resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.4.2.tgz#d19b7732ebb6116b06e2038da74a55366faef560"
integrity sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==
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"