Merge remote-tracking branch 'origin/master' into

3466-introduce-community-selection-logic-in-transactionlink-page
This commit is contained in:
clauspeterhuebner 2025-05-13 23:00:34 +02:00
commit 29c08babcc
496 changed files with 3642 additions and 7679 deletions

View File

@ -43,18 +43,20 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3
- name: Backend | docker-compose mariadb
- name: docker-compose mariadb
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb
- name: Backend | install and build
run: cd database && yarn && yarn build && cd ../config && yarn && cd ../backend && yarn && yarn build
- name: Sleep for 30 seconds
run: sleep 30s
shell: bash
- name: wait for database to be ready
run: docker run --rm --network gradido_internal-net busybox sh -c 'until nc -z mariadb 3306; do echo waiting for db; sleep 1; done;'
- name: Backend | docker-compose database
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
- name: Backend | prepare database
run: cd database && yarn up:backend_test
- name: Backend | Unit tests
run: cd database && yarn && yarn build && cd ../config && yarn install && cd ../backend && yarn && yarn test
- name: Backend | Unit tests
run: cd backend && yarn test
lint:
if: needs.files-changed.outputs.backend == 'true'
@ -66,7 +68,7 @@ jobs:
uses: actions/checkout@v3
- name: Backend | Lint
run: cd database && yarn && cd ../config && yarn install && cd ../backend && yarn && yarn run lint
run: cd database && yarn && cd ../backend && yarn && yarn run lint
locales:
if: needs.files-changed.outputs.backend == 'true'

View File

@ -42,7 +42,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3
- name: Database | docker-compose
- name: docker-compose mariadb
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach mariadb
- name: Database | up

View File

@ -31,15 +31,7 @@ jobs:
uses: actions/checkout@v3
- name: Build 'test' image
run: |
docker build --target test -t "gradido/dht-node:test" -f dht-node/Dockerfile .
docker save "gradido/dht-node:test" > /tmp/dht-node.tar
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: docker-dht-node-test
path: /tmp/dht-node.tar
run: docker build --target test -t "gradido/dht-node:test" -f dht-node/Dockerfile .
lint:
name: Lint - DHT Node
@ -50,8 +42,8 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3
- name: Lint
run: cd database && yarn && cd ../config && yarn install && cd ../dht-node && yarn && yarn run lint
- name: DHT-Node | Lint
run: cd database && yarn && cd ../dht-node && yarn && yarn run lint
unit_test:
name: Unit Tests - DHT Node
@ -62,30 +54,18 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3
- name: Download Docker Image
uses: actions/download-artifact@v4
with:
name: docker-dht-node-test
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/dht-node.tar
- name: docker-compose mariadb
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb
- name: Sleep for 30 seconds
run: sleep 30s
shell: bash
- name: DHT-Node | install and build
run: cd database && yarn && yarn build && cd ../config && yarn && cd ../dht-node && yarn && yarn build
- name: docker-compose database
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
- name: wait for database to be ready
run: docker run --rm --network gradido_internal-net busybox sh -c 'until nc -z mariadb 3306; do echo waiting for db; sleep 1; done;'
- name: Sleep for 30 seconds
run: sleep 30s
shell: bash
- name: DHT-Node | prepare database
run: cd database && yarn up:dht_test
- name: DHT-Node | Unit tests
run: cd dht-node && yarn test
- name: Unit tests
run: cd database && yarn && yarn build && cd ../config && yarn install && cd ../dht-node && yarn && yarn test
#- name: Unit tests
# run: docker run --env NODE_ENV=test --env DB_HOST=mariadb --network gradido_internal-net --rm gradido/dht-node:test yarn run test

View File

@ -13,25 +13,12 @@ jobs:
- name: Boot up test system | docker-compose mariadb
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach mariadb
- name: Sleep for 10 seconds
run: sleep 10s
- name: Boot up test system | seed backend
- name: Prepare test system
run: |
sudo chown runner:docker -R *
cd database
yarn && yarn dev_reset
cd ../config
yarn install
cd ../backend
yarn && yarn seed
- name: Boot up test system | docker-compose backend, frontend, admin, nginx, mailserver
run: |
cd backend
cp .env.test_e2e .env
cd ..
docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps backend frontend admin nginx mailserver
cd database && yarn && yarn build
cd ../config && yarn
cd ../backend && yarn
- name: End-to-end tests | prepare
run: |
@ -41,6 +28,21 @@ jobs:
cd e2e-tests/
yarn
- name: wait for database to be ready
run: docker run --rm --network gradido_internal-net busybox sh -c 'until nc -z mariadb 3306; do echo waiting for db; sleep 1; done;'
- name: Boot up test system | seed backend
run: |
cd database && yarn dev_reset
cd ../backend && yarn seed
- name: Boot up test system | docker-compose backend, frontend, admin, nginx, mailserver
run: |
cd backend
cp .env.test_e2e .env
cd ..
docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps backend frontend admin nginx mailserver
- name: End-to-end tests | run tests
id: e2e-tests
run: |

View File

@ -72,20 +72,15 @@ jobs:
- name: docker-compose mariadb
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb
- name: Sleep for 30 seconds
run: sleep 30s
shell: bash
- name: docker-compose database
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
- name: Federation | install and build
run: cd database && yarn && yarn build && cd ../config && yarn && cd ../federation && yarn && yarn build
- name: Sleep for 30 seconds
run: sleep 30s
shell: bash
- name: wait for database to be ready
run: docker run --rm --network gradido_internal-net busybox sh -c 'until nc -z mariadb 3306; do echo waiting for db; sleep 1; done;'
#- name: Unit tests
# run: cd database && yarn && yarn build && cd ../dht-node && yarn && yarn test
- name: Unit tests
run: |
docker run --env NODE_ENV=test --env DB_HOST=mariadb --network gradido_internal-net --rm gradido/federation:test yarn run test
- name: Federation | prepare database
run: cd database && yarn up:federation_test
- name: Federation | Unit tests
run: docker run --env NODE_ENV=test --env DB_HOST=mariadb --network gradido_internal-net --rm gradido/federation:test yarn run test

12
.vscode/settings.json vendored
View File

@ -1,5 +1,17 @@
{
"git.ignoreLimitWarning": true,
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome",
"editor.codeActionsOnSave": {
"source.organizeImports.biome": "explicit"
}
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome",
"editor.codeActionsOnSave": {
"source.organizeImports.biome": "explicit"
}
},
"sqltools.connections": [
{
"mysqlOptions": {

View File

@ -4,8 +4,31 @@ All notable changes to this project will be documented in this file. Dates are d
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
#### [2.5.1](https://github.com/gradido/gradido/compare/2.3.1...2.5.1)
#### [2.5.2](https://github.com/gradido/gradido/compare/2.3.1...2.5.2)
- refactor(other): use biome instead of eslint [`#3472`](https://github.com/gradido/gradido/pull/3472)
- fix(frontend): set explicit page size for admin and moderator user on information page [`#3474`](https://github.com/gradido/gradido/pull/3474)
- fix(backend): humhub sync on edge cases [`#3471`](https://github.com/gradido/gradido/pull/3471)
- fix(frontend): community messsages reload [`#3476`](https://github.com/gradido/gradido/pull/3476)
- refactor(frontend): show skeleton only as long as needed [`#3477`](https://github.com/gradido/gradido/pull/3477)
- refactor(database): faster db version check [`#3473`](https://github.com/gradido/gradido/pull/3473)
- feat(frontend): humhub project auto register for legacy user [`#3469`](https://github.com/gradido/gradido/pull/3469)
- fix(backend): humhub username case with initails and empty alias [`#3470`](https://github.com/gradido/gradido/pull/3470)
- feat(frontend): gms improvements [`#3468`](https://github.com/gradido/gradido/pull/3468)
- feat(other): x-com tx per link concept [`#3464`](https://github.com/gradido/gradido/pull/3464)
- fix(frontend): update texte, fix minor bugs in new overview [`#3463`](https://github.com/gradido/gradido/pull/3463)
- fix(admin): update field key according to changed graphlq request [`#3462`](https://github.com/gradido/gradido/pull/3462)
- refactor(admin): admin contribution list [`#3460`](https://github.com/gradido/gradido/pull/3460)
- fix(admin): date formatting, hashtag copy filter, no-select [`#3461`](https://github.com/gradido/gradido/pull/3461)
- feat(backend): 3347 modify overview page with humhub- and usersearch-card [`#3451`](https://github.com/gradido/gradido/pull/3451)
- feat(admin): add ai chat [`#3456`](https://github.com/gradido/gradido/pull/3456)
- feat(backend): upgrade typescript [`#3458`](https://github.com/gradido/gradido/pull/3458)
- build(other): increase nodejs version in deployment [`#3457`](https://github.com/gradido/gradido/pull/3457)
- feat(frontend): use gear instead fingerprint icon, replace svg icon [`#3454`](https://github.com/gradido/gradido/pull/3454)
- fix(frontend): auth style on mobile menu like in desktop [`#3453`](https://github.com/gradido/gradido/pull/3453)
- feat(admin): optimize contribution list for ai usage [`#3452`](https://github.com/gradido/gradido/pull/3452)
- chore(release): v2.5.1 beta [`#3455`](https://github.com/gradido/gradido/pull/3455)
- feat(frontend): project branding and login forwarding to circle [`#3443`](https://github.com/gradido/gradido/pull/3443)
- refactor(frontend): contribution form refactor [`#3442`](https://github.com/gradido/gradido/pull/3442)
- feat(backend): correct user data transfer to gms [`#3433`](https://github.com/gradido/gradido/pull/3433)
- feat(backend): switch marker colors of community location and user location [`#3445`](https://github.com/gradido/gradido/pull/3445)

View File

@ -1 +0,0 @@
v18.20

View File

@ -3,7 +3,7 @@
"description": "Administration Interface for Gradido",
"main": "index.js",
"author": "Moriz Wahl",
"version": "2.5.1",
"version": "2.5.2",
"license": "Apache-2.0",
"scripts": {
"start": "node run/server.js",

View File

@ -71,7 +71,8 @@ const defaultData = {
const defaultUser = {
firstName: 'Peter',
lastName: 'Lustig',
humhubUsername: 'peter.lustig',
uniqueUsername: 'peter.lustig',
publicName: 'PeLu',
createdAt: new Date().toString(),
emailContact: {
email: 'peter.lustig@example.com',

View File

@ -12,7 +12,7 @@
{{ $t('filter.byEmail') }}
</BTooltip>
&nbsp;
{{ contribution.user.humhubUsername }}
{{ contribution.user.publicName }}
&nbsp;
<span>
<a
@ -103,7 +103,7 @@ const humhubProfileLink = computed(() => {
if (url.endsWith('/')) {
url = url.slice(0, -1)
}
return `${url}/u/${props.contribution.humhubUsername}`
return `${url}/u/${props.contribution.user.userIdentifier}`
})
const messages = ref([])

View File

@ -13,7 +13,8 @@ query adminListContributions(
email
}
...UserCommonFields
humhubUsername
publicName
userIdentifier
createdAt
}
amount

View File

@ -135,6 +135,7 @@ const noHashtag = ref(null)
const hideResubmissionModel = ref(true)
const formatDateOrDash = (value) => (value ? new Date(value).toLocaleDateString() : '—')
const baseFields = {
firstName: { key: 'user.firstName', label: t('firstname'), class: 'no-select' },
lastName: { key: 'user.lastName', label: t('lastname'), class: 'no-select' },
@ -181,6 +182,7 @@ const fields = computed(
[
baseFields.firstName,
baseFields.lastName,
baseFields.amount,
baseFields.memo,
baseFields.contributionDate,
baseFields.createdAt,

View File

@ -40,6 +40,10 @@ export default defineConfig(async ({ command }) => {
host: CONFIG.ADMIN_MODULE_HOST, // '0.0.0.0',
port: CONFIG.ADMIN_MODULE_PORT, // 8080,
},
preview: {
host: CONFIG.ADMIN_MODULE_HOST, // '0.0.0.0',
port: CONFIG.ADMIN_MODULE_PORT, // 8080,
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),

View File

@ -1,4 +0,0 @@
node_modules
**/*.min.js
build
coverage

View File

@ -1,213 +0,0 @@
// 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: ['FederationClient_*', '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/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',
},
},
],
}

View File

@ -1,14 +0,0 @@
module.exports = {
printWidth: 100,
tabWidth: 2,
useTabs: false,
semi: false,
singleQuote: true,
quoteProps: "as-needed",
jsxSingleQuote: true,
trailingComma: "all",
bracketSpacing: true,
bracketSameLine: false,
arrowParens: "always",
endOfLine: "auto",
};

View File

@ -7,7 +7,7 @@ module.exports = {
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'],
coverageThreshold: {
global: {
lines: 78,
lines: 77,
},
},
setupFiles: ['<rootDir>/test/testSetup.ts'],

View File

@ -57,6 +57,20 @@
"fileNameSep" : "_",
"numBackups" : 30
},
"gms":
{
"type": "dateFile",
"filename": "../logs/backend/gms.log",
"pattern": "yyyy-MM-dd",
"layout":
{
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
},
"compress": true,
"keepFileExt" : true,
"fileNameSep" : "_",
"numBackups" : 30
},
"errorFile":
{
"type": "dateFile",
@ -137,6 +151,16 @@
"level": "debug",
"enableCallStack": true
},
"gms":
{
"appenders":
[
"gms",
"errors"
],
"level": "debug",
"enableCallStack": true
},
"http":
{
"appenders":

View File

@ -1,6 +1,6 @@
{
"name": "gradido-backend",
"version": "2.5.1",
"version": "2.5.2",
"description": "Gradido unified backend providing an API-Service for Gradido Transactions",
"main": "src/index.ts",
"repository": "https://github.com/gradido/gradido/backend",
@ -12,12 +12,12 @@
"clean": "tsc --build --clean",
"start": "cross-env TZ=UTC TS_NODE_BASEURL=./build node -r tsconfig-paths/register build/src/index.js",
"dev": "cross-env TZ=UTC nodemon -w src --ext ts,pug,json,css --exec ts-node -r tsconfig-paths/register src/index.ts",
"lint": "eslint --max-warnings=0 .",
"test": "cross-env TZ=UTC NODE_ENV=development jest --runInBand --forceExit --detectOpenHandles",
"lint": "biome check --error-on-warnings .",
"lint:fix": "biome check --error-on-warnings . --write",
"test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_backend jest --runInBand --forceExit --detectOpenHandles",
"seed": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/index.ts",
"klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/executeKlicktipp.ts",
"gmsusers": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/gmsUsers.ts",
"gmsuserList": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/gmsUserList.ts",
"gmsusers": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/apis/gms/ExportUsers.ts",
"humhubUserExport": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/apis/humhub/ExportUsers.ts",
"locales": "scripts/sort.sh"
},
@ -59,7 +59,7 @@
"xregexp": "^5.1.1"
},
"devDependencies": {
"@eslint-community/eslint-plugin-eslint-comments": "^3.2.1",
"@biomejs/biome": "1.9.4",
"@types/email-templates": "^10.0.1",
"@types/express": "^4.17.12",
"@types/faker": "^5.5.9",
@ -71,20 +71,7 @@
"@types/nodemailer": "^6.4.4",
"@types/sodium-native": "^2.3.5",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"apollo-server-testing": "^2.25.2",
"eslint": "^8.57.1",
"eslint-config-prettier": "^8.8.0",
"eslint-config-standard": "^17.0.0",
"eslint-import-resolver-typescript": "^3.5.4",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jest": "^27.2.1",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-security": "^1.7.1",
"eslint-plugin-type-graphql": "^1.0.0",
"faker": "^5.5.3",
"graphql-tag": "^2.12.6",
"jest": "^27.2.4",

View File

@ -1,7 +1,3 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
import axios from 'axios'
import { LogError } from '@/server/LogError'

View File

@ -1,13 +1,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { CONFIG } from '@/config'
import { backendLogger as logger } from '@/server/logger'
// eslint-disable-next-line import/no-relative-parent-imports
import KlicktippConnector from 'klicktipp-api'
const klicktippConnector = new KlicktippConnector()
@ -18,7 +11,9 @@ export const subscribe = async (
firstName?: string,
lastName?: string,
): Promise<boolean> => {
if (!CONFIG.KLICKTIPP) return true
if (!CONFIG.KLICKTIPP) {
return true
}
const fields = {
fieldFirstName: firstName,
fieldLastName: lastName,
@ -29,7 +24,9 @@ export const subscribe = async (
}
export const unsubscribe = async (email: string): Promise<boolean> => {
if (!CONFIG.KLICKTIPP) return true
if (!CONFIG.KLICKTIPP) {
return true
}
const isLogin = await loginKlicktippUser()
if (isLogin) {
return await klicktippConnector.unsubscribe(email)
@ -38,12 +35,14 @@ export const unsubscribe = async (email: string): Promise<boolean> => {
}
export const getKlickTippUser = async (email: string): Promise<any> => {
if (!CONFIG.KLICKTIPP) return true
if (!CONFIG.KLICKTIPP) {
return true
}
const isLogin = await loginKlicktippUser()
if (isLogin) {
try {
return klicktippConnector.subscriberGet(await klicktippConnector.subscriberSearch(email))
} catch (e) {
} catch (_e) {
logger.error('Could not find subscriber', email)
return false
}
@ -52,7 +51,9 @@ export const getKlickTippUser = async (email: string): Promise<any> => {
}
export const loginKlicktippUser = async (): Promise<boolean> => {
if (!CONFIG.KLICKTIPP) return true
if (!CONFIG.KLICKTIPP) {
return true
}
return await klicktippConnector.login(CONFIG.KLICKTIPP_USER, CONFIG.KLICKTIPP_PASSWORD)
}
@ -62,7 +63,9 @@ export const addFieldsToSubscriber = async (
newemail = '',
newsmsnumber = '',
) => {
if (!CONFIG.KLICKTIPP) return true
if (!CONFIG.KLICKTIPP) {
return true
}
const isLogin = await loginKlicktippUser()
if (isLogin) {
try {

View File

@ -1,8 +1,3 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable security/detect-object-injection */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { Connection } from '@dbTools/typeorm'
import { Transaction as DbTransaction } from '@entity/Transaction'
import { Decimal } from 'decimal.js-light'

View File

@ -1,5 +1,5 @@
import { Transaction as DbTransaction } from '@entity/Transaction'
import { gql, GraphQLClient } from 'graphql-request'
import { GraphQLClient, gql } from 'graphql-request'
import { CONFIG } from '@/config'
import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId'
@ -34,16 +34,15 @@ function getTransactionTypeString(id: TransactionTypeId): string {
* A Singleton class defines the `getInstance` method that lets clients access
* the unique singleton instance.
*/
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class DltConnectorClient {
// eslint-disable-next-line no-use-before-define
private static instance: DltConnectorClient
client: GraphQLClient
/**
* The Singleton's constructor should always be private to prevent direct
* construction calls with the `new` operator.
*/
// eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-empty-function
private constructor() {}
/**

View File

@ -1,10 +1,3 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */
import { entities } from '@entity/index'
import { User as DbUser } from '@entity/User'
// import { createTestClient } from 'apollo-server-testing'
@ -13,43 +6,29 @@ import { User as DbUser } from '@entity/User'
import { CONFIG } from '@/config'
import { getHomeCommunity } from '@/graphql/resolver/util/communities'
import { sendUserToGms } from '@/graphql/resolver/util/sendUserToGms'
import { createServer } from '@/server/createServer'
import { LogError } from '@/server/LogError'
import { backendLogger as logger } from '@/server/logger'
import { checkDBVersion } from '@/typeorm/DBVersion'
import { Connection } from '@/typeorm/connection'
CONFIG.EMAIL = false
// use force to copy over all user even if gmsRegistered is set to true
const forceMode = process.argv.includes('--force')
const context = {
token: '',
setHeaders: {
push: (value: { key: string; value: string }): void => {
context.token = value.value
},
// eslint-disable-next-line @typescript-eslint/no-empty-function
forEach: (): void => {},
},
clientTimezoneOffset: 0,
}
export const cleanDB = async () => {
// this only works as long we do not have foreign key constraints
for (const entity of entities) {
await resetEntity(entity)
async function main() {
// open mysql connection
const con = await Connection.getInstance()
if (!con?.isConnected) {
logger.fatal(`Couldn't open connection to database!`)
throw new Error(`Fatal: Couldn't open connection to database`)
}
}
const resetEntity = async (entity: any) => {
const items = await entity.find({ withDeleted: true })
if (items.length > 0) {
const ids = items.map((e: any) => e.id)
await entity.delete(ids)
// check for correct database version
const dbVersion = await checkDBVersion(CONFIG.DB_VERSION)
if (!dbVersion) {
logger.fatal('Fatal: Database Version incorrect')
throw new Error('Fatal: Database Version incorrect')
}
}
const run = async () => {
const server = await createServer(context)
// const seedClient = createTestClient(server.apollo)
const { con } = server
const homeCom = await getHomeCommunity()
if (homeCom.gmsApiKey === null) {
@ -59,11 +38,11 @@ const run = async () => {
const userIds = await DbUser.createQueryBuilder()
.select('id')
.where({ foreign: false })
// .andWhere('deleted_at is null')
// .andWhere({ gmsRegistered: false })
.andWhere('deleted_at is null')
.getRawMany()
logger.debug('userIds:', userIds)
let alreadyUpdatedUserCount = 0
for (const idStr of userIds) {
logger.debug('Id:', idStr.id)
const user = await DbUser.findOne({
@ -73,11 +52,11 @@ const run = async () => {
if (user) {
logger.debug('found local User:', user)
if (user.gmsAllowed) {
await sendUserToGms(user, homeCom)
await sendUserToGms(user, homeCom, forceMode)
/*
const gmsUser = new GmsUser(user)
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
if (await createGmsUser(homeCom.gmsApiKey, gmsUser)) {
logger.debug('GMS user published successfully:', gmsUser)
user.gmsRegistered = true
@ -93,10 +72,16 @@ const run = async () => {
logger.debug('GMS-Publishing not allowed by user settings:', user)
}
}
alreadyUpdatedUserCount++
process.stdout.write(`updated user: ${alreadyUpdatedUserCount}/${userIds.length}\r`)
}
logger.info('##gms## publishing all local users successful...')
await con.close()
await con.destroy()
}
void run()
main().catch((e) => {
// biome-ignore lint/suspicious/noConsole: logger isn't used here
console.error(e)
process.exit(1)
})

View File

@ -1,7 +1,3 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
import axios from 'axios'
import { httpAgent, httpsAgent } from '@/apis/ConnectionAgents'
@ -33,10 +29,10 @@ export async function communityList(): Promise<GmsCommunity[] | string | undefin
throw new LogError('HTTP Status Error in community/list:', result.status, result.statusText)
}
logger.debug('responseData:', result.data.responseData.data)
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
// const gmsCom = JSON.parse(result.data.responseData.data)
// logger.debug('gmsCom:', gmsCom)
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return result.data.responseData.data
} catch (error: any) {
logger.error('Error in Get community/list:', error)
@ -69,10 +65,10 @@ export async function userList(): Promise<GmsUser[] | string | undefined> {
)
}
logger.debug('responseData:', result.data.responseData.data)
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
// const gmsUser = JSON.parse(result.data.responseData.data)
// logger.debug('gmsUser:', gmsUser)
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return result.data.responseData.data
} catch (error: any) {
logger.error('Error in Get community-user/list:', error)
@ -105,10 +101,10 @@ export async function userByUuid(uuid: string): Promise<GmsUser[] | string | und
)
}
logger.debug('responseData:', result.data.responseData.data)
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
// const gmsUser = JSON.parse(result.data.responseData.data)
// logger.debug('gmsUser:', gmsUser)
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return result.data.responseData.data
} catch (error: any) {
logger.error('Error in Get community-user/list:', error)
@ -139,13 +135,16 @@ export async function createGmsUser(apiKey: string, user: GmsUser): Promise<bool
throw new LogError('HTTP Status Error in community-user:', result.status, result.statusText)
}
logger.debug('responseData:', result.data.responseData)
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
// const gmsUser = JSON.parse(result.data.responseData)
// logger.debug('gmsUser:', gmsUser)
return true
} catch (error: any) {
} catch (error: unknown) {
logger.error('Error in post community-user:', error)
throw new LogError(error.message)
if (error instanceof Error) {
throw new LogError(error.message)
}
throw new LogError('Unknown error in post community-user')
}
} else {
logger.info('GMS-Communication disabled per ConfigKey GMS_ACTIVE=false!')
@ -174,13 +173,16 @@ export async function updateGmsUser(apiKey: string, user: GmsUser): Promise<bool
throw new LogError('HTTP Status Error in community-user:', result.status, result.statusText)
}
logger.debug('responseData:', result.data.responseData)
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
// const gmsUser = JSON.parse(result.data.responseData)
// logger.debug('gmsUser:', gmsUser)
return true
} catch (error: any) {
} catch (error: unknown) {
logger.error('Error in patch community-user:', error)
throw new LogError(error.message)
if (error instanceof Error) {
throw new LogError(error.message)
}
throw new LogError('Unknown error in patch community-user')
}
} else {
logger.info('GMS-Communication disabled per ConfigKey GMS_ACTIVE=false!')
@ -194,6 +196,7 @@ export async function verifyAuthToken(
token: string,
): Promise<string> {
const baseUrl = ensureUrlEndsWithSlash(CONFIG.GMS_API_URL)
// TODO: NEVER pass user JWT token to another server - serious security risk! 😱⚠️
const service = 'verify-auth-token?token='.concat(token).concat('&uuid=').concat(communityUuid)
const config = {
headers: {
@ -216,12 +219,15 @@ export async function verifyAuthToken(
)
}
logger.debug('responseData:', result.data.responseData)
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
const token: string = result.data.responseData.token
logger.debug('verifyAuthToken=', token)
return token
} catch (error: any) {
} catch (error: unknown) {
logger.error('Error in verifyAuthToken:', error)
throw new LogError(error.message)
if (error instanceof Error) {
throw new LogError(error.message)
}
throw new LogError('Unknown error in verifyAuthToken')
}
}

View File

@ -10,7 +10,7 @@ export class GmsCommunityProfile {
communityId: number
radius: number
description: string
// eslint-disable-next-line camelcase
api_key: string
communityAuthUrl: unknown
profileImage: unknown

View File

@ -2,6 +2,7 @@ import { User as dbUser } from '@entity/User'
import { PublishNameLogic } from '@/data/PublishName.logic'
// import { GmsPublishLocationType } from '@/graphql/enum/GmsPublishLocationType'
import { GmsPublishLocationType } from '@/graphql/enum/GmsPublishLocationType'
import { GmsPublishPhoneType } from '@/graphql/enum/GmsPublishPhoneType'
import { PublishNameType } from '@/graphql/enum/PublishNameType'
@ -15,13 +16,19 @@ export class GmsUser {
this.email = this.getGmsEmail(user)
this.countryCode = this.getGmsCountryCode(user)
this.mobile = this.getGmsPhone(user)
const fn = pnLogic.getFirstName(user.gmsPublishName)
const fn = pnLogic.getFirstName(user.gmsPublishName as PublishNameType)
this.firstName = fn !== '' ? fn : null // getGmsFirstName(user)
const ln = pnLogic.getLastName(user.gmsPublishName)
const ln = pnLogic.getLastName(user.gmsPublishName as PublishNameType)
this.lastName = ln !== '' ? ln : null // getGmsLastName(user)
this.alias = this.getGmsAlias(user)
this.alias = pnLogic.getPublicName(user.gmsPublishName as PublishNameType)
this.type = user.gmsPublishLocation // GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM
this.location = user.location
if ((this.type as GmsPublishLocationType) === GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM) {
this.type = GmsPublishLocationType.GMS_LOCATION_TYPE_APPROXIMATE
}
if (!this.location) {
this.type = GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM
}
}
id: number

View File

@ -11,7 +11,7 @@ export class GmsUserAccount {
address: string
radius: number
description: string
// eslint-disable-next-line camelcase
api_key: string
profileImage: unknown
}

View File

@ -4,8 +4,8 @@ import { User } from '@entity/User'
import { CONFIG } from '@/config'
import { LogError } from '@/server/LogError'
import { backendLogger as logger } from '@/server/logger'
import { Connection } from '@/typeorm/connection'
import { checkDBVersion } from '@/typeorm/DBVersion'
import { Connection } from '@/typeorm/connection'
import { HumHubClient } from './HumHubClient'
import { GetUser } from './model/GetUser'
@ -26,7 +26,7 @@ function getUsersPage(page: number, limit: number): Promise<[User[], number]> {
/**
* @param client
* @returns user map indices with email
* @returns user map indices with username
*/
async function loadUsersFromHumHub(client: HumHubClient): Promise<Map<string, GetUser>> {
const start = new Date().getTime()
@ -39,19 +39,20 @@ async function loadUsersFromHumHub(client: HumHubClient): Promise<Map<string, Ge
if (!usersPage) {
throw new LogError('error requesting next users page from humhub')
}
usersPage.results.forEach((user) => {
for (const user of usersPage.results) {
// deleted users have empty emails
if (user.account.email) {
humhubUsers.set(user.account.email.trim(), user)
humhubUsers.set(user.account.username, user)
} else {
skippedUsersCount++
}
})
}
page++
process.stdout.write(
`load users from humhub: ${humhubUsers.size}/${usersPage.total}, skipped: ${skippedUsersCount}\r`,
)
} while (usersPage && usersPage.results.length === HUMHUB_BULK_SIZE)
process.stdout.write('\n')
const elapsed = new Date().getTime() - start
logger.info('load users from humhub', {
@ -87,23 +88,31 @@ async function main() {
const humhubUsers = await loadUsersFromHumHub(humHubClient)
let dbUserCount = 0
const executedHumhubActionsCount = [0, 0, 0, 0]
const executedHumhubActionsCount = [0, 0, 0, 0, 0]
do {
const [users, totalUsers] = await getUsersPage(page, USER_BULK_SIZE)
dbUserCount += users.length
userCount = users.length
page++
const promises: Promise<ExecutedHumhubAction>[] = []
users.forEach((user: User) => promises.push(syncUser(user, humhubUsers)))
const executedActions = await Promise.all(promises)
executedActions.forEach((executedAction: ExecutedHumhubAction) => {
executedHumhubActionsCount[executedAction as number]++
})
// using process.stdout.write here so that carriage-return is working analog to c
// printf("\rchecked user: %d/%d", dbUserCount, totalUsers);
process.stdout.write(`checked user: ${dbUserCount}/${totalUsers}\r`)
try {
const [users, totalUsers] = await getUsersPage(page, USER_BULK_SIZE)
dbUserCount += users.length
userCount = users.length
page++
const promises: Promise<ExecutedHumhubAction>[] = []
for (const user of users) {
promises.push(syncUser(user, humhubUsers))
}
const executedActions = await Promise.all(promises)
for (const executedAction of executedActions) {
executedHumhubActionsCount[executedAction as number]++
}
// using process.stdout.write here so that carriage-return is working analog to c
// printf("\rchecked user: %d/%d", dbUserCount, totalUsers);
process.stdout.write(`checked user: ${dbUserCount}/${totalUsers}\r`)
} catch (e) {
process.stdout.write('\n')
throw e
}
} while (userCount === USER_BULK_SIZE)
process.stdout.write('\n')
await con.destroy()
const elapsed = new Date().getTime() - start
@ -114,12 +123,12 @@ async function main() {
updatedCount: executedHumhubActionsCount[ExecutedHumhubAction.UPDATE],
skippedCount: executedHumhubActionsCount[ExecutedHumhubAction.SKIP],
deletedCount: executedHumhubActionsCount[ExecutedHumhubAction.DELETE],
validationErrorCount: executedHumhubActionsCount[ExecutedHumhubAction.VALIDATION_ERROR],
})
}
main().catch((e) => {
// eslint-disable-next-line no-console
// biome-ignore lint/suspicious/noConsole: logger isn't used here
console.error(e)
// eslint-disable-next-line n/no-process-exit
process.exit(1)
})

View File

@ -17,11 +17,9 @@ import { UsersResponse } from './model/UsersResponse'
* HumHubClient as singleton class
*/
export class HumHubClient {
// eslint-disable-next-line no-use-before-define
private static instance: HumHubClient
private restClient: RestClient
// eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-empty-function
private constructor() {
this.restClient = new RestClient('gradido-backend', CONFIG.HUMHUB_API_URL, undefined, {
keepAlive: true,

View File

@ -10,10 +10,8 @@ import { UsersResponse } from '@/apis/humhub/model/UsersResponse'
* HumHubClient as singleton class
*/
export class HumHubClient {
// eslint-disable-next-line no-use-before-define
private static instance: HumHubClient
// eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-empty-function
private constructor() {}
public static getInstance(): HumHubClient {

View File

@ -2,6 +2,7 @@ import { User } from '@entity/User'
import { isHumhubUserIdenticalToDbUser } from '@/apis/humhub/compareHumhubUserDbUser'
import { GetUser } from '@/apis/humhub/model/GetUser'
import { PostUser } from '@/apis/humhub/model/PostUser'
export enum ExecutedHumhubAction {
UPDATE,
@ -26,7 +27,8 @@ export async function syncUser(
user: User,
humhubUsers: Map<string, GetUser>,
): Promise<ExecutedHumhubAction> {
const humhubUser = humhubUsers.get(user.emailContact.email.trim())
const postUser = new PostUser(user)
const humhubUser = humhubUsers.get(postUser.account.username)
if (humhubUser) {
if (!user.humhubAllowed) {
return Promise.resolve(ExecutedHumhubAction.DELETE)

View File

@ -1,4 +1,5 @@
/* eslint-disable prettier/prettier */
import 'reflect-metadata'
import { PublishNameType } from '@/graphql/enum/PublishNameType'
import { communityDbUser } from '@/util/communityUser'
import { isHumhubUserIdenticalToDbUser } from './compareHumhubUserDbUser'
@ -7,58 +8,73 @@ import { GetUser } from './model/GetUser'
const defaultUser = communityDbUser
describe('isHumhubUserIdenticalToDbUser', () => {
beforeEach(() => {
defaultUser.firstName = 'first name'
defaultUser.lastName = 'last name'
defaultUser.alias = 'alias'
defaultUser.emailContact.email = 'email@gmail.com'
defaultUser.language = 'en'
})
beforeEach(() => {
defaultUser.firstName = 'first name'
defaultUser.lastName = 'last name'
defaultUser.alias = 'alias'
defaultUser.emailContact.email = 'email@gmail.com'
defaultUser.language = 'en'
defaultUser.gradidoID = 'gradidoID'
})
it('Should return true because humhubUser was created from entity user', () => {
const humhubUser = new GetUser(defaultUser, 1)
const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser)
expect(result).toBe(true)
})
it('Should return true because humhubUser was created from entity user', () => {
const humhubUser = new GetUser(defaultUser, 1)
const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser)
expect(result).toBe(true)
})
it('Should return false because first name differ', () => {
const humhubUser = new GetUser(defaultUser, 1)
humhubUser.profile.firstname = 'changed first name'
const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser)
expect(result).toBe(false)
})
it('Should return false because last name differ', () => {
const humhubUser = new GetUser(defaultUser, 1)
humhubUser.profile.lastname = 'changed last name'
const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser)
expect(result).toBe(false)
})
it('Should return false because username differ', () => {
const humhubUser = new GetUser(defaultUser, 1)
humhubUser.account.username = 'changed username'
const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser)
expect(result).toBe(false)
})
it('Should return false, because last name differ because of publish name type', () => {
const humhubUser = new GetUser(defaultUser, 1)
defaultUser.humhubPublishName = PublishNameType.PUBLISH_NAME_FIRST
const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser)
expect(result).toBe(false)
})
it('Should return false because email differ', () => {
const humhubUser = new GetUser(defaultUser, 1)
humhubUser.account.email = 'new@gmail.com'
const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser)
expect(result).toBe(false)
})
it('Should return true, even if alias is empty', () => {
defaultUser.alias = ''
const humhubUser = new GetUser(defaultUser, 1)
const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser)
expect(result).toBe(true)
})
it('Should return false because language differ', () => {
const humhubUser = new GetUser(defaultUser, 1)
humhubUser.account.language = 'de'
const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser)
expect(result).toBe(false)
})
it('Should return false because first name differ', () => {
const humhubUser = new GetUser(defaultUser, 1)
humhubUser.profile.firstname = 'changed first name'
const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser)
expect(result).toBe(false)
})
it('Should return false because last name differ', () => {
const humhubUser = new GetUser(defaultUser, 1)
humhubUser.profile.lastname = 'changed last name'
const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser)
expect(result).toBe(false)
})
it('Should return false because username differ', () => {
const humhubUser = new GetUser(defaultUser, 1)
humhubUser.account.username = 'changed username'
const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser)
expect(result).toBe(false)
})
it('Should return false because gradido_address differ', () => {
const humhubUser = new GetUser(defaultUser, 1)
// eslint-disable-next-line camelcase
humhubUser.profile.gradido_address = 'changed gradido address'
const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser)
expect(result).toBe(false)
})
it('Should return false because email differ', () => {
const humhubUser = new GetUser(defaultUser, 1)
humhubUser.account.email = 'new@gmail.com'
const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser)
expect(result).toBe(false)
})
it('Should return false because language differ', () => {
const humhubUser = new GetUser(defaultUser, 1)
humhubUser.account.language = 'de'
const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser)
expect(result).toBe(false)
})
it('Should return false because gradido_address differ', () => {
const humhubUser = new GetUser(defaultUser, 1)
humhubUser.profile.gradido_address = 'changed gradido address'
const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser)
expect(result).toBe(false)
})
})

View File

@ -6,18 +6,32 @@ import { Profile } from './model/Profile'
function profileIsTheSame(profile: Profile, user: User): boolean {
const gradidoUserProfile = new Profile(user)
if (profile.firstname !== gradidoUserProfile.firstname) return false
if (profile.lastname !== gradidoUserProfile.lastname) return false
if (profile.gradido_address !== gradidoUserProfile.gradido_address) return false
if (profile.firstname !== gradidoUserProfile.firstname) {
return false
}
if (profile.lastname !== gradidoUserProfile.lastname) {
return false
}
if (profile.gradido_address !== gradidoUserProfile.gradido_address) {
return false
}
return true
}
function accountIsTheSame(account: Account, user: User): boolean {
const gradidoUserAccount = new Account(user)
if (account.username !== gradidoUserAccount.username) return false
if (account.email !== gradidoUserAccount.email) return false
if (account.language !== gradidoUserAccount.language) return false
if (account.status !== gradidoUserAccount.status) return false
if (account.username !== gradidoUserAccount.username) {
return false
}
if (account.email !== gradidoUserAccount.email) {
return false
}
if (account.language !== gradidoUserAccount.language) {
return false
}
if (account.status !== gradidoUserAccount.status) {
return false
}
return true
}

View File

@ -12,7 +12,7 @@ export class ProfileLoggingView extends AbstractLoggingView {
return {
firstname: this.self.firstname.substring(0, 3) + '...',
lastname: this.self.lastname.substring(0, 3) + '...',
// eslint-disable-next-line camelcase
gradido_address:
gradidoAddressParts[0] + '/' + gradidoAddressParts[1].substring(0, 3) + '...',
}

View File

@ -1,8 +1,5 @@
import { User } from '@entity/User'
import { PublishNameLogic } from '@/data/PublishName.logic'
import { PublishNameType } from '@/graphql/enum/PublishNameType'
import { Account } from './Account'
import { Profile } from './Profile'
@ -10,12 +7,6 @@ export abstract class AbstractUser {
public constructor(user: User) {
this.account = new Account(user)
this.profile = new Profile(user)
// temp fix for prevent double usernames in humhub, if the username ist created from initials
const publishNameLogic = new PublishNameLogic(user)
if (publishNameLogic.isUsernameFromInitials(user.humhubPublishName as PublishNameType)) {
this.profile.firstname = this.account.username
this.account.username = user.gradidoID
}
}
account: Account

View File

@ -1,4 +1,3 @@
/* eslint-disable camelcase */
import { User } from '@entity/User'
import { convertGradidoLanguageToHumhub } from '@/apis/humhub/convertLanguage'
@ -8,7 +7,7 @@ import { PublishNameType } from '@/graphql/enum/PublishNameType'
export class Account {
public constructor(user: User) {
const publishNameLogic = new PublishNameLogic(user)
this.username = publishNameLogic.getUsername(user.humhubPublishName as PublishNameType)
this.username = publishNameLogic.getUserIdentifier(user.humhubPublishName as PublishNameType)
this.email = user.emailContact.email
this.language = convertGradidoLanguageToHumhub(user.language)
this.status = 1

View File

@ -10,6 +10,6 @@ export class GetUser extends AbstractUser {
id: number
guid: string
// eslint-disable-next-line camelcase
display_name: string
}

View File

@ -0,0 +1,139 @@
import { User } from '@entity/User'
import { UserContact } from '@entity/UserContact'
import { v4 as uuidv4 } from 'uuid'
import { PublishNameType } from '@/graphql/enum/PublishNameType'
import { PostUser as HumhubUser } from './PostUser'
const gradidoUuid = uuidv4()
let user: User
/*
export enum PublishNameType {
PUBLISH_NAME_ALIAS_OR_INITALS = 0,
PUBLISH_NAME_INITIALS = 1,
PUBLISH_NAME_FIRST = 2,
PUBLISH_NAME_FIRST_INITIAL = 3,
PUBLISH_NAME_FULL = 4,
}
*/
describe('test creation of a humhub user from db user', () => {
beforeEach(() => {
const emailContact = new UserContact()
emailContact.email = 'john.smith@gradido.de'
user = new User()
user.alias = 'alias'
user.firstName = 'John'
user.lastName = 'Smith'
user.emailContact = emailContact
user.gradidoID = gradidoUuid
})
describe('for alias or initials', () => {
it('with alias set', () => {
user.humhubPublishName = PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS
const humhubUser = new HumhubUser(user)
expect(humhubUser.account.username).toBe('alias')
expect(humhubUser.profile.firstname).toBe('')
expect(humhubUser.profile.lastname).toBe('')
})
it('with empty alias', () => {
user.alias = ''
user.humhubPublishName = PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS
const humhubUser = new HumhubUser(user)
expect(humhubUser.account.username).toBe(gradidoUuid)
expect(humhubUser.profile.firstname).toBe('JoSm')
expect(humhubUser.profile.lastname).toBe('')
})
})
describe('for initials', () => {
it('with alias set', () => {
user.humhubPublishName = PublishNameType.PUBLISH_NAME_INITIALS
const humhubUser = new HumhubUser(user)
expect(humhubUser.account.username).toBe(gradidoUuid)
expect(humhubUser.profile.firstname).toBe('JoSm')
expect(humhubUser.profile.lastname).toBe('')
})
it('with empty alias', () => {
user.alias = ''
user.humhubPublishName = PublishNameType.PUBLISH_NAME_INITIALS
const humhubUser = new HumhubUser(user)
expect(humhubUser.account.username).toBe(gradidoUuid)
expect(humhubUser.profile.firstname).toBe('JoSm')
expect(humhubUser.profile.lastname).toBe('')
})
})
describe('for first name only', () => {
it('with alias set', () => {
user.humhubPublishName = PublishNameType.PUBLISH_NAME_FIRST
const humhubUser = new HumhubUser(user)
expect(humhubUser.account.username).toBe(gradidoUuid)
expect(humhubUser.profile.firstname).toBe('John')
expect(humhubUser.profile.lastname).toBe('')
})
it('with empty alias', () => {
user.alias = ''
user.humhubPublishName = PublishNameType.PUBLISH_NAME_FIRST
const humhubUser = new HumhubUser(user)
expect(humhubUser.account.username).toBe(gradidoUuid)
expect(humhubUser.profile.firstname).toBe('John')
expect(humhubUser.profile.lastname).toBe('')
})
})
describe('for first name and last name initial', () => {
it('with alias set', () => {
user.humhubPublishName = PublishNameType.PUBLISH_NAME_FIRST_INITIAL
const humhubUser = new HumhubUser(user)
expect(humhubUser.account.username).toBe(gradidoUuid)
expect(humhubUser.profile.firstname).toBe('John')
expect(humhubUser.profile.lastname).toBe('S')
})
it('with empty alias', () => {
user.alias = ''
user.humhubPublishName = PublishNameType.PUBLISH_NAME_FIRST_INITIAL
const humhubUser = new HumhubUser(user)
expect(humhubUser.account.username).toBe(gradidoUuid)
expect(humhubUser.profile.firstname).toBe('John')
expect(humhubUser.profile.lastname).toBe('S')
})
})
describe('for full name', () => {
it('with alias set', () => {
user.humhubPublishName = PublishNameType.PUBLISH_NAME_FULL
const humhubUser = new HumhubUser(user)
expect(humhubUser.account.username).toBe(gradidoUuid)
expect(humhubUser.profile.firstname).toBe('John')
expect(humhubUser.profile.lastname).toBe('Smith')
})
it('with empty alias', () => {
user.alias = ''
user.humhubPublishName = PublishNameType.PUBLISH_NAME_FULL
const humhubUser = new HumhubUser(user)
expect(humhubUser.account.username).toBe(gradidoUuid)
expect(humhubUser.profile.firstname).toBe('John')
expect(humhubUser.profile.lastname).toBe('Smith')
})
})
})

View File

@ -1,4 +1,3 @@
/* eslint-disable camelcase */
import { User } from '@entity/User'
import { CONFIG } from '@/config'
@ -14,6 +13,18 @@ export class Profile {
this.gradido_address = `${CONFIG.COMMUNITY_NAME}/${
publishNameLogic.hasAlias() ? user.alias : user.gradidoID
}`
// we need to get our public name to humhub, but the public name isn't always unique,
// so in some cases we must cheat and put the public name into first_name, if it isn't unique,
// to let the username to be unique either alias or gradido id
// in humhub first name is shown if exist else username
// if it shows first_name it will also show last_name if exist
// if we have public name from alias, we have only 2 character for first name and 2 for last name,
// but this isn't searchable in humhub, so we put both into first_name
if (publishNameLogic.isUsernameFromInitials(user.humhubPublishName as PublishNameType)) {
this.firstname = publishNameLogic.getUsernameFromInitials()
this.lastname = ''
}
}
firstname: string

View File

@ -1,9 +1,8 @@
/* eslint-disable @typescript-eslint/no-empty-function */
import { User } from '@entity/User'
import { UserContact } from '@entity/UserContact'
import { GetUser } from './model/GetUser'
import { syncUser, ExecutedHumhubAction } from './syncUser'
import { ExecutedHumhubAction, syncUser } from './syncUser'
jest.mock('@/apis/humhub/HumHubClient')
@ -28,7 +27,8 @@ describe('syncUser function', () => {
it('When humhubUser exists and user.humhubAllowed is false, should return DELETE action', async () => {
const humhubUsers = new Map<string, GetUser>()
humhubUsers.set(defaultUser.emailContact.email, new GetUser(defaultUser, 1))
const humhubUser = new GetUser(defaultUser, 1)
humhubUsers.set(humhubUser.account.username, humhubUser)
defaultUser.humhubAllowed = false
const result = await syncUser(defaultUser, humhubUsers)
@ -39,8 +39,8 @@ describe('syncUser function', () => {
it('When humhubUser exists and user.humhubAllowed is true and there are changes in user data, should return UPDATE action', async () => {
const humhubUsers = new Map<string, GetUser>()
const humhubUser = new GetUser(defaultUser, 1)
humhubUsers.set(humhubUser.account.username, humhubUser)
humhubUser.account.username = 'test username'
humhubUsers.set(defaultUser.emailContact.email, humhubUser)
defaultUser.humhubAllowed = true
const result = await syncUser(defaultUser, humhubUsers)
@ -51,7 +51,7 @@ describe('syncUser function', () => {
it('When humhubUser exists and user.humhubAllowed is true and there are no changes in user data, should return SKIP action', async () => {
const humhubUsers = new Map<string, GetUser>()
const humhubUser = new GetUser(defaultUser, 1)
humhubUsers.set(defaultUser.emailContact.email, humhubUser)
humhubUsers.set(humhubUser.account.username, humhubUser)
defaultUser.humhubAllowed = true
const result = await syncUser(defaultUser, humhubUsers)

View File

@ -1,9 +1,10 @@
import { User } from '@entity/User'
import { LogError } from '@/server/LogError'
import { backendLogger as logger } from '@/server/logger'
import { isHumhubUserIdenticalToDbUser } from './compareHumhubUserDbUser'
import { HumHubClient } from './HumHubClient'
import { isHumhubUserIdenticalToDbUser } from './compareHumhubUserDbUser'
import { GetUser } from './model/GetUser'
import { PostUser } from './model/PostUser'
@ -12,7 +13,22 @@ export enum ExecutedHumhubAction {
CREATE,
SKIP,
DELETE,
VALIDATION_ERROR,
}
// todo: replace with full validation (schema)
function isValid(postUser: PostUser, userId: number): boolean {
if (postUser.profile.firstname.length > 20) {
logger.error('firstname too long for humhub, for user with id:', userId)
return false
}
if (postUser.profile.lastname.length > 20) {
logger.error('lastname too long for humhub, for user with id:', userId)
return false
}
return true
}
/**
* Trigger action according to conditions
* | User exist on humhub | export to humhub allowed | changes in user data | ACTION
@ -21,9 +37,8 @@ export enum ExecutedHumhubAction {
* | true | true | false | SKIP
* | false | false | ignored | SKIP
* | false | true | ignored | CREATE
* @param user
* @param humHubClient
* @param humhubUsers
* @param user user entity
* @param humhubUsers user map indices with username
* @returns
*/
export async function syncUser(
@ -31,7 +46,10 @@ export async function syncUser(
humhubUsers: Map<string, GetUser>,
): Promise<ExecutedHumhubAction> {
const postUser = new PostUser(user)
const humhubUser = humhubUsers.get(user.emailContact.email.trim())
if (!isValid(postUser, user.id)) {
return ExecutedHumhubAction.VALIDATION_ERROR
}
const humhubUser = humhubUsers.get(postUser.account.username)
const humHubClient = HumHubClient.getInstance()
if (!humHubClient) {
throw new LogError('Error creating humhub client')

View File

@ -1,4 +1,3 @@
/* eslint-disable camelcase */
import { OpenaiThreads } from '@entity/OpenaiThreads'
import { User } from '@entity/User'
import { OpenAI } from 'openai'
@ -18,7 +17,7 @@ export class OpenaiClient {
/**
* The singleton instance of the `OpenaiClient`.
*/
// eslint-disable-next-line no-use-before-define
private static instance: OpenaiClient
/**

View File

@ -6,7 +6,9 @@ import { LogError } from '@/server/LogError'
import { CustomJwtPayload } from './CustomJwtPayload'
export const decode = async (token: string): Promise<CustomJwtPayload | null> => {
if (!token) throw new LogError('401 Unauthorized')
if (!token) {
throw new LogError('401 Unauthorized')
}
try {
const secret = new TextEncoder().encode(CONFIG.JWT_SECRET)
@ -15,7 +17,7 @@ export const decode = async (token: string): Promise<CustomJwtPayload | null> =>
audience: 'urn:gradido:audience',
})
return payload as CustomJwtPayload
} catch (err) {
} catch (_err) {
return null
}
}

View File

@ -1,7 +1,5 @@
// 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'
@ -90,8 +88,8 @@ const email = {
EMAIL_PASSWORD: process.env.EMAIL_PASSWORD ?? '',
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_TLS: process.env.EMAIL_TLS !== 'false',
EMAIL_LINK_VERIFICATION:
COMMUNITY_URL + (process.env.EMAIL_LINK_VERIFICATION_PATH ?? '/checkEmail/'),
EMAIL_LINK_SETPASSWORD:
@ -101,11 +99,11 @@ const email = {
EMAIL_LINK_OVERVIEW: COMMUNITY_URL + (process.env.EMAIL_LINK_OVERVIEW_PATH ?? '/overview'),
// time in minutes a optin code is valid
EMAIL_CODE_VALID_TIME: process.env.EMAIL_CODE_VALID_TIME
? parseInt(process.env.EMAIL_CODE_VALID_TIME) ?? 1440
? (parseInt(process.env.EMAIL_CODE_VALID_TIME) ?? 1440)
: 1440,
// time in minutes that must pass to request a new optin code
EMAIL_CODE_REQUEST_TIME: process.env.EMAIL_CODE_REQUEST_TIME
? parseInt(process.env.EMAIL_CODE_REQUEST_TIME) ?? 10
? (parseInt(process.env.EMAIL_CODE_REQUEST_TIME) ?? 10)
: 10,
}
@ -139,6 +137,7 @@ const gms = {
// koordinates of Illuminz-instance of GMS
GMS_API_URL: process.env.GMS_API_URL ?? 'http://localhost:4044/',
GMS_DASHBOARD_URL: process.env.GMS_DASHBOARD_URL ?? 'http://localhost:8080/',
GMS_USER_SEARCH_FRONTEND_ROUTE: process.env.GMS_USER_SEARCH_FRONTEND_ROUTE ?? 'user-search',
// used as secret postfix attached at the gms community-auth-url endpoint ('/hook/gms/' + 'secret')
GMS_WEBHOOK_SECRET: process.env.GMS_WEBHOOK_SECRET ?? 'secret',
}

View File

@ -1,19 +1,17 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
// eslint-disable-next-line import/no-unresolved
import {
COMMUNITY_NAME,
COMMUNITY_URL,
COMMUNITY_DESCRIPTION,
COMMUNITY_NAME,
COMMUNITY_SUPPORT_MAIL,
COMMUNITY_URL,
DB_DATABASE,
DB_HOST,
DB_PASSWORD,
DB_PORT,
DB_USER,
DB_VERSION,
DB_DATABASE,
DECAY_START_TIME,
GDT_API_URL,
GDT_ACTIVE,
GDT_API_URL,
GMS_ACTIVE,
GRAPHIQL,
HUMHUB_ACTIVE,
@ -61,7 +59,6 @@ export const schema = Joi.object({
.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>): 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 })
}
@ -72,7 +69,6 @@ export const schema = Joi.object({
COMMUNITY_REDEEM_CONTRIBUTION_URL: Joi.string()
.uri({ scheme: ['http', 'https'] })
.custom((value: string, helpers: Joi.CustomHelpers<string>): 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 })
}
@ -168,7 +164,6 @@ export const schema = Joi.object({
EMAIL_LINK_VERIFICATION: Joi.string()
.uri({ scheme: ['http', 'https'] })
.custom((value: string, helpers: Joi.CustomHelpers<string>): 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 })
}
@ -180,7 +175,6 @@ export const schema = Joi.object({
EMAIL_LINK_SETPASSWORD: Joi.string()
.uri({ scheme: ['http', 'https'] })
.custom((value: string, helpers: Joi.CustomHelpers<string>): 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 })
}
@ -192,7 +186,6 @@ export const schema = Joi.object({
EMAIL_LINK_FORGOTPASSWORD: Joi.string()
.uri({ scheme: ['http', 'https'] })
.custom((value: string, helpers: Joi.CustomHelpers<string>): 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 })
}
@ -204,7 +197,6 @@ export const schema = Joi.object({
EMAIL_LINK_OVERVIEW: Joi.string()
.uri({ scheme: ['http', 'https'] })
.custom((value: string, helpers: Joi.CustomHelpers<string>): 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 })
}
@ -279,6 +271,14 @@ export const schema = Joi.object({
.default('http://localhost:8080/')
.description('The URL for the GMS dashboard'),
GMS_USER_SEARCH_FRONTEND_ROUTE: Joi.string()
.pattern(/^[\w_-]*$/)
.allow('')
.default('user-search')
.description(
'gms frontend playground route, user-search for standalone playground, usersearch-playground for old, empty for testing local',
),
GMS_WEBHOOK_SECRET: Joi.string()
.min(1)
.default('secret')

View File

@ -4,7 +4,6 @@ import { User } from '@entity/User'
import { ContributionMessageType } from '@/graphql/enum/ContributionMessageType'
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class ContributionMessageBuilder {
private contributionMessage: ContributionMessage

View File

@ -1,23 +1,156 @@
import { User } from '@entity/User'
import { v4 as uuidv4 } from 'uuid'
import { PublishNameType } from '@/graphql/enum/PublishNameType'
import { PublishNameLogic } from './PublishName.logic'
const gradidoUuid = uuidv4()
let user: User
let logic: PublishNameLogic
/*
export enum PublishNameType {
PUBLISH_NAME_ALIAS_OR_INITALS = 0,
PUBLISH_NAME_INITIALS = 1,
PUBLISH_NAME_FIRST = 2,
PUBLISH_NAME_FIRST_INITIAL = 3,
PUBLISH_NAME_FULL = 4,
}
*/
describe('test publish name logic', () => {
describe('test username', () => {
it('alias or initials with alias set', () => {
const user = new User()
user.alias = 'alias'
const logic = new PublishNameLogic(user)
expect(logic.getUsername(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe(user.alias)
beforeEach(() => {
user = new User()
user.alias = 'alias'
user.firstName = 'John'
user.lastName = 'Smith'
user.gradidoID = gradidoUuid
logic = new PublishNameLogic(user)
})
describe('test hasAlias', () => {
it('for alias set', () => {
expect(logic.hasAlias()).toBe(true)
})
it('alias or initials with empty alias', () => {
const user = new User()
user.firstName = 'John'
user.lastName = 'Smith'
const logic = new PublishNameLogic(user)
expect(logic.getUsername(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe('JoSm')
it('for alias empty string', () => {
user.alias = ''
expect(logic.hasAlias()).toBe(false)
})
it('for alias string to short 1', () => {
user.alias = 'a'
expect(logic.hasAlias()).toBe(false)
})
it('for alias string to short 2', () => {
user.alias = 'ab'
expect(logic.hasAlias()).toBe(false)
})
it('for alias string 3', () => {
user.alias = 'abc'
expect(logic.hasAlias()).toBe(true)
})
})
describe('test isUsernameFromInitials', () => {
it('for publish name initials', () => {
expect(logic.isUsernameFromInitials(PublishNameType.PUBLISH_NAME_INITIALS)).toBe(true)
})
it('for publish name alias or initials, with alias set', () => {
expect(logic.isUsernameFromInitials(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe(
false,
)
})
it('for publish name alias or initials, with alias not set', () => {
user.alias = ''
expect(logic.isUsernameFromInitials(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe(true)
})
it('for publish name first', () => {
expect(logic.isUsernameFromInitials(PublishNameType.PUBLISH_NAME_FIRST)).toBe(false)
})
it('for publish name first initial', () => {
expect(logic.isUsernameFromInitials(PublishNameType.PUBLISH_NAME_FIRST_INITIAL)).toBe(false)
})
it('for publish name full', () => {
expect(logic.isUsernameFromInitials(PublishNameType.PUBLISH_NAME_FULL)).toBe(false)
})
})
describe('test isUsernameFromAlias', () => {
it('for publish name initials', () => {
expect(logic.isUsernameFromAlias(PublishNameType.PUBLISH_NAME_INITIALS)).toBe(false)
})
it('for publish name alias or initials with alias set', () => {
expect(logic.isUsernameFromAlias(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe(true)
})
it('for publish name alias or initials with alias empty', () => {
user.alias = ''
expect(logic.isUsernameFromAlias(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe(false)
})
it('for publish name first', () => {
expect(logic.isUsernameFromAlias(PublishNameType.PUBLISH_NAME_FIRST)).toBe(false)
})
it('for publish name first initial', () => {
expect(logic.isUsernameFromAlias(PublishNameType.PUBLISH_NAME_FIRST_INITIAL)).toBe(false)
})
it('for publish name full', () => {
expect(logic.isUsernameFromAlias(PublishNameType.PUBLISH_NAME_FULL)).toBe(false)
})
})
describe('test user identifier ', () => {
it('for alias or initials with alias set', () => {
expect(logic.getUserIdentifier(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe('alias')
})
it('for alias or initials with empty alias', () => {
user.alias = ''
expect(logic.getUserIdentifier(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe(
gradidoUuid,
)
})
it('for publish name initials', () => {
expect(logic.getUserIdentifier(PublishNameType.PUBLISH_NAME_INITIALS)).toBe(gradidoUuid)
})
it('for publish name first', () => {
expect(logic.getUserIdentifier(PublishNameType.PUBLISH_NAME_FIRST)).toBe(gradidoUuid)
})
it('for publish name first initial', () => {
expect(logic.getUserIdentifier(PublishNameType.PUBLISH_NAME_FIRST_INITIAL)).toBe(gradidoUuid)
})
it('for publish name full', () => {
expect(logic.getUserIdentifier(PublishNameType.PUBLISH_NAME_FULL)).toBe(gradidoUuid)
})
})
describe('test public name', () => {
it('for alias or initials with alias set', () => {
expect(logic.getPublicName(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe('alias')
})
it('for alias or initials with empty alias', () => {
user.alias = ''
expect(logic.getPublicName(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe('JoSm')
})
it('for alias or initials with empty alias and lower case written names', () => {
user.alias = ''
user.firstName = 'john'
user.lastName = 'smith'
expect(logic.getPublicName(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe('JoSm')
})
it('for publish name initials', () => {
expect(logic.getPublicName(PublishNameType.PUBLISH_NAME_INITIALS)).toBe('JoSm')
})
it('for publish name initials with lower case written names', () => {
user.firstName = 'john'
user.lastName = 'smith'
expect(logic.getPublicName(PublishNameType.PUBLISH_NAME_INITIALS)).toBe('JoSm')
})
it('for publish name first', () => {
expect(logic.getPublicName(PublishNameType.PUBLISH_NAME_FIRST)).toBe('John')
})
it('for publish name first initial', () => {
expect(logic.getPublicName(PublishNameType.PUBLISH_NAME_FIRST_INITIAL)).toBe('John S')
})
it('for publish name full', () => {
expect(logic.getPublicName(PublishNameType.PUBLISH_NAME_FULL)).toBe('John Smith')
})
})
})

View File

@ -18,7 +18,6 @@ export class PublishNameLogic {
// remove character which are invalid for humhub username
private filterOutInvalidChar(name: string) {
// eslint-disable-next-line import/no-named-as-default-member
return XRegExp.match(name, this.usernameRegex, 'all').join('')
}
@ -35,12 +34,16 @@ export class PublishNameLogic {
* @returns user.firstName for PUBLISH_NAME_FIRST, PUBLISH_NAME_FIRST_INITIAL or PUBLISH_NAME_FULL
*/
public getFirstName(publishNameType: PublishNameType): string {
let firstName = ''
if (this.user && typeof this.user.firstName === 'string') {
firstName = this.user.firstName
}
return [
PublishNameType.PUBLISH_NAME_FIRST,
PublishNameType.PUBLISH_NAME_FIRST_INITIAL,
PublishNameType.PUBLISH_NAME_FULL,
].includes(publishNameType)
? this.user.firstName
? firstName.slice(0, 20)
: ''
}
@ -51,31 +54,56 @@ export class PublishNameLogic {
* first initial from user.lastName for PUBLISH_NAME_FIRST_INITIAL
*/
public getLastName(publishNameType: PublishNameType): string {
let lastName = ''
if (this.user && typeof this.user.lastName === 'string') {
lastName = this.user.lastName
}
return publishNameType === PublishNameType.PUBLISH_NAME_FULL
? this.user.lastName
: publishNameType === PublishNameType.PUBLISH_NAME_FIRST_INITIAL
? this.user.lastName.charAt(0)
: ''
? lastName.slice(0, 20)
: publishNameType === PublishNameType.PUBLISH_NAME_FIRST_INITIAL && lastName.length > 0
? lastName.charAt(0)
: ''
}
/**
* get username from user.alias for PUBLISH_NAME_ALIAS_OR_INITALS and if user has alias
* get first name first two characters and last name first two characters for PUBLISH_NAME_ALIAS_OR_INITALS
* if no alias or PUBLISH_NAME_INITIALS
* get unique username
* @param publishNameType
* @returns user.alias for publishNameType = PUBLISH_NAME_ALIAS_OR_INITALS and user has alias
* else return user.firstName[0,2] + user.lastName[0,2] for publishNameType = [PUBLISH_NAME_ALIAS_OR_INITALS, PUBLISH_NAME_INITIALS]
* @return when alias if exist and publishNameType = [PUBLISH_NAME_ALIAS_OR_INITALS, PUBLISH_NAME_INITIALS]
* return alias
* else return gradido id
*/
public getUsername(publishNameType: PublishNameType): string {
if (this.isUsernameFromInitials(publishNameType)) {
return (
this.firstUpperCaseSecondLowerCase(this.filterOutInvalidChar(this.user.firstName)) +
this.firstUpperCaseSecondLowerCase(this.filterOutInvalidChar(this.user.lastName))
)
} else if (this.isUsernameFromAlias(publishNameType)) {
return this.filterOutInvalidChar(this.user.alias)
}
return this.user.gradidoID
public getUserIdentifier(publishNameType: PublishNameType): string {
return this.isUsernameFromAlias(publishNameType)
? this.getUsernameFromAlias()
: this.user.gradidoID
}
/**
* get public name based on publishNameType: PublishNameType value
* @param publishNameType: PublishNameType
* @return alias if exist and type = PUBLISH_NAME_ALIAS_OR_INITALS
* initials if type = PUBLISH_NAME_INITIALS
* full first name if type = PUBLISH_NAME_FIRST
* full first name and last name initial if type = PUBLISH_NAME_FIRST_INITIAL
* full first name and full last name if type = PUBLISH_NAME_FULL
*/
public getPublicName(publishNameType: PublishNameType): string {
return this.isUsernameFromAlias(publishNameType)
? this.getUsernameFromAlias()
: this.isUsernameFromInitials(publishNameType)
? this.getUsernameFromInitials()
: (this.getFirstName(publishNameType) + ' ' + this.getLastName(publishNameType)).trim()
}
public getUsernameFromInitials(): string {
return (
this.firstUpperCaseSecondLowerCase(this.user.firstName) +
this.firstUpperCaseSecondLowerCase(this.user.lastName)
).trim()
}
public getUsernameFromAlias(): string {
return this.filterOutInvalidChar(this.user.alias)
}
public isUsernameFromInitials(publishNameType: PublishNameType): boolean {

View File

@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { createTransport } from 'nodemailer'
import { logger, i18n } from '@test/testSetup'
import { i18n, logger } from '@test/testSetup'
import { CONFIG } from '@/config'
@ -103,12 +102,10 @@ describe('sendEmailTranslated', () => {
})
})
// eslint-disable-next-line jest/no-disabled-tests
it.skip('calls "i18n.setLocale" with "en"', () => {
expect(i18n.setLocale).toBeCalledWith('en')
})
// eslint-disable-next-line jest/no-disabled-tests
it.skip('calls "i18n.__" for translation', () => {
expect(i18n.__).toBeCalled()
})

View File

@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-return */
import path from 'path'
import Email from 'email-templates'

View File

@ -1,29 +1,24 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { Connection } from '@dbTools/typeorm'
import { ApolloServerTestClient } from 'apollo-server-testing'
import { Decimal } from 'decimal.js-light'
import { testEnvironment } from '@test/helpers'
import { logger, i18n as localization } from '@test/testSetup'
import { i18n as localization, logger } from '@test/testSetup'
import { CONFIG } from '@/config'
// eslint-disable-next-line import/no-namespace
import * as sendEmailTranslatedApi from './sendEmailTranslated'
import {
sendAddedContributionMessageEmail,
sendAccountActivationEmail,
sendAccountMultiRegistrationEmail,
sendAddedContributionMessageEmail,
sendContributionChangedByModeratorEmail,
sendContributionConfirmedEmail,
sendContributionDeniedEmail,
sendContributionDeletedEmail,
sendContributionDeniedEmail,
sendResetPasswordEmail,
sendTransactionLinkRedeemedEmail,
sendTransactionReceivedEmail,
sendContributionChangedByModeratorEmail,
} from './sendEmailVariants'
const testMailServerHost = 'localhost'
@ -69,7 +64,6 @@ afterAll(async () => {
const sendEmailTranslatedSpy = jest.spyOn(sendEmailTranslatedApi, 'sendEmailTranslated')
describe('sendEmailVariants', () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let result: any
describe('sendAddedContributionMessageEmail', () => {

View File

@ -3,7 +3,7 @@ import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCom
import { validate as validateUUID, version as versionUUID } from 'uuid'
import { CONFIG } from '@/config'
// eslint-disable-next-line camelcase
import { AuthenticationClient as V1_0_AuthenticationClient } from '@/federation/client/1_0/AuthenticationClient'
import { backendLogger as logger } from '@/server/logger'
import { ensureUrlEndsWithSlash } from '@/util/utilities'
@ -35,7 +35,7 @@ export async function startCommunityAuthentication(
) {
try {
const client = AuthenticationClientFactory.getInstance(foreignFedCom)
// eslint-disable-next-line camelcase
if (client instanceof V1_0_AuthenticationClient) {
const args = new OpenConnectionArgs()
args.publicKey = homeCom.publicKey.toString('hex')

View File

@ -132,7 +132,7 @@ export class SendCoinsClient {
{ args },
)
logger.debug(`X-Com: SendCoinsClient: after revertSettledSendCoins: data=`, data)
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (!data?.revertSettledSendCoins) {
logger.warn(
'X-Com: SendCoinsClient: revertSettledSendCoins without response data from endpoint',

View File

@ -7,7 +7,6 @@ export class PublicCommunityInfoLoggingView extends AbstractLoggingView {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
name: this.self.name,

View File

@ -7,7 +7,6 @@ export class SendCoinsArgsLoggingView extends AbstractLoggingView {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
recipientCommunityUuid: this.self.recipientCommunityUuid,

View File

@ -7,7 +7,6 @@ export class SendCoinsResultLoggingView extends AbstractLoggingView {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
vote: this.self.vote,

View File

@ -1,8 +1,6 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { Field, ObjectType } from 'type-graphql'
@ObjectType()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export class GetPublicKeyResult {
constructor(pubKey: string) {
this.publicKey = pubKey

View File

@ -1,5 +1,3 @@
// eslint-disable-next-line camelcase
import { AuthenticationClient as V1_0_AuthenticationClient } from '@/federation/client/1_0/AuthenticationClient'
// eslint-disable-next-line camelcase
export class AuthenticationClient extends V1_0_AuthenticationClient {}

View File

@ -1,5 +1,3 @@
// eslint-disable-next-line camelcase
import { FederationClient as V1_0_FederationClient } from '@/federation/client/1_0/FederationClient'
// eslint-disable-next-line camelcase
export class FederationClient extends V1_0_FederationClient {}

View File

@ -1,5 +1,3 @@
// eslint-disable-next-line camelcase
import { SendCoinsClient as V1_0_SendCoinsClient } from '@/federation/client/1_0/SendCoinsClient'
// eslint-disable-next-line camelcase
export class SendCoinsClient extends V1_0_SendCoinsClient {}

View File

@ -1,21 +1,18 @@
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
// eslint-disable-next-line camelcase
import { AuthenticationClient as V1_0_AuthenticationClient } from '@/federation/client/1_0/AuthenticationClient'
// eslint-disable-next-line camelcase
import { AuthenticationClient as V1_1_AuthenticationClient } from '@/federation/client/1_1/AuthenticationClient'
import { ApiVersionType } from '@/federation/enum/apiVersionType'
// eslint-disable-next-line camelcase
type AuthenticationClient = V1_0_AuthenticationClient | V1_1_AuthenticationClient
interface AuthenticationClientInstance {
id: number
// eslint-disable-next-line no-use-before-define
client: AuthenticationClient
}
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class AuthenticationClientFactory {
private static instanceArray: AuthenticationClientInstance[] = []
@ -23,7 +20,7 @@ export class AuthenticationClientFactory {
* The Singleton's constructor should always be private to prevent direct
* construction calls with the `new` operator.
*/
// eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-empty-function
private constructor() {}
private static createAuthenticationClient = (dbCom: DbFederatedCommunity) => {

View File

@ -1,22 +1,19 @@
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
// eslint-disable-next-line camelcase
import { FederationClient as V1_0_FederationClient } from '@/federation/client/1_0/FederationClient'
// eslint-disable-next-line camelcase
import { FederationClient as V1_1_FederationClient } from '@/federation/client/1_1/FederationClient'
import { ApiVersionType } from '@/federation/enum/apiVersionType'
import { ensureUrlEndsWithSlash } from '@/util/utilities'
// eslint-disable-next-line camelcase
type FederationClient = V1_0_FederationClient | V1_1_FederationClient
interface FederationClientInstance {
id: number
// eslint-disable-next-line no-use-before-define
client: FederationClient
}
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class FederationClientFactory {
private static instanceArray: FederationClientInstance[] = []
@ -24,7 +21,7 @@ export class FederationClientFactory {
* The Singleton's constructor should always be private to prevent direct
* construction calls with the `new` operator.
*/
// eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-empty-function
private constructor() {}
private static createFederationClient = (dbCom: DbFederatedCommunity) => {

View File

@ -1,21 +1,18 @@
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
// eslint-disable-next-line camelcase
import { SendCoinsClient as V1_0_SendCoinsClient } from '@/federation/client/1_0/SendCoinsClient'
// eslint-disable-next-line camelcase
import { SendCoinsClient as V1_1_SendCoinsClient } from '@/federation/client/1_1/SendCoinsClient'
import { ApiVersionType } from '@/federation/enum/apiVersionType'
// eslint-disable-next-line camelcase
type SendCoinsClient = V1_0_SendCoinsClient | V1_1_SendCoinsClient
interface SendCoinsClientInstance {
id: number
// eslint-disable-next-line no-use-before-define
client: SendCoinsClient
}
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class SendCoinsClientFactory {
private static instanceArray: SendCoinsClientInstance[] = []
@ -23,7 +20,7 @@ export class SendCoinsClientFactory {
* The Singleton's constructor should always be private to prevent direct
* construction calls with the `new` operator.
*/
// eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-empty-function
private constructor() {}
private static createSendCoinsClient = (dbCom: DbFederatedCommunity) => {

View File

@ -1,17 +1,10 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/unbound-method */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Connection } from '@dbTools/typeorm'
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
import { ApolloServerTestClient } from 'apollo-server-testing'
import { GraphQLClient } from 'graphql-request'
import { Response } from 'graphql-request/dist/types'
import { testEnvironment, cleanDB } from '@test/helpers'
import { cleanDB, testEnvironment } from '@test/helpers'
import { logger } from '@test/testSetup'
import { validateCommunities } from './validateCommunities'
@ -62,9 +55,8 @@ describe('validate Communities', () => {
describe('with one Community of api 1_0 but missing pubKey response', () => {
beforeEach(async () => {
jest.clearAllMocks()
// eslint-disable-next-line @typescript-eslint/require-await
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return { data: {} } as Response<unknown>
})
const variables1 = {
@ -78,7 +70,6 @@ describe('validate Communities', () => {
.into(DbFederatedCommunity)
.values(variables1)
.orUpdate({
// eslint-disable-next-line camelcase
conflict_target: ['id', 'publicKey', 'apiVersion'],
overwrite: ['end_point', 'last_announced_at'],
})
@ -101,9 +92,8 @@ describe('validate Communities', () => {
describe('with one Community of api 1_0 and not matching pubKey', () => {
beforeEach(async () => {
jest.clearAllMocks()
// eslint-disable-next-line @typescript-eslint/require-await
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return {
data: {
getPublicKey: {
@ -123,15 +113,14 @@ describe('validate Communities', () => {
.into(DbFederatedCommunity)
.values(variables1)
.orUpdate({
// eslint-disable-next-line camelcase
conflict_target: ['id', 'publicKey', 'apiVersion'],
overwrite: ['end_point', 'last_announced_at'],
})
.execute()
/*
// eslint-disable-next-line @typescript-eslint/require-await
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return {
data: {
getPublicCommunityInfo: {
@ -183,9 +172,8 @@ describe('validate Communities', () => {
describe('with one Community of api 1_0 and matching pubKey', () => {
beforeEach(async () => {
jest.clearAllMocks()
// eslint-disable-next-line @typescript-eslint/require-await
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return {
data: {
getPublicKey: {
@ -205,7 +193,6 @@ describe('validate Communities', () => {
.into(DbFederatedCommunity)
.values(variables1)
.orUpdate({
// eslint-disable-next-line camelcase
conflict_target: ['id', 'publicKey', 'apiVersion'],
overwrite: ['end_point', 'last_announced_at'],
})
@ -251,9 +238,8 @@ describe('validate Communities', () => {
describe('with two Communities of api 1_0 and 1_1', () => {
beforeEach(async () => {
jest.clearAllMocks()
// eslint-disable-next-line @typescript-eslint/require-await
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return {
data: {
getPublicKey: {
@ -273,7 +259,6 @@ describe('validate Communities', () => {
.into(DbFederatedCommunity)
.values(variables2)
.orUpdate({
// eslint-disable-next-line camelcase
conflict_target: ['id', 'publicKey', 'apiVersion'],
overwrite: ['end_point', 'last_announced_at'],
})
@ -303,9 +288,8 @@ describe('validate Communities', () => {
let dbCom: DbFederatedCommunity
beforeEach(async () => {
jest.clearAllMocks()
// eslint-disable-next-line @typescript-eslint/require-await
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return {
data: {
getPublicKey: {
@ -325,7 +309,6 @@ describe('validate Communities', () => {
.into(DbFederatedCommunity)
.values(variables3)
.orUpdate({
// eslint-disable-next-line camelcase
conflict_target: ['id', 'publicKey', 'apiVersion'],
overwrite: ['end_point', 'last_announced_at'],
})

View File

@ -1,5 +1,3 @@
/** eslint-disable @typescript-eslint/no-unsafe-assignment */
/** eslint-disable @typescript-eslint/no-unsafe-call */
import { IsNull } from '@dbTools/typeorm'
import { Community as DbCommunity } from '@entity/Community'
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
@ -27,8 +25,8 @@ export async function startValidateCommunities(timerInterval: number): Promise<v
// TODO: replace the timer-loop by an event-based communication to verify announced foreign communities
// better to use setTimeout twice than setInterval once -> see https://javascript.info/settimeout-setinterval
setTimeout(function run() {
void validateCommunities()
setTimeout(async function run() {
await validateCommunities()
setTimeout(run, timerInterval)
}, timerInterval)
}
@ -55,7 +53,7 @@ export async function validateCommunities(): Promise<void> {
}
try {
const client = FederationClientFactory.getInstance(dbCom)
// eslint-disable-next-line camelcase
if (client instanceof V1_0_FederationClient) {
const pubKey = await client.getPublicKey()
if (pubKey && pubKey === dbCom.publicKey.toString('hex')) {

View File

@ -3,10 +3,10 @@ import { Decimal } from 'decimal.js-light'
import { ArgsType, Field, Int } from 'type-graphql'
import {
CONTRIBUTIONLINK_NAME_MAX_CHARS,
CONTRIBUTIONLINK_NAME_MIN_CHARS,
MEMO_MAX_CHARS,
MEMO_MIN_CHARS,
CONTRIBUTIONLINK_NAME_MIN_CHARS,
CONTRIBUTIONLINK_NAME_MAX_CHARS,
} from '@/graphql/resolver/const/const'
import { isValidDateString } from '@/graphql/validator/DateString'
import { IsPositiveDecimal } from '@/graphql/validator/Decimal'

View File

@ -1,5 +1,5 @@
import { IsInt, IsString, IsEnum } from 'class-validator'
import { ArgsType, Field, Int, InputType } from 'type-graphql'
import { IsEnum, IsInt, IsString } from 'class-validator'
import { ArgsType, Field, InputType, Int } from 'type-graphql'
import { ContributionMessageType } from '@enum/ContributionMessageType'

View File

@ -1,6 +1,5 @@
/* eslint-disable type-graphql/invalid-nullable-input-type */
import { IsPositive, IsEnum } from 'class-validator'
import { ArgsType, Field, Int, InputType } from 'type-graphql'
import { IsEnum, IsPositive } from 'class-validator'
import { ArgsType, Field, InputType, Int } from 'type-graphql'
import { Order } from '@enum/Order'

View File

@ -1,5 +1,5 @@
import { IsBoolean, IsPositive, IsString } from 'class-validator'
import { Field, ArgsType, Int, InputType } from 'type-graphql'
import { ArgsType, Field, InputType, Int } from 'type-graphql'
import { ContributionStatus } from '@enum/ContributionStatus'

View File

@ -1,5 +1,5 @@
import { IsPositive, IsEnum } from 'class-validator'
import { ArgsType, Field, Int, InputType } from 'type-graphql'
import { IsEnum, IsPositive } from 'class-validator'
import { ArgsType, Field, InputType, Int } from 'type-graphql'
import { RoleNames } from '@enum/RoleNames'

View File

@ -1,4 +1,3 @@
/* eslint-disable type-graphql/invalid-nullable-input-type */
import { IsBoolean } from 'class-validator'
import { Field, InputType } from 'type-graphql'

View File

@ -1,4 +1,4 @@
import { MaxLength, MinLength, IsString } from 'class-validator'
import { IsString, MaxLength, MinLength } from 'class-validator'
import { Decimal } from 'decimal.js-light'
import { ArgsType, Field } from 'type-graphql'

View File

@ -7,22 +7,25 @@ import { INALIENABLE_RIGHTS } from '@/auth/INALIENABLE_RIGHTS'
import { decode, encode } from '@/auth/JWT'
import { RIGHTS } from '@/auth/RIGHTS'
import {
ROLE_UNAUTHORIZED,
ROLE_USER,
ROLE_ADMIN,
ROLE_DLT_CONNECTOR,
ROLE_MODERATOR,
ROLE_MODERATOR_AI,
ROLE_DLT_CONNECTOR,
ROLE_UNAUTHORIZED,
ROLE_USER,
} from '@/auth/ROLES'
import { Context } from '@/server/context'
import { LogError } from '@/server/LogError'
import { Context } from '@/server/context'
export const isAuthorized: AuthChecker<Context> = async ({ context }, rights) => {
context.role = ROLE_UNAUTHORIZED // unauthorized user
// is rights an inalienable right?
if ((rights as RIGHTS[]).reduce((acc, right) => acc && INALIENABLE_RIGHTS.includes(right), true))
if (
(rights as RIGHTS[]).reduce((acc, right) => acc && INALIENABLE_RIGHTS.includes(right), true)
) {
return true
}
// Do we have a token?
if (!context.token) {

View File

@ -1,5 +1,5 @@
import { IsString, IsOptional, MaxLength, IsNumber, IsBoolean, IsUrl } from 'class-validator'
import { InputType, Field, Int } from 'type-graphql'
import { IsBoolean, IsNumber, IsOptional, IsString, IsUrl, MaxLength } from 'class-validator'
import { Field, InputType, Int } from 'type-graphql'
@InputType()
export class ProjectBrandingInput {

View File

@ -1,7 +1,7 @@
import { Point } from '@dbTools/typeorm'
import { Community as DbCommunity } from '@entity/Community'
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
import { ObjectType, Field } from 'type-graphql'
import { Field, ObjectType } from 'type-graphql'
import { Point2Location } from '@/graphql/resolver/util/Location2Point'

View File

@ -1,5 +1,5 @@
import { Decimal } from 'decimal.js-light'
import { ObjectType, Field } from 'type-graphql'
import { Field, ObjectType } from 'type-graphql'
@ObjectType()
export class AdminUpdateContribution {

View File

@ -1,5 +1,5 @@
import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int, Float } from 'type-graphql'
import { Field, Float, Int, ObjectType } from 'type-graphql'
@ObjectType()
export class Balance {

View File

@ -1,4 +1,4 @@
import { ObjectType, Field } from 'type-graphql'
import { Field, ObjectType } from 'type-graphql'
import { Message } from '@/apis/openai/model/Message'

View File

@ -1,5 +1,5 @@
import { Community as DbCommunity } from '@entity/Community'
import { ObjectType, Field, Int } from 'type-graphql'
import { Field, Int, ObjectType } from 'type-graphql'
@ObjectType()
export class Community {

View File

@ -1,5 +1,5 @@
import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
import { Field, Int, ObjectType } from 'type-graphql'
@ObjectType()
export class DynamicStatisticsFields {

View File

@ -1,7 +1,7 @@
import { Contribution as dbContribution } from '@entity/Contribution'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
import { Field, Int, ObjectType } from 'type-graphql'
import { User } from './User'

View File

@ -1,6 +1,6 @@
import { ContributionLink as dbContributionLink } from '@entity/ContributionLink'
import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
import { Field, Int, ObjectType } from 'type-graphql'
import { CONFIG } from '@/config'

View File

@ -1,4 +1,4 @@
import { ObjectType, Field, Int } from 'type-graphql'
import { Field, Int, ObjectType } from 'type-graphql'
import { ContributionLink } from '@model/ContributionLink'

View File

@ -1,5 +1,5 @@
import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
import { Field, Int, ObjectType } from 'type-graphql'
interface DecayInterface {
balance: Decimal

View File

@ -1,5 +1,5 @@
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
import { ObjectType, Field, Int } from 'type-graphql'
import { Field, Int, ObjectType } from 'type-graphql'
import { ensureUrlEndsWithSlash } from '@/util/utilities'

View File

@ -1,6 +1,4 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ObjectType, Field, Float, Int } from 'type-graphql'
import { Field, Float, Int, ObjectType } from 'type-graphql'
import { GdtEntryType } from '@enum/GdtEntryType'
@ -12,9 +10,9 @@ export class GdtEntry {
date,
email,
comment,
// eslint-disable-next-line camelcase
coupon_code,
// eslint-disable-next-line camelcase
gdt_entry_type_id,
factor,
amount2,
@ -26,9 +24,9 @@ export class GdtEntry {
this.date = date
this.email = email
this.comment = comment
// eslint-disable-next-line camelcase
this.couponCode = coupon_code
// eslint-disable-next-line camelcase
this.gdtEntryType = gdt_entry_type_id
this.factor = factor
this.amount2 = amount2

View File

@ -1,4 +1,4 @@
import { ObjectType, Field, Int, Float } from 'type-graphql'
import { Field, Float, Int, ObjectType } from 'type-graphql'
import { GdtEntry } from './GdtEntry'

View File

@ -1,4 +1,4 @@
import { ObjectType, Field } from 'type-graphql'
import { Field, ObjectType } from 'type-graphql'
@ObjectType()
export class KlickTipp {

View File

@ -1,5 +1,5 @@
import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
import { Field, Int, ObjectType } from 'type-graphql'
@ObjectType()
export class OpenCreation {

View File

@ -1,4 +1,4 @@
import { ObjectType, Field, Int } from 'type-graphql'
import { Field, Int, ObjectType } from 'type-graphql'
@ObjectType()
export class Pagination {

View File

@ -1,5 +1,5 @@
import { ProjectBranding as dbProjectBranding } from '@entity/ProjectBranding'
import { ObjectType, Field, Int } from 'type-graphql'
import { Field, Int, ObjectType } from 'type-graphql'
@ObjectType()
export class ProjectBranding {

View File

@ -1,4 +1,4 @@
import { ObjectType, Field, Int } from 'type-graphql'
import { Field, Int, ObjectType } from 'type-graphql'
import { Space as HumhubSpace } from '@/apis/humhub/model/Space'

View File

@ -1,4 +1,4 @@
import { ObjectType, Field } from 'type-graphql'
import { Field, ObjectType } from 'type-graphql'
import { SpacesResponse } from '@/apis/humhub/model/SpacesResponse'

View File

@ -1,6 +1,6 @@
import { Transaction as dbTransaction } from '@entity/Transaction'
import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
import { Field, Int, ObjectType } from 'type-graphql'
import { TransactionTypeId } from '@enum/TransactionTypeId'
@ -46,7 +46,7 @@ export class Transaction {
this.linkedTransactionId = transaction.linkedTransactionId ?? null
this.linkId = transaction.contribution
? transaction.contribution.contributionLinkId
: transaction.transactionLinkId ?? null
: (transaction.transactionLinkId ?? null)
this.previousBalance =
transaction.previousTransaction?.balance.toDecimalPlaces(2, Decimal.ROUND_DOWN) ??
new Decimal(0)

View File

@ -1,7 +1,7 @@
import { Community as DbCommunity } from '@entity/Community'
import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
import { Field, Int, ObjectType } from 'type-graphql'
import { CONFIG } from '@/config'

View File

@ -1,4 +1,4 @@
import { ObjectType, Field } from 'type-graphql'
import { Field, ObjectType } from 'type-graphql'
import { Balance } from './Balance'
import { Transaction } from './Transaction'

View File

@ -1,7 +1,7 @@
import { Contribution } from '@entity/Contribution'
import { User } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
import { Field, Int, ObjectType } from 'type-graphql'
@ObjectType()
export class UnconfirmedContribution {

Some files were not shown because too many files have changed in this diff Show More