diff --git a/.github/workflows/docker-push.yml b/.github/workflows/docker-push.yml index 643c5436d..406d8304b 100644 --- a/.github/workflows/docker-push.yml +++ b/.github/workflows/docker-push.yml @@ -61,14 +61,14 @@ jobs: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 - name: Log in to the Container registry - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@70b2cdc6480c1a8b86edf1777157f8f437de2166 + uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | @@ -81,7 +81,7 @@ jobs: type=sha - name: Build and push Docker images id: push - uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 + uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 with: context: ${{ matrix.app.context }} target: ${{ matrix.app.target }} diff --git a/backend/.eslintrc.cjs b/backend/.eslintrc.cjs new file mode 100644 index 000000000..e781e15b7 --- /dev/null +++ b/backend/.eslintrc.cjs @@ -0,0 +1,209 @@ +// eslint-disable-next-line import/no-commonjs +module.exports = { + root: true, + env: { + node: true, + }, + parser: '@typescript-eslint/parser', + plugins: ['prettier', '@typescript-eslint', 'import', 'n', 'promise', 'security', 'no-catch-all',], + extends: [ + 'standard', + 'eslint:recommended', + 'plugin:prettier/recommended', + 'plugin:import/recommended', + 'plugin:import/typescript', + 'plugin:security/recommended-legacy', + 'plugin:@eslint-community/eslint-comments/recommended', + ], + settings: { + 'import/parsers': { + '@typescript-eslint/parser': ['.ts', '.tsx'], + }, + 'import/resolver': { + typescript: { + project: ['./tsconfig.json'], + }, + node: true, + }, + }, + rules: { + 'no-catch-all/no-catch-all': 'error', + 'no-console': 'error', + camelcase: 'error', + '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': 'off', // not compatible with scriptless vue files + 'import/default': 'error', + // 'import/named': 'error', + 'import/namespace': 'error', + 'import/no-absolute-path': 'error', + 'import/no-cycle': 'error', + 'import/no-dynamic-require': 'error', + 'import/no-internal-modules': 'off', + 'import/no-relative-packages': 'error', + // 'import/no-relative-parent-imports': ['error', { ignore: ['@/*'] }], + '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', + ], + 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'], + // this is to properly reference the referenced project database without requirement of compiling it + // eslint-disable-next-line camelcase + EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true, + }, + }, + { + files: ['*.spec.ts'], + plugins: ['jest'], + env: { + jest: true, + }, + rules: { + 'jest/no-disabled-tests': 'error', + 'jest/no-focused-tests': 'error', + 'jest/no-identical-title': 'error', + 'jest/prefer-to-have-length': 'error', + 'jest/valid-expect': 'error', + '@typescript-eslint/unbound-method': 'off', + 'jest/unbound-method': 'error', + }, + }, + ], +}; diff --git a/backend/.eslintrc.js b/backend/.eslintrc.js deleted file mode 100644 index cc5440d82..000000000 --- a/backend/.eslintrc.js +++ /dev/null @@ -1,219 +0,0 @@ -module.exports = { - root: true, - env: { - // es6: true, - node: true, - }, - /* parserOptions: { - parser: 'babel-eslint' - },*/ - parser: '@typescript-eslint/parser', - plugins: ['prettier', '@typescript-eslint' /*, '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'], - }, - node: true, - }, - }, - /* rules: { - //'indent': [ 'error', 2 ], - //'quotes': [ "error", "single"], - // 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', - > 'no-console': ['error'], - > 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', - > 'prettier/prettier': ['error'], - }, */ - rules: { - 'no-console': 'error', - camelcase: 'error', - 'no-debugger': 'error', - 'prettier/prettier': [ - 'error', - { - htmlWhitespaceSensitivity: 'ignore', - }, - ], - // import - // 'import/export': 'error', - // 'import/no-deprecated': 'error', - // 'import/no-empty-named-blocks': 'error', - // 'import/no-extraneous-dependencies': 'error', - // 'import/no-mutable-exports': 'error', - // 'import/no-unused-modules': 'error', - // 'import/no-named-as-default': 'error', - // 'import/no-named-as-default-member': 'error', - // 'import/no-amd': 'error', - // 'import/no-commonjs': 'error', - // 'import/no-import-module-exports': 'error', - // 'import/no-nodejs-modules': 'off', - // 'import/unambiguous': 'error', - // 'import/default': 'error', - // 'import/named': 'error', - // 'import/namespace': 'error', - // 'import/no-absolute-path': 'error', - // 'import/no-cycle': 'error', - // 'import/no-dynamic-require': 'error', - // 'import/no-internal-modules': 'off', - // 'import/no-relative-packages': 'error', - // 'import/no-relative-parent-imports': ['error', { ignore: ['@/*'] }], - // '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', - ], - 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'], - // this is to properly reference the referenced project database without requirement of compiling it - // eslint-disable-next-line camelcase - EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true, - }, - }, - { - files: ['*.spec.ts'], - plugins: ['jest'], - env: { - jest: true, - }, - rules: { - 'jest/no-disabled-tests': 'error', - 'jest/no-focused-tests': 'error', - 'jest/no-identical-title': 'error', - 'jest/prefer-to-have-length': 'error', - 'jest/valid-expect': 'error', - '@typescript-eslint/unbound-method': 'off', - // 'jest/unbound-method': 'error', - }, - }, - ], -}; diff --git a/backend/package.json b/backend/package.json index 9c52815a1..2cff584d2 100644 --- a/backend/package.json +++ b/backend/package.json @@ -76,7 +76,7 @@ "metascraper-video": "^5.46.11", "metascraper-youtube": "^5.46.11", "migrate": "^2.1.0", - "mime-types": "^2.1.35", + "mime-types": "^3.0.1", "minimatch": "^9.0.4", "mustache": "^4.2.0", "neo4j-driver": "^4.4.11", @@ -95,6 +95,7 @@ "xregexp": "^5.1.2" }, "devDependencies": { + "@eslint-community/eslint-plugin-eslint-comments": "^4.4.1", "@faker-js/faker": "9.6.0", "@types/jest": "^29.5.14", "@types/node": "^22.14.0", @@ -108,6 +109,7 @@ "eslint-plugin-import": "^2.31.0", "eslint-plugin-jest": "^28.11.0", "eslint-plugin-n": "^16.6.2", + "eslint-plugin-no-catch-all": "^1.1.0", "eslint-plugin-prettier": "^5.2.6", "eslint-plugin-promise": "^6.1.1", "eslint-plugin-security": "^3.0.1", @@ -121,7 +123,9 @@ }, "resolutions": { "**/**/fs-capacitor": "^6.2.0", - "**/graphql-upload": "^11.0.0", - "nan": "2.17.0" + "**/graphql-upload": "^11.0.0" + }, + "engines": { + "node": ">=20.12.1" } } diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 9f03622a5..e6a02a87d 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -1,11 +1,14 @@ -import dotenv from 'dotenv' +/* eslint-disable n/no-process-env */ +/* eslint-disable n/no-unpublished-require */ +/* eslint-disable n/no-missing-require */ +import { config } from 'dotenv' import emails from './emails' import metadata from './metadata' // Load env file if (require.resolve) { try { - dotenv.config({ path: require.resolve('../../.env') }) + config({ path: require.resolve('../../.env') }) } catch (error) { // This error is thrown when the .env is not found if (error.code !== 'MODULE_NOT_FOUND') { diff --git a/backend/src/db/clean.ts b/backend/src/db/clean.ts index eac26036c..ae5ce7320 100644 --- a/backend/src/db/clean.ts +++ b/backend/src/db/clean.ts @@ -1,5 +1,6 @@ +/* eslint-disable n/no-process-exit */ import CONFIG from '../config' -import { cleanDatabase } from '../db/factories' +import { cleanDatabase } from './factories' if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) { throw new Error(`You cannot clean the database in a non-staging and real production environment!`) @@ -10,6 +11,7 @@ if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) { await cleanDatabase() console.log('Successfully deleted all nodes and relations!') // eslint-disable-line no-console process.exit(0) + // eslint-disable-next-line no-catch-all/no-catch-all } catch (err) { console.log(`Error occurred deleting the nodes and relations (reset the db)\n\n${err}`) // eslint-disable-line no-console process.exit(1) diff --git a/backend/src/db/compiler.ts b/backend/src/db/compiler.ts index 8b09ac9c3..4dd36f16b 100644 --- a/backend/src/db/compiler.ts +++ b/backend/src/db/compiler.ts @@ -1,2 +1,5 @@ +/* eslint-disable import/no-commonjs */ +// eslint-disable-next-line n/no-unpublished-require const tsNode = require('ts-node') + module.exports = tsNode.register diff --git a/backend/src/db/migrate/store.ts b/backend/src/db/migrate/store.ts index 0a9788b9a..6f8d248d9 100644 --- a/backend/src/db/migrate/store.ts +++ b/backend/src/db/migrate/store.ts @@ -1,4 +1,4 @@ -import { getDriver, getNeode } from '../../db/neo4j' +import { getDriver, getNeode } from '../neo4j' import { hashSync } from 'bcryptjs' import { v4 as uuid } from 'uuid' import { categories } from '../../constants/categories' @@ -30,6 +30,7 @@ const createCategories = async (session) => { try { await createCategoriesTxResultPromise console.log('Successfully created categories!') // eslint-disable-line no-console + // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { console.log(`Error creating categories: ${error}`) // eslint-disable-line no-console } @@ -44,6 +45,7 @@ const createDefaultAdminUser = async (session) => { try { const userCount = parseInt(String(await readTxResultPromise)) if (userCount === 0) createAdmin = true + // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { console.log(error) // eslint-disable-line no-console } @@ -70,6 +72,7 @@ const createDefaultAdminUser = async (session) => { try { await createAdminTxResultPromise console.log('Successfully created default admin user!') // eslint-disable-line no-console + // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { console.log(error) // eslint-disable-line no-console } @@ -92,6 +95,7 @@ class Store { // eslint-disable-next-line no-console console.log('Successfully created database indices and constraints!') next() + // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { console.log(error) // eslint-disable-line no-console next(error, null) @@ -121,6 +125,7 @@ class Store { } const [{ title: lastRun }] = migrations next(null, { lastRun, migrations }) + // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { console.log(error) // eslint-disable-line no-console next(error) @@ -156,6 +161,7 @@ class Store { try { await writeTxResultPromise next() + // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { console.log(error) // eslint-disable-line no-console next(error) @@ -165,4 +171,4 @@ class Store { } } -module.exports = Store +export default Store diff --git a/backend/src/db/migrate/template.ts b/backend/src/db/migrate/template.ts index 72bfc9b1b..9661dcf9c 100644 --- a/backend/src/db/migrate/template.ts +++ b/backend/src/db/migrate/template.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = '' diff --git a/backend/src/db/migrations-examples/20200123150105-merge_duplicate_user_accounts.ts b/backend/src/db/migrations-examples/20200123150105-merge_duplicate_user_accounts.ts index 7d98d9dcc..6eb9e0ed0 100644 --- a/backend/src/db/migrations-examples/20200123150105-merge_duplicate_user_accounts.ts +++ b/backend/src/db/migrations-examples/20200123150105-merge_duplicate_user_accounts.ts @@ -1,7 +1,8 @@ +/* eslint-disable promise/prefer-await-to-callbacks */ import { throwError, concat } from 'rxjs' import { flatMap, mergeMap, map, catchError, filter } from 'rxjs/operators' import { getDriver } from '../neo4j' -import normalizeEmail from '../../schema/resolvers//helpers/normalizeEmail' +import normalizeEmail from '../../schema/resolvers/helpers/normalizeEmail' export const description = ` This migration merges duplicate :User and :EmailAddress nodes. It became diff --git a/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts b/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts index 10b77c6dd..23d1d55bc 100644 --- a/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts +++ b/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts @@ -1,3 +1,4 @@ +/* eslint-disable promise/prefer-await-to-callbacks */ import { throwError, concat } from 'rxjs' import { flatMap, mergeMap, map, catchError } from 'rxjs/operators' import { getDriver } from '../neo4j' diff --git a/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts b/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts index ce46be9d6..49506aae3 100644 --- a/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts +++ b/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` This migration creates a MUTED relationship between two edges(:User) that have a pre-existing BLOCKED relationship. @@ -21,6 +21,7 @@ export async function up(next) { `, ) await transaction.commit() + // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { // eslint-disable-next-line no-console console.log(error) @@ -38,6 +39,7 @@ export function down(next) { try { // Rollback your migration here. next() + // eslint-disable-next-line no-catch-all/no-catch-all } catch (err) { next(err) } finally { diff --git a/backend/src/db/migrations-examples/20200206190233-swap_latitude_with_longitude.ts b/backend/src/db/migrations-examples/20200206190233-swap_latitude_with_longitude.ts index 94a2f442d..73c329bfc 100644 --- a/backend/src/db/migrations-examples/20200206190233-swap_latitude_with_longitude.ts +++ b/backend/src/db/migrations-examples/20200206190233-swap_latitude_with_longitude.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` This migration swaps the value stored in Location.lat with the value diff --git a/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts b/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts index ffcd3d4b6..8ef6976a3 100644 --- a/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts +++ b/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = 'This migration adds a fulltext index for the tags in order to search for Hasthags.' diff --git a/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts b/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts index 4582d938c..e949713b8 100644 --- a/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts +++ b/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` We introduced a new node label 'Image' and we need a primary key for it. Best @@ -48,6 +48,7 @@ export async function down(next) { `) await transaction.commit() next() + // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { // eslint-disable-next-line no-console console.log(error) diff --git a/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts b/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts index 356004237..7818001fb 100644 --- a/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts +++ b/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts @@ -1,4 +1,5 @@ -import { getDriver } from '../../db/neo4j' +/* eslint-disable security/detect-non-literal-fs-filename */ +import { getDriver } from '../neo4j' import { existsSync, createReadStream } from 'fs' import path from 'path' import { S3 } from 'aws-sdk' @@ -95,6 +96,7 @@ export async function down(next) { await transaction.run(``) await transaction.commit() next() + // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { // eslint-disable-next-line no-console console.log(error) diff --git a/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts b/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts index 1ad5e645d..6f347b99b 100644 --- a/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts +++ b/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` Refactor all our image properties on posts and users to a dedicated type diff --git a/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts b/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts index e4852f79c..a8880d8e8 100644 --- a/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts +++ b/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = 'We should not maintain obsolete attributes for users who have been deleted.' diff --git a/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts b/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts index 8c1efe5c6..70d81e5c0 100644 --- a/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts +++ b/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = 'We should not maintain obsolete attributes for posts which have been deleted.' diff --git a/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts b/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts index a77ac360c..9d0d44f26 100644 --- a/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts +++ b/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts @@ -1,4 +1,5 @@ -import { getDriver } from '../../db/neo4j' +/* eslint-disable security/detect-non-literal-fs-filename */ +import { getDriver } from '../neo4j' import { existsSync } from 'fs' export const description = ` diff --git a/backend/src/db/migrations/1613589876420-null_mutation.ts b/backend/src/db/migrations/1613589876420-null_mutation.ts index f158549de..8efe667be 100644 --- a/backend/src/db/migrations/1613589876420-null_mutation.ts +++ b/backend/src/db/migrations/1613589876420-null_mutation.ts @@ -1,9 +1,9 @@ 'use strict' -module.exports.up = function (next) { +export async function up(next) { next() } -module.exports.down = function (next) { +export async function down(next) { next() } diff --git a/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts b/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts index ff95a25df..0d8f28e1b 100644 --- a/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts +++ b/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts @@ -1,10 +1,10 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` This migration adds the clickedCount property to all posts, setting it to 0. ` -module.exports.up = async function (next) { +export async function up(next) { const driver = getDriver() const session = driver.session() const transaction = session.beginTransaction() @@ -28,7 +28,7 @@ module.exports.up = async function (next) { } } -module.exports.down = async function (next) { +export async function down(next) { const driver = getDriver() const session = driver.session() const transaction = session.beginTransaction() diff --git a/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts b/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts index ee1fad124..31b9d69ff 100644 --- a/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts +++ b/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts @@ -1,10 +1,10 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` This migration adds the viewedTeaserCount property to all posts, setting it to 0. ` -module.exports.up = async function (next) { +export async function up(next) { const driver = getDriver() const session = driver.session() const transaction = session.beginTransaction() @@ -28,7 +28,7 @@ module.exports.up = async function (next) { } } -module.exports.down = async function (next) { +export async function down(next) { const driver = getDriver() const session = driver.session() const transaction = session.beginTransaction() diff --git a/backend/src/db/migrations/20210506150512-add-donations-node.ts b/backend/src/db/migrations/20210506150512-add-donations-node.ts index 6cbc1e897..b7e0e026a 100644 --- a/backend/src/db/migrations/20210506150512-add-donations-node.ts +++ b/backend/src/db/migrations/20210506150512-add-donations-node.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' import { v4 as uuid } from 'uuid' export const description = diff --git a/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts b/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts index 0d1f4fb91..a555efa3a 100644 --- a/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts +++ b/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = '' diff --git a/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts b/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts index 63e40c72b..586a090f4 100644 --- a/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts +++ b/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` We introduced a new node label 'Group' and we need two primary keys 'id' and 'slug' for it. diff --git a/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts b/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts index 40ebc6c2e..34cf7b7a2 100644 --- a/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts +++ b/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = '' diff --git a/backend/src/db/migrations/20230329150329-article-label-for-posts.ts b/backend/src/db/migrations/20230329150329-article-label-for-posts.ts index 3cf435203..2ca705bf4 100644 --- a/backend/src/db/migrations/20230329150329-article-label-for-posts.ts +++ b/backend/src/db/migrations/20230329150329-article-label-for-posts.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = 'Add to all existing posts the Article label' diff --git a/backend/src/db/migrations/20230608130637-add-postType-property.ts b/backend/src/db/migrations/20230608130637-add-postType-property.ts index 433577715..83c2f4ed3 100644 --- a/backend/src/db/migrations/20230608130637-add-postType-property.ts +++ b/backend/src/db/migrations/20230608130637-add-postType-property.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = 'Add postType property Article to all posts' diff --git a/backend/src/db/migrations/20231017141022-fix-event-dates.ts b/backend/src/db/migrations/20231017141022-fix-event-dates.ts index 3c4302f13..e793e173c 100644 --- a/backend/src/db/migrations/20231017141022-fix-event-dates.ts +++ b/backend/src/db/migrations/20231017141022-fix-event-dates.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` Transform event start and end date of format 'YYYY-MM-DD HH:MM:SS' in CEST diff --git a/backend/src/db/migrations/20250331130323-author-observes-own-post.ts b/backend/src/db/migrations/20250331130323-author-observes-own-post.ts index 7343d5010..026f7f29c 100644 --- a/backend/src/db/migrations/20250331130323-author-observes-own-post.ts +++ b/backend/src/db/migrations/20250331130323-author-observes-own-post.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` All authors observe their posts. diff --git a/backend/src/db/neo4j.ts b/backend/src/db/neo4j.ts index 78b52237e..dc5bf2764 100644 --- a/backend/src/db/neo4j.ts +++ b/backend/src/db/neo4j.ts @@ -1,5 +1,6 @@ +/* eslint-disable import/no-named-as-default-member */ import neo4j from 'neo4j-driver' -import CONFIG from './../config' +import CONFIG from '../config' import Neode from 'neode' import models from '../models' diff --git a/backend/src/db/seed.ts b/backend/src/db/seed.ts index bff236f64..4183b8ce5 100644 --- a/backend/src/db/seed.ts +++ b/backend/src/db/seed.ts @@ -1,10 +1,11 @@ +/* eslint-disable n/no-process-exit */ import sample from 'lodash/sample' import { createTestClient } from 'apollo-server-testing' import CONFIG from '../config' import createServer from '../server' import { faker } from '@faker-js/faker' -import Factory from '../db/factories' -import { getNeode, getDriver } from '../db/neo4j' +import Factory from './factories' +import { getNeode, getDriver } from './neo4j' import { createGroupMutation, joinGroupMutation, @@ -1565,6 +1566,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl'] await driver.close() await neode.close() process.exit(0) + // eslint-disable-next-line no-catch-all/no-catch-all } catch (err) { /* eslint-disable-next-line no-console */ console.error(err) diff --git a/backend/src/helpers/asyncForEach.ts b/backend/src/helpers/asyncForEach.ts index 5577cce14..00b0f85a3 100644 --- a/backend/src/helpers/asyncForEach.ts +++ b/backend/src/helpers/asyncForEach.ts @@ -1,3 +1,5 @@ +/* eslint-disable promise/prefer-await-to-callbacks */ +/* eslint-disable security/detect-object-injection */ /** * Provide a way to iterate for each element in an array while waiting for async functions to finish * diff --git a/backend/src/helpers/jest.ts b/backend/src/helpers/jest.ts index 09744e9f2..f1a0deb15 100644 --- a/backend/src/helpers/jest.ts +++ b/backend/src/helpers/jest.ts @@ -1,3 +1,4 @@ +/* eslint-disable promise/avoid-new */ // sometime we have to wait to check a db state by having a look into the db in a certain moment // or we wait a bit to check if we missed to set an await somewhere // see: https://www.sitepoint.com/delay-sleep-pause-wait/ diff --git a/backend/src/helpers/walkRecursive.ts b/backend/src/helpers/walkRecursive.ts index f3be67575..4937f61bb 100644 --- a/backend/src/helpers/walkRecursive.ts +++ b/backend/src/helpers/walkRecursive.ts @@ -1,3 +1,5 @@ +/* eslint-disable promise/prefer-await-to-callbacks */ +/* eslint-disable security/detect-object-injection */ /** * iterate through all fields and replace it with the callback result * @property data Array diff --git a/backend/src/jwt/decode.ts b/backend/src/jwt/decode.ts index e02dcc8d4..45888dead 100644 --- a/backend/src/jwt/decode.ts +++ b/backend/src/jwt/decode.ts @@ -1,5 +1,5 @@ import jwt from 'jsonwebtoken' -import CONFIG from './../config' +import CONFIG from '../config' export default async (driver, authorizationHeader) => { if (!authorizationHeader) return null @@ -8,6 +8,7 @@ export default async (driver, authorizationHeader) => { try { const decoded = await jwt.verify(token, CONFIG.JWT_SECRET) id = decoded.sub + // eslint-disable-next-line no-catch-all/no-catch-all } catch (err) { return null } diff --git a/backend/src/jwt/encode.spec.ts b/backend/src/jwt/encode.spec.ts index 21ebdffec..37775eb55 100644 --- a/backend/src/jwt/encode.spec.ts +++ b/backend/src/jwt/encode.spec.ts @@ -1,6 +1,6 @@ import encode from './encode' import jwt from 'jsonwebtoken' -import CONFIG from './../config' +import CONFIG from '../config' describe('encode', () => { let payload diff --git a/backend/src/jwt/encode.ts b/backend/src/jwt/encode.ts index baeb62d3d..0df81fa02 100644 --- a/backend/src/jwt/encode.ts +++ b/backend/src/jwt/encode.ts @@ -1,5 +1,5 @@ import jwt from 'jsonwebtoken' -import CONFIG from './../config' +import CONFIG from '../config' // Generate an Access Token for the given User ID export default function encode(user) { diff --git a/backend/src/middleware/hashtags/extractHashtags.ts b/backend/src/middleware/hashtags/extractHashtags.ts index 670673bf4..fc7a93d17 100644 --- a/backend/src/middleware/hashtags/extractHashtags.ts +++ b/backend/src/middleware/hashtags/extractHashtags.ts @@ -1,4 +1,5 @@ -import * as cheerio from 'cheerio' +import { load } from 'cheerio' +// eslint-disable-next-line import/extensions import { exec, build } from 'xregexp/xregexp-all.js' // formats of a Hashtag: // https://en.wikipedia.org/w/index.php?title=Hashtag&oldid=905141980#Style @@ -10,7 +11,7 @@ const regX = build('^((\\pL+[\\pL0-9]*)|([0-9]+\\pL+[\\pL0-9]*))$') export default function (content?) { if (!content) return [] - const $ = cheerio.load(content) + const $ = load(content) // We can not search for class '.hashtag', because the classes are removed at the 'xss' middleware. // But we have to know, which Hashtags are removed from the content as well, so we search for the 'a' html-tag. const ids = $('a[data-hashtag-id]') diff --git a/backend/src/middleware/hashtags/hashtagsMiddleware.ts b/backend/src/middleware/hashtags/hashtagsMiddleware.ts index 985cd3c92..76939d59d 100644 --- a/backend/src/middleware/hashtags/hashtagsMiddleware.ts +++ b/backend/src/middleware/hashtags/hashtagsMiddleware.ts @@ -1,4 +1,4 @@ -import extractHashtags from '../hashtags/extractHashtags' +import extractHashtags from './extractHashtags' const updateHashtagsOfPost = async (postId, hashtags, context) => { if (!hashtags.length) return diff --git a/backend/src/middleware/helpers/cleanHtml.ts b/backend/src/middleware/helpers/cleanHtml.ts index 72129274c..04d6deae4 100644 --- a/backend/src/middleware/helpers/cleanHtml.ts +++ b/backend/src/middleware/helpers/cleanHtml.ts @@ -1,3 +1,4 @@ +/* eslint-disable security/detect-unsafe-regex */ import sanitizeHtml from 'sanitize-html' import linkifyHtml from 'linkify-html' diff --git a/backend/src/middleware/helpers/email/sendMail.ts b/backend/src/middleware/helpers/email/sendMail.ts index c0e54e7f7..6c1e0d8ba 100644 --- a/backend/src/middleware/helpers/email/sendMail.ts +++ b/backend/src/middleware/helpers/email/sendMail.ts @@ -1,5 +1,5 @@ import CONFIG from '../../../config' -import { cleanHtml } from '../../../middleware/helpers/cleanHtml' +import { cleanHtml } from '../cleanHtml' import nodemailer from 'nodemailer' import { htmlToText } from 'nodemailer-html-to-text' diff --git a/backend/src/middleware/helpers/email/templateBuilder.spec.ts b/backend/src/middleware/helpers/email/templateBuilder.spec.ts index cb516c0a9..437672a9a 100644 --- a/backend/src/middleware/helpers/email/templateBuilder.spec.ts +++ b/backend/src/middleware/helpers/email/templateBuilder.spec.ts @@ -6,6 +6,7 @@ import { resetPasswordTemplate, wrongAccountTemplate, notificationTemplate, + chatMessageTemplate, } from './templateBuilder' const englishHint = 'English version below!' @@ -34,6 +35,12 @@ const resetPasswordTemplateData = () => ({ name: 'Mr Example', }, }) +const chatMessageTemplateData = { + email: 'test@example.org', + variables: { + name: 'Mr Example', + }, +} const wrongAccountTemplateData = () => ({ email: 'test@example.org', variables: {}, @@ -163,6 +170,31 @@ describe('templateBuilder', () => { }) }) + describe('chatMessageTemplate', () => { + describe('multi language', () => { + it('e-mail is build with all data', () => { + const subject = 'Neue Chatnachricht | New chat message' + const actionUrl = new URL('/chat', CONFIG.CLIENT_URI).toString() + const enContent = 'You have received a new chat message.' + const deContent = 'Du hast eine neue Chatnachricht erhalten.' + testEmailData(null, chatMessageTemplate, chatMessageTemplateData, [ + ...textsStandard, + { + templPropName: 'subject', + isContaining: false, + text: subject, + }, + englishHint, + actionUrl, + chatMessageTemplateData.variables.name, + enContent, + deContent, + supportUrl, + ]) + }) + }) + }) + describe('wrongAccountTemplate', () => { describe('multi language', () => { it('e-mail is build with all data', () => { diff --git a/backend/src/middleware/helpers/email/templateBuilder.ts b/backend/src/middleware/helpers/email/templateBuilder.ts index 78d7a9bf9..398cbabf9 100644 --- a/backend/src/middleware/helpers/email/templateBuilder.ts +++ b/backend/src/middleware/helpers/email/templateBuilder.ts @@ -1,3 +1,4 @@ +/* eslint-disable import/no-namespace */ import mustache from 'mustache' import CONFIG from '../../../config' import metadata from '../../../config/metadata' @@ -71,6 +72,19 @@ export const resetPasswordTemplate = ({ email, variables: { nonce, name } }) => } } +export const chatMessageTemplate = ({ email, variables: { name } }) => { + const subject = 'Neue Chatnachricht | New chat message' + const actionUrl = new URL('/chat', CONFIG.CLIENT_URI) + const renderParams = { ...defaultParams, englishHint, actionUrl, name, subject } + + return { + from, + to: email, + subject, + html: mustache.render(templates.layout, renderParams, { content: templates.chatMessage }), + } +} + export const wrongAccountTemplate = ({ email, _variables = {} }) => { const subject = 'Falsche Mailadresse? | Wrong E-mail?' const actionUrl = new URL('/password-reset/request', CONFIG.CLIENT_URI) diff --git a/backend/src/middleware/helpers/email/templates/chatMessage.html b/backend/src/middleware/helpers/email/templates/chatMessage.html new file mode 100644 index 000000000..0b1bacb08 --- /dev/null +++ b/backend/src/middleware/helpers/email/templates/chatMessage.html @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/src/middleware/helpers/email/templates/de/index.ts b/backend/src/middleware/helpers/email/templates/de/index.ts index 0f9d13c36..f29e2c485 100644 --- a/backend/src/middleware/helpers/email/templates/de/index.ts +++ b/backend/src/middleware/helpers/email/templates/de/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable security/detect-non-literal-fs-filename */ import fs from 'fs' import path from 'path' diff --git a/backend/src/middleware/helpers/email/templates/en/index.ts b/backend/src/middleware/helpers/email/templates/en/index.ts index 0f9d13c36..f29e2c485 100644 --- a/backend/src/middleware/helpers/email/templates/en/index.ts +++ b/backend/src/middleware/helpers/email/templates/en/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable security/detect-non-literal-fs-filename */ import fs from 'fs' import path from 'path' diff --git a/backend/src/middleware/helpers/email/templates/index.ts b/backend/src/middleware/helpers/email/templates/index.ts index b8ae01bdb..bcb5c2b64 100644 --- a/backend/src/middleware/helpers/email/templates/index.ts +++ b/backend/src/middleware/helpers/email/templates/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable security/detect-non-literal-fs-filename */ import fs from 'fs' import path from 'path' @@ -7,5 +8,6 @@ export const signup = readFile('./signup.html') export const passwordReset = readFile('./resetPassword.html') export const wrongAccount = readFile('./wrongAccount.html') export const emailVerification = readFile('./emailVerification.html') +export const chatMessage = readFile('./chatMessage.html') export const layout = readFile('./layout.html') diff --git a/backend/src/middleware/helpers/isUserOnline.spec.ts b/backend/src/middleware/helpers/isUserOnline.spec.ts new file mode 100644 index 000000000..bf2cb8d17 --- /dev/null +++ b/backend/src/middleware/helpers/isUserOnline.spec.ts @@ -0,0 +1,46 @@ +import { isUserOnline } from './isUserOnline' + +let user + +describe('isUserOnline', () => { + beforeEach(() => { + user = { + properties: { + lastActiveAt: null, + awaySince: null, + lastOnlineStatus: null, + }, + } + }) + describe('user has lastOnlineStatus `online`', () => { + it('returns true if he was active within the last 90 seconds', () => { + user.properties.lastOnlineStatus = 'online' + user.properties.lastActiveAt = new Date() + expect(isUserOnline(user)).toBe(true) + }) + it('returns false if he was not active within the last 90 seconds', () => { + user.properties.lastOnlineStatus = 'online' + user.properties.lastActiveAt = new Date().getTime() - 90001 + expect(isUserOnline(user)).toBe(false) + }) + }) + + describe('user has lastOnlineStatus `away`', () => { + it('returns true if he went away less then 180 seconds ago', () => { + user.properties.lastOnlineStatus = 'away' + user.properties.awaySince = new Date() + expect(isUserOnline(user)).toBe(true) + }) + it('returns false if he went away more then 180 seconds ago', () => { + user.properties.lastOnlineStatus = 'away' + user.properties.awaySince = new Date().getTime() - 180001 + expect(isUserOnline(user)).toBe(false) + }) + }) + + describe('user is freshly created and has never logged in', () => { + it('returns false', () => { + expect(isUserOnline(user)).toBe(false) + }) + }) +}) diff --git a/backend/src/middleware/helpers/isUserOnline.ts b/backend/src/middleware/helpers/isUserOnline.ts new file mode 100644 index 000000000..679953f81 --- /dev/null +++ b/backend/src/middleware/helpers/isUserOnline.ts @@ -0,0 +1,16 @@ +export const isUserOnline = (user) => { + // Is Recipient considered online + const lastActive = new Date(user.properties.lastActiveAt).getTime() + const awaySince = new Date(user.properties.awaySince).getTime() + const now = new Date().getTime() + const status = user.properties.lastOnlineStatus + if ( + // online & last active less than 1.5min -> online + (status === 'online' && now - lastActive < 90000) || + // away for less then 3min -> online + (status === 'away' && now - awaySince < 180000) + ) { + return true + } + return false +} diff --git a/backend/src/middleware/index.ts b/backend/src/middleware/index.ts index 08c872db7..8eca3c8e8 100644 --- a/backend/src/middleware/index.ts +++ b/backend/src/middleware/index.ts @@ -1,5 +1,6 @@ +/* eslint-disable security/detect-object-injection */ import { applyMiddleware } from 'graphql-middleware' -import CONFIG from './../config' +import CONFIG from '../config' import softDelete from './softDelete/softDeleteMiddleware' import sluggify from './sluggifyMiddleware' import excerpt from './excerptMiddleware' @@ -8,6 +9,7 @@ import permissions from './permissionsMiddleware' import includedFields from './includedFieldsMiddleware' import orderBy from './orderByMiddleware' import validation from './validation/validationMiddleware' +// eslint-disable-next-line import/no-cycle import notifications from './notifications/notificationsMiddleware' import hashtags from './hashtags/hashtagsMiddleware' import login from './login/loginMiddleware' diff --git a/backend/src/middleware/notifications/mentions/extractMentionedUsers.ts b/backend/src/middleware/notifications/mentions/extractMentionedUsers.ts index ff80bb77a..b7dc0fed1 100644 --- a/backend/src/middleware/notifications/mentions/extractMentionedUsers.ts +++ b/backend/src/middleware/notifications/mentions/extractMentionedUsers.ts @@ -1,8 +1,8 @@ -import * as cheerio from 'cheerio' +import { load } from 'cheerio' export default (content?) => { if (!content) return [] - const $ = cheerio.load(content) + const $ = load(content) const userIds = $('a.mention[data-mention-id]') .map((_, el) => { return $(el).attr('data-mention-id') diff --git a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts index 57354d13f..50d655484 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts @@ -1,5 +1,5 @@ import gql from 'graphql-tag' -import { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '../../db/factories' import { createTestClient } from 'apollo-server-testing' import { getNeode, getDriver } from '../../db/neo4j' import createServer, { pubsub } from '../../server' @@ -10,6 +10,23 @@ import { changeGroupMemberRoleMutation, removeUserFromGroupMutation, } from '../../graphql/groups' +import { createMessageMutation } from '../../graphql/messages' +import { createRoomMutation } from '../../graphql/rooms' + +const sendMailMock = jest.fn() +jest.mock('../helpers/email/sendMail', () => ({ + sendMail: () => sendMailMock(), +})) + +const chatMessageTemplateMock = jest.fn() +jest.mock('../helpers/email/templateBuilder', () => ({ + chatMessageTemplate: () => chatMessageTemplateMock(), +})) + +let isUserOnlineMock = jest.fn() +jest.mock('../helpers/isUserOnline', () => ({ + isUserOnline: () => isUserOnlineMock(), +})) let server, query, mutate, notifiedUser, authenticatedUser let publishSpy @@ -633,6 +650,115 @@ describe('notifications', () => { }) }) + describe('chat email notifications', () => { + let chatSender + let chatReceiver + let roomId + + beforeEach(async () => { + jest.clearAllMocks() + + chatSender = await neode.create( + 'User', + { + id: 'chatSender', + name: 'chatSender', + slug: 'chatSender', + }, + { + email: 'chatSender@example.org', + password: '1234', + }, + ) + + chatReceiver = await Factory.build( + 'user', + { id: 'chatReceiver', name: 'chatReceiver', slug: 'chatReceiver' }, + { email: 'user@example.org' }, + ) + + authenticatedUser = await chatSender.toJson() + + const room = await mutate({ + mutation: createRoomMutation(), + variables: { + userId: 'chatReceiver', + }, + }) + roomId = room.data.CreateRoom.id + }) + + describe('chatReceiver is online', () => { + it('sends no email', async () => { + isUserOnlineMock = jest.fn().mockReturnValue(true) + + await mutate({ + mutation: createMessageMutation(), + variables: { + roomId, + content: 'Some nice message to chatReceiver', + }, + }) + + expect(sendMailMock).not.toHaveBeenCalled() + expect(chatMessageTemplateMock).not.toHaveBeenCalled() + }) + }) + + describe('chatReceiver is offline', () => { + it('sends an email', async () => { + isUserOnlineMock = jest.fn().mockReturnValue(false) + + await mutate({ + mutation: createMessageMutation(), + variables: { + roomId, + content: 'Some nice message to chatReceiver', + }, + }) + + expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(chatMessageTemplateMock).toHaveBeenCalledTimes(1) + }) + }) + + describe('chatReceiver has blocked chatSender', () => { + it('sends no email', async () => { + isUserOnlineMock = jest.fn().mockReturnValue(false) + await chatReceiver.relateTo(chatSender, 'blocked') + + await mutate({ + mutation: createMessageMutation(), + variables: { + roomId, + content: 'Some nice message to chatReceiver', + }, + }) + + expect(sendMailMock).not.toHaveBeenCalled() + expect(chatMessageTemplateMock).not.toHaveBeenCalled() + }) + }) + + describe('chatReceiver has disabled email notifications', () => { + it('sends no email', async () => { + isUserOnlineMock = jest.fn().mockReturnValue(false) + await chatReceiver.update({ sendNotificationEmails: false }) + + await mutate({ + mutation: createMessageMutation(), + variables: { + roomId, + content: 'Some nice message to chatReceiver', + }, + }) + + expect(sendMailMock).not.toHaveBeenCalled() + expect(chatMessageTemplateMock).not.toHaveBeenCalled() + }) + }) + }) + describe('group notifications', () => { let groupOwner diff --git a/backend/src/middleware/notifications/notificationsMiddleware.ts b/backend/src/middleware/notifications/notificationsMiddleware.ts index 7b55ac7dd..deca37bf7 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.ts @@ -1,8 +1,11 @@ +/* eslint-disable security/detect-object-injection */ +// eslint-disable-next-line import/no-cycle import { pubsub, NOTIFICATION_ADDED } from '../../server' import extractMentionedUsers from './mentions/extractMentionedUsers' import { validateNotifyUsers } from '../validation/validationMiddleware' import { sendMail } from '../helpers/email/sendMail' -import { notificationTemplate } from '../helpers/email/templateBuilder' +import { chatMessageTemplate, notificationTemplate } from '../helpers/email/templateBuilder' +import { isUserOnline } from '../helpers/isUserOnline' const queryNotificationEmails = async (context, notificationUserIds) => { if (!(notificationUserIds && notificationUserIds.length)) return [] @@ -335,6 +338,56 @@ const notifyUsersOfComment = async (label, commentId, reason, context) => { } } +const handleCreateMessage = async (resolve, root, args, context, resolveInfo) => { + // Execute resolver + const result = await resolve(root, args, context, resolveInfo) + + // Query Parameters + const { roomId } = args + const { + user: { id: currentUserId }, + } = context + + // Find Recipient + const session = context.driver.session() + const messageRecipient = session.readTransaction(async (transaction) => { + const messageRecipientCypher = ` + MATCH (currentUser:User { id: $currentUserId })-[:CHATS_IN]->(room:Room { id: $roomId }) + MATCH (room)<-[:CHATS_IN]-(recipientUser:User)-[:PRIMARY_EMAIL]->(emailAddress:EmailAddress) + WHERE NOT recipientUser.id = $currentUserId + AND NOT (recipientUser)-[:BLOCKED]-(currentUser) + AND recipientUser.sendNotificationEmails = true + RETURN recipientUser, emailAddress {.email} + ` + const txResponse = await transaction.run(messageRecipientCypher, { + currentUserId, + roomId, + }) + + return { + user: await txResponse.records.map((record) => record.get('recipientUser'))[0], + email: await txResponse.records.map((record) => record.get('emailAddress'))[0]?.email, + } + }) + + try { + // Execute Query + const { user, email } = await messageRecipient + + // Send EMail if we found a user(not blocked) and he is not considered online + if (user && !isUserOnline(user)) { + void sendMail(chatMessageTemplate({ email, variables: { name: user.properties.name } })) + } + + // Return resolver result to client + return result + } catch (error) { + throw new Error(error) + } finally { + session.close() + } +} + export default { Mutation: { CreatePost: handleContentDataOfPost, @@ -345,5 +398,6 @@ export default { LeaveGroup: handleLeaveGroup, ChangeGroupMemberRole: handleChangeGroupMemberRole, RemoveUserFromGroup: handleRemoveUserFromGroup, + CreateMessage: handleCreateMessage, }, } diff --git a/backend/src/middleware/sentryMiddleware.ts b/backend/src/middleware/sentryMiddleware.ts index 73f393eef..ace2c4eeb 100644 --- a/backend/src/middleware/sentryMiddleware.ts +++ b/backend/src/middleware/sentryMiddleware.ts @@ -1,6 +1,7 @@ import { sentry } from 'graphql-middleware-sentry' import CONFIG from '../config' +// eslint-disable-next-line import/no-mutable-exports let sentryMiddleware: any = (resolve, root, args, context, resolveInfo) => resolve(root, args, context, resolveInfo) diff --git a/backend/src/middleware/xssMiddleware.ts b/backend/src/middleware/xssMiddleware.ts index c10997e8d..7b1b66145 100644 --- a/backend/src/middleware/xssMiddleware.ts +++ b/backend/src/middleware/xssMiddleware.ts @@ -1,5 +1,5 @@ import walkRecursive from '../helpers/walkRecursive' -import { cleanHtml } from '../middleware/helpers/cleanHtml' +import { cleanHtml } from './helpers/cleanHtml' // exclamation mark separetes field names, that should not be sanitized const fields = [ diff --git a/backend/src/models/index.ts b/backend/src/models/index.ts index f7d338684..e02cbc242 100644 --- a/backend/src/models/index.ts +++ b/backend/src/models/index.ts @@ -1,3 +1,5 @@ +/* eslint-disable n/no-missing-require */ +/* eslint-disable n/global-require */ // NOTE: We cannot use `fs` here to clean up the code. Cypress breaks on any npm // module that is not browser-compatible. Node's `fs` module is server-side only declare let Cypress: any | undefined diff --git a/backend/src/schema/resolvers/emails.ts b/backend/src/schema/resolvers/emails.ts index d705781ca..ff37948f2 100644 --- a/backend/src/schema/resolvers/emails.ts +++ b/backend/src/schema/resolvers/emails.ts @@ -2,6 +2,7 @@ import generateNonce from './helpers/generateNonce' import Resolver from './helpers/Resolver' import existingEmailAddress from './helpers/existingEmailAddress' import { UserInputError } from 'apollo-server' +// eslint-disable-next-line import/extensions import Validator from 'neode/build/Services/Validator.js' import normalizeEmail from './helpers/normalizeEmail' diff --git a/backend/src/schema/resolvers/embeds/scraper.ts b/backend/src/schema/resolvers/embeds/scraper.ts index 79dd5a368..afc2b1df6 100644 --- a/backend/src/schema/resolvers/embeds/scraper.ts +++ b/backend/src/schema/resolvers/embeds/scraper.ts @@ -1,3 +1,7 @@ +/* eslint-disable n/no-extraneous-require */ +/* eslint-disable n/global-require */ +/* eslint-disable import/no-commonjs */ +/* eslint-disable import/no-named-as-default */ import Metascraper from 'metascraper' import fetch from 'node-fetch' @@ -37,6 +41,7 @@ const fetchEmbed = async (url) => { try { const response = await fetch(endpointUrl) json = await response.json() + // eslint-disable-next-line no-catch-all/no-catch-all } catch (err) { error(`Error fetching embed data: ${err.message}`) return {} diff --git a/backend/src/schema/resolvers/helpers/Resolver.ts b/backend/src/schema/resolvers/helpers/Resolver.ts index 58d1512d7..a21893f7d 100644 --- a/backend/src/schema/resolvers/helpers/Resolver.ts +++ b/backend/src/schema/resolvers/helpers/Resolver.ts @@ -1,3 +1,4 @@ +/* eslint-disable security/detect-object-injection */ import log from './databaseLogger' export const undefinedToNullResolver = (list) => { diff --git a/backend/src/schema/resolvers/helpers/databaseLogger.ts b/backend/src/schema/resolvers/helpers/databaseLogger.ts index fac1a5c4a..98544087b 100644 --- a/backend/src/schema/resolvers/helpers/databaseLogger.ts +++ b/backend/src/schema/resolvers/helpers/databaseLogger.ts @@ -1,4 +1,6 @@ +/* eslint-disable import/no-named-as-default */ import Debug from 'debug' + const debugCypher = Debug('human-connection:neo4j:cypher') const debugStats = Debug('human-connection:neo4j:stats') diff --git a/backend/src/schema/resolvers/helpers/generateInviteCode.ts b/backend/src/schema/resolvers/helpers/generateInviteCode.ts index 5a123ff88..e3f555931 100644 --- a/backend/src/schema/resolvers/helpers/generateInviteCode.ts +++ b/backend/src/schema/resolvers/helpers/generateInviteCode.ts @@ -1,4 +1,4 @@ -import CONSTANTS_REGISTRATION from './../../../constants/registration' +import CONSTANTS_REGISTRATION from '../../../constants/registration' export default function generateInviteCode() { // 6 random numbers in [ 0, 35 ] are 36 possible numbers (10 [0-9] + 26 [A-Z]) diff --git a/backend/src/schema/resolvers/helpers/generateNonce.ts b/backend/src/schema/resolvers/helpers/generateNonce.ts index f08b3ccd6..de1294567 100644 --- a/backend/src/schema/resolvers/helpers/generateNonce.ts +++ b/backend/src/schema/resolvers/helpers/generateNonce.ts @@ -1,4 +1,4 @@ -import CONSTANTS_REGISTRATION from './../../../constants/registration' +import CONSTANTS_REGISTRATION from '../../../constants/registration' // TODO: why this is not used in resolver 'requestPasswordReset'? export default function generateNonce() { diff --git a/backend/src/schema/resolvers/images.ts b/backend/src/schema/resolvers/images.ts index 111f84888..ea596a183 100644 --- a/backend/src/schema/resolvers/images.ts +++ b/backend/src/schema/resolvers/images.ts @@ -1,4 +1,5 @@ import Resolver from './helpers/Resolver' + export default { Image: { ...Resolver('Image', { diff --git a/backend/src/schema/resolvers/images/images.spec.ts b/backend/src/schema/resolvers/images/images.spec.ts index d46972ce0..94602ccd8 100644 --- a/backend/src/schema/resolvers/images/images.spec.ts +++ b/backend/src/schema/resolvers/images/images.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable promise/prefer-await-to-callbacks */ import { deleteImage, mergeImage } from './images' import { getNeode, getDriver } from '../../../db/neo4j' import Factory, { cleanDatabase } from '../../../db/factories' @@ -90,6 +91,7 @@ describe('deleteImage', () => { }) throw new Error('Ouch!') }) + // eslint-disable-next-line no-catch-all/no-catch-all } catch (err) { // nothing has been deleted await expect(neode.all('Image')).resolves.toHaveLength(1) @@ -251,6 +253,7 @@ describe('mergeImage', () => { }) return transaction.run('Ooops invalid cypher!', { image }) }) + // eslint-disable-next-line no-catch-all/no-catch-all } catch (err) { // nothing has been created await expect(neode.all('Image')).resolves.toHaveLength(0) diff --git a/backend/src/schema/resolvers/images/images.ts b/backend/src/schema/resolvers/images/images.ts index b99b13a10..4566aa5bf 100644 --- a/backend/src/schema/resolvers/images/images.ts +++ b/backend/src/schema/resolvers/images/images.ts @@ -1,3 +1,5 @@ +/* eslint-disable promise/avoid-new */ +/* eslint-disable security/detect-non-literal-fs-filename */ import path from 'path' import { v4 as uuid } from 'uuid' import { S3 } from 'aws-sdk' diff --git a/backend/src/schema/resolvers/inviteCodes.spec.ts b/backend/src/schema/resolvers/inviteCodes.spec.ts index 1df791ba6..bd6a55bc8 100644 --- a/backend/src/schema/resolvers/inviteCodes.spec.ts +++ b/backend/src/schema/resolvers/inviteCodes.spec.ts @@ -1,9 +1,10 @@ +/* eslint-disable security/detect-non-literal-regexp */ import Factory, { cleanDatabase } from '../../db/factories' import { getDriver } from '../../db/neo4j' import gql from 'graphql-tag' import createServer from '../../server' import { createTestClient } from 'apollo-server-testing' -import CONSTANTS_REGISTRATION from './../../constants/registration' +import CONSTANTS_REGISTRATION from '../../constants/registration' let user let query diff --git a/backend/src/schema/resolvers/notifications.spec.ts b/backend/src/schema/resolvers/notifications.spec.ts index 58757c92d..e3bcb9489 100644 --- a/backend/src/schema/resolvers/notifications.spec.ts +++ b/backend/src/schema/resolvers/notifications.spec.ts @@ -2,7 +2,7 @@ import Factory, { cleanDatabase } from '../../db/factories' import gql from 'graphql-tag' import { getDriver } from '../../db/neo4j' import { createTestClient } from 'apollo-server-testing' -import createServer from '../.././server' +import createServer from '../../server' import { markAsReadMutation, markAllAsReadMutation, diff --git a/backend/src/schema/resolvers/passwordReset.spec.ts b/backend/src/schema/resolvers/passwordReset.spec.ts index 3d17ff481..d0ca3e4a8 100644 --- a/backend/src/schema/resolvers/passwordReset.spec.ts +++ b/backend/src/schema/resolvers/passwordReset.spec.ts @@ -1,7 +1,7 @@ import Factory, { cleanDatabase } from '../../db/factories' import gql from 'graphql-tag' import { getNeode, getDriver } from '../../db/neo4j' -import CONSTANTS_REGISTRATION from './../../constants/registration' +import CONSTANTS_REGISTRATION from '../../constants/registration' import createPasswordReset from './helpers/createPasswordReset' import createServer from '../../server' import { createTestClient } from 'apollo-server-testing' diff --git a/backend/src/schema/resolvers/passwordReset.ts b/backend/src/schema/resolvers/passwordReset.ts index 6fea020dd..4adca11d3 100644 --- a/backend/src/schema/resolvers/passwordReset.ts +++ b/backend/src/schema/resolvers/passwordReset.ts @@ -1,6 +1,6 @@ import { v4 as uuid } from 'uuid' import bcrypt from 'bcryptjs' -import CONSTANTS_REGISTRATION from './../../constants/registration' +import CONSTANTS_REGISTRATION from '../../constants/registration' import createPasswordReset from './helpers/createPasswordReset' export default { diff --git a/backend/src/schema/resolvers/reports.spec.ts b/backend/src/schema/resolvers/reports.spec.ts index bc47778c1..2e6b4d302 100644 --- a/backend/src/schema/resolvers/reports.spec.ts +++ b/backend/src/schema/resolvers/reports.spec.ts @@ -1,5 +1,5 @@ import { createTestClient } from 'apollo-server-testing' -import createServer from '../.././server' +import createServer from '../../server' import Factory, { cleanDatabase } from '../../db/factories' import gql from 'graphql-tag' import { getDriver, getNeode } from '../../db/neo4j' diff --git a/backend/src/schema/resolvers/statistics.ts b/backend/src/schema/resolvers/statistics.ts index b454ce8f4..6bf73b0b2 100644 --- a/backend/src/schema/resolvers/statistics.ts +++ b/backend/src/schema/resolvers/statistics.ts @@ -1,3 +1,4 @@ +/* eslint-disable security/detect-object-injection */ import log from './helpers/databaseLogger' export default { diff --git a/backend/src/schema/resolvers/user_management.spec.ts b/backend/src/schema/resolvers/user_management.spec.ts index 546c7a748..797f08126 100644 --- a/backend/src/schema/resolvers/user_management.spec.ts +++ b/backend/src/schema/resolvers/user_management.spec.ts @@ -1,5 +1,6 @@ +/* eslint-disable promise/prefer-await-to-callbacks */ import jwt from 'jsonwebtoken' -import CONFIG from './../../config' +import CONFIG from '../../config' import Factory, { cleanDatabase } from '../../db/factories' import gql from 'graphql-tag' import { loginMutation } from '../../graphql/userManagement' diff --git a/backend/src/schema/resolvers/users/location.ts b/backend/src/schema/resolvers/users/location.ts index 0c3f55595..9a8b5430b 100644 --- a/backend/src/schema/resolvers/users/location.ts +++ b/backend/src/schema/resolvers/users/location.ts @@ -1,3 +1,6 @@ +/* eslint-disable promise/avoid-new */ +/* eslint-disable promise/prefer-await-to-callbacks */ +/* eslint-disable import/no-named-as-default */ import request from 'request' import { UserInputError } from 'apollo-server' import Debug from 'debug' diff --git a/backend/src/server.ts b/backend/src/server.ts index 0522f5fc8..7451e3e4a 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -1,8 +1,10 @@ +/* eslint-disable import/no-named-as-default-member */ import express from 'express' import http from 'http' import helmet from 'helmet' import { ApolloServer } from 'apollo-server-express' import CONFIG from './config' +// eslint-disable-next-line import/no-cycle import middleware from './middleware' import { getNeode, getDriver } from './db/neo4j' import decode from './jwt/decode' diff --git a/backend/yarn.lock b/backend/yarn.lock index ab611aea5..41a80a121 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1091,6 +1091,14 @@ dependencies: tslib "^2.4.0" +"@eslint-community/eslint-plugin-eslint-comments@^4.4.1": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.4.1.tgz#dbfab6f2447c22be8758a0a9a9c80e56d2e2b93f" + integrity sha512-lb/Z/MzbTf7CaVYM9WCFNQZ4L1yi3ev2fsFPF99h31ljhSEyUoyEsKsNWiU+qD1glbYTDJdqgyaLKtyTkkqtuQ== + dependencies: + escape-string-regexp "^4.0.0" + ignore "^5.2.4" + "@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -4606,6 +4614,11 @@ eslint-plugin-n@^16.6.2: resolve "^1.22.2" semver "^7.5.3" +eslint-plugin-no-catch-all@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-no-catch-all/-/eslint-plugin-no-catch-all-1.1.0.tgz#f2e8950cc2b0bdde5faa4ab339d0986c6ae32fb0" + integrity sha512-VkP62jLTmccPrFGN/W6V7a3SEwdtTZm+Su2k4T3uyJirtkm0OMMm97h7qd8pRFAHus/jQg9FpUpLRc7sAylBEQ== + eslint-plugin-prettier@^5.2.6: version "5.2.6" resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.6.tgz#be39e3bb23bb3eeb7e7df0927cdb46e4d7945096" @@ -7353,29 +7366,29 @@ migrate@^2.1.0: mkdirp "^3.0.1" slug "^8.2.2" -mime-db@1.43.0: - version "1.43.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" - integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== - mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.35, mime-types@~2.1.19, mime-types@~2.1.34: +mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + +mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.22, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" -mime-types@~2.1.22, mime-types@~2.1.24: - version "2.1.26" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" - integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== +mime-types@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.1.tgz#b1d94d6997a9b32fd69ebaed0db73de8acb519ce" + integrity sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA== dependencies: - mime-db "1.43.0" + mime-db "^1.54.0" mime@1.6.0: version "1.6.0" @@ -7534,10 +7547,10 @@ n-gram@^1.0.0: resolved "https://registry.yarnpkg.com/n-gram/-/n-gram-1.1.1.tgz#a374dc176a9063a2388d1be18ed7c35828be2a97" integrity sha512-qibRqvUghLIVsq+RTwVuwOzgOxf0l4DDZKVYAK0bMam5sG9ZzaJ6BUSJyG2Td8kTc7c/HcMUtjiN5ShobZA2bA== -nan@2.17.0, nan@^2.20.0: - version "2.17.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" - integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== +nan@^2.20.0: + version "2.22.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.2.tgz#6b504fd029fb8f38c0990e52ad5c26772fdacfbb" + integrity sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ== nanoid@^3.3.6: version "3.3.7" diff --git a/package-lock.json b/package-lock.json index 5a083c2c1..0b708bace 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@badeball/cypress-cucumber-preprocessor": "^22.0.1", "@cucumber/cucumber": "11.2.0", "@cypress/browserify-preprocessor": "^3.0.2", - "@faker-js/faker": "9.5.0", + "@faker-js/faker": "9.6.0", "auto-changelog": "^2.5.0", "bcryptjs": "^2.4.3", "cross-env": "^7.0.3", @@ -2786,9 +2786,9 @@ } }, "node_modules/@faker-js/faker": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.5.0.tgz", - "integrity": "sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw==", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.6.0.tgz", + "integrity": "sha512-3vm4by+B5lvsFPSyep3ELWmZfE3kicDtmemVpuwl1yH7tqtnHdsA6hG8fbXedMVdkzgtvzWoRgjSB4Q+FHnZiw==", "dev": true, "funding": [ { diff --git a/package.json b/package.json index b90501c4b..dd27c53ca 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@badeball/cypress-cucumber-preprocessor": "^22.0.1", "@cucumber/cucumber": "11.2.0", "@cypress/browserify-preprocessor": "^3.0.2", - "@faker-js/faker": "9.5.0", + "@faker-js/faker": "9.6.0", "auto-changelog": "^2.5.0", "bcryptjs": "^2.4.3", "cross-env": "^7.0.3", diff --git a/webapp/package.json b/webapp/package.json index edcc620eb..5c1d946fc 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -48,7 +48,7 @@ "nuxt": "~2.12.1", "nuxt-dropzone": "^1.0.4", "nuxt-env": "~0.1.0", - "sass": "^1.85.0", + "sass": "^1.86.3", "stack-utils": "^2.0.3", "tippy.js": "^4.3.5", "tiptap": "~1.26.6", @@ -56,7 +56,7 @@ "trunc-html": "^1.1.2", "v-mapbox": "^1.11.2", "v-tooltip": "~2.1.3", - "validator": "^13.12.0", + "validator": "^13.15.0", "vue-advanced-chat": "^2.0.11", "vue-count-to": "~1.0.13", "vue-infinite-loading": "^2.4.5", @@ -73,7 +73,7 @@ "@babel/core": "^7.25.8", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/preset-env": "^7.25.8", - "@faker-js/faker": "9.5.0", + "@faker-js/faker": "9.6.0", "@storybook/addon-a11y": "^8.0.8", "@storybook/addon-actions": "^5.3.21", "@storybook/addon-notes": "^5.3.18", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index f318a66d2..87e849383 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -2447,10 +2447,10 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@faker-js/faker@9.5.0": - version "9.5.0" - resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.5.0.tgz#ce254c83706250ca8a5a0e05683608160610dd84" - integrity sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw== +"@faker-js/faker@9.6.0": + version "9.6.0" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.6.0.tgz#64235d20330b142eef3d1d1638ba56c083b4bf1d" + integrity sha512-3vm4by+B5lvsFPSyep3ELWmZfE3kicDtmemVpuwl1yH7tqtnHdsA6hG8fbXedMVdkzgtvzWoRgjSB4Q+FHnZiw== "@hapi/address@2.x.x": version "2.0.0" @@ -17426,10 +17426,10 @@ sass-resources-loader@^2.2.1: glob "^7.1.6" loader-utils "^2.0.0" -sass@^1.85.0: - version "1.85.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.85.0.tgz#0127ef697d83144496401553f0a0e87be83df45d" - integrity sha512-3ToiC1xZ1Y8aU7+CkgCI/tqyuPXEmYGJXO7H4uqp0xkLXUqp88rQQ4j1HmP37xSJLbCJPaIiv+cT1y+grssrww== +sass@^1.86.3: + version "1.86.3" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.86.3.tgz#0a0d9ea97cb6665e73f409639f8533ce057464c9" + integrity sha512-iGtg8kus4GrsGLRDLRBRHY9dNVA78ZaS7xr01cWnS7PEMQyFtTqBiyCrfpTYTZXRWM94akzckYjh8oADfFNTzw== dependencies: chokidar "^4.0.0" immutable "^5.0.2" @@ -17823,12 +17823,7 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" - integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== - -source-map-js@^1.0.2, source-map-js@^1.2.0: +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2, source-map-js@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== @@ -19666,10 +19661,10 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -validator@^13.12.0: - version "13.12.0" - resolved "https://registry.yarnpkg.com/validator/-/validator-13.12.0.tgz#7d78e76ba85504da3fee4fd1922b385914d4b35f" - integrity sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg== +validator@^13.15.0: + version "13.15.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.0.tgz#2dc7ce057e7513a55585109eec29b2c8e8c1aefd" + integrity sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA== vary@^1, vary@^1.1.2, vary@~1.1.2: version "1.1.2" diff --git a/yarn.lock b/yarn.lock index f01c83a18..4e1450d9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1543,10 +1543,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz#81fd50d11e2c32b2d6241470e3185b70c7b30699" integrity sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg== -"@faker-js/faker@9.5.0": - version "9.5.0" - resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.5.0.tgz#ce254c83706250ca8a5a0e05683608160610dd84" - integrity sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw== +"@faker-js/faker@9.6.0": + version "9.6.0" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.6.0.tgz#64235d20330b142eef3d1d1638ba56c083b4bf1d" + integrity sha512-3vm4by+B5lvsFPSyep3ELWmZfE3kicDtmemVpuwl1yH7tqtnHdsA6hG8fbXedMVdkzgtvzWoRgjSB4Q+FHnZiw== "@fastify/busboy@^2.0.0": version "2.1.1"