mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge branch 'master' into backend-email-notification-settings
This commit is contained in:
commit
1db7fbbc65
6
.github/workflows/docker-push.yml
vendored
6
.github/workflows/docker-push.yml
vendored
@ -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 }}
|
||||
|
||||
209
backend/.eslintrc.cjs
Normal file
209
backend/.eslintrc.cjs
Normal file
@ -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',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -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',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@ -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') {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { getDriver } from '../../db/neo4j'
|
||||
import { getDriver } from '../neo4j'
|
||||
|
||||
export const description = ''
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.'
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.'
|
||||
|
||||
@ -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.'
|
||||
|
||||
@ -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 = `
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { getDriver } from '../../db/neo4j'
|
||||
import { getDriver } from '../neo4j'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
export const description =
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { getDriver } from '../../db/neo4j'
|
||||
import { getDriver } from '../neo4j'
|
||||
|
||||
export const description = ''
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { getDriver } from '../../db/neo4j'
|
||||
import { getDriver } from '../neo4j'
|
||||
|
||||
export const description = ''
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { getDriver } from '../../db/neo4j'
|
||||
import { getDriver } from '../neo4j'
|
||||
|
||||
export const description = 'Add to all existing posts the Article label'
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { getDriver } from '../../db/neo4j'
|
||||
import { getDriver } from '../neo4j'
|
||||
|
||||
export const description = 'Add postType property Article to all posts'
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { getDriver } from '../../db/neo4j'
|
||||
import { getDriver } from '../neo4j'
|
||||
|
||||
export const description = `
|
||||
All authors observe their posts.
|
||||
|
||||
@ -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'
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
*
|
||||
|
||||
@ -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/
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import encode from './encode'
|
||||
import jwt from 'jsonwebtoken'
|
||||
import CONFIG from './../config'
|
||||
import CONFIG from '../config'
|
||||
|
||||
describe('encode', () => {
|
||||
let payload
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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]')
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import extractHashtags from '../hashtags/extractHashtags'
|
||||
import extractHashtags from './extractHashtags'
|
||||
|
||||
const updateHashtagsOfPost = async (postId, hashtags, context) => {
|
||||
if (!hashtags.length) return
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/* eslint-disable security/detect-unsafe-regex */
|
||||
import sanitizeHtml from 'sanitize-html'
|
||||
import linkifyHtml from 'linkify-html'
|
||||
|
||||
|
||||
@ -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'
|
||||
|
||||
|
||||
@ -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', () => {
|
||||
|
||||
@ -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)
|
||||
|
||||
105
backend/src/middleware/helpers/email/templates/chatMessage.html
Normal file
105
backend/src/middleware/helpers/email/templates/chatMessage.html
Normal file
@ -0,0 +1,105 @@
|
||||
<!-- Email Body German : BEGIN -->
|
||||
<table class="email-german" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
|
||||
style="margin: auto;">
|
||||
|
||||
<!-- Hero Image, Flush : BEGIN -->
|
||||
<tr>
|
||||
<td style="background-color: #ffffff;">
|
||||
<img
|
||||
src="{{{ welcomeImageUrl }}}"
|
||||
width="300" height="" alt="Welcome image" border="0"
|
||||
style="width: 100%; max-width: 300px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block; padding: 20px;"
|
||||
class="g-img">
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Hero Image, Flush : END -->
|
||||
|
||||
<!-- 1 Column Text + Button : BEGIN -->
|
||||
<tr>
|
||||
<td style="background-color: #ffffff; padding: 20px;">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td
|
||||
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
|
||||
<h1
|
||||
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
|
||||
Hallo {{ name }}!</h1>
|
||||
<p style="margin: 0;">Du hast eine neue Chatnachricht erhalten.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 0 20px;">
|
||||
<!-- Button : BEGIN -->
|
||||
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;">
|
||||
<tr>
|
||||
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
|
||||
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
|
||||
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">Chat anzeigen</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- Button : END -->
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 1 Column Text + Button : END -->
|
||||
|
||||
</table>
|
||||
<!-- Email Body German : END -->
|
||||
|
||||
<!-- Email Body English : BEGIN -->
|
||||
<table class="email-english" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"
|
||||
style="margin: auto;">
|
||||
<tr>
|
||||
<td style="padding: 20px 0; text-align: center">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Hero Image, Flush : BEGIN -->
|
||||
<tr>
|
||||
<td style="background-color: #ffffff;">
|
||||
<img
|
||||
src="{{{ welcomeImageUrl }}}"
|
||||
width="300" height="" alt="Welcome image" border="0"
|
||||
style="width: 100%; max-width: 300px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block; padding: 20px;"
|
||||
class="g-img">
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Hero Image, Flush : END -->
|
||||
|
||||
<!-- 1 Column Text + Button : BEGIN -->
|
||||
<tr>
|
||||
<td style="background-color: #ffffff; padding: 20px;">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td
|
||||
style="padding: 20px; padding-top: 0; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
|
||||
<h1
|
||||
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
|
||||
Hello {{ name }}!</h1>
|
||||
<p style="margin: 0;">You have received a new chat message.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 0 20px;">
|
||||
<!-- Button : BEGIN -->
|
||||
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;">
|
||||
<tr>
|
||||
<td class="button-td button-td-primary" style="border-radius: 4px; background: #17b53e;">
|
||||
<a class="button-a button-a-primary" href="{{{ actionUrl }}}"
|
||||
style="background: #17b53e; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; text-decoration: none; padding: 13px 17px; color: #ffffff; display: block; border-radius: 4px;">Show Chat</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- Button : END -->
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 1 Column Text + Button : END -->
|
||||
|
||||
</table>
|
||||
<!-- Email Body English : END -->
|
||||
@ -1,3 +1,4 @@
|
||||
/* eslint-disable security/detect-non-literal-fs-filename */
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/* eslint-disable security/detect-non-literal-fs-filename */
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
|
||||
@ -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')
|
||||
|
||||
46
backend/src/middleware/helpers/isUserOnline.spec.ts
Normal file
46
backend/src/middleware/helpers/isUserOnline.spec.ts
Normal file
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
16
backend/src/middleware/helpers/isUserOnline.ts
Normal file
16
backend/src/middleware/helpers/isUserOnline.ts
Normal file
@ -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
|
||||
}
|
||||
@ -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'
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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 = [
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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'
|
||||
|
||||
|
||||
@ -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 {}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/* eslint-disable security/detect-object-injection */
|
||||
import log from './databaseLogger'
|
||||
|
||||
export const undefinedToNullResolver = (list) => {
|
||||
|
||||
@ -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')
|
||||
|
||||
|
||||
@ -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])
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import Resolver from './helpers/Resolver'
|
||||
|
||||
export default {
|
||||
Image: {
|
||||
...Resolver('Image', {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/* eslint-disable security/detect-object-injection */
|
||||
import log from './helpers/databaseLogger'
|
||||
|
||||
export default {
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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"
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@ -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": [
|
||||
{
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user