diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..01c8b1b48 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +**/node_modules +**/build +**/coverage \ No newline at end of file diff --git a/.github/workflows/test_admin_interface.yml b/.github/workflows/test_admin_interface.yml index 160c819d2..d57c78ef8 100644 --- a/.github/workflows/test_admin_interface.yml +++ b/.github/workflows/test_admin_interface.yml @@ -33,7 +33,7 @@ jobs: uses: actions/checkout@v3 - name: Admin Interface | Build 'test' image - run: docker build --target test -t "gradido/admin:test" admin/ --build-arg NODE_ENV="test" + run: docker build -f ./admin/Dockerfile --target test -t "gradido/admin:test" --build-arg NODE_ENV="test" . unit_test: if: needs.files-changed.outputs.admin == 'true' diff --git a/.github/workflows/test_backend.yml b/.github/workflows/test_backend.yml index ed20f91bb..96415cfff 100644 --- a/.github/workflows/test_backend.yml +++ b/.github/workflows/test_backend.yml @@ -44,7 +44,7 @@ jobs: uses: actions/checkout@v3 - name: Backend | docker-compose mariadb - run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb + run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb mailserver - name: Sleep for 30 seconds run: sleep 30s @@ -54,7 +54,7 @@ jobs: run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database - name: Backend | Unit tests - run: cd database && yarn && yarn build && cd ../backend && yarn && yarn test + run: cd database && yarn && yarn build && cd ../config && yarn install && cd ../backend && yarn && yarn test lint: if: needs.files-changed.outputs.backend == 'true' @@ -66,7 +66,7 @@ jobs: uses: actions/checkout@v3 - name: Backend | Lint - run: cd database && yarn && cd ../backend && yarn && yarn run lint + run: cd database && yarn && cd ../config && yarn install && cd ../backend && yarn && yarn run lint locales: if: needs.files-changed.outputs.backend == 'true' diff --git a/.github/workflows/test_dht_node.yml b/.github/workflows/test_dht_node.yml index 5452d3f0b..2bbbd5e6d 100644 --- a/.github/workflows/test_dht_node.yml +++ b/.github/workflows/test_dht_node.yml @@ -51,7 +51,7 @@ jobs: uses: actions/checkout@v3 - name: Lint - run: cd database && yarn && cd ../dht-node && yarn && yarn run lint + run: cd database && yarn && cd ../config && yarn install && cd ../dht-node && yarn && yarn run lint unit_test: name: Unit Tests - DHT Node diff --git a/.github/workflows/test_e2e.yml b/.github/workflows/test_e2e.yml index 48aff9c0b..3fb92562c 100644 --- a/.github/workflows/test_e2e.yml +++ b/.github/workflows/test_e2e.yml @@ -21,6 +21,8 @@ jobs: sudo chown runner:docker -R * cd database yarn && yarn dev_reset + cd ../config + yarn install cd ../backend yarn && yarn seed diff --git a/.github/workflows/test_frontend.yml b/.github/workflows/test_frontend.yml index 44137216b..208607fbd 100644 --- a/.github/workflows/test_frontend.yml +++ b/.github/workflows/test_frontend.yml @@ -33,7 +33,7 @@ jobs: uses: actions/checkout@v3 - name: Frontend | Build 'test' image - run: docker build --target test -t "gradido/frontend:test" frontend/ --build-arg NODE_ENV="test" + run: docker build -f ./frontend/Dockerfile --target test -t "gradido/frontend:test" --build-arg NODE_ENV="test" . unit_test: if: needs.files-changed.outputs.frontend == 'true' diff --git a/admin/Dockerfile b/admin/Dockerfile index a01903cc3..2678c7c29 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 @@ -42,6 +42,8 @@ EXPOSE ${PORT} RUN mkdir -p ${DOCKER_WORKDIR} WORKDIR ${DOCKER_WORKDIR} +RUN mkdir -p /config + ################################################################################## # DEVELOPMENT (Connected to the local environment, to reload on demand) ########## ################################################################################## @@ -53,7 +55,7 @@ FROM base as development # Run command # (for development we need to execute yarn install since the # node_modules are on another volume and need updating) -CMD /bin/sh -c "yarn install && yarn run dev" +CMD /bin/sh -c "cd /config && yarn install && yarn build && cd /app && yarn install && yarn run dev" ################################################################################## # BUILD (Does contain all files and is therefore bloated) ######################## @@ -61,9 +63,16 @@ CMD /bin/sh -c "yarn install && yarn run dev" FROM base as build # Copy everything -COPY . . -# yarn install +COPY ./admin/ . +# Copy everything from config +COPY ./config/ ../config/ + +# yarn install and build config +RUN cd ../config && yarn install --production=false --frozen-lockfile --non-interactive && yarn build + +# yarn install admin RUN yarn install --production=false --frozen-lockfile --non-interactive + # yarn build RUN yarn run build @@ -85,6 +94,7 @@ FROM base as production # Copy "binary"-files from build image COPY --from=build ${DOCKER_WORKDIR}/build ./build +COPY --from=build ${DOCKER_WORKDIR}/../config/build ../config/build # We also copy the node_modules express and serve-static for the run script COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules # Copy static files diff --git a/admin/package.json b/admin/package.json index 878eae220..b6943dde2 100644 --- a/admin/package.json +++ b/admin/package.json @@ -74,6 +74,8 @@ "eslint-plugin-prettier": "5.2.1", "eslint-plugin-promise": "^5.1.1", "eslint-plugin-vue": "8.7.1", + "gradido-config": "../config", + "joi": "^17.13.3", "jsdom": "^25.0.0", "mock-apollo-client": "^1.2.1", "postcss": "^8.4.8", diff --git a/admin/src/components/NavBar.spec.js b/admin/src/components/NavBar.spec.js index b88e0d2fe..c6a63db84 100644 --- a/admin/src/components/NavBar.spec.js +++ b/admin/src/components/NavBar.spec.js @@ -114,7 +114,7 @@ describe('NavBar', () => { it('changes window location to wallet and dispatches logout', async () => { const dispatchSpy = vi.spyOn(store, 'dispatch') await wrapper.vm.handleWallet() - expect(window.location).toBe(CONFIG.WALLET_AUTH_URL.replace('{token}', 'valid-token')) + expect(window.location).toBe(CONFIG.WALLET_AUTH_URL + 'valid-token') expect(dispatchSpy).toHaveBeenCalledWith('logout') }) }) diff --git a/admin/src/components/NavBar.vue b/admin/src/components/NavBar.vue index dc0f7828a..b9dc12106 100644 --- a/admin/src/components/NavBar.vue +++ b/admin/src/components/NavBar.vue @@ -80,7 +80,7 @@ const handleLogout = async () => { } const handleWallet = () => { - window.location = CONFIG.WALLET_AUTH_URL.replace('{token}', store.state.token) + window.location = CONFIG.WALLET_AUTH_URL + store.state.token store.dispatch('logout') // logout without redirect } diff --git a/admin/src/config/index.js b/admin/src/config/index.js index 2c6302e29..b73657219 100644 --- a/admin/src/config/index.js +++ b/admin/src/config/index.js @@ -4,20 +4,12 @@ // Load Package Details for some default values const pkg = require('../../package') -const constants = { - CONFIG_VERSION: { - DEFAULT: 'DEFAULT', - EXPECTED: 'v3.2024-08-06', - CURRENT: '', - }, -} - 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), } @@ -47,11 +39,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 +51,13 @@ 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, + ADMIN_MODULE_URL, + COMMUNITY_URL, } module.exports = CONFIG diff --git a/admin/src/config/schema.js b/admin/src/config/schema.js new file mode 100644 index 000000000..2fa938200 --- /dev/null +++ b/admin/src/config/schema.js @@ -0,0 +1,108 @@ +const { + browserUrls, + APP_VERSION, + BUILD_COMMIT, + BUILD_COMMIT_SHORT, + COMMUNITY_URL, + DEBUG, + GRAPHQL_URI, + NODE_ENV, + PRODUCTION, +} = require('gradido-config/build/src/commonSchema.js') +const Joi = require('joi') + +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() + .when('ADMIN_HOSTING', { + is: Joi.valid('nodejs'), + then: Joi.valid('http').required(), + otherwise: Joi.valid('http', 'https').required(), + }) + .description( + ` + 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_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' }), + ) + .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'), + + 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) + .when('ADMIN_HOSTING', { + is: 'nodejs', + then: Joi.required(), + otherwise: Joi.optional(), + }), + + WALLET_AUTH_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .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'] }) + .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 +}) diff --git a/admin/vite.config.js b/admin/vite.config.js index 825690c29..a889b664b 100644 --- a/admin/vite.config.js +++ b/admin/vite.config.js @@ -6,6 +6,8 @@ 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 { validate } from 'gradido-config/build/src/index.js' import dotenv from 'dotenv' @@ -15,56 +17,78 @@ 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, + validate(schema, configDataForValidation) + + 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, // null, - COMMUNITY_HOST: CONFIG.ADMIN_MODULE_HOST ?? null, // null, - COMMUNITY_URL: CONFIG.COMMUNITY_URL ?? null, - URL_PROTOCOL: CONFIG.ADMIN_MODULE_PROTOCOL ?? null, // null, - WALLET_AUTH_URL: CONFIG.WALLET_AUTH_URL ?? null, - GRAPHQL_URL: CONFIG.GRAPHQL_URI ?? null, // null, - GRAPHQL_PATH: process.env.GRAPHQL_PATH ?? '/graphql', // null, - WALLET_AUTH_PATH: CONFIG.WALLET_AUTH_PATH ?? null, - WALLET_LOGIN_PATH: CONFIG.WALLET_LOGIN_URL ?? null, // null, - DEBUG_DISABLE_AUTH: CONFIG.DEBUG_DISABLE_AUTH ?? null, // 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, // null, + COMMUNITY_HOST: CONFIG.ADMIN_MODULE_HOST ?? null, // null, + COMMUNITY_URL: CONFIG.COMMUNITY_URL ?? null, + URL_PROTOCOL: CONFIG.ADMIN_MODULE_PROTOCOL ?? null, // null, + WALLET_AUTH_URL: CONFIG.WALLET_AUTH_URL ?? null, + GRAPHQL_URL: CONFIG.GRAPHQL_URI ?? null, // null, + GRAPHQL_PATH: process.env.GRAPHQL_PATH ?? '/graphql', // null, + WALLET_AUTH_PATH: CONFIG.WALLET_AUTH_PATH ?? null, + WALLET_LOGIN_PATH: CONFIG.WALLET_LOGIN_URL ?? null, // null, + DEBUG_DISABLE_AUTH: CONFIG.DEBUG_DISABLE_AUTH ?? null, // null, + // CONFIG_VERSION: CONFIG.CONFIG_VERSION, // null, + }), + commonjs(), + ], + build: { + outDir: path.resolve(__dirname, './build'), + chunkSizeWarningLimit: 1600, + }, + publicDir: '/admin', + } }) diff --git a/admin/yarn.lock b/admin/yarn.lock index 03e984ff8..3e3a8b8c4 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" @@ -4195,6 +4224,11 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +gradido-config@../config: + version "1.0.0" + dependencies: + joi "^17.13.3" + graphemer@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" @@ -4712,6 +4746,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/backend/.env.dist b/backend/.env.dist index a3cf28bd4..db80c2fa4 100644 --- a/backend/.env.dist +++ b/backend/.env.dist @@ -3,7 +3,7 @@ PORT=4000 JWT_SECRET=secret123 JWT_EXPIRES_IN=10m GRAPHIQL=false -GDT_API_URL=https://gdt.gradido.net +GDT_ACTIVE=false # Database DB_HOST=localhost @@ -45,7 +45,7 @@ EMAIL_TEST_RECEIVER=stage1@gradido.net EMAIL_USERNAME=gradido_email EMAIL_SENDER=info@gradido.net EMAIL_PASSWORD=xxx -EMAIL_SMTP_URL=gmail.com +EMAIL_SMTP_HOST=gmail.com EMAIL_SMTP_PORT=587 EMAIL_LINK_VERIFICATION_PATH=/checkEmail/{optin}{code} EMAIL_LINK_SETPASSWORD_PATH=/reset-password/{optin} diff --git a/backend/.env.org b/backend/.env.org index bbd7b5c05..fb95c950d 100644 --- a/backend/.env.org +++ b/backend/.env.org @@ -45,7 +45,7 @@ EMAIL_TEST_RECEIVER=stage1@gradido.net EMAIL_USERNAME=gradido_email EMAIL_SENDER=info@gradido.net EMAIL_PASSWORD=xxx -EMAIL_SMTP_URL=gmail.com +EMAIL_SMTP_HOST=gmail.com EMAIL_SMTP_PORT=587 EMAIL_LINK_VERIFICATION_PATH=/checkEmail/{optin}{code} EMAIL_LINK_SETPASSWORD_PATH=/reset-password/{optin} diff --git a/backend/.env.template b/backend/.env.template index 7d588ff49..2205417a3 100644 --- a/backend/.env.template +++ b/backend/.env.template @@ -49,7 +49,7 @@ EMAIL_TEST_RECEIVER=$EMAIL_TEST_RECEIVER EMAIL_USERNAME=$EMAIL_USERNAME EMAIL_SENDER=$EMAIL_SENDER EMAIL_PASSWORD=$EMAIL_PASSWORD -EMAIL_SMTP_URL=$EMAIL_SMTP_URL +EMAIL_SMTP_HOST=$EMAIL_SMTP_HOST EMAIL_SMTP_PORT=$EMAIL_SMTP_PORT EMAIL_LINK_VERIFICATION_PATH=$EMAIL_LINK_VERIFICATION_PATH EMAIL_LINK_SETPASSWORD_PATH=$EMAIL_LINK_SETPASSWORD_PATH diff --git a/backend/.env.test_e2e b/backend/.env.test_e2e index 99d6c14d5..8b6559e97 100644 --- a/backend/.env.test_e2e +++ b/backend/.env.test_e2e @@ -1,6 +1,8 @@ # Server JWT_EXPIRES_IN=2m +GDT_ACTIVE=false + # Email EMAIL=true EMAIL_TEST_MODUS=false diff --git a/backend/Dockerfile b/backend/Dockerfile index 910bdd504..d8b415e47 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -43,6 +43,7 @@ RUN mkdir -p ${DOCKER_WORKDIR} WORKDIR ${DOCKER_WORKDIR} RUN mkdir -p /database +RUN mkdir -p /config ################################################################################## # DEVELOPMENT (Connected to the local environment, to reload on demand) ########## @@ -55,7 +56,7 @@ FROM base as development # Run command # (for development we need to execute yarn install since the # node_modules are on another volume and need updating) -CMD /bin/sh -c "cd /database && yarn install && yarn build && cd /app && yarn install && yarn run dev" +CMD /bin/sh -c "cd /database && yarn install && yarn build && cd /config && yarn build && ls ./build/ && cd /app && ls -la ../config/build && yarn install && yarn run dev" ################################################################################## # BUILD (Does contain all files and is therefore bloated) ######################## @@ -66,6 +67,11 @@ FROM base as build COPY ./backend/ ./ # Copy everything from database COPY ./database/ ../database/ +# Copy everything from config +COPY ./config/ ../config/ + +# yarn install and build config +RUN cd ../config && yarn install --production=false --frozen-lockfile --non-interactive && yarn build # yarn install backend RUN yarn install --production=false --frozen-lockfile --non-interactive @@ -74,10 +80,10 @@ RUN yarn install --production=false --frozen-lockfile --non-interactive RUN cd ../database && yarn install --production=false --frozen-lockfile --non-interactive # yarn build -RUN yarn run build +RUN yarn build # yarn build database -RUN cd ../database && yarn run build +RUN cd ../database && yarn build ################################################################################## # TEST ########################################################################### @@ -95,9 +101,11 @@ FROM base as production # Copy "binary"-files from build image COPY --from=build ${DOCKER_WORKDIR}/build ./build COPY --from=build ${DOCKER_WORKDIR}/../database/build ../database/build +COPY --from=build ${DOCKER_WORKDIR}/../config/build ../config/build # We also copy the node_modules express and serve-static for the run script COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules COPY --from=build ${DOCKER_WORKDIR}/../database/node_modules ../database/node_modules +COPY --from=build ${DOCKER_WORKDIR}/../config/node_modules ../config/node_modules # Copy static files # COPY --from=build ${DOCKER_WORKDIR}/public ./public diff --git a/backend/jest.config.js b/backend/jest.config.js index 23b9ed387..6b9d1621a 100644 --- a/backend/jest.config.js +++ b/backend/jest.config.js @@ -7,7 +7,7 @@ module.exports = { collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'], coverageThreshold: { global: { - lines: 81, + lines: 80, }, }, setupFiles: ['/test/testSetup.ts'], @@ -39,5 +39,10 @@ module.exports = { process.env.NODE_ENV === 'development' ? '/../database/src/$1' : '/../database/build/src/$1', + '@config/(.*)': + // eslint-disable-next-line n/no-process-env + process.env.NODE_ENV === 'development' + ? '/../config/src/$1' + : '/../config/build/$1', }, } diff --git a/backend/package.json b/backend/package.json index d9152a1b9..18ecc99e8 100644 --- a/backend/package.json +++ b/backend/package.json @@ -33,12 +33,14 @@ "email-templates": "^10.0.1", "express": "^4.17.1", "express-slow-down": "^2.0.1", + "gradido-config": "file:../config", "gradido-database": "file:../database", "graphql": "^15.5.1", "graphql-request": "5.0.0", "graphql-type-json": "0.3.2", "helmet": "^5.1.1", "i18n": "^0.15.1", + "joi": "^17.13.3", "jose": "^4.14.4", "lodash.clonedeep": "^4.5.0", "log4js": "^6.4.6", @@ -61,8 +63,9 @@ "@types/faker": "^5.5.9", "@types/i18n": "^0.13.4", "@types/jest": "^27.0.2", + "@types/joi": "^17.2.3", "@types/lodash.clonedeep": "^4.5.6", - "@types/node": "^16.10.3", + "@types/node": "^17.0.21", "@types/nodemailer": "^6.4.4", "@types/sodium-native": "^2.3.5", "@types/uuid": "^8.3.4", diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 274cb34dd..a49e882aa 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -1,9 +1,14 @@ // ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env) /* eslint-disable n/no-process-env */ +// eslint-disable-next-line import/no-unresolved +import { validate } from '@config/index' +import { latestDbVersion } from '@dbTools/config/detectLastDBVersion' import { Decimal } from 'decimal.js-light' import dotenv from 'dotenv' +import { schema } from './schema' + dotenv.config() Decimal.set({ @@ -12,16 +17,10 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0087-add_index_on_user_roles', + // DB_VERSION: '0087-add_index_on_user_roles', + DB_VERSION: latestDbVersion, 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 = { @@ -29,8 +28,11 @@ const server = { JWT_SECRET: process.env.JWT_SECRET ?? 'secret123', JWT_EXPIRES_IN: process.env.JWT_EXPIRES_IN ?? '10m', GRAPHIQL: process.env.GRAPHIQL === 'true' || false, + GDT_ACTIVE: process.env.GDT_ACTIVE === '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 = { @@ -64,10 +66,9 @@ 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_URL: COMMUNITY_URL + (process.env.COMMUNITY_REDEEM_PATH ?? '/redeem/'), COMMUNITY_REDEEM_CONTRIBUTION_URL: - COMMUNITY_URL + (process.env.COMMUNITY_REDEEM_CONTRIBUTION_PATH ?? '/redeem/CL-{code}'), + COMMUNITY_URL + (process.env.COMMUNITY_REDEEM_CONTRIBUTION_PATH ?? '/redeem/CL-'), COMMUNITY_DESCRIPTION: process.env.COMMUNITY_DESCRIPTION ?? 'Die lokale Entwicklungsumgebung von Gradido.', COMMUNITY_SUPPORT_MAIL: process.env.COMMUNITY_SUPPORT_MAIL ?? 'support@supportmail.com', @@ -86,14 +87,14 @@ const email = { EMAIL_USERNAME: process.env.EMAIL_USERNAME ?? '', EMAIL_SENDER: process.env.EMAIL_SENDER ?? 'info@gradido.net', EMAIL_PASSWORD: process.env.EMAIL_PASSWORD ?? '', - EMAIL_SMTP_URL: process.env.EMAIL_SMTP_URL ?? 'mailserver', + EMAIL_SMTP_HOST: process.env.EMAIL_SMTP_HOST ?? 'mailserver', EMAIL_SMTP_PORT: Number(process.env.EMAIL_SMTP_PORT) || 1025, // eslint-disable-next-line no-unneeded-ternary EMAIL_TLS: process.env.EMAIL_TLS === 'false' ? false : true, EMAIL_LINK_VERIFICATION: - COMMUNITY_URL + (process.env.EMAIL_LINK_VERIFICATION_PATH ?? '/checkEmail/{optin}{code}'), + COMMUNITY_URL + (process.env.EMAIL_LINK_VERIFICATION_PATH ?? '/checkEmail/'), EMAIL_LINK_SETPASSWORD: - COMMUNITY_URL + (process.env.EMAIL_LINK_SETPASSWORD_PATH ?? '/reset-password/{optin}'), + COMMUNITY_URL + (process.env.EMAIL_LINK_SETPASSWORD_PATH ?? '/reset-password/'), EMAIL_LINK_FORGOTPASSWORD: COMMUNITY_URL + (process.env.EMAIL_LINK_FORGOTPASSWORD_PATH ?? '/forgot-password'), EMAIL_LINK_OVERVIEW: COMMUNITY_URL + (process.env.EMAIL_LINK_OVERVIEW_PATH ?? '/overview'), @@ -115,18 +116,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 @@ -172,3 +161,5 @@ export const CONFIG = { ...gms, ...humhub, } + +validate(schema, CONFIG) diff --git a/backend/src/config/schema.ts b/backend/src/config/schema.ts new file mode 100644 index 000000000..fcdbdd439 --- /dev/null +++ b/backend/src/config/schema.ts @@ -0,0 +1,355 @@ +// eslint-disable-next-line import/no-unresolved +import { + COMMUNITY_NAME, + COMMUNITY_URL, + COMMUNITY_DESCRIPTION, + COMMUNITY_SUPPORT_MAIL, + DB_HOST, + DB_PASSWORD, + DB_PORT, + DB_USER, + DB_VERSION, + DB_DATABASE, + DECAY_START_TIME, + GDT_API_URL, + GDT_ACTIVE, + GMS_ACTIVE, + GRAPHIQL, + HUMHUB_ACTIVE, + LOG4JS_CONFIG, + LOGIN_APP_SECRET, + LOGIN_SERVER_KEY, + LOG_LEVEL, + NODE_ENV, + PRODUCTION, + TYPEORM_LOGGING_RELATIVE_PATH, +} from '@config/commonSchema' +import Joi from 'joi' + +export const schema = Joi.object({ + COMMUNITY_NAME, + COMMUNITY_URL, + COMMUNITY_DESCRIPTION, + COMMUNITY_SUPPORT_MAIL, + DB_HOST, + DB_PASSWORD, + DB_PORT, + DB_USER, + DB_VERSION, + DB_DATABASE, + DECAY_START_TIME, + GDT_API_URL, + GDT_ACTIVE, + GMS_ACTIVE, + GRAPHIQL, + HUMHUB_ACTIVE, + LOG4JS_CONFIG, + LOGIN_APP_SECRET, + LOGIN_SERVER_KEY, + LOG_LEVEL, + NODE_ENV, + PRODUCTION, + TYPEORM_LOGGING_RELATIVE_PATH, + + COMMUNITY_REDEEM_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .description('The url for redeeming link transactions, must start with frontend base url') + .default('http://0.0.0.0/redeem/') + .custom((value: string, helpers: Joi.CustomHelpers): string | Joi.ErrorReport => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access + if (!value.startsWith(helpers.state.ancestors[0].COMMUNITY_URL)) { + return helpers.error('string.pattern.base', { value, communityUrl: COMMUNITY_URL }) + } + return value + }) + .required(), + + COMMUNITY_REDEEM_CONTRIBUTION_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .custom((value: string, helpers: Joi.CustomHelpers): string | Joi.ErrorReport => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access + if (!value.startsWith(helpers.state.ancestors[0].COMMUNITY_URL)) { + return helpers.error('string.pattern.base', { value, communityUrl: COMMUNITY_URL }) + } + return value + }) + .description( + 'The url for redeeming contribution link transactions, must start with frontend base url.', + ) + .default('http://0.0.0.0/redeem/CL-') + .required(), + + DLT_CONNECTOR: Joi.boolean() + .description('Flag to indicate if DLT-Connector is used. (Still in development)') + .default(false) + .required(), + + DLT_CONNECTOR_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .default('http://localhost:6010') + .when('DLT_CONNECTOR', { is: true, then: Joi.required() }) + .description('The URL for GDT API endpoint'), + + EMAIL: Joi.boolean() + .default(false) + .description('Enable or disable email functionality') + .required(), + + EMAIL_TEST_MODUS: Joi.boolean() + .default(false) + .description('When enabled, all emails are sended to EMAIL_TEST_RECEIVER') + .optional(), + + EMAIL_TEST_RECEIVER: Joi.string() + .email() + .default('stage1@gradido.net') + .when('EMAIL_TEST_MODUS', { is: true, then: Joi.required() }) + .description('Email address used in test mode'), + + EMAIL_USERNAME: Joi.alternatives().conditional(Joi.ref('EMAIL'), { + is: true, + then: Joi.alternatives().conditional(Joi.ref('NODE_ENV'), { + is: 'development', + then: Joi.string() + .allow('') + .description('Username for SMTP authentication (optional in development)'), + otherwise: Joi.string() + .pattern(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/) + .description('Valid SMTP username required in production') + .required(), + }), + otherwise: Joi.string().allow('').optional(), + }), + + EMAIL_SENDER: Joi.string() + .email() + .when('EMAIL', { is: true, then: Joi.required() }) + .default('info@gradido.net') + .description('Email address used as sender'), + + EMAIL_PASSWORD: Joi.alternatives().conditional(Joi.ref('EMAIL'), { + is: true, + then: Joi.alternatives().conditional(Joi.ref('NODE_ENV'), { + is: 'development', + then: Joi.string() + .allow('') + .description('Password for SMTP authentication (optional in development)'), + otherwise: Joi.string() + .min(8) + .pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&#]).{8,}$/) + .description( + 'Password must be at least 8 characters long, include uppercase and lowercase letters, a number, and a special character', + ) + .required(), + }), + otherwise: Joi.string().allow('').optional(), + }), + + EMAIL_SMTP_HOST: Joi.string() + .hostname() + .when('EMAIL', { is: true, then: Joi.required() }) + .default('mailserver') + .description('SMTP server hostname'), + + EMAIL_SMTP_PORT: Joi.number() + .integer() + .positive() + .when('EMAIL', { is: true, then: Joi.required() }) + .default(1025) + .description('SMTP server port'), + + EMAIL_TLS: Joi.boolean().default(true).description('Enable or disable TLS for SMTP').optional(), + + EMAIL_LINK_VERIFICATION: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .custom((value: string, helpers: Joi.CustomHelpers): string | Joi.ErrorReport => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access + if (!value.startsWith(helpers.state.ancestors[0].COMMUNITY_URL)) { + return helpers.error('string.pattern.base', { value, communityUrl: COMMUNITY_URL }) + } + return value + }) + .description('Email Verification link for activate Email.') + .required(), + + EMAIL_LINK_SETPASSWORD: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .custom((value: string, helpers: Joi.CustomHelpers): string | Joi.ErrorReport => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access + if (!value.startsWith(helpers.state.ancestors[0].COMMUNITY_URL)) { + return helpers.error('string.pattern.base', { value, communityUrl: COMMUNITY_URL }) + } + return value + }) + .description('Email Verification link for set initial Password.') + .required(), + + EMAIL_LINK_FORGOTPASSWORD: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .custom((value: string, helpers: Joi.CustomHelpers): string | Joi.ErrorReport => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access + if (!value.startsWith(helpers.state.ancestors[0].COMMUNITY_URL)) { + return helpers.error('string.pattern.base', { value, communityUrl: COMMUNITY_URL }) + } + return value + }) + .description('Email Verification link for set new Password, when old Password was forgotten.') + .required(), + + EMAIL_LINK_OVERVIEW: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .custom((value: string, helpers: Joi.CustomHelpers): string | Joi.ErrorReport => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access + if (!value.startsWith(helpers.state.ancestors[0].COMMUNITY_URL)) { + return helpers.error('string.pattern.base', { value, communityUrl: COMMUNITY_URL }) + } + return value + }) + .description('Link to Wallet Overview') + .required(), + + EMAIL_CODE_VALID_TIME: Joi.number() + .integer() + .positive() + .max(43200) // max at 30 days + .default(1440) + .description('Time in minutes a code is valid') + .required(), + + EMAIL_CODE_REQUEST_TIME: Joi.number() + .integer() + .positive() + .max(43200) // max at 30 days + .default(10) + .description('Time in minutes before a new code can be requested') + .required(), + + FEDERATION_BACKEND_SEND_ON_API: Joi.string() + .pattern(/^\d+_\d+$/) + .default('1_0') + .description('API Version of sending requests to another communities, e.g., "1_0"') + .required(), + + FEDERATION_VALIDATE_COMMUNITY_TIMER: Joi.number() + .integer() + .min(1000) + .default(60000) + .description('Timer interval in milliseconds for community validation') + .required(), + + FEDERATION_XCOM_SENDCOINS_ENABLED: Joi.boolean() + .default(false) + .description('Enable or disable the federation send coins feature') + .optional(), + + FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID: Joi.string() + .uuid() + .default('56a55482-909e-46a4-bfa2-cd025e894ebc') + .description( + 'UUID of the receiver community for federation cross-community transactions if the receiver is unknown', + ) + .required(), + + FEDERATION_XCOM_MAXREPEAT_REVERTSENDCOINS: Joi.number() + .integer() + .min(0) + .default(3) + .description('Maximum number of retries for reverting send coins transactions') + .required(), + + GMS_CREATE_USER_THROW_ERRORS: Joi.boolean() + .default(false) + .when('GMS_ACTIVE', { is: true, then: Joi.required(), otherwise: Joi.optional() }) + .description('Whether errors should be thrown when creating users in GMS'), + + GMS_API_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .when('GMS_ACTIVE', { is: true, then: Joi.required(), otherwise: Joi.optional() }) + .default('http://localhost:4044/') + .description('The API URL for the GMS service'), + + GMS_DASHBOARD_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .when('GMS_ACTIVE', { is: true, then: Joi.required(), otherwise: Joi.optional() }) + .default('http://localhost:8080/') + .description('The URL for the GMS dashboard'), + + GMS_WEBHOOK_SECRET: Joi.string() + .min(1) + .default('secret') + .when('GMS_ACTIVE', { is: true, then: Joi.required(), otherwise: Joi.optional() }) + .description('The secret postfix for the GMS webhook endpoint'), + + HUMHUB_API_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .when('HUMHUB_ACTIVE', { is: true, then: Joi.required(), otherwise: Joi.optional() }) + .description('The API URL for HumHub integration'), + + HUMHUB_JWT_KEY: Joi.string() + .min(1) + .when('HUMHUB_ACTIVE', { + is: true, + then: Joi.required(), + otherwise: Joi.string().allow('').optional(), + }) + .description('JWT key for HumHub integration, must be the same as configured in humhub'), + + PORT: Joi.number() + .integer() + .min(1024) + .max(49151) + .description('Port for hosting backend, default: 4000') + .default(4000) + .required(), + + KLICKTIPP: Joi.boolean() + .default(false) + .description("Indicates whether Klicktipp integration is enabled, 'true' or 'false'"), + + KLICKTTIPP_API_URL: Joi.string() + .uri({ scheme: ['https'] }) // Sicherstellen, dass es eine gültige HTTPS-URL ist + .default('https://api.klicktipp.com') + .description("The API URL for Klicktipp, must be a valid URL starting with 'https'"), + + KLICKTIPP_USER: Joi.string().default('gradido_test').description('The username for Klicktipp'), + + KLICKTIPP_PASSWORD: Joi.string() + .min(6) + .default('secret321') + .description('The password for Klicktipp, should be at least 6 characters'), + + KLICKTIPP_APIKEY_DE: Joi.string() + .default('SomeFakeKeyDE') + .description('The API key for Klicktipp (German version)'), + + KLICKTIPP_APIKEY_EN: Joi.string() + .default('SomeFakeKeyEN') + .description('The API key for Klicktipp (English version)'), + + USE_CRYPTO_WORKER: Joi.boolean() + .default(false) + .description( + 'Flag to enable or disable password encryption in separate thread, should be enabled if possible', + ), + + // TODO: check format + JWT_SECRET: Joi.string() + .default('secret123') + .description('jwt secret for jwt tokens used for login') + .required(), + + JWT_EXPIRES_IN: Joi.alternatives() + .try( + Joi.string() + .pattern(/^\d+[smhdw]$/) // Time specification such as “10m”, “1h”, “2d”, etc. + .description('Expiration time for JWT login token, in format like "10m", "1h", "1d"') + .default('10m'), + Joi.number() + .positive() // positive number to accept seconds + .description('Expiration time for JWT login token in seconds'), + ) + .required() + .description('Time for JWT token to expire, auto logout'), + + WEBHOOK_ELOPAGE_SECRET: Joi.string().description("isn't really used any more").optional(), +}) diff --git a/backend/src/emails/sendEmailTranslated.test.ts b/backend/src/emails/sendEmailTranslated.test.ts index d09d11bd5..c10c76a62 100644 --- a/backend/src/emails/sendEmailTranslated.test.ts +++ b/backend/src/emails/sendEmailTranslated.test.ts @@ -7,9 +7,12 @@ import { CONFIG } from '@/config' import { sendEmailTranslated } from './sendEmailTranslated' +const testMailServerHost = 'localhost' +const testMailServerPort = 1025 + CONFIG.EMAIL = false -CONFIG.EMAIL_SMTP_URL = 'EMAIL_SMTP_URL' -CONFIG.EMAIL_SMTP_PORT = 1234 +CONFIG.EMAIL_SMTP_HOST = testMailServerHost +CONFIG.EMAIL_SMTP_PORT = testMailServerPort CONFIG.EMAIL_SENDER = 'info@gradido.net' CONFIG.EMAIL_USERNAME = 'user' CONFIG.EMAIL_PASSWORD = 'pwd' @@ -73,8 +76,8 @@ describe('sendEmailTranslated', () => { it('calls the transporter', () => { expect(createTransport).toBeCalledWith({ - host: 'EMAIL_SMTP_URL', - port: 1234, + host: testMailServerHost, + port: testMailServerPort, secure: false, requireTLS: true, auth: { @@ -87,11 +90,6 @@ describe('sendEmailTranslated', () => { describe('call of "sendEmailTranslated"', () => { it('has expected result', () => { expect(result).toMatchObject({ - envelope: { - from: 'info@gradido.net', - to: ['receiver@mail.org', 'support@gradido.net'], - }, - message: expect.any(String), originalMessage: expect.objectContaining({ to: 'receiver@mail.org', cc: 'support@gradido.net', @@ -135,11 +133,6 @@ describe('sendEmailTranslated', () => { it('call of "sendEmailTranslated" with faked "to"', () => { expect(result).toMatchObject({ - envelope: { - from: CONFIG.EMAIL_SENDER, - to: [CONFIG.EMAIL_TEST_RECEIVER, 'support@gradido.net'], - }, - message: expect.any(String), originalMessage: expect.objectContaining({ to: CONFIG.EMAIL_TEST_RECEIVER, cc: 'support@gradido.net', diff --git a/backend/src/emails/sendEmailTranslated.ts b/backend/src/emails/sendEmailTranslated.ts index acef8a11f..d61ff3339 100644 --- a/backend/src/emails/sendEmailTranslated.ts +++ b/backend/src/emails/sendEmailTranslated.ts @@ -45,7 +45,7 @@ export const sendEmailTranslated = async ({ receiver.to = CONFIG.EMAIL_TEST_RECEIVER } const transport = createTransport({ - host: CONFIG.EMAIL_SMTP_URL, + host: CONFIG.EMAIL_SMTP_HOST, port: CONFIG.EMAIL_SMTP_PORT, secure: false, // true for 465, false for other ports requireTLS: CONFIG.EMAIL_TLS, @@ -62,6 +62,7 @@ export const sendEmailTranslated = async ({ message: { from: `Gradido (${i18n.__('emails.general.doNotAnswer')}) <${CONFIG.EMAIL_SENDER}>`, }, + send: CONFIG.EMAIL, transport, preview: false, // i18n, // is only needed if you don't install i18n diff --git a/backend/src/emails/sendEmailVariants.test.ts b/backend/src/emails/sendEmailVariants.test.ts index 9a2cb4ce0..aee479441 100644 --- a/backend/src/emails/sendEmailVariants.test.ts +++ b/backend/src/emails/sendEmailVariants.test.ts @@ -25,7 +25,14 @@ import { sendContributionChangedByModeratorEmail, } from './sendEmailVariants' +const testMailServerHost = 'localhost' +const testMailServerPort = 1025 +const testMailTLS = false + CONFIG.EMAIL_SENDER = 'info@gradido.net' +CONFIG.EMAIL_SMTP_HOST = testMailServerHost +CONFIG.EMAIL_SMTP_PORT = testMailServerPort +CONFIG.EMAIL_TLS = testMailTLS let con: Connection let testEnv: { @@ -93,11 +100,6 @@ describe('sendEmailVariants', () => { describe('result', () => { it('is the expected object', () => { expect(result).toMatchObject({ - envelope: { - from: 'info@gradido.net', - to: ['peter@lustig.de'], - }, - message: expect.any(String), originalMessage: expect.objectContaining({ to: 'Peter Lustig ', from: 'Gradido (emails.general.doNotAnswer) ', @@ -151,11 +153,6 @@ describe('sendEmailVariants', () => { describe('result', () => { it('is the expected object', () => { expect(result).toMatchObject({ - envelope: { - from: 'info@gradido.net', - to: ['peter@lustig.de'], - }, - message: expect.any(String), originalMessage: expect.objectContaining({ to: 'Peter Lustig ', from: 'Gradido (emails.general.doNotAnswer) ', @@ -204,11 +201,6 @@ describe('sendEmailVariants', () => { describe('result', () => { it('is the expected object', () => { expect(result).toMatchObject({ - envelope: { - from: 'info@gradido.net', - to: ['peter@lustig.de'], - }, - message: expect.any(String), originalMessage: expect.objectContaining({ to: 'Peter Lustig ', from: 'Gradido (emails.general.doNotAnswer) ', @@ -267,11 +259,6 @@ describe('sendEmailVariants', () => { describe('result', () => { it('is the expected object', () => { expect(result).toMatchObject({ - envelope: { - from: 'info@gradido.net', - to: ['peter@lustig.de'], - }, - message: expect.any(String), originalMessage: expect.objectContaining({ to: 'Peter Lustig ', from: 'Gradido (emails.general.doNotAnswer) ', @@ -329,11 +316,6 @@ describe('sendEmailVariants', () => { describe('result', () => { it('is the expected object', () => { expect(result).toMatchObject({ - envelope: { - from: 'info@gradido.net', - to: ['peter@lustig.de'], - }, - message: expect.any(String), originalMessage: expect.objectContaining({ to: 'Peter Lustig ', from: 'Gradido (emails.general.doNotAnswer) ', @@ -389,11 +371,6 @@ describe('sendEmailVariants', () => { describe('result', () => { it('has expected result', () => { expect(result).toMatchObject({ - envelope: { - from: 'info@gradido.net', - to: ['peter@lustig.de'], - }, - message: expect.any(String), originalMessage: expect.objectContaining({ to: 'Peter Lustig ', from: 'Gradido (emails.general.doNotAnswer) ', @@ -449,11 +426,6 @@ describe('sendEmailVariants', () => { describe('result', () => { it('is the expected object', () => { expect(result).toMatchObject({ - envelope: { - from: 'info@gradido.net', - to: ['peter@lustig.de'], - }, - message: expect.any(String), originalMessage: expect.objectContaining({ to: 'Peter Lustig ', from: 'Gradido (emails.general.doNotAnswer) ', @@ -507,11 +479,6 @@ describe('sendEmailVariants', () => { describe('result', () => { it('is the expected object', () => { expect(result).toMatchObject({ - envelope: { - from: 'info@gradido.net', - to: ['peter@lustig.de'], - }, - message: expect.any(String), originalMessage: expect.objectContaining({ to: 'Peter Lustig ', from: 'Gradido (emails.general.doNotAnswer) ', @@ -571,11 +538,6 @@ describe('sendEmailVariants', () => { describe('result', () => { it('is the expected object', () => { expect(result).toMatchObject({ - envelope: { - from: 'info@gradido.net', - to: ['peter@lustig.de'], - }, - message: expect.any(String), originalMessage: expect.objectContaining({ to: 'Peter Lustig ', from: 'Gradido (emails.general.doNotAnswer) ', @@ -633,11 +595,6 @@ describe('sendEmailVariants', () => { describe('result', () => { it('is the expected object', () => { expect(result).toMatchObject({ - envelope: { - from: 'info@gradido.net', - to: ['peter@lustig.de'], - }, - message: expect.any(String), originalMessage: expect.objectContaining({ to: 'Peter Lustig ', from: 'Gradido (emails.general.doNotAnswer) ', diff --git a/backend/src/graphql/model/ContributionLink.ts b/backend/src/graphql/model/ContributionLink.ts index 1576fc97a..3d02953b3 100644 --- a/backend/src/graphql/model/ContributionLink.ts +++ b/backend/src/graphql/model/ContributionLink.ts @@ -19,7 +19,7 @@ export class ContributionLink { this.cycle = contributionLink.cycle this.maxPerCycle = contributionLink.maxPerCycle this.code = contributionLink.code - this.link = CONFIG.COMMUNITY_REDEEM_CONTRIBUTION_URL.replace(/{code}/g, this.code) + this.link = CONFIG.COMMUNITY_REDEEM_CONTRIBUTION_URL + this.code } @Field(() => Int) diff --git a/backend/src/graphql/model/TransactionLink.ts b/backend/src/graphql/model/TransactionLink.ts index 74178610c..995fe5d50 100644 --- a/backend/src/graphql/model/TransactionLink.ts +++ b/backend/src/graphql/model/TransactionLink.ts @@ -20,7 +20,7 @@ export class TransactionLink { this.deletedAt = transactionLink.deletedAt this.redeemedAt = transactionLink.redeemedAt this.redeemedBy = redeemedBy - this.link = CONFIG.COMMUNITY_REDEEM_URL.replace(/{code}/g, this.code) + this.link = CONFIG.COMMUNITY_REDEEM_URL + this.code } @Field(() => Int) diff --git a/backend/src/graphql/resolver/GdtResolver.ts b/backend/src/graphql/resolver/GdtResolver.ts index 74cd81f01..2703be8ed 100644 --- a/backend/src/graphql/resolver/GdtResolver.ts +++ b/backend/src/graphql/resolver/GdtResolver.ts @@ -24,6 +24,16 @@ export class GdtResolver { { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, @Ctx() context: Context, ): Promise { + if (!CONFIG.GDT_ACTIVE) { + return new GdtEntryList( + 'disabled', + 0, + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any + [], + 0, + 0, + ) + } const userEntity = getUser(context) try { @@ -51,6 +61,9 @@ export class GdtResolver { @Authorized([RIGHTS.GDT_BALANCE]) @Query(() => Float, { nullable: true }) async gdtBalance(@Ctx() context: Context): Promise { + if (!CONFIG.GDT_ACTIVE) { + return null + } const user = getUser(context) try { const resultGDTSum = await apiPost(`${CONFIG.GDT_API_URL}/GdtEntries/sumPerEmailApi`, { @@ -71,6 +84,9 @@ export class GdtResolver { @Query(() => Int) // eslint-disable-next-line @typescript-eslint/no-explicit-any async existPid(@Arg('pid', () => Int) pid: number): Promise { + if (!CONFIG.GDT_ACTIVE) { + return 0 + } // load user const resultPID = await apiGet(`${CONFIG.GDT_API_URL}/publishers/checkPidApi/${pid}`) if (!resultPID.success) { diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 1889e3be0..26e4dcbcd 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -230,8 +230,11 @@ export class TransactionResolver { logger.addContext('user', user.id) logger.info(`transactionList(user=${user.firstName}.${user.lastName}, ${user.emailId})`) - const gdtResolver = new GdtResolver() - const balanceGDTPromise = gdtResolver.gdtBalance(context) + let balanceGDTPromise: Promise = Promise.resolve(null) + if (CONFIG.GDT_ACTIVE) { + const gdtResolver = new GdtResolver() + balanceGDTPromise = gdtResolver.gdtBalance(context) + } // find current balance const lastTransaction = await getLastTransaction(user.id) @@ -418,8 +421,10 @@ export class TransactionResolver { ).toDecimalPlaces(2, Decimal.ROUND_HALF_UP) } }) - const balanceGDT = await balanceGDTPromise - context.balanceGDT = balanceGDT + if (CONFIG.GDT_ACTIVE) { + const balanceGDT = await balanceGDTPromise + context.balanceGDT = balanceGDT + } // Construct Result return new TransactionList(await balanceResolver.balance(context), transactions) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 58b6b33c4..182447a33 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -244,10 +244,10 @@ describe('UserResolver', () => { describe('account activation email', () => { it('sends an account activation email', () => { - const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( - /{optin}/g, - emailVerificationCode, - ).replace(/{code}/g, '') + const activationLink = `${ + CONFIG.EMAIL_LINK_VERIFICATION + }${emailVerificationCode.toString()}` + expect(sendAccountActivationEmail).toBeCalledWith({ firstName: 'Peter', lastName: 'Lustig', @@ -2230,14 +2230,13 @@ describe('UserResolver', () => { }) it('sends an account activation email', async () => { - const userConatct = await UserContact.findOneOrFail({ + const userContact = await UserContact.findOneOrFail({ where: { email: 'bibi@bloxberg.de' }, relations: ['user'], }) - const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( - /{optin}/g, - userConatct.emailVerificationCode.toString(), - ).replace(/{code}/g, '') + const activationLink = `${ + CONFIG.EMAIL_LINK_VERIFICATION + }${userContact.emailVerificationCode.toString()}` expect(sendAccountActivationEmail).toBeCalledWith({ firstName: 'Bibi', lastName: 'Bloxberg', @@ -2252,14 +2251,14 @@ describe('UserResolver', () => { }) it('stores the EMAIL_ADMIN_CONFIRMATION event in the database', async () => { - const userConatct = await UserContact.findOneOrFail({ + const userContact = await UserContact.findOneOrFail({ where: { email: 'bibi@bloxberg.de' }, relations: ['user'], }) await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventType.EMAIL_ADMIN_CONFIRMATION, - affectedUserId: userConatct.user.id, + affectedUserId: userContact.user.id, actingUserId: admin.id, }), ) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 843e43794..93d5777f8 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -109,7 +109,7 @@ const newEmailContact = (email: string, userId: number): DbUserContact => { // eslint-disable-next-line @typescript-eslint/ban-types export const activationLink = (verificationCode: string): string => { logger.debug(`activationLink(${verificationCode})...`) - return CONFIG.EMAIL_LINK_SETPASSWORD.replace(/{optin}/g, verificationCode.toString()) + return CONFIG.EMAIL_LINK_SETPASSWORD + verificationCode.toString() } const newGradidoID = async (): Promise => { @@ -371,10 +371,9 @@ export class UserResolver { throw new LogError('Error while updating dbUser', error) }) - const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( - /{optin}/g, - emailContact.emailVerificationCode.toString(), - ).replace(/{code}/g, redeemCode ? '/' + redeemCode : '') + const activationLink = `${ + CONFIG.EMAIL_LINK_VERIFICATION + }${emailContact.emailVerificationCode.toString()}${redeemCode ? `/${redeemCode}` : ''}` void sendAccountActivationEmail({ firstName, diff --git a/backend/tsconfig.json b/backend/tsconfig.json index c61539e12..cd310e8a3 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -15,6 +15,7 @@ // "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ "outDir": "./build", /* Redirect output structure to the directory. */ + // "rootDir" : ".", // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ @@ -60,7 +61,8 @@ /* external */ "@dbTools/*": ["../database/src/*", "../../database/build/src/*"], "@entity/*": ["../database/entity/*", "../../database/build/entity/*"], - "@logging/*": ["../database/logging/*", "../../database/build/logging/*"] + "@logging/*": ["../database/logging/*", "../../database/build/logging/*"], + "@config/*": ["../config/src/*", "../../config/build/src/*"] }, // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ "typeRoots": ["@types", "node_modules/@types"], /* List of folders to include type definitions from. */ @@ -86,9 +88,12 @@ }, "references": [ { - "path": "../database/tsconfig.json", + "path": "../database/tsconfig.json" // add 'prepend' if you want to include the referenced project in your output file // "prepend": true + }, + { + "path": "../config/tsconfig.json" } ] } diff --git a/backend/yarn.lock b/backend/yarn.lock index 571e2eb59..a1777c229 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -332,13 +332,6 @@ core-js-pure "^3.30.2" regenerator-runtime "^0.14.0" -"@babel/runtime@^7.21.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec" - integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA== - dependencies: - regenerator-runtime "^0.13.11" - "@babel/template@^7.15.4", "@babel/template@^7.3.3": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" @@ -454,6 +447,18 @@ resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-10.0.1.tgz#ee9da297fabc557e1c040a0f44ee89c266ccc306" integrity sha512-CvlW7jmOhWzuqOqiJQ3rQVLMcREh0eel4IBnxDx2FAcK8g7qoJRQK4L1CPBASoCY6y8e6zuCy3f2g+HWdkzcMw== +"@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.8": version "0.11.8" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" @@ -473,6 +478,18 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -750,6 +767,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@pkgr/utils@^2.3.1": version "2.3.1" resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.3.1.tgz#0a9b06ffddee364d6642b3cd562ca76f55b34a03" @@ -823,6 +845,23 @@ domhandler "^4.2.0" selderee "^0.6.0" +"@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/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -1064,6 +1103,13 @@ jest-diff "^27.0.0" pretty-format "^27.0.0" +"@types/joi@^17.2.3": + version "17.2.3" + resolved "https://registry.yarnpkg.com/@types/joi/-/joi-17.2.3.tgz#b7768ed9d84f1ebd393328b9f97c1cf3d2b94798" + integrity sha512-dGjs/lhrWOa+eO0HwgxCSnDm5eMGCsXuvLglMghJq32F6q5LyyNuXb41DHzrg501CKNOSSAHmfB7FDGeUnDmzw== + dependencies: + joi "*" + "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" @@ -1128,13 +1174,13 @@ integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== "@types/mysql@^2.15.8": - version "2.15.21" - resolved "https://registry.yarnpkg.com/@types/mysql/-/mysql-2.15.21.tgz#7516cba7f9d077f980100c85fd500c8210bd5e45" - integrity sha512-NPotx5CVful7yB+qZbWtXL2fA4e7aEHkihHLjklc6ID8aq7bhguHgeIoC1EmSNTAuCgI6ZXrjt2ZSaXnYX0EUg== + version "2.15.26" + resolved "https://registry.yarnpkg.com/@types/mysql/-/mysql-2.15.26.tgz#f0de1484b9e2354d587e7d2bd17a873cc8300836" + integrity sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ== dependencies: "@types/node" "*" -"@types/node@*", "@types/node@^16.10.3": +"@types/node@*": version "16.10.3" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.3.tgz#7a8f2838603ea314d1d22bb3171d899e15c57bd5" integrity sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ== @@ -1149,6 +1195,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.21.tgz#6359d8cf73481e312a43886fa50afc70ce5592c6" integrity sha512-zv8ukKci1mrILYiQOwGSV4FpkZhyxQtuFWGya2GujWg+zVAeRQ4qbaMmWp9vb9889CFA8JECH7lkwCL6Ygg8kA== +"@types/node@^17.0.21": + version "17.0.45" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" + integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== + "@types/nodemailer@*": version "6.4.6" resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.6.tgz#ce21b4b474a08f672f182e15982b7945dde1f288" @@ -1528,6 +1579,11 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -1547,6 +1603,11 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + any-promise@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -2396,6 +2457,15 @@ cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^7.0.0: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -2469,18 +2539,16 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -date-fns@^2.29.3: - version "2.30.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" - integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== - dependencies: - "@babel/runtime" "^7.21.0" - date-format@^4.0.9: version "4.0.9" resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.9.tgz#4788015ac56dedebe83b03bc361f00c1ddcf1923" integrity sha512-+8J+BOUpSrlKLQLeF8xJJVTxS8QfRSuJgwxSVvslzgO3E6khbI0F5mMEPf5mTYhCCm4h99knYP6H3W9n3BQFrg== +dayjs@^1.11.9: + version "1.11.13" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" + integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== + debug@2.6.9, debug@^2.2.0: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -2714,15 +2782,20 @@ dotenv@^10.0.0: integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== dotenv@^16.0.3: - version "16.3.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" - integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== + version "16.4.7" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.7.tgz#0e20c5b82950140aa99be360a8a5f52335f53c26" + integrity sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ== duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -2757,6 +2830,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -3421,6 +3499,14 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -3592,6 +3678,18 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" +glob@^10.3.10: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" @@ -3604,17 +3702,6 @@ glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - global-dirs@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" @@ -3708,8 +3795,13 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +"gradido-config@file:../config": + version "1.0.0" + dependencies: + joi "^17.13.3" + "gradido-database@file:../database": - version "2.4.1" + version "2.4.4" dependencies: "@types/uuid" "^8.3.4" cross-env "^7.0.3" @@ -4451,6 +4543,15 @@ iterall@^1.1.3, iterall@^1.2.1, iterall@^1.3.0: resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jest-changed-files@^27.2.5: version "27.2.5" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.2.5.tgz#9dfd550d158260bcb6fa80aff491f5647f7daeca" @@ -4862,6 +4963,17 @@ jest@^27.2.4: import-local "^3.0.2" jest-cli "^27.2.5" +joi@*, 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" + jose@^4.14.4: version "4.14.4" resolved "https://registry.yarnpkg.com/jose/-/jose-4.14.4.tgz#59e09204e2670c3164ee24cbfe7115c6f8bff9ca" @@ -5148,6 +5260,11 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + lru-cache@^4.1.3: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -5300,10 +5417,10 @@ minimatch@^3.0.5, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimatch@^5.0.1: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== dependencies: brace-expansion "^2.0.1" @@ -5317,6 +5434,11 @@ minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + mkdirp@^2.1.3: version "2.1.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19" @@ -5740,6 +5862,11 @@ p-wait-for@3.2.0: dependencies: p-timeout "^3.0.0" +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + package-json@^6.3.0: version "6.5.0" resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" @@ -5812,6 +5939,14 @@ path-parse@^1.0.6, path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -6173,10 +6308,10 @@ reflect-metadata@^0.1.13: resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== -regenerator-runtime@^0.13.11: - version "0.13.11" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== +reflect-metadata@^0.2.1: + version "0.2.2" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b" + integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q== regenerator-runtime@^0.14.0: version "0.14.1" @@ -6479,6 +6614,11 @@ signal-exit@^3.0.2, signal-exit@^3.0.3: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -6578,6 +6718,15 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -6587,6 +6736,15 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2 is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" @@ -6628,6 +6786,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -6635,6 +6800,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -6874,9 +7046,9 @@ ts-jest@^27.0.5: yargs-parser "20.x" ts-mysql-migrate@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/ts-mysql-migrate/-/ts-mysql-migrate-1.0.2.tgz#736d37c3aa3fef92f226b869098e939950d0e18c" - integrity sha512-zDW6iQsfPCJfQ3JMhfUGjhy8aK+VNTvPrXmJH66PB2EGEvyn4m7x2nBdhDNhKuwYU9LMxW1p+l39Ei+btXNpxA== + version "1.1.2" + resolved "https://registry.yarnpkg.com/ts-mysql-migrate/-/ts-mysql-migrate-1.1.2.tgz#6f280f4684cb95440ffa7254b926b494badb8cf3" + integrity sha512-jwhVaMrYBNNhAoZ5XISxzqbnXTHqwazqu/r1UQ6kUaGNPGL43ZFnBiXVj4Gm3pfe3xtCGIaNInehDfdDuigPgw== dependencies: "@types/mysql" "^2.15.8" mysql "^2.18.1" @@ -7033,21 +7205,21 @@ typedarray-to-buffer@^3.1.5: is-typedarray "^1.0.0" typeorm@^0.3.16: - version "0.3.17" - resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.17.tgz#a73c121a52e4fbe419b596b244777be4e4b57949" - integrity sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig== + version "0.3.20" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.20.tgz#4b61d737c6fed4e9f63006f88d58a5e54816b7ab" + integrity sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q== dependencies: "@sqltools/formatter" "^1.2.5" app-root-path "^3.1.0" buffer "^6.0.3" chalk "^4.1.2" cli-highlight "^2.1.11" - date-fns "^2.29.3" + dayjs "^1.11.9" debug "^4.3.4" dotenv "^16.0.3" - glob "^8.1.0" + glob "^10.3.10" mkdirp "^2.1.3" - reflect-metadata "^0.1.13" + reflect-metadata "^0.2.1" sha.js "^2.4.11" tslib "^2.5.0" uuid "^9.0.0" @@ -7193,9 +7365,9 @@ uuid@^8.0.0, uuid@^8.3.2: integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== uuid@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" - integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== v8-to-istanbul@^8.1.0: version "8.1.0" @@ -7374,6 +7546,15 @@ workerpool@^9.2.0: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-9.2.0.tgz#f74427cbb61234708332ed8ab9cbf56dcb1c4371" integrity sha512-PKZqBOCo6CYkVOwAxWxQaSF2Fvb5Iv2fCeTP7buyWI2GiynWr46NcXSgK/idoV6e60dgCBfgYc+Un3HMvmqP8w== +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -7383,6 +7564,15 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" diff --git a/config/.eslintignore b/config/.eslintignore new file mode 100644 index 000000000..1ae86fe5e --- /dev/null +++ b/config/.eslintignore @@ -0,0 +1,4 @@ +node_modules +**/*.min.js +build +coverage \ No newline at end of file diff --git a/config/.eslintrc.js b/config/.eslintrc.js new file mode 100644 index 000000000..4be107982 --- /dev/null +++ b/config/.eslintrc.js @@ -0,0 +1,214 @@ +// eslint-disable-next-line import/no-commonjs, import/unambiguous +module.exports = { + root: true, + env: { + node: true, + }, + parser: '@typescript-eslint/parser', + plugins: ['prettier', '@typescript-eslint', 'type-graphql', 'import', 'n', 'promise'], + extends: [ + 'standard', + 'eslint:recommended', + 'plugin:prettier/recommended', + 'plugin:import/recommended', + 'plugin:import/typescript', + 'plugin:security/recommended', + 'plugin:@eslint-community/eslint-comments/recommended', + ], + settings: { + 'import/parsers': { + '@typescript-eslint/parser': ['.ts', '.tsx'], + }, + 'import/resolver': { + typescript: { + project: ['./tsconfig.json', '**/tsconfig.json'], + }, + node: true, + }, + // the parser cannot handle the split sodium import + 'import/ignore': ['sodium-native'], + }, + rules: { + 'no-console': 'error', + camelcase: ['error', { allow: ['crypto_*', 'randombytes_random'] }], + 'no-debugger': 'error', + 'prettier/prettier': [ + 'error', + { + htmlWhitespaceSensitivity: 'ignore', + }, + ], + // import + 'import/export': 'error', + // 'import/no-deprecated': 'error', + 'import/no-empty-named-blocks': 'error', + 'import/no-extraneous-dependencies': 'error', + 'import/no-mutable-exports': 'error', + 'import/no-unused-modules': 'error', + 'import/no-named-as-default': 'error', + 'import/no-named-as-default-member': 'error', + 'import/no-amd': 'error', + 'import/no-commonjs': 'error', + 'import/no-import-module-exports': 'error', + 'import/no-nodejs-modules': 'off', + 'import/unambiguous': 'error', + 'import/default': 'error', + 'import/named': 'error', + 'import/namespace': 'error', + 'import/no-absolute-path': 'error', + 'import/no-cycle': 'error', + 'import/no-dynamic-require': 'error', + 'import/no-internal-modules': 'off', + 'import/no-relative-packages': 'error', + 'import/no-relative-parent-imports': [ + 'error', + { ignore: ['@/*', 'random-bigint', 'sodium-native'] }, + ], + 'import/no-self-import': 'error', + 'import/no-unresolved': 'error', + 'import/no-useless-path-segments': 'error', + 'import/no-webpack-loader-syntax': 'error', + 'import/consistent-type-specifier-style': 'error', + 'import/exports-last': 'off', + 'import/extensions': 'error', + 'import/first': 'error', + 'import/group-exports': 'off', + 'import/newline-after-import': 'error', + 'import/no-anonymous-default-export': 'error', + 'import/no-default-export': 'error', + 'import/no-duplicates': 'error', + 'import/no-named-default': 'error', + 'import/no-namespace': 'error', + 'import/no-unassigned-import': 'error', + 'import/order': [ + 'error', + { + groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'], + 'newlines-between': 'always', + pathGroups: [ + { + pattern: '@?*/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/**', + group: 'external', + position: 'after', + }, + ], + alphabetize: { + order: 'asc' /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */, + caseInsensitive: true /* ignore case. Options: [true, false] */, + }, + distinctGroup: true, + }, + ], + 'import/prefer-default-export': 'off', + // n + 'n/handle-callback-err': 'error', + 'n/no-callback-literal': 'error', + 'n/no-exports-assign': 'error', + 'n/no-extraneous-import': 'error', + 'n/no-extraneous-require': 'error', + 'n/no-hide-core-modules': 'error', + 'n/no-missing-import': 'off', // not compatible with typescript + 'n/no-missing-require': 'error', + 'n/no-new-require': 'error', + 'n/no-path-concat': 'error', + 'n/no-process-exit': 'error', + 'n/no-unpublished-bin': 'error', + 'n/no-unpublished-import': 'off', // TODO need to exclude seeds + 'n/no-unpublished-require': 'error', + 'n/no-unsupported-features': ['error', { ignores: ['modules'] }], + 'n/no-unsupported-features/es-builtins': 'error', + 'n/no-unsupported-features/es-syntax': 'error', + 'n/no-unsupported-features/node-builtins': 'error', + 'n/process-exit-as-throw': 'error', + 'n/shebang': 'error', + 'n/callback-return': 'error', + 'n/exports-style': 'error', + 'n/file-extension-in-import': 'off', + 'n/global-require': 'error', + 'n/no-mixed-requires': 'error', + 'n/no-process-env': 'error', + 'n/no-restricted-import': 'error', + 'n/no-restricted-require': 'error', + 'n/no-sync': 'error', + 'n/prefer-global/buffer': 'error', + 'n/prefer-global/console': 'error', + 'n/prefer-global/process': 'error', + 'n/prefer-global/text-decoder': 'error', + 'n/prefer-global/text-encoder': 'error', + 'n/prefer-global/url': 'error', + 'n/prefer-global/url-search-params': 'error', + 'n/prefer-promises/dns': 'error', + 'n/prefer-promises/fs': 'error', + // promise + 'promise/catch-or-return': 'error', + 'promise/no-return-wrap': 'error', + 'promise/param-names': 'error', + 'promise/always-return': 'error', + 'promise/no-native': 'off', + 'promise/no-nesting': 'warn', + 'promise/no-promise-in-callback': 'warn', + 'promise/no-callback-in-promise': 'warn', + 'promise/avoid-new': 'warn', + 'promise/no-new-statics': 'error', + 'promise/no-return-in-finally': 'warn', + 'promise/valid-params': 'warn', + 'promise/prefer-await-to-callbacks': 'error', + 'promise/no-multiple-resolved': 'error', + // eslint comments + '@eslint-community/eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }], + '@eslint-community/eslint-comments/no-restricted-disable': 'error', + '@eslint-community/eslint-comments/no-use': 'off', + '@eslint-community/eslint-comments/require-description': 'off', + }, + overrides: [ + // only for ts files + { + files: ['*.ts', '*.tsx'], + extends: [ + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + 'plugin:@typescript-eslint/strict', + 'plugin:type-graphql/recommended', + ], + rules: { + // allow explicitly defined dangling promises + '@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }], + 'no-void': ['error', { allowAsStatement: true }], + // ignore prefer-regexp-exec rule to allow string.match(regex) + '@typescript-eslint/prefer-regexp-exec': 'off', + // this should not run on ts files: https://github.com/import-js/eslint-plugin-import/issues/2215#issuecomment-911245486 + 'import/unambiguous': 'off', + // this is not compatible with typeorm, due to joined tables can be null, but are not defined as nullable + '@typescript-eslint/no-unnecessary-condition': 'off', + }, + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json', '**/tsconfig.json'], + // this is to properly reference the referenced project database without requirement of compiling it + // eslint-disable-next-line camelcase + EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true, + }, + }, + { + files: ['*.test.ts'], + plugins: ['jest'], + env: { + jest: true, + }, + rules: { + 'jest/no-disabled-tests': 'error', + 'jest/no-focused-tests': 'error', + 'jest/no-identical-title': 'error', + 'jest/prefer-to-have-length': 'error', + 'jest/valid-expect': 'error', + '@typescript-eslint/unbound-method': 'off', + 'jest/unbound-method': 'error', + }, + }, + ], +} diff --git a/config/.gitignore b/config/.gitignore new file mode 100644 index 000000000..ce6695205 --- /dev/null +++ b/config/.gitignore @@ -0,0 +1,14 @@ +node_modules/ +build/ +.cache/ +.yarn/install-state.gz + +/.env +/.env.bak + +# coverage folder +coverage/ + +# emacs +*~ + diff --git a/config/.nvmrc b/config/.nvmrc new file mode 100644 index 000000000..79bdb1b97 --- /dev/null +++ b/config/.nvmrc @@ -0,0 +1 @@ +v18.7.0 diff --git a/config/package.json b/config/package.json new file mode 100644 index 000000000..f2d12e58e --- /dev/null +++ b/config/package.json @@ -0,0 +1,25 @@ +{ + "name": "gradido-config", + "version": "1.0.0", + "description": "Gradido Config for validate config", + "main": "src/index.ts", + "repository": "https://github.com/gradido/gradido/config", + "author": "einhornimmond", + "license": "Apache-2.0", + "private": false, + "scripts": { + "build": "tsc --build", + "clean": "tsc --build --clean", + "test": "node test/index" + }, + "devDependencies": { + "@types/joi": "^17.2.3", + "@types/node": "^17.0.21", + "typescript": "^4.3.5", + "mkdirp": "^3.0.1", + "ncp": "^2.0.0" + }, + "dependencies": { + "joi": "^17.13.3" + } +} diff --git a/config/src/commonSchema.ts b/config/src/commonSchema.ts new file mode 100644 index 000000000..46264217b --- /dev/null +++ b/config/src/commonSchema.ts @@ -0,0 +1,229 @@ +import Joi from 'joi' + +export const browserUrls = Joi.array() + .items(Joi.string().uri()) + .custom((value: string[], helpers: Joi.CustomHelpers) => { + const protocol = new URL(value[0]).protocol + for (const url of value) { + if (new URL(url).protocol !== protocol) { + return helpers.error('any.invalid') + } + } + return value; + }) + .required() + .description('All URLs need to have same protocol to prevent mixed block errors') + +export const DECAY_START_TIME = Joi.date() + .iso() // ISO 8601 format for date validation + .description('The start time for decay, expected in ISO 8601 format (e.g. 2021-05-13T17:46:31Z)') + .default(new Date('2021-05-13T17:46:31Z')) // default to the specified date if not provided + .required() + +export const DB_VERSION = Joi.string() + .pattern(/^\d{4}-[a-z0-9-_]+$/) + .message('DB_VERSION must be in the format: YYYY-description, e.g. "0087-add_index_on_user_roles".') + .description('db version string, last migration file name without ending or last folder in entity') + .required() + +export const COMMUNITY_URL = Joi.string() + .uri({ scheme: ['http', 'https'] }) + .custom((value: string, helpers: Joi.CustomHelpers) => { + if (value.endsWith('/')) { + return helpers.error('any.invalid', { message: 'URL should not end with a slash (/)' }) + } + return value; + }) + .description('The base URL of the community, should have the same protocol as frontend, admin and backend api to prevent mixed contend issues.') + .default('http://0.0.0.0') + .required() + +export const GRAPHQL_URI = Joi.string() + .uri({ scheme: ['http', 'https'] }) + .description( + ` + The external URL of the backend service, + accessible from outside the server (e.g., via Nginx or the server's public URL), + should have the same protocol as frontend and admin to prevent mixed contend issues. + `, + ) + .default('http://0.0.0.0/graphql') + .required() + +export const COMMUNITY_NAME = Joi.string() + .min(3) + .max(40) + .description('The name of the community') + .default('Gradido Entwicklung') + .required() + +export const COMMUNITY_DESCRIPTION = Joi.string() + .min(10) + .max(255) + .description('A short description of the community') + .default('Die lokale Entwicklungsumgebung von Gradido.') + .required() + +export const 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') + .required() + +export const COMMUNITY_LOCATION = Joi.string() + .pattern(/^[-+]?[0-9]{1,2}(\.[0-9]+)?,\s?[-+]?[0-9]{1,3}(\.[0-9]+)?$/) + .when('GMS_ACTIVE', { + is: true, + then: Joi.string().required(), + otherwise: Joi.string().optional() + }) + .description('Geographical location of the community in "latitude, longitude" format') + .default('49.280377, 9.690151') + +export const GRAPHIQL = Joi.boolean() + .description('Flag for enabling GraphQL playground for debugging.') + .default(false) + .when('NODE_ENV', { + is: 'development', + then: Joi.boolean().valid(true, false).required(), // only allow true in development mode + otherwise: Joi.boolean().valid(false).required() // false in any other mode + }) + +export const GMS_ACTIVE = Joi.boolean() + .description('Flag to indicate if the GMS (Geographic Member Search) service is used.') + .default(false) + .required() + +export const GDT_ACTIVE = Joi.boolean() + .description('Flag to indicate if the GMS (Geographic Member Search) service is used.') + .default(false) + .required() + +export const GDT_API_URL = Joi.string() + .uri({ scheme: ['http', 'https'] }) + .default('https://gdt.gradido.net') + .when('GDT_ACTIVE', { is: true, then: Joi.required() }) + .description('The URL for GDT API endpoint') + +export const HUMHUB_ACTIVE = Joi.boolean() + .description('Flag to indicate if the HumHub based Community Server is used.') + .default(false) + .required() + +export const LOG_LEVEL = Joi.string() + .valid('all', 'mark', 'trace', 'debug', 'info', 'warn', 'error', 'fatal', 'off') + .description('set log level') + .default('info') + .required() + +export const LOG4JS_CONFIG = Joi.string() + .pattern(/^[a-zA-Z0-9-_]+\.json$/) + .message('LOG4JS_CONFIG must be a valid filename ending with .json') + .description('config file name for log4js config file') + .default('log4js-config.json') + .required() + +export const LOGIN_APP_SECRET = Joi.string() + .pattern(/^[a-fA-F0-9]+$/) + .message('need to be valid hex') + .default('21ffbbc616fe') + .description('App secret for salt component for libsodium crypto_pwhash') + .required() + +export const LOGIN_SERVER_KEY = Joi.string() + .pattern(/^[a-fA-F0-9]+$/) + .length(32) + .message('need to be valid hex and 32 character') + .default('a51ef8ac7ef1abf162fb7a65261acd7a') + .description('Server key for password hashing as additional salt for libsodium crypto_shorthash_keygen') + .required() + +export const TYPEORM_LOGGING_RELATIVE_PATH = Joi.string() + .pattern(/^[a-zA-Z0-9-_\.]+\.log$/) + .message('TYPEORM_LOGGING_RELATIVE_PATH must be a valid filename ending with .log') + .description('log file name for logging typeorm activities') + .default('typeorm.log') + .required() + +export const DB_HOST = Joi.string() + .hostname() + .message('must be a valid host with alphanumeric characters, numbers, points and -') + .description("database host like 'localhost' or 'mariadb' in docker setup") + .default('localhost') + .required() + +export const DB_PORT = Joi.number() + .integer() + .min(1024) + .max(49151) + .description('database port, default: 3306') + .default(3306) + .required() + +export const DB_USER = Joi.string() + .pattern(/^[A-Za-z0-9]([A-Za-z0-9-_\.]*[A-Za-z0-9])?$/) // Validates MariaDB username rules + .min(1) // Minimum length 1 + .max(16) // Maximum length 16 + .message( + 'Valid database username (letters, numbers, hyphens, underscores, dots allowed; no spaces, must not start or end with hyphen, dot, or underscore)' + ) + .description('database username for mariadb') + .default('root') + .required() + +export const DB_PASSWORD = Joi.string() + .when(Joi.ref('NODE_ENV'), { + is: 'development', + then: Joi.string().allow(''), + otherwise: Joi.string() + .min(8) + .max(32) + .pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*(),.?":{}|<>]).+$/) + .message( + 'Password must be between 8 and 32 characters long, and contain at least one uppercase letter, one lowercase letter, one number, and one special character (e.g., !@#$%^&*).' + ) + }) + .description( + 'Password for the database user. In development mode, an empty password is allowed. In other environments, a complex password is required.' + ) + .default('') + .required() + +export const DB_DATABASE = Joi.string() + .pattern(/^[a-zA-Z][a-zA-Z0-9_-]{1,63}$/) + .description('Database name like gradido_community (must start with a letter, and can only contain letters, numbers, underscores, or dashes)') + .default('gradido_community') + .required() + +export const APP_VERSION = Joi.string() + .pattern(/^\d+\.\d+\.\d+$/) + .message('Version must be in the format "major.minor.patch" (e.g., "2.4.1")') + .description('App Version from package.json, alle modules share one version') + .required() + +export const BUILD_COMMIT = Joi.string() + .pattern(/^[0-9a-f]{40}$/) + .message('The commit hash must be a 40-character hexadecimal string.') + .description('The full git commit hash.') + .optional() + +export const BUILD_COMMIT_SHORT = Joi.string() + .pattern(/^[0-9a-f]{7}$/) + .message('The first 7 hexadecimal character from git commit hash.') + .description('A short version from the git commit hash.') + .required() + +export const NODE_ENV = Joi.string() + .valid('production', 'development', 'test') + .default('development') + .description('Specifies the environment in which the application is running.') + +export const DEBUG = Joi.boolean() + .description('Indicates whether the application is in debugging mode. Set to true when NODE_ENV is not "production".') + .default(false) + .required() + +export const PRODUCTION = Joi.boolean() + .default(false) + .description('Indicates whether the application is running in production mode. Set to true when NODE_ENV is "production".') + .required() diff --git a/config/src/index.ts b/config/src/index.ts new file mode 100644 index 000000000..04e096d0a --- /dev/null +++ b/config/src/index.ts @@ -0,0 +1,29 @@ +import { ObjectSchema } from 'joi' +export * from './commonSchema' + +export function validate(schema: ObjectSchema, data: any) { + const { error } = schema.validate(data) + const schemaJson = schema.describe() + if (error) { + error.details.forEach((err) => { + if (!err.context) { + throw new Error('missing context in config validation with joi') + } + const key = err.context.key + if (!key) { + throw new Error('missing key in config validation with joi') + } + const value = err.context.value + const description = schemaJson.keys[key] + ? schema.describe().keys[key].flags.description + : 'No description available' + if (data[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}`, + ) + } + }) + } +} \ No newline at end of file diff --git a/config/test/index.js b/config/test/index.js new file mode 100644 index 000000000..5aff2f2e9 --- /dev/null +++ b/config/test/index.js @@ -0,0 +1,8 @@ +const Joi = require('joi') +const commonSchema = require('../dist/commonSchema') + +const schema = Joi.object({ + commonSchema +}) + +// console.log(commonSchema.DECAY_START_TIME) \ No newline at end of file diff --git a/config/tsconfig.json b/config/tsconfig.json new file mode 100644 index 000000000..04306edad --- /dev/null +++ b/config/tsconfig.json @@ -0,0 +1,73 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ + "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./build/outfile.js", /* Concatenate and emit output to single file. */ + "outDir": "./build", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + "strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": ".", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [".", "../database"], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "references": [] /* Any project that is referenced must itself have a `references` array (which may be empty). */ +} diff --git a/config/yarn.lock b/config/yarn.lock new file mode 100644 index 000000000..2aa822598 --- /dev/null +++ b/config/yarn.lock @@ -0,0 +1,70 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@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" + +"@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/joi@^17.2.3": + version "17.2.3" + resolved "https://registry.yarnpkg.com/@types/joi/-/joi-17.2.3.tgz#b7768ed9d84f1ebd393328b9f97c1cf3d2b94798" + integrity sha512-dGjs/lhrWOa+eO0HwgxCSnDm5eMGCsXuvLglMghJq32F6q5LyyNuXb41DHzrg501CKNOSSAHmfB7FDGeUnDmzw== + dependencies: + joi "*" + +"@types/node@^17.0.21": + version "17.0.45" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" + integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== + +joi@*, 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" + +mkdirp@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" + integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== + +ncp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" + integrity sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA== + +typescript@^4.3.5: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== diff --git a/database/package.json b/database/package.json index 8f3810010..3c9f4bcd8 100644 --- a/database/package.json +++ b/database/package.json @@ -22,7 +22,7 @@ "@eslint-community/eslint-plugin-eslint-comments": "^3.2.1", "@types/faker": "^5.5.9", "@types/geojson": "^7946.0.13", - "@types/node": "^16.10.3", + "@types/node": "^17.0.21", "@typescript-eslint/eslint-plugin": "^5.57.1", "@typescript-eslint/parser": "^5.57.1", "eslint": "^8.37.0", diff --git a/database/src/config/detectLastDBVersion.ts b/database/src/config/detectLastDBVersion.ts new file mode 100644 index 000000000..bc9e7766e --- /dev/null +++ b/database/src/config/detectLastDBVersion.ts @@ -0,0 +1,43 @@ +import path from 'path' +import fs from 'fs' + +// Define the regular expression pattern once +const DB_VERSION_PATTERN = /^(\d{4}-[a-z0-9-_]+)/ + +// Define the paths to check +const migrationsDir = path.join(__dirname, '..', '..', 'migrations') +const entitiesDir = path.join(__dirname, '..', '..', 'entity') + +// Helper function to get the highest version number from the directory +function getLatestDbVersion(dir: string): string { + // Read all files and folders in the directory + const files = fs.readdirSync(dir) + + // Filter out files/folders that don't match the pattern for DB_VERSION + const dbVersionFiles = files.filter((file) => file.match(DB_VERSION_PATTERN)) + + if (dbVersionFiles.length === 0) { + throw new Error(`couldn't found any file in ${dir} matching ${DB_VERSION_PATTERN}`) + } + + // Sort files by the version number (extract the version from the filename) + const sortedFiles = dbVersionFiles.sort() + + const lastFile = sortedFiles[sortedFiles.length - 1] + const matches = lastFile.match(DB_VERSION_PATTERN) + if (!matches || matches.length < 1) { + throw new Error('invalid match') + } + // Return the latest version file or folder + return matches[1] +} + +// Get the latest version from migrations and entities +const latestMigrationVersion = getLatestDbVersion(migrationsDir) +const latestEntityVersion = getLatestDbVersion(entitiesDir) + +// Determine which directory has the latest version and return it +export const latestDbVersion = + latestMigrationVersion && (!latestEntityVersion || latestMigrationVersion > latestEntityVersion) + ? latestMigrationVersion + : latestEntityVersion diff --git a/database/yarn.lock b/database/yarn.lock index fd5598693..ed3d01507 100644 --- a/database/yarn.lock +++ b/database/yarn.lock @@ -170,10 +170,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.7.1.tgz#c6b9198178da504dfca1fd0be9b2e1002f1586f0" integrity sha512-ncRdc45SoYJ2H4eWU9ReDfp3vtFqDYhjOsKlFFUDEn8V1Bgr2RjYal8YT5byfadWIRluhPFU6JiDOl0H6Sl87A== -"@types/node@^16.10.3": - version "16.10.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.3.tgz#7a8f2838603ea314d1d22bb3171d899e15c57bd5" - integrity sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ== +"@types/node@^17.0.21": + version "17.0.45" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" + integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== "@types/semver@^7.3.12": version "7.5.0" diff --git a/deployment/bare_metal/.env.dist b/deployment/bare_metal/.env.dist index a07d92dde..f361f6331 100644 --- a/deployment/bare_metal/.env.dist +++ b/deployment/bare_metal/.env.dist @@ -9,7 +9,7 @@ EMAIL=true EMAIL_USERNAME=peter@lustig.de EMAIL_SENDER=peter@lustig.de EMAIL_PASSWORD=1234 -EMAIL_SMTP_URL=smtp.lustig.de +EMAIL_SMTP_HOST=smtp.lustig.de EMAIL_SMTP_PORT=587 # if set to true allow sending gradidos to another communities @@ -55,15 +55,15 @@ WEBHOOK_GITHUB_BRANCH=master # frontend and admin paths, usually don't need changes # used in nginx config and for links in emails COMMUNITY_REGISTER_PATH=/register -COMMUNITY_REDEEM_PATH=/redeem/{code} -COMMUNITY_REDEEM_CONTRIBUTION_PATH=/redeem/CL-{code} +COMMUNITY_REDEEM_PATH=/redeem/ +COMMUNITY_REDEEM_CONTRIBUTION_PATH=/redeem/CL- WALLET_LOGIN_PATH=/login -WALLET_AUTH_PATH=/authenticate?token={token} -EMAIL_LINK_VERIFICATION_PATH=/checkEmail/{optin}{code} -EMAIL_LINK_SETPASSWORD_PATH=/reset-password/{optin} +WALLET_AUTH_PATH=/authenticate?token= +EMAIL_LINK_VERIFICATION_PATH=/checkEmail/ +EMAIL_LINK_SETPASSWORD_PATH=/reset-password/ EMAIL_LINK_FORGOTPASSWORD_PATH=/forgot-password EMAIL_LINK_OVERVIEW_PATH=/overview -ADMIN_AUTH_PATH=/admin/authenticate?token={token} +ADMIN_AUTH_PATH=/admin/authenticate?token= GRAPHQL_PATH=/graphql # login expire time @@ -82,7 +82,7 @@ FEDERATION_VALIDATE_COMMUNITY_TIMER=60000 FEDERATION_COMMUNITY_APIS=1_0 # externe gradido services (more added in future) -GDT_API_URL=https://gdt.gradido.net +GDT_ACTIVE=false # DLT-Connector (still in develop) DLT_CONNECTOR=false @@ -117,7 +117,6 @@ NGINX_SSL_INCLUDE=/etc/letsencrypt/options-ssl-nginx.conf NGINX_REWRITE_LEGACY_URLS=false # LEGACY -DEFAULT_PUBLISHER_ID=2896 WEBHOOK_ELOPAGE_SECRET=secret # GMS diff --git a/dht-node/Dockerfile b/dht-node/Dockerfile index 9f1bbabf7..7b2aa6ad7 100644 --- a/dht-node/Dockerfile +++ b/dht-node/Dockerfile @@ -47,6 +47,7 @@ RUN mkdir -p ${DOCKER_WORKDIR} WORKDIR ${DOCKER_WORKDIR} RUN mkdir -p /database +RUN mkdir -p /config ################################################################################## # DEVELOPMENT (Connected to the local environment, to reload on demand) ########## @@ -59,7 +60,7 @@ FROM base as development # Run command # (for development we need to execute yarn install since the # node_modules are on another volume and need updating) -CMD /bin/sh -c "cd /database && yarn install && yarn build && cd /app && yarn install && yarn run dev" +CMD /bin/sh -c "cd /database && yarn install && yarn build && cd /config && yarn build && cd /app && yarn install && yarn run dev" ################################################################################## # BUILD (Does contain all files and is therefore bloated) ######################## @@ -70,6 +71,11 @@ FROM base as build COPY ./dht-node/ ./ # Copy everything from database COPY ./database/ ../database/ +# Copy everything from config +COPY ./config/ ../config/ + +# yarn install and build config +RUN cd ../config && yarn install --production=false --frozen-lockfile --non-interactive && yarn run build # yarn install dht-node RUN yarn install --production=false --frozen-lockfile --non-interactive @@ -99,9 +105,11 @@ FROM base as production # Copy "binary"-files from build image COPY --from=build ${DOCKER_WORKDIR}/build ./build COPY --from=build ${DOCKER_WORKDIR}/../database/build ../database/build +COPY --from=build ${DOCKER_WORKDIR}/../config/build ../config/build # We also copy the node_modules express and serve-static for the run script COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules COPY --from=build ${DOCKER_WORKDIR}/../database/node_modules ../database/node_modules +COPY --from=build ${DOCKER_WORKDIR}/../config/node_modules ../config/node_modules # Copy static files # COPY --from=build ${DOCKER_WORKDIR}/public ./public diff --git a/dht-node/jest.config.js b/dht-node/jest.config.js index 0bb09f0e4..66c5fba14 100644 --- a/dht-node/jest.config.js +++ b/dht-node/jest.config.js @@ -31,5 +31,10 @@ module.exports = { process.env.NODE_ENV === 'development' ? '/../database/src/$1' : '/../database/build/src/$1', + '@config/(.*)': + // eslint-disable-next-line n/no-process-env + process.env.NODE_ENV === 'development' + ? '/../config/src/$1' + : '/../config/build/$1', }, } diff --git a/dht-node/package.json b/dht-node/package.json index 5a481b823..e9c006bfd 100644 --- a/dht-node/package.json +++ b/dht-node/package.json @@ -20,6 +20,7 @@ "cross-env": "^7.0.3", "dotenv": "10.0.0", "gradido-database": "file:../database", + "joi": "^17.13.3", "log4js": "^6.7.1", "nodemon": "^2.0.20", "tsconfig-paths": "^4.1.2", @@ -29,6 +30,7 @@ "@eslint-community/eslint-plugin-eslint-comments": "^3.2.1", "@types/dotenv": "^8.2.0", "@types/jest": "^27.0.2", + "@types/joi": "^17.2.3", "@types/node": "^18.11.18", "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^5.57.1", @@ -43,6 +45,7 @@ "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-promise": "^6.1.1", "eslint-plugin-security": "^1.7.1", + "gradido-config": "../config", "jest": "^27.2.4", "prettier": "^2.8.7", "ts-jest": "^27.0.5", diff --git a/dht-node/src/config/index.ts b/dht-node/src/config/index.ts index 70ec452bf..6389a22af 100644 --- a/dht-node/src/config/index.ts +++ b/dht-node/src/config/index.ts @@ -1,18 +1,17 @@ /* eslint-disable n/no-process-env */ +import { validate } from '@config/index' +import { latestDbVersion } from '@dbTools/config/detectLastDBVersion' import dotenv from 'dotenv' +import { schema } from './schema' + dotenv.config() const constants = { - DB_VERSION: '0087-add_index_on_user_roles', + DB_VERSION: latestDbVersion, 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: 'v4.2024-01-17', - CURRENT: '', - }, } const server = { @@ -46,18 +45,6 @@ const federation = { FEDERATION_COMMUNITY_APIS: process.env.FEDERATION_COMMUNITY_APIS ?? '1_0', } -// 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}"`, - ) -} - export const CONFIG = { ...constants, ...server, @@ -65,3 +52,4 @@ export const CONFIG = { ...community, ...federation, } +validate(schema, CONFIG) diff --git a/dht-node/src/config/schema.ts b/dht-node/src/config/schema.ts new file mode 100644 index 000000000..cb7618cf4 --- /dev/null +++ b/dht-node/src/config/schema.ts @@ -0,0 +1,64 @@ +import { + COMMUNITY_NAME, + COMMUNITY_DESCRIPTION, + DB_DATABASE, + DB_HOST, + DB_PASSWORD, + DB_PORT, + DB_USER, + DB_VERSION, + LOG4JS_CONFIG, + LOG_LEVEL, + NODE_ENV, + PRODUCTION, + TYPEORM_LOGGING_RELATIVE_PATH, +} from '@config/commonSchema' +import Joi from 'joi' + +export const schema = Joi.object({ + COMMUNITY_NAME, + COMMUNITY_DESCRIPTION, + DB_DATABASE, + DB_HOST, + DB_PASSWORD, + DB_PORT, + DB_USER, + DB_VERSION, + LOG4JS_CONFIG, + LOG_LEVEL, + NODE_ENV, + PRODUCTION, + TYPEORM_LOGGING_RELATIVE_PATH, + + FEDERATION_DHT_TOPIC: Joi.string() + .default('GRADIDO_HUB') + .description('The topic for the DHT (Distributed Hash Table), defaults to GRADIDO_HUB') + .required(), + + FEDERATION_DHT_SEED: Joi.string() + .when('NODE_ENV', { + is: 'development', + then: Joi.allow(null), // Allow empty for development + otherwise: Joi.string() + .hex() + .length(64) + .message('need to be valid hex with 64 character') + .required(), // Valid hex and 64 characters for other environments + }) + .description( + 'Seed for libsodium crypto_sign_seed_keypair. Valid hex with 64 characters in production', + ) + .default(''), // Default to empty string + + FEDERATION_COMMUNITY_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .default(Joi.ref('COMMUNITY_URL')) + .description('Community URL for federation, defaults to COMMUNITY_URL') + .required(), + + FEDERATION_COMMUNITY_APIS: Joi.string() + .valid('1_0', '1_1') + .default('1_0') + .description('Federation community API version, defaults to 1_0') + .required(), +}) diff --git a/dht-node/tsconfig.json b/dht-node/tsconfig.json index 33362b054..0caefb6e5 100644 --- a/dht-node/tsconfig.json +++ b/dht-node/tsconfig.json @@ -53,7 +53,8 @@ "@typeorm/*": ["../backend/src/typeorm/*", "../../backend/src/typeorm/*"], "@dbTools/*": ["../database/src/*", "../../database/build/src/*"], "@entity/*": ["../database/entity/*", "../../database/build/entity/*"], - "@logging/*": ["../database/logging/*", "../../database/build/logging/*"] + "@logging/*": ["../database/logging/*", "../../database/build/logging/*"], + "@config/*": ["../config/src/*", "../../config/build/*"] }, // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ "typeRoots": ["src/dht_node/@types", "node_modules/@types"], /* List of folders to include type definitions from. */ diff --git a/dht-node/yarn.lock b/dht-node/yarn.lock index 4bf9fa916..b4292a644 100644 --- a/dht-node/yarn.lock +++ b/dht-node/yarn.lock @@ -345,6 +345,18 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.42.0.tgz#484a1d638de2911e6f5a30c12f49c7e4a3270fb6" integrity sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw== +"@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.10": version "0.11.10" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" @@ -665,6 +677,23 @@ picocolors "^1.0.0" tslib "^2.5.0" +"@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== + "@sinonjs/commons@^1.7.0": version "1.8.6" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" @@ -783,6 +812,13 @@ jest-matcher-utils "^27.0.0" pretty-format "^27.0.0" +"@types/joi@^17.2.3": + version "17.2.3" + resolved "https://registry.yarnpkg.com/@types/joi/-/joi-17.2.3.tgz#b7768ed9d84f1ebd393328b9f97c1cf3d2b94798" + integrity sha512-dGjs/lhrWOa+eO0HwgxCSnDm5eMGCsXuvLglMghJq32F6q5LyyNuXb41DHzrg501CKNOSSAHmfB7FDGeUnDmzw== + dependencies: + joi "*" + "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" @@ -2393,8 +2429,13 @@ graceful-fs@^4.2.4: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +gradido-config@../config: + version "1.0.0" + dependencies: + joi "^17.13.3" + "gradido-database@file:../database": - version "2.2.1" + version "2.4.1" dependencies: "@types/uuid" "^8.3.4" cross-env "^7.0.3" @@ -3226,6 +3267,17 @@ jest@^27.2.4: import-local "^3.0.2" jest-cli "^27.5.1" +joi@*, 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-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 4807a6bbf..e8c2d4d90 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -9,7 +9,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. @@ -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. @@ -55,9 +55,12 @@ services: - backend_node_modules:/app/node_modules - backend_database_node_modules:/database/node_modules - backend_database_build:/database/build + - backend_config_node_modules:/config/node_modules + - backend_config_build:/config/build # bind the local folder to the docker to allow live reload - ./backend:/app - ./database:/database + - ./config:/config ######################################################## # DHT-NODE ############################################# @@ -78,9 +81,12 @@ services: - dht_node_modules:/app/node_modules - dht_database_node_modules:/database/node_modules - dht_database_build:/database/build + - dht_config_node_modules:/config/node_modules + - dht_config_build:/config/build # bind the local folder to the docker to allow live reload - ./dht-node:/app - ./database:/database + - ./config:/config ######################################################## # DLT-CONNECTOR ######################################## @@ -122,9 +128,12 @@ services: - federation_node_modules:/app/node_modules - federation_database_node_modules:/database/node_modules - federation_database_build:/database/build + - federation_config_node_modules:/config/node_modules + - federation_config_build:/config/build # bind the local folder to the docker to allow live reload - ./federation:/app - ./database:/database + - ./config:/config ######################################################## # DATABASE ############################################## @@ -216,13 +225,19 @@ volumes: backend_node_modules: backend_database_node_modules: backend_database_build: + backend_config_node_modules: + backend_config_build: dht_node_modules: dht_database_node_modules: dht_database_build: + dht_config_node_modules: + dht_config_build: dlt_connector_modules: federation_node_modules: federation_database_node_modules: federation_database_build: + federation_config_node_modules: + federation_config_build: database_node_modules: database_build: dlt-database_node_modules: diff --git a/docker-compose.test.yml b/docker-compose.test.yml index fba2715bd..de631ed15 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -7,7 +7,7 @@ services: build: target: test environment: - - NODE_ENV="test" + - NODE_ENV=test ######################################################## # ADMIN INTERFACE ###################################### @@ -17,7 +17,7 @@ services: build: target: test environment: - - NODE_ENV="test" + - NODE_ENV=test ######################################################## # BACKEND ############################################## @@ -31,7 +31,7 @@ services: - external-net - internal-net environment: - - NODE_ENV="test" + - NODE_ENV=test - DB_HOST=mariadb ######################################################## @@ -46,7 +46,7 @@ services: - external-net - internal-net environment: - - NODE_ENV="test" + - NODE_ENV="test - DB_HOST=mariadb ######################################################## @@ -61,7 +61,7 @@ services: - external-net - internal-net environment: - - NODE_ENV="test" + - NODE_ENV=test - DB_HOST=mariadb ######################################################## @@ -73,7 +73,7 @@ services: context: ./database target: test_up environment: - - NODE_ENV="test" + - NODE_ENV=test # restart: always # this is very dangerous, but worth a test for the delayed mariadb startup at first run ######################################################## @@ -85,7 +85,7 @@ services: context: ./dlt-database target: test_up environment: - - NODE_ENV="test" + - NODE_ENV=test # restart: always # this is very dangerous, but worth a test for the delayed mariadb startup at first run ######################################################### diff --git a/docker-compose.yml b/docker-compose.yml index eaf828aab..372877bf1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,7 +12,8 @@ services: # name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there image: gradido/frontend:local-production build: - context: ./frontend + context: ./ + dockerfile: ./frontend/Dockerfile target: production networks: - external-net @@ -26,7 +27,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 # - ./frontend/.env @@ -38,7 +41,8 @@ services: # name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there image: gradido/admin:local-production build: - context: ./admin + context: ./ + dockerfile: ./admin/Dockerfile target: production networks: - external-net @@ -52,7 +56,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/federation/Dockerfile b/federation/Dockerfile index 81b95010e..6cc70e2b0 100644 --- a/federation/Dockerfile +++ b/federation/Dockerfile @@ -44,6 +44,7 @@ RUN mkdir -p ${DOCKER_WORKDIR} WORKDIR ${DOCKER_WORKDIR} RUN mkdir -p /database +RUN mkdir -p /config ################################################################################## # DEVELOPMENT (Connected to the local environment, to reload on demand) ########## @@ -56,7 +57,7 @@ FROM base as development # Run command # (for development we need to execute yarn install since the # node_modules are on another volume and need updating) -CMD /bin/sh -c "cd /database && yarn install && yarn build && cd /app && yarn install && yarn run dev" +CMD /bin/sh -c "cd /database && yarn install && yarn build && cd /config && yarn build && cd /app && yarn install && yarn run dev" ################################################################################## # BUILD (Does contain all files and is therefore bloated) ######################## @@ -67,6 +68,11 @@ FROM base as build COPY ./federation/ ./ # Copy everything from database COPY ./database/ ../database/ +# Copy everything from config +COPY ./config/ ../config/ + +# yarn install and build config +RUN cd ../config && yarn install --production=false --frozen-lockfile --non-interactive && yarn run build # yarn install federation RUN yarn install --production=false --frozen-lockfile --non-interactive @@ -96,9 +102,11 @@ FROM base as production # Copy "binary"-files from build image COPY --from=build ${DOCKER_WORKDIR}/build ./build COPY --from=build ${DOCKER_WORKDIR}/../database/build ../database/build +COPY --from=build ${DOCKER_WORKDIR}/../config/build ../config/build # We also copy the node_modules express and serve-static for the run script COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules COPY --from=build ${DOCKER_WORKDIR}/../database/node_modules ../database/node_modules +COPY --from=build ${DOCKER_WORKDIR}/../config/node_modules ../config/node_modules # Copy static files # COPY --from=build ${DOCKER_WORKDIR}/public ./public diff --git a/federation/jest.config.js b/federation/jest.config.js index 42bac0002..17386f225 100644 --- a/federation/jest.config.js +++ b/federation/jest.config.js @@ -33,5 +33,10 @@ module.exports = { process.env.NODE_ENV === 'development' ? '/../database/src/$1' : '/../database/build/src/$1', + '@config/(.*)': + // eslint-disable-next-line n/no-process-env + process.env.NODE_ENV === 'development' + ? '/../config/src/$1' + : '/../config/build/$1', }, } diff --git a/federation/package.json b/federation/package.json index 13481b069..9d171d353 100644 --- a/federation/package.json +++ b/federation/package.json @@ -28,6 +28,7 @@ "graphql": "15.5.1", "graphql-request": "5.0.0", "helmet": "^7.1.0", + "joi": "^17.13.3", "lodash.clonedeep": "^4.5.0", "log4js": "^6.7.1", "reflect-metadata": "^0.1.13", @@ -38,6 +39,7 @@ "devDependencies": { "@types/express": "4.17.12", "@types/jest": "27.0.2", + "@types/joi": "^17.2.3", "@types/lodash.clonedeep": "^4.5.6", "@types/node": "^16.10.3", "@types/sodium-native": "^2.3.5", @@ -56,6 +58,7 @@ "eslint-plugin-promise": "^6.1.1", "eslint-plugin-security": "^1.7.1", "eslint-plugin-type-graphql": "^1.0.0", + "gradido-config": "../config", "graphql-tag": "^2.12.6", "jest": "^27.2.4", "nodemon": "^2.0.7", diff --git a/federation/src/config/index.ts b/federation/src/config/index.ts index 4f72859c1..6649e2ba3 100644 --- a/federation/src/config/index.ts +++ b/federation/src/config/index.ts @@ -1,7 +1,12 @@ // ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env) import { Decimal } from 'decimal.js-light' +import { latestDbVersion } from '@dbTools/config/detectLastDBVersion' import dotenv from 'dotenv' +import { validate } from '@config/index' + +import { schema } from './schema' + dotenv.config() Decimal.set({ @@ -10,16 +15,11 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0087-add_index_on_user_roles', + DB_VERSION: latestDbVersion, 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: 'v2.2023-08-24', - CURRENT: '', - }, } const server = { @@ -38,18 +38,6 @@ const database = { TYPEORM_LOGGING_RELATIVE_PATH: process.env.TYPEORM_LOGGING_RELATIVE_PATH ?? 'typeorm.backend.log', } -// 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 COMMUNITY_HOST = process.env.COMMUNITY_HOST ?? 'localhost' const URL_PROTOCOL = process.env.URL_PROTOCOL ?? 'http' const COMMUNITY_URL = process.env.COMMUNITY_URL ?? `${URL_PROTOCOL}://${COMMUNITY_HOST}` @@ -74,4 +62,6 @@ export const CONFIG = { ...federation, } +validate(schema, CONFIG) + export default CONFIG diff --git a/federation/src/config/schema.ts b/federation/src/config/schema.ts new file mode 100644 index 000000000..8aa54736b --- /dev/null +++ b/federation/src/config/schema.ts @@ -0,0 +1,75 @@ +import { + DB_DATABASE, + DB_HOST, + DB_PASSWORD, + DB_PORT, + DB_USER, + DB_VERSION, + DECAY_START_TIME, + GRAPHIQL, + LOG4JS_CONFIG, + LOG_LEVEL, + NODE_ENV, + PRODUCTION, + TYPEORM_LOGGING_RELATIVE_PATH, +} from '@config/commonSchema' +import Joi from 'joi' + +export const schema = Joi.object({ + DB_DATABASE, + DB_HOST, + DB_PASSWORD, + DB_PORT, + DB_USER, + DB_VERSION, + DECAY_START_TIME, + GRAPHIQL, + LOG4JS_CONFIG, + LOG_LEVEL, + NODE_ENV, + PRODUCTION, + TYPEORM_LOGGING_RELATIVE_PATH, + + FEDERATION_API: Joi.string() + .valid('1_0', '1_1') + .default('1_0') + .description('Federation API version, defaults to 1_0') + .required(), + + FEDERATION_PORT: Joi.number() + .integer() + .min(1024) + .max(49151) + .default(5010) + .description('Port number for the federation service, defaults to 5010') + .required(), + + FEDERATION_COMMUNITY_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .default(Joi.ref('COMMUNITY_URL')) + .description('Community URL for federation, defaults to COMMUNITY_URL') + .required(), + + FEDERATION_TRADING_LEVEL: Joi.object({ + RECEIVER_COMMUNITY_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .default('https://stage3.gradido.net/api/') + .description('URL of the receiver community for trading') + .required(), + + SEND_COINS: Joi.boolean() + .default(true) + .description('Indicates if coins can be sent to the receiver community') + .required(), + + AMOUNT: Joi.number() + .integer() + .min(1) + .default(100) + .description('Maximum amount of coins allowed for trading') + .required(), + }) + .default() + .description('Trading level configuration for federation') + .optional(), +}) diff --git a/federation/tsconfig.json b/federation/tsconfig.json index 0f96e80e5..d9c642232 100644 --- a/federation/tsconfig.json +++ b/federation/tsconfig.json @@ -61,7 +61,8 @@ "@typeorm/*": ["../backend/src/typeorm/*", "../../backend/src/typeorm/*"], "@dbTools/*": ["../database/src/*", "../../database/build/src/*"], "@entity/*": ["../database/entity/*", "../../database/build/entity/*"], - "@logging/*": ["../database/logging/*", "../../database/build/logging/*"] + "@logging/*": ["../database/logging/*", "../../database/build/logging/*"], + "@config/*": ["../config/src/*", "../../config/build/*"] }, // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ "typeRoots": ["node_modules/@types"], /* List of folders to include type definitions from. */ diff --git a/federation/yarn.lock b/federation/yarn.lock index 74cc04521..3596473a4 100644 --- a/federation/yarn.lock +++ b/federation/yarn.lock @@ -379,6 +379,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.10": version "0.11.10" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" @@ -722,6 +734,23 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== +"@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== + "@sinonjs/commons@^1.7.0": version "1.8.6" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" @@ -941,6 +970,13 @@ jest-diff "^27.0.0" pretty-format "^27.0.0" +"@types/joi@^17.2.3": + version "17.2.3" + resolved "https://registry.yarnpkg.com/@types/joi/-/joi-17.2.3.tgz#b7768ed9d84f1ebd393328b9f97c1cf3d2b94798" + integrity sha512-dGjs/lhrWOa+eO0HwgxCSnDm5eMGCsXuvLglMghJq32F6q5LyyNuXb41DHzrg501CKNOSSAHmfB7FDGeUnDmzw== + dependencies: + joi "*" + "@types/json-schema@^7.0.9": version "7.0.12" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" @@ -3027,6 +3063,11 @@ graceful-fs@^4.2.4: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +gradido-config@../config: + version "1.0.0" + dependencies: + joi "^17.13.3" + grapheme-splitter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" @@ -3944,6 +3985,17 @@ jest@^27.2.4: import-local "^3.0.2" jest-cli "^27.5.1" +joi@*, 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-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" diff --git a/frontend/.env.dist b/frontend/.env.dist index f11a70873..817bbc5de 100644 --- a/frontend/.env.dist +++ b/frontend/.env.dist @@ -1,9 +1,6 @@ -# Environment -DEFAULT_PUBLISHER_ID=2896 - # Endpoints GRAPHQL_PATH=/graphql -ADMIN_AUTH_PATH=/admin/authenticate?token={token} +ADMIN_AUTH_PATH=/admin/authenticate?token= # Community COMMUNITY_NAME=Gradido Entwicklung diff --git a/frontend/.env.template b/frontend/.env.template index 40d0e7ee1..76b53c784 100644 --- a/frontend/.env.template +++ b/frontend/.env.template @@ -1,8 +1,5 @@ CONFIG_VERSION=$FRONTEND_CONFIG_VERSION -# Environment -DEFAULT_PUBLISHER_ID=$DEFAULT_PUBLISHER_ID - # Endpoints GRAPHQL_PATH=$GRAPHQL_PATH ADMIN_AUTH_PATH=$ADMIN_AUTH_PATH diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 365906941..96d0106bb 100644 --- a/frontend/Dockerfile +++ b/frontend/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 ENV NODE_ENV="production" ## App relevant Envs @@ -42,6 +42,8 @@ EXPOSE ${PORT} RUN mkdir -p ${DOCKER_WORKDIR} WORKDIR ${DOCKER_WORKDIR} +RUN mkdir -p /config + ################################################################################## # DEVELOPMENT (Connected to the local environment, to reload on demand) ########## ################################################################################## @@ -53,7 +55,7 @@ FROM base as development # Run command # (for development we need to execute npm install since the # node_modules are on another volume and need updating) -CMD /bin/sh -c "yarn install && yarn run dev" +CMD /bin/sh -c "cd /config && yarn install && yarn build && cd /app && yarn install && yarn run dev" ################################################################################## # BUILD (Does contain all files and is therefore bloated) ######################## @@ -61,9 +63,17 @@ CMD /bin/sh -c "yarn install && yarn run dev" FROM base as build # Copy everything -COPY . . +COPY ./frontend ./ + +# Copy everything from config +COPY ./config/ ../config/ + +# install and build config +RUN cd ../config && yarn install --production=false --frozen-lockfile --non-interactive && yarn build + # npm install RUN yarn global add node-gyp && yarn install --production=false --frozen-lockfile --non-interactive + # npm build RUN yarn run build @@ -85,6 +95,7 @@ FROM base as production # Copy "binary"-files from build image COPY --from=build ${DOCKER_WORKDIR}/build ./build +COPY --from=build ${DOCKER_WORKDIR}/../config/build ../config/build # We also copy the node_modules express and serve-static for the run script COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules # Copy static files diff --git a/frontend/package.json b/frontend/package.json index 168bd54d1..cac2428ec 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -91,6 +91,8 @@ "eslint-plugin-promise": "^5.1.1", "eslint-plugin-vitest": "^0.5.4", "eslint-plugin-vue": "8.7.1", + "gradido-config": "../config", + "joi": "^17.13.3", "jsdom": "^25.0.0", "mock-apollo-client": "^1.2.1", "postcss": "^8.4.8", diff --git a/frontend/src/config/index.js b/frontend/src/config/index.js index c20e8e8cf..1ace61a74 100644 --- a/frontend/src/config/index.js +++ b/frontend/src/config/index.js @@ -6,11 +6,6 @@ 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 = { @@ -18,12 +13,13 @@ const version = { 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, + 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), } let FRONTEND_MODULE_URL + // in case of hosting the frontend module with a nodejs-instance if (process.env.FRONTEND_HOSTING === 'nodejs') { FRONTEND_MODULE_URL = @@ -50,7 +46,6 @@ 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' @@ -61,7 +56,7 @@ 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}'), + COMMUNITY_URL + (process.env.ADMIN_AUTH_PATH ?? '/admin/authenticate?token='), } const community = { @@ -93,26 +88,15 @@ const meta = { 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}"`, - ) -} - const CONFIG = { - ...constants, ...version, ...features, ...environment, ...endpoints, ...community, ...meta, + ...constants, + FRONTEND_MODULE_URL, } module.exports = CONFIG diff --git a/frontend/src/config/schema.js b/frontend/src/config/schema.js new file mode 100644 index 000000000..fdae0ae0d --- /dev/null +++ b/frontend/src/config/schema.js @@ -0,0 +1,169 @@ +const { + browserUrls, + APP_VERSION, + BUILD_COMMIT, + BUILD_COMMIT_SHORT, + COMMUNITY_DESCRIPTION, + COMMUNITY_NAME, + COMMUNITY_SUPPORT_MAIL, + COMMUNITY_LOCATION, + COMMUNITY_URL, + DEBUG, + DECAY_START_TIME, + GMS_ACTIVE, + GRAPHQL_URI, + HUMHUB_ACTIVE, + NODE_ENV, + PRODUCTION, +} = require('gradido-config/build/src/commonSchema.js') +const Joi = require('joi') + +// console.log(commonSchema) + +module.exports = Joi.object({ + browserUrls, + APP_VERSION, + BUILD_COMMIT, + BUILD_COMMIT_SHORT, + COMMUNITY_DESCRIPTION, + COMMUNITY_NAME, + COMMUNITY_SUPPORT_MAIL, + COMMUNITY_LOCATION, + COMMUNITY_URL, + DEBUG, + DECAY_START_TIME, + GMS_ACTIVE, + GRAPHQL_URI, + HUMHUB_ACTIVE, + NODE_ENV, + PRODUCTION, + + ADMIN_AUTH_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .description('Extern Url for admin-frontend') + .default('http://0.0.0.0/admin/authenticate?token=') + .required(), + + COMMUNITY_REGISTER_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .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', { + 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 frontend in browser, only needed if COMMUNITY_URL wasn't set", + ) + .optional(), // optional in general, but conditionally required + + FRONTEND_MODULE_PROTOCOL: Joi.string() + .when('FRONTEND_HOSTING', { + is: Joi.valid('nodejs'), + then: Joi.valid('http').required(), + otherwise: Joi.valid('http', 'https').required(), + }) + .description( + ` + Protocol for frontend module hosting + - it has to be the same as for backend api url and admin to prevent mixed block errors, + - if frontend is served with nodejs: + is have to be http or setup must be updated to include a ssl certificate + `, + ) + .default('http') + .required(), + + FRONTEND_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' }), + ) + .when('FRONTEND_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 frontend, default is 0.0.0.0 for local hosting during development.', + ) + .default('0.0.0.0'), + + FRONTEND_MODULE_PORT: Joi.number() + .integer() + .min(1024) + .max(49151) + .description('Port for hosting Frontend with Vite as a Node.js instance, default: 3000') + .default(3000) + .when('FRONTEND_HOSTING', { + is: 'nodejs', + then: Joi.required(), + otherwise: Joi.optional(), + }), + + META_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .description('The base URL for the meta tags.') + .default('http://localhost') + .required(), + + META_TITLE_DE: Joi.string() + .description('Meta title in German.') + .default('Gradido – Dein Dankbarkeitskonto') + .required(), + + META_TITLE_EN: Joi.string() + .description('Meta title in English.') + .default('Gradido - Your gratitude account') + .required(), + + 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() + .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() + .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() + .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() + .description('The author for the meta tags.') + .default('Bernd Hückstädt - Gradido-Akademie') + .required(), +}) diff --git a/frontend/src/layouts/DashboardLayout.vue b/frontend/src/layouts/DashboardLayout.vue index 25151cf39..95bd858f9 100755 --- a/frontend/src/layouts/DashboardLayout.vue +++ b/frontend/src/layouts/DashboardLayout.vue @@ -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 } diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 3ee187e2a..53d3a2796 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -7,6 +7,8 @@ import Icons from 'unplugin-icons/vite' import IconsResolve from 'unplugin-icons/resolver' import EnvironmentPlugin from 'vite-plugin-environment' import { createHtmlPlugin } from 'vite-plugin-html' +import schema from './src/config/schema' +import { validate } from 'gradido-config/build/src/index.js' import { BootstrapVueNextResolver } from 'bootstrap-vue-next' import dotenv from 'dotenv' @@ -16,100 +18,123 @@ dotenv.config() // load env vars from .env const CONFIG = require('./src/config') // https://vitejs.dev/config/ -export default defineConfig({ - server: { - host: CONFIG.FRONTEND_MODULE_HOST, // '0.0.0.0', - port: CONFIG.FRONTEND_MODULE_PORT, // 3000, - }, - resolve: { - alias: { - '@': path.resolve(__dirname, './src'), - assets: path.join(__dirname, 'src/assets'), - '@vee-validate/i18n/dist/locale/en.json': - '/node_modules/@vee-validate/i18n/dist/locale/en.json', - '@vee-validate/i18n/dist/locale/de.json': - '/node_modules/@vee-validate/i18n/dist/locale/de.json', - '@vee-validate/i18n/dist/locale/es.json': - '/node_modules/@vee-validate/i18n/dist/locale/es.json', - '@vee-validate/i18n/dist/locale/fr.json': - '/node_modules/@vee-validate/i18n/dist/locale/fr.json', - '@vee-validate/i18n/dist/locale/nl.json': - '/node_modules/@vee-validate/i18n/dist/locale/nl.json', - '@vee-validate/i18n/dist/locale/tr.json': - '/node_modules/@vee-validate/i18n/dist/locale/tr.json', +export default defineConfig(({ command }) => { + if (command === 'serve') { + CONFIG.FRONTEND_HOSTING = 'nodejs' + } else { + CONFIG.FRONTEND_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.ADMIN_AUTH_URL, + CONFIG.COMMUNITY_URL, + CONFIG.COMMUNITY_REGISTER_URL, + CONFIG.GRAPHQL_URI, + CONFIG.FRONTEND_MODULE_URL, + ], + } + validate(schema, configDataForValidation) + + return { + server: { + host: CONFIG.FRONTEND_MODULE_HOST, // '0.0.0.0', + port: CONFIG.FRONTEND_MODULE_PORT, // 3000, + https: CONFIG.FRONTEND_MODULE_PROTOCOL === 'https', + fs: { + strict: true, + }, + esbuild: { + minify: CONFIG.PRODUCTION === true, + }, }, - extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'], - }, - plugins: [ - vue(), - createHtmlPlugin({ - minify: true, - inject: { - data: { - VITE_META_TITLE_DE: CONFIG.META_TITLE_DE, - VITE_META_TITLE_EN: CONFIG.META_TITLE_EN, - VITE_META_DESCRIPTION_DE: CONFIG.META_DESCRIPTION_DE, - VITE_META_DESCRIPTION_EN: CONFIG.META_DESCRIPTION_EN, - VITE_META_KEYWORDS_DE: CONFIG.META_KEYWORDS_DE, - VITE_META_KEYWORDS_EN: CONFIG.META_KEYWORDS_EN, - VITE_META_AUTHOR: CONFIG.META_AUTHOR, - VITE_META_URL: CONFIG.META_URL, + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + assets: path.join(__dirname, 'src/assets'), + '@vee-validate/i18n/dist/locale/en.json': + '/node_modules/@vee-validate/i18n/dist/locale/en.json', + '@vee-validate/i18n/dist/locale/de.json': + '/node_modules/@vee-validate/i18n/dist/locale/de.json', + '@vee-validate/i18n/dist/locale/es.json': + '/node_modules/@vee-validate/i18n/dist/locale/es.json', + '@vee-validate/i18n/dist/locale/fr.json': + '/node_modules/@vee-validate/i18n/dist/locale/fr.json', + '@vee-validate/i18n/dist/locale/nl.json': + '/node_modules/@vee-validate/i18n/dist/locale/nl.json', + '@vee-validate/i18n/dist/locale/tr.json': + '/node_modules/@vee-validate/i18n/dist/locale/tr.json', + }, + extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'], + }, + plugins: [ + vue(), + createHtmlPlugin({ + minify: CONFIG.PRODUCTION === true, + inject: { + data: { + VITE_META_TITLE_DE: CONFIG.META_TITLE_DE, + VITE_META_TITLE_EN: CONFIG.META_TITLE_EN, + VITE_META_DESCRIPTION_DE: CONFIG.META_DESCRIPTION_DE, + VITE_META_DESCRIPTION_EN: CONFIG.META_DESCRIPTION_EN, + VITE_META_KEYWORDS_DE: CONFIG.META_KEYWORDS_DE, + VITE_META_KEYWORDS_EN: CONFIG.META_KEYWORDS_EN, + VITE_META_AUTHOR: CONFIG.META_AUTHOR, + VITE_META_URL: CONFIG.META_URL, + }, + }, + }), + Components({ + resolvers: [BootstrapVueNextResolver(), IconsResolve()], + dts: true, + }), + Icons({ + compiler: 'vue3', + autoInstall: true, + }), + EnvironmentPlugin({ + GMS_ACTIVE: null, + HUMHUB_ACTIVE: null, + DEFAULT_PUBLISHER_ID: null, + PORT: null, + COMMUNITY_HOST: null, + URL_PROTOCOL: null, + COMMUNITY_URL: CONFIG.COMMUNITY_URL, + GRAPHQL_PATH: null, + GRAPHQL_URI: CONFIG.GRAPHQL_URI, // 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, + COMMUNITY_REGISTER_PATH: null, + COMMUNITY_REGISTER_URL: null, + COMMUNITY_DESCRIPTION: null, + COMMUNITY_SUPPORT_MAIL: null, + META_URL: null, + META_TITLE_DE: null, + META_TITLE_EN: null, + META_DESCRIPTION_DE: null, + META_DESCRIPTION_EN: null, + META_KEYWORDS_DE: null, + META_KEYWORDS_EN: null, + META_AUTHOR: null, + }), + commonjs(), + ], + css: { + extract: CONFIG.PRODUCTION === true, + preprocessorOptions: { + scss: { + additionalData: `@use "@/assets/scss/custom/gradido-custom/color" as *;`, }, }, - }), - Components({ - resolvers: [BootstrapVueNextResolver(), IconsResolve()], - dts: true, - }), - Icons({ - compiler: 'vue3', - autoInstall: true, - }), - EnvironmentPlugin({ - BUILD_COMMIT: null, - GMS_ACTIVE: null, - HUMHUB_ACTIVE: null, - NODE_ENV: null, - DEFAULT_PUBLISHER_ID: null, - PORT: null, - COMMUNITY_HOST: null, - URL_PROTOCOL: null, - COMMUNITY_URL: null, - GRAPHQL_PATH: null, - GRAPHQL_URI: CONFIG.GRAPHQL_URI, // 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, - COMMUNITY_REGISTER_PATH: null, - COMMUNITY_REGISTER_URL: null, - COMMUNITY_DESCRIPTION: null, - COMMUNITY_SUPPORT_MAIL: null, - META_URL: null, - META_TITLE_DE: null, - META_TITLE_EN: null, - META_DESCRIPTION_DE: null, - META_DESCRIPTION_EN: null, - META_KEYWORDS_DE: null, - META_KEYWORDS_EN: null, - META_AUTHOR: null, - CONFIG_VERSION: null, - }), - commonjs(), - ], - css: { - extract: true, - preprocessorOptions: { - scss: { - additionalData: `@use "@/assets/scss/custom/gradido-custom/color" as *;`, - }, }, - }, - build: { - outDir: path.resolve(__dirname, './build'), - chunkSizeWarningLimit: 1600, - }, - optimizeDeps: { - include: ['vue', 'vue-router', 'bootstrap'], - exclude: ['@popperjs/core'], - }, + build: { + outDir: path.resolve(__dirname, './build'), + chunkSizeWarningLimit: 1600, + minify: 'esbuild', + sourcemap: false, + }, + } }) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 2c42e24b4..7eb244d40 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -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" @@ -1522,6 +1534,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" @@ -4173,6 +4202,11 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +gradido-config@../config: + version "1.0.0" + dependencies: + joi "^17.13.3" + graphemer@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" @@ -4739,6 +4773,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"