From 54c7f70976448fc16fd33878f17d1e642e7262b0 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sat, 22 Nov 2025 12:40:05 +0100 Subject: [PATCH 01/22] change import to type, import actually entity later, speedup database test with bun --- bun.lock | 41 +++++++++--- database/bunfig.toml | 2 + database/package.json | 16 ++--- database/src/entity/Community.ts | 18 +++--- database/src/entity/Contribution.ts | 24 +++---- database/src/entity/ContributionMessage.ts | 16 ++--- database/src/entity/DltTransaction.ts | 24 +++---- database/src/entity/Event.ts | 44 ++++++------- database/src/entity/TransactionLink.ts | 24 +++---- database/src/entity/User.ts | 64 +++++++++---------- .../entity/transformer/GeometryTransformer.ts | 2 +- database/src/queries/communities.test.ts | 12 ++-- .../src/queries/communityHandshakes.test.ts | 1 - .../src/queries/pendingTransactions.test.ts | 1 - database/src/queries/user.test.ts | 3 +- database/vitest.config.js | 7 -- 16 files changed, 158 insertions(+), 141 deletions(-) create mode 100644 database/bunfig.toml delete mode 100644 database/vitest.config.js diff --git a/bun.lock b/bun.lock index 7537ba4fb..0ac634ee8 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "gradido", @@ -213,7 +214,7 @@ }, "database": { "name": "database", - "version": "2.7.1", + "version": "2.7.0", "dependencies": { "@types/uuid": "^8.3.4", "cross-env": "^7.0.3", @@ -221,9 +222,10 @@ "dotenv": "^10.0.0", "esbuild": "^0.25.2", "geojson": "^0.5.0", + "ioredis": "^5.8.2", "log4js": "^6.9.1", "mysql": "^2.18.1", - "mysql2": "^2.3.0", + "mysql2": "^3.15.3", "reflect-metadata": "^0.1.13", "shared": "*", "source-map-support": "^0.5.21", @@ -250,7 +252,6 @@ "ts-jest": "27.0.5", "ts-node": "^10.9.2", "typescript": "^4.9.5", - "vitest": "^2.0.5", }, }, "dht-node": { @@ -774,6 +775,8 @@ "@intlify/vue-i18n-extensions": ["@intlify/vue-i18n-extensions@8.0.0", "", { "dependencies": { "@babel/parser": "^7.24.6", "@intlify/shared": "^10.0.0", "@vue/compiler-dom": "^3.2.45", "vue-i18n": "^10.0.0" }, "peerDependencies": { "vue": "^3.0.0" }, "optionalPeers": ["vue"] }, "sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ=="], + "@ioredis/commands": ["@ioredis/commands@1.4.0", "", {}, "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ=="], + "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="], "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="], @@ -1594,6 +1597,8 @@ "await-semaphore": ["await-semaphore@0.1.3", "", {}, "sha512-d1W2aNSYcz/sxYO4pMGX9vq65qOTu0P800epMud+6cYYX0QcT7zyqcxec3VWzpgvdXo57UWmVbZpLMjX2m1I7Q=="], + "aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="], + "axios": ["axios@0.21.4", "", { "dependencies": { "follow-redirects": "^1.14.0" } }, "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg=="], "b4a": ["b4a@1.7.3", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q=="], @@ -1744,6 +1749,8 @@ "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + "cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="], + "co": ["co@4.6.0", "", {}, "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ=="], "collect-v8-coverage": ["collect-v8-coverage@1.0.3", "", {}, "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw=="], @@ -2322,7 +2329,7 @@ "i18n-locales": ["i18n-locales@0.0.5", "", { "dependencies": { "@ladjs/country-language": "^0.2.1" } }, "sha512-Kve1AHy6rqyfJHPy8MIvaKBKhHhHPXV+a/TgMkjp3UBhO3gfWR40ZQn8Xy7LI6g3FhmbvkFtv+GCZy6yvuyeHQ=="], - "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "iconv-lite": ["iconv-lite@0.7.0", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="], "identity-obj-proxy": ["identity-obj-proxy@3.0.0", "", { "dependencies": { "harmony-reflect": "^1.4.6" } }, "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA=="], @@ -2354,6 +2361,8 @@ "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], + "ioredis": ["ioredis@5.8.2", "", { "dependencies": { "@ioredis/commands": "1.4.0", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q=="], + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], @@ -2638,8 +2647,12 @@ "lodash.clonedeep": ["lodash.clonedeep@4.5.0", "", {}, "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="], + "lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="], + "lodash.get": ["lodash.get@4.4.2", "", {}, "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="], + "lodash.isarguments": ["lodash.isarguments@3.1.0", "", {}, "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="], + "lodash.memoize": ["lodash.memoize@4.1.2", "", {}, "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag=="], "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], @@ -2652,7 +2665,7 @@ "loglevel": ["loglevel@1.9.2", "", {}, "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg=="], - "long": ["long@4.0.0", "", {}, "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="], + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], @@ -2664,6 +2677,8 @@ "lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], + "lru.min": ["lru.min@1.1.3", "", {}, "sha512-Lkk/vx6ak3rYkRR0Nhu4lFUT2VDnQSxBe8Hbl7f36358p6ow8Bnvr8lrLt98H8J1aGxfhbX4Fs5tYg2+FTwr5Q=="], + "magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="], "magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], @@ -2740,7 +2755,7 @@ "mysql": ["mysql@2.18.1", "", { "dependencies": { "bignumber.js": "9.0.0", "readable-stream": "2.3.7", "safe-buffer": "5.1.2", "sqlstring": "2.3.1" } }, "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig=="], - "mysql2": ["mysql2@2.3.3", "", { "dependencies": { "denque": "^2.0.1", "generate-function": "^2.3.1", "iconv-lite": "^0.6.3", "long": "^4.0.0", "lru-cache": "^6.0.0", "named-placeholders": "^1.1.2", "seq-queue": "^0.0.5", "sqlstring": "^2.3.2" } }, "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA=="], + "mysql2": ["mysql2@3.15.3", "", { "dependencies": { "aws-ssl-profiles": "^1.1.1", "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.7.0", "long": "^5.2.1", "lru.min": "^1.0.0", "named-placeholders": "^1.1.3", "seq-queue": "^0.0.5", "sqlstring": "^2.3.2" } }, "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg=="], "named-placeholders": ["named-placeholders@1.1.3", "", { "dependencies": { "lru-cache": "^7.14.1" } }, "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w=="], @@ -3036,6 +3051,10 @@ "record-cache": ["record-cache@1.2.0", "", { "dependencies": { "b4a": "^1.3.1" } }, "sha512-kyy3HWCez2WrotaL3O4fTn0rsIdfRKOdQQcEJ9KpvmKmbffKVvwsloX063EgRUlpJIXHiDQFhJcTbZequ2uTZw=="], + "redis-errors": ["redis-errors@1.2.0", "", {}, "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="], + + "redis-parser": ["redis-parser@3.0.0", "", { "dependencies": { "redis-errors": "^1.0.0" } }, "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A=="], + "reflect-metadata": ["reflect-metadata@0.1.14", "", {}, "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A=="], "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="], @@ -3202,6 +3221,8 @@ "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + "standard-as-callback": ["standard-as-callback@2.1.0", "", {}, "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="], + "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], @@ -3636,6 +3657,8 @@ "@apollo/protobufjs/@types/node": ["@types/node@10.17.60", "", {}, "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="], + "@apollo/protobufjs/long": ["long@4.0.0", "", {}, "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="], + "@apollographql/graphql-upload-8-fork/http-errors": ["http-errors@1.8.1", "", { "dependencies": { "depd": "~1.1.2", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.1" } }, "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g=="], "@asamuzakjp/css-color/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], @@ -4076,14 +4099,14 @@ "juice/commander": ["commander@6.2.1", "", {}, "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="], + "libmime/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "local-pkg/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], "loose-envify/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "mailparser/html-to-text": ["html-to-text@9.0.5", "", { "dependencies": { "@selderee/plugin-htmlparser2": "^0.11.0", "deepmerge": "^4.3.1", "dom-serializer": "^2.0.0", "htmlparser2": "^8.0.2", "selderee": "^0.11.0" } }, "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg=="], - "mailparser/iconv-lite": ["iconv-lite@0.7.0", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="], - "mailparser/nodemailer": ["nodemailer@7.0.9", "", {}, "sha512-9/Qm0qXIByEP8lEV2qOqcAW7bRpL8CR9jcTwk3NBnHJNmP9fIJ86g2fgmIXqHY+nj55ZEMwWqYAT2QTDpRUYiQ=="], "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -4282,6 +4305,8 @@ "webpack/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="], + "whatwg-encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "which-builtin-type/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], "wkx/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], diff --git a/database/bunfig.toml b/database/bunfig.toml new file mode 100644 index 000000000..6e74bf7ca --- /dev/null +++ b/database/bunfig.toml @@ -0,0 +1,2 @@ +[test] +preload = ["../config-schema/test/testSetup.bun.ts"] diff --git a/database/package.json b/database/package.json index 73cfa260b..ed7aa56e4 100644 --- a/database/package.json +++ b/database/package.json @@ -1,6 +1,6 @@ { "name": "database", - "version": "2.7.1", + "version": "2.7.0", "description": "Gradido Database Tool to execute database migrations", "main": "./build/index.js", "types": "./src/index.ts", @@ -15,13 +15,13 @@ "license": "Apache-2.0", "private": true, "scripts": { - "build": "tsx ./esbuild.config.ts", + "build": "bun ./esbuild.config.ts", "typecheck": "tsc --noEmit", "lint": "biome check --error-on-warnings .", "lint:fix": "biome check --error-on-warnings . --write", "clearDB": "cross-env TZ=UTC tsx migration/index.ts clear", - "test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test vitest --reporter verbose --no-file-parallelism run", - "test:debug": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test node --inspect-brk node_modules/.bin/jest --bail --runInBand --forceExit --detectOpenHandles", + "test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test bun test", + "test:debug": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test bun test --inspect-brk", "up": "cross-env TZ=UTC tsx migration/index.ts up", "down": "cross-env TZ=UTC tsx migration/index.ts down", "reset": "cross-env TZ=UTC tsx migration/index.ts reset", @@ -39,16 +39,12 @@ "@swc/helpers": "^0.5.17", "@types/faker": "^5.5.9", "@types/geojson": "^7946.0.13", - "@types/jest": "27.0.2", "@types/mysql": "^2.15.27", "@types/node": "^18.7.14", "await-semaphore": "^0.1.3", "crypto-random-bigint": "^2.1.1", - "jest": "27.2.4", - "ts-jest": "27.0.5", "ts-node": "^10.9.2", - "typescript": "^4.9.5", - "vitest": "^2.0.5" + "typescript": "^4.9.5" }, "dependencies": { "@types/uuid": "^8.3.4", @@ -59,7 +55,7 @@ "geojson": "^0.5.0", "log4js": "^6.9.1", "mysql": "^2.18.1", - "mysql2": "^2.3.0", + "mysql2": "^3.15.3", "reflect-metadata": "^0.1.13", "shared": "*", "source-map-support": "^0.5.21", diff --git a/database/src/entity/Community.ts b/database/src/entity/Community.ts index f6597306a..e21226d44 100644 --- a/database/src/entity/Community.ts +++ b/database/src/entity/Community.ts @@ -3,14 +3,14 @@ import { Column, CreateDateColumn, Entity, - Geometry, + type Geometry, JoinColumn, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm' -import { FederatedCommunity } from './FederatedCommunity' -import { User } from './User' +import { type FederatedCommunity as FederatedCommunityType } from './FederatedCommunity' +import { type User as UserType } from './User' import { GeometryTransformer } from './transformer/GeometryTransformer' @Entity('communities') @@ -95,16 +95,16 @@ export class Community extends BaseEntity { updatedAt: Date | null @OneToMany( - () => User, - (user) => user.community, + () => require('./User').User, + (user: UserType) => user.community, ) @JoinColumn({ name: 'community_uuid', referencedColumnName: 'communityUuid' }) - users: User[] + users: UserType[] @OneToMany( - () => FederatedCommunity, - (federatedCommunity) => federatedCommunity.community, + () => require('./FederatedCommunity').FederatedCommunity, + (federatedCommunity: FederatedCommunityType) => federatedCommunity.community, ) @JoinColumn({ name: 'public_key', referencedColumnName: 'publicKey' }) - federatedCommunities?: FederatedCommunity[] + federatedCommunities?: FederatedCommunityType[] } diff --git a/database/src/entity/Contribution.ts b/database/src/entity/Contribution.ts index 976385263..242eae06f 100644 --- a/database/src/entity/Contribution.ts +++ b/database/src/entity/Contribution.ts @@ -10,9 +10,9 @@ import { OneToOne, PrimaryGeneratedColumn, } from 'typeorm' -import { ContributionMessage } from './ContributionMessage' -import { Transaction } from './Transaction' -import { User } from './User' +import { type ContributionMessage as ContributionMessageType } from './ContributionMessage' +import { type Transaction as TransactionType } from './Transaction' +import { type User as UserType } from './User' import { DecimalTransformer } from './transformer/DecimalTransformer' @Entity('contributions') @@ -24,11 +24,11 @@ export class Contribution extends BaseEntity { userId: number @ManyToOne( - () => User, - (user) => user.contributions, + () => require('./User').User, + (user: UserType) => user.contributions, ) @JoinColumn({ name: 'user_id' }) - user: User + user: UserType @Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP', name: 'created_at' }) createdAt: Date @@ -103,16 +103,16 @@ export class Contribution extends BaseEntity { deletedBy: number @OneToMany( - () => ContributionMessage, - (message) => message.contribution, + () => require('./ContributionMessage').ContributionMessage, + (message: ContributionMessageType) => message.contribution, ) @JoinColumn({ name: 'contribution_id' }) - messages?: ContributionMessage[] + messages?: ContributionMessageType[] @OneToOne( - () => Transaction, - (transaction) => transaction.contribution, + () => require('./Transaction').Transaction, + (transaction: TransactionType) => transaction.contribution, ) @JoinColumn({ name: 'transaction_id' }) - transaction?: Transaction | null + transaction?: TransactionType | null } diff --git a/database/src/entity/ContributionMessage.ts b/database/src/entity/ContributionMessage.ts index 1b76243e8..b6fde3815 100644 --- a/database/src/entity/ContributionMessage.ts +++ b/database/src/entity/ContributionMessage.ts @@ -10,8 +10,8 @@ import { PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm' -import { Contribution } from './Contribution' -import { User } from './User' +import { type Contribution as ContributionType } from './Contribution' +import { type User as UserType } from './User' @Entity('contribution_messages', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci', @@ -25,21 +25,21 @@ export class ContributionMessage extends BaseEntity { contributionId: number @ManyToOne( - () => Contribution, - (contribution) => contribution.messages, + () => require('./Contribution').Contribution, + (contribution: ContributionType) => contribution.messages, ) @JoinColumn({ name: 'contribution_id' }) - contribution: Contribution + contribution: ContributionType @Column({ name: 'user_id', type: 'bigint', unsigned: true, nullable: false }) userId: number @ManyToOne( - () => User, - (user) => user.messages, + () => require('./User').User, + (user: UserType) => user.messages, ) @JoinColumn({ name: 'user_id' }) - user: User + user: UserType @Column({ type: 'varchar', length: 2000, nullable: false, collation: 'utf8mb4_unicode_ci' }) message: string diff --git a/database/src/entity/DltTransaction.ts b/database/src/entity/DltTransaction.ts index 2df3fb92c..46cccd0f6 100644 --- a/database/src/entity/DltTransaction.ts +++ b/database/src/entity/DltTransaction.ts @@ -1,7 +1,7 @@ import { BaseEntity, Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from 'typeorm' -import { Transaction } from './Transaction' -import { TransactionLink } from './TransactionLink' -import { User } from './User' +import { type Transaction as TransactionType } from './Transaction' +import { type TransactionLink as TransactionLinkType } from './TransactionLink' +import { type User as UserType } from './User' @Entity('dlt_transactions', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) export class DltTransaction extends BaseEntity { @@ -49,23 +49,23 @@ export class DltTransaction extends BaseEntity { error: string | null @OneToOne( - () => Transaction, - (transaction) => transaction.dltTransaction, + () => require('./Transaction').Transaction, + (transaction: TransactionType) => transaction.dltTransaction, ) @JoinColumn({ name: 'transaction_id' }) - transaction?: Transaction | null + transaction?: TransactionType | null @OneToOne( - () => User, - (user) => user.dltTransaction, + () => require('./User').User, + (user: UserType) => user.dltTransaction, ) @JoinColumn({ name: 'user_id' }) - user?: User | null + user?: UserType | null @OneToOne( - () => TransactionLink, - (transactionLink) => transactionLink.dltTransaction, + () => require('./TransactionLink').TransactionLink, + (transactionLink: TransactionLinkType) => transactionLink.dltTransaction, ) @JoinColumn({ name: 'transaction_link_id' }) - transactionLink?: TransactionLink | null + transactionLink?: TransactionLinkType | null } diff --git a/database/src/entity/Event.ts b/database/src/entity/Event.ts index 9d17ffdeb..4adbe6e51 100644 --- a/database/src/entity/Event.ts +++ b/database/src/entity/Event.ts @@ -8,12 +8,12 @@ import { ManyToOne, PrimaryGeneratedColumn, } from 'typeorm' -import { Contribution } from './Contribution' -import { ContributionLink } from './ContributionLink' -import { ContributionMessage } from './ContributionMessage' -import { Transaction } from './Transaction' -import { TransactionLink } from './TransactionLink' -import { User } from './User' +import { type Contribution as ContributionType } from './Contribution' +import { type ContributionLink as ContributionLinkType } from './ContributionLink' +import { type ContributionMessage as ContributionMessageType } from './ContributionMessage' +import { type Transaction as TransactionType } from './Transaction' +import { type TransactionLink as TransactionLinkType } from './TransactionLink' +import { type User as UserType } from './User' import { DecimalTransformer } from './transformer/DecimalTransformer' @Entity('events') @@ -36,37 +36,37 @@ export class Event extends BaseEntity { @Column({ name: 'affected_user_id', type: 'bigint', unsigned: true, nullable: false }) affectedUserId: number - @ManyToOne(() => User) + @ManyToOne(() => require('./User').User) @JoinColumn({ name: 'affected_user_id', referencedColumnName: 'id' }) - affectedUser: User + affectedUser: UserType @Column({ name: 'acting_user_id', type: 'bigint', unsigned: true, nullable: false }) actingUserId: number - @ManyToOne(() => User) + @ManyToOne(() => require('./User').User) @JoinColumn({ name: 'acting_user_id', referencedColumnName: 'id' }) - actingUser: User + actingUser: UserType @Column({ name: 'involved_user_id', type: 'bigint', unsigned: true, nullable: true }) involvedUserId: number | null - @ManyToOne(() => User) + @ManyToOne(() => require('./User').User) @JoinColumn({ name: 'involved_user_id', referencedColumnName: 'id' }) - involvedUser: User | null + involvedUser: UserType | null @Column({ name: 'involved_transaction_id', type: 'bigint', unsigned: true, nullable: true }) involvedTransactionId: number | null - @ManyToOne(() => Transaction) + @ManyToOne(() => require('./Transaction').Transaction) @JoinColumn({ name: 'involved_transaction_id', referencedColumnName: 'id' }) - involvedTransaction: Transaction | null + involvedTransaction: TransactionType | null @Column({ name: 'involved_contribution_id', type: 'bigint', unsigned: true, nullable: true }) involvedContributionId: number | null - @ManyToOne(() => Contribution) + @ManyToOne(() => require('./Contribution').Contribution) @JoinColumn({ name: 'involved_contribution_id', referencedColumnName: 'id' }) - involvedContribution: Contribution | null + involvedContribution: ContributionType | null @Column({ name: 'involved_contribution_message_id', @@ -76,23 +76,23 @@ export class Event extends BaseEntity { }) involvedContributionMessageId: number | null - @ManyToOne(() => ContributionMessage) + @ManyToOne(() => require('./ContributionMessage').ContributionMessage) @JoinColumn({ name: 'involved_contribution_message_id', referencedColumnName: 'id' }) - involvedContributionMessage: ContributionMessage | null + involvedContributionMessage: ContributionMessageType | null @Column({ name: 'involved_transaction_link_id', type: 'bigint', unsigned: true, nullable: true }) involvedTransactionLinkId: number | null - @ManyToOne(() => TransactionLink) + @ManyToOne(() => require('./TransactionLink').TransactionLink) @JoinColumn({ name: 'involved_transaction_link_id', referencedColumnName: 'id' }) - involvedTransactionLink: TransactionLink | null + involvedTransactionLink: TransactionLinkType | null @Column({ name: 'involved_contribution_link_id', type: 'bigint', unsigned: true, nullable: true }) involvedContributionLinkId: number | null - @ManyToOne(() => ContributionLink) + @ManyToOne(() => require('./ContributionLink').ContributionLink) @JoinColumn({ name: 'involved_contribution_link_id', referencedColumnName: 'id' }) - involvedContributionLink: ContributionLink | null + involvedContributionLink: ContributionLinkType | null @Column({ type: 'decimal', diff --git a/database/src/entity/TransactionLink.ts b/database/src/entity/TransactionLink.ts index 326573c16..7fe755a7a 100644 --- a/database/src/entity/TransactionLink.ts +++ b/database/src/entity/TransactionLink.ts @@ -9,10 +9,10 @@ import { OneToOne, PrimaryGeneratedColumn, } from 'typeorm' -import { DltTransaction } from './DltTransaction' -import { Transaction } from './Transaction' +import { type DltTransaction as DltTransactionType } from './DltTransaction' +import { type Transaction as TransactionType } from './Transaction' import { DecimalTransformer } from './transformer/DecimalTransformer' -import { User } from './User' +import { type User as UserType } from './User' @Entity('transaction_links') export class TransactionLink extends BaseEntity { @@ -72,23 +72,23 @@ export class TransactionLink extends BaseEntity { redeemedBy: number | null @OneToOne( - () => DltTransaction, - (dlt) => dlt.transactionLinkId, + () => require('./DltTransaction').DltTransaction, + (dlt: DltTransactionType) => dlt.transactionLinkId, ) @JoinColumn({ name: 'id', referencedColumnName: 'transactionLinkId' }) - dltTransaction?: DltTransaction | null + dltTransaction?: DltTransactionType | null @OneToOne( - () => User, - (user) => user.transactionLink, + () => require('./User').User, + (user: UserType) => user.transactionLink, ) @JoinColumn({ name: 'userId' }) - user: User + user: UserType @OneToMany( - () => Transaction, - (transaction) => transaction.transactionLink, + () => require('./Transaction').Transaction, + (transaction: TransactionType) => transaction.transactionLink, ) @JoinColumn({ referencedColumnName: 'transaction_link_id' }) - transactions: Transaction[] + transactions: TransactionType[] } diff --git a/database/src/entity/User.ts b/database/src/entity/User.ts index 6a40e9858..20219e4f7 100644 --- a/database/src/entity/User.ts +++ b/database/src/entity/User.ts @@ -3,20 +3,20 @@ import { Column, DeleteDateColumn, Entity, - Geometry, + type Geometry, JoinColumn, ManyToOne, OneToMany, OneToOne, PrimaryGeneratedColumn, } from 'typeorm' -import { Community } from './Community' -import { Contribution } from './Contribution' -import { ContributionMessage } from './ContributionMessage' -import { DltTransaction } from './DltTransaction' -import { TransactionLink } from './TransactionLink' -import { UserContact } from './UserContact' -import { UserRole } from './UserRole' +import { type Community as CommunityType } from './Community' +import { type Contribution as ContributionType } from './Contribution' +import { type ContributionMessage as ContributionMessageType } from './ContributionMessage' +import { type DltTransaction as DltTransactionType } from './DltTransaction' +import { type TransactionLink as TransactionLinkType } from './TransactionLink' +import { type UserContact as UserContactType } from './UserContact' +import { type UserRole as UserRoleType } from './UserRole' import { GeometryTransformer } from './transformer/GeometryTransformer' @Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) @@ -46,11 +46,11 @@ export class User extends BaseEntity { communityUuid: string @ManyToOne( - () => Community, - (community) => community.users, + () => require('./Community').Community, + (community: CommunityType) => community.users, ) @JoinColumn({ name: 'community_uuid', referencedColumnName: 'communityUuid' }) - community: Community | null + community: CommunityType | null @Column({ name: 'alias', @@ -63,11 +63,11 @@ export class User extends BaseEntity { alias: string @OneToOne( - () => UserContact, - (emailContact: UserContact) => emailContact.user, + () => require('./UserContact').UserContact, + (emailContact: UserContactType) => emailContact.user, ) @JoinColumn({ name: 'email_id' }) - emailContact: UserContact + emailContact: UserContactType @Column({ name: 'email_id', type: 'int', unsigned: true, nullable: true, default: null }) emailId: number | null @@ -138,11 +138,11 @@ export class User extends BaseEntity { hideAmountGDT: boolean @OneToMany( - () => UserRole, - (userRole) => userRole.user, + () => require('./UserRole').UserRole, + (userRole: UserRoleType) => userRole.user, ) @JoinColumn({ name: 'user_id' }) - userRoles: UserRole[] + userRoles: UserRoleType[] @Column({ name: 'referrer_id', type: 'bigint', unsigned: true, nullable: true, default: null }) referrerId?: number | null @@ -196,37 +196,37 @@ export class User extends BaseEntity { humhubAllowed: boolean @OneToMany( - () => Contribution, - (contribution) => contribution.user, + () => require('./Contribution').Contribution, + (contribution: ContributionType) => contribution.user, ) @JoinColumn({ name: 'user_id' }) - contributions?: Contribution[] + contributions?: ContributionType[] @OneToMany( - () => ContributionMessage, - (message) => message.user, + () => require('./ContributionMessage').ContributionMessage, + (message: ContributionMessageType) => message.user, ) @JoinColumn({ name: 'user_id' }) - messages?: ContributionMessage[] + messages?: ContributionMessageType[] @OneToMany( - () => UserContact, - (userContact: UserContact) => userContact.user, + () => require('./UserContact').UserContact, + (userContact: UserContactType) => userContact.user, ) @JoinColumn({ name: 'user_id' }) - userContacts?: UserContact[] + userContacts?: UserContactType[] @OneToOne( - () => DltTransaction, - (dlt) => dlt.userId, + () => require('./DltTransaction').DltTransaction, + (dlt: DltTransactionType) => dlt.userId, ) @JoinColumn({ name: 'id', referencedColumnName: 'userId' }) - dltTransaction?: DltTransaction | null + dltTransaction?: DltTransactionType | null @OneToOne( - () => TransactionLink, - (transactionLink) => transactionLink.userId, + () => require('./TransactionLink').TransactionLink, + (transactionLink: TransactionLinkType) => transactionLink.userId, ) @JoinColumn({ name: 'id', referencedColumnName: 'userId' }) - transactionLink?: TransactionLink | null + transactionLink?: TransactionLinkType | null } diff --git a/database/src/entity/transformer/GeometryTransformer.ts b/database/src/entity/transformer/GeometryTransformer.ts index bc80d256b..b877f2c38 100644 --- a/database/src/entity/transformer/GeometryTransformer.ts +++ b/database/src/entity/transformer/GeometryTransformer.ts @@ -1,4 +1,4 @@ -import { Geometry } from 'geojson' +import { type Geometry } from 'geojson' import { ValueTransformer } from 'typeorm/decorator/options/ValueTransformer' import { Geometry as wkx_Geometry } from 'wkx' diff --git a/database/src/queries/communities.test.ts b/database/src/queries/communities.test.ts index b435c3649..1f5135994 100644 --- a/database/src/queries/communities.test.ts +++ b/database/src/queries/communities.test.ts @@ -1,7 +1,11 @@ import { Community as DbCommunity, FederatedCommunity as DbFederatedCommunity } from '..' import { AppDatabase } from '../AppDatabase' -import { getCommunityByPublicKeyOrFail, getHomeCommunity, getHomeCommunityWithFederatedCommunityOrFail, getReachableCommunities } from './communities' -import { describe, expect, it, beforeEach, beforeAll, afterAll } from 'vitest' +import { + getCommunityByPublicKeyOrFail, + getHomeCommunity, + getHomeCommunityWithFederatedCommunityOrFail, + getReachableCommunities +} from './communities' import { createCommunity, createVerifiedFederatedCommunity } from '../seeds/community' import { Ed25519PublicKey } from 'shared' @@ -50,12 +54,12 @@ describe('community.queries', () => { }) it('should throw if no home community exists', async () => { - expect(() => getHomeCommunityWithFederatedCommunityOrFail('1_0')).rejects.toThrow() + expect(getHomeCommunityWithFederatedCommunityOrFail('1_0')).rejects.toThrow() }) it('should throw if no federated community exists', async () => { await createCommunity(false) - expect(() => getHomeCommunityWithFederatedCommunityOrFail('1_0')).rejects.toThrow() + expect(getHomeCommunityWithFederatedCommunityOrFail('1_0')).rejects.toThrow() }) it('load community by public key returned from getHomeCommunityWithFederatedCommunityOrFail', async () => { diff --git a/database/src/queries/communityHandshakes.test.ts b/database/src/queries/communityHandshakes.test.ts index 372fb1293..09937cc66 100644 --- a/database/src/queries/communityHandshakes.test.ts +++ b/database/src/queries/communityHandshakes.test.ts @@ -6,7 +6,6 @@ import { findPendingCommunityHandshake, CommunityHandshakeStateType } from '..' -import { describe, expect, it, beforeEach, beforeAll, afterAll } from 'vitest' import { createCommunity, createVerifiedFederatedCommunity } from '../seeds/community' import { Ed25519PublicKey } from 'shared' import { randomBytes } from 'node:crypto' diff --git a/database/src/queries/pendingTransactions.test.ts b/database/src/queries/pendingTransactions.test.ts index c59c312e4..a4765dcee 100644 --- a/database/src/queries/pendingTransactions.test.ts +++ b/database/src/queries/pendingTransactions.test.ts @@ -13,7 +13,6 @@ import { bibiBloxberg } from '../seeds/users/bibi-bloxberg' import { peterLustig } from '../seeds/users/peter-lustig' import { bobBaumeister } from '../seeds/users/bob-baumeister' import { garrickOllivander } from '../seeds/users/garrick-ollivander' -import { describe, expect, it, beforeAll, afterAll } from 'vitest' import { createCommunity } from '../seeds/community' import { v4 as uuidv4 } from 'uuid' import Decimal from 'decimal.js-light' diff --git a/database/src/queries/user.test.ts b/database/src/queries/user.test.ts index b653a5349..6348c01d8 100644 --- a/database/src/queries/user.test.ts +++ b/database/src/queries/user.test.ts @@ -3,11 +3,10 @@ import { AppDatabase } from '../AppDatabase' import { aliasExists, findUserByIdentifier } from './user' import { userFactory } from '../seeds/factory/user' import { bibiBloxberg } from '../seeds/users/bibi-bloxberg' -import { describe, expect, it, beforeAll, afterAll, beforeEach, } from 'vitest' import { createCommunity } from '../seeds/community' import { peterLustig } from '../seeds/users/peter-lustig' import { bobBaumeister } from '../seeds/users/bob-baumeister' -import { getLogger, printLogs, clearLogs } from '../../../config-schema/test/testSetup.vitest' +import { getLogger, printLogs, clearLogs } from '../../../config-schema/test/testSetup.bun' import { LOG4JS_QUERIES_CATEGORY_NAME } from '.' const db = AppDatabase.getInstance() diff --git a/database/vitest.config.js b/database/vitest.config.js deleted file mode 100644 index b0e269a1a..000000000 --- a/database/vitest.config.js +++ /dev/null @@ -1,7 +0,0 @@ -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - test: { - setupFiles: '../config-schema/test/testSetup.vitest.ts', - }, -}) \ No newline at end of file From fc7987460655e193c9f610124437c1cce9aacbe3 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sat, 22 Nov 2025 12:41:20 +0100 Subject: [PATCH 02/22] fix wrong version --- bun.lock | 10 +--------- database/package.json | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/bun.lock b/bun.lock index 0ac634ee8..4b94f4bc7 100644 --- a/bun.lock +++ b/bun.lock @@ -214,7 +214,7 @@ }, "database": { "name": "database", - "version": "2.7.0", + "version": "2.7.1", "dependencies": { "@types/uuid": "^8.3.4", "cross-env": "^7.0.3", @@ -222,7 +222,6 @@ "dotenv": "^10.0.0", "esbuild": "^0.25.2", "geojson": "^0.5.0", - "ioredis": "^5.8.2", "log4js": "^6.9.1", "mysql": "^2.18.1", "mysql2": "^3.15.3", @@ -243,13 +242,10 @@ "@swc/helpers": "^0.5.17", "@types/faker": "^5.5.9", "@types/geojson": "^7946.0.13", - "@types/jest": "27.0.2", "@types/mysql": "^2.15.27", "@types/node": "^18.7.14", "await-semaphore": "^0.1.3", "crypto-random-bigint": "^2.1.1", - "jest": "27.2.4", - "ts-jest": "27.0.5", "ts-node": "^10.9.2", "typescript": "^4.9.5", }, @@ -3945,8 +3941,6 @@ "database/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], - "database/ts-jest": ["ts-jest@27.0.5", "", { "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", "jest-util": "^27.0.0", "json5": "2.x", "lodash": "4.x", "make-error": "1.x", "semver": "7.x", "yargs-parser": "20.x" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@types/jest": "^27.0.0", "babel-jest": ">=27.0.0 <28", "jest": "^27.0.0", "typescript": ">=3.8 <5.0" }, "optionalPeers": ["@babel/core", "@types/jest", "babel-jest"], "bin": { "ts-jest": "cli.js" } }, "sha512-lIJApzfTaSSbtlksfFNHkWOzLJuuSm4faFAfo5kvzOiRAuoN4/eKxVJ2zEAho8aecE04qX6K1pAzfH5QHL1/8w=="], - "decompress-response/mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], "dht-node/@types/jest": ["@types/jest@27.5.1", "", { "dependencies": { "jest-matcher-utils": "^27.0.0", "pretty-format": "^27.0.0" } }, "sha512-fUy7YRpT+rHXto1YlL+J9rs0uLGyiqVt3ZOTQR+4ROc47yNl8WLdVLgUloBRhOxP1PZvguHl44T3H0wAWxahYQ=="], @@ -4383,8 +4377,6 @@ "css-select/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="], - "database/ts-jest/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], - "dht-node/ts-jest/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], "dht-rpc/sodium-universal/sodium-native": ["sodium-native@5.0.9", "", { "dependencies": { "require-addon": "^1.1.0", "which-runtime": "^1.2.1" } }, "sha512-6fpu3d6zdrRpLhuV3CDIBO5g90KkgaeR+c3xvDDz0ZnDkAlqbbPhFW7zhMJfsskfZ9SuC3SvBbqvxcECkXRyKw=="], diff --git a/database/package.json b/database/package.json index ed7aa56e4..2adcd10af 100644 --- a/database/package.json +++ b/database/package.json @@ -1,6 +1,6 @@ { "name": "database", - "version": "2.7.0", + "version": "2.7.1", "description": "Gradido Database Tool to execute database migrations", "main": "./build/index.js", "types": "./src/index.ts", From 5803c34d7b545a7fa52078763612b60a851be1fc Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sat, 22 Nov 2025 12:43:23 +0100 Subject: [PATCH 03/22] remove not longer used module --- bun.lock | 60 ------------------------------------------ database/package.json | 4 --- database/tsconfig.json | 3 --- 3 files changed, 67 deletions(-) diff --git a/bun.lock b/bun.lock index 4b94f4bc7..0f2ac4c20 100644 --- a/bun.lock +++ b/bun.lock @@ -236,10 +236,6 @@ }, "devDependencies": { "@biomejs/biome": "2.0.0", - "@swc-node/register": "^1.10.10", - "@swc/cli": "^0.7.3", - "@swc/core": "^1.11.24", - "@swc/helpers": "^0.5.17", "@types/faker": "^5.5.9", "@types/geojson": "^7946.0.13", "@types/mysql": "^2.15.27", @@ -653,12 +649,6 @@ "@dual-bundle/import-meta-resolve": ["@dual-bundle/import-meta-resolve@4.2.1", "", {}, "sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg=="], - "@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="], - - "@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="], - - "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.11", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg=="], "@esbuild/android-arm": ["@esbuild/android-arm@0.25.11", "", { "os": "android", "cpu": "arm" }, "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg=="], @@ -883,8 +873,6 @@ "@napi-rs/nice-win32-x64-msvc": ["@napi-rs/nice-win32-x64-msvc@1.1.1", "", { "os": "win32", "cpu": "x64" }, "sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ=="], - "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.7", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" } }, "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw=="], - "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], @@ -895,44 +883,6 @@ "@one-ini/wasm": ["@one-ini/wasm@0.1.1", "", {}, "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw=="], - "@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.11.0", "", { "os": "android", "cpu": "arm" }, "sha512-aN0UJg1xr0N1dADQ135z4p3bP9AYAUN1Ey2VvLMK6IwWYIJGWpKT+cr1l3AiyBeLK8QZyFDb4IDU8LHgjO9TDQ=="], - - "@oxc-resolver/binding-android-arm64": ["@oxc-resolver/binding-android-arm64@11.11.0", "", { "os": "android", "cpu": "arm64" }, "sha512-FckvvMclo8CSJqQjKpHueIIbKrg9L638NKWQTiJQaD8W9F61h8hTjF8+QFLlCHh6R9RcE5roVHdkkiBKHlB2Zw=="], - - "@oxc-resolver/binding-darwin-arm64": ["@oxc-resolver/binding-darwin-arm64@11.11.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7ZcpgaXSBnwRHM1YR8Vazq7mCTtGdYRvM7k46CscA+oipCVqmI4LbW2wLsc6HVjqX+SM/KPOfFGoGjEgmQPFTQ=="], - - "@oxc-resolver/binding-darwin-x64": ["@oxc-resolver/binding-darwin-x64@11.11.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Wsd1JWORokMmOKrR4t4jxpwYEWG11+AHWu9bdzjCO5EIyi0AuNpPIAEcEFCP9FNd0h8c+VUYbMRU/GooD2zOIg=="], - - "@oxc-resolver/binding-freebsd-x64": ["@oxc-resolver/binding-freebsd-x64@11.11.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-YX+W10kHrMouu/+Y+rqJdCWO3dFBKM1DIils30PHsmXWp1v+ZZvhibaST2BP6zrWkWquZ8pMmsObD6N10lLgiA=="], - - "@oxc-resolver/binding-linux-arm-gnueabihf": ["@oxc-resolver/binding-linux-arm-gnueabihf@11.11.0", "", { "os": "linux", "cpu": "arm" }, "sha512-UAhlhVkW2ui98bClmEkDLKQz4XBSccxMahG7rMeX2RepS2QByAWxYFFThaNbHtBSB+B4Rc1hudkihq8grQkU3g=="], - - "@oxc-resolver/binding-linux-arm-musleabihf": ["@oxc-resolver/binding-linux-arm-musleabihf@11.11.0", "", { "os": "linux", "cpu": "arm" }, "sha512-5pEliabSEiimXz/YyPxzyBST82q8PbM6BoEMS8kOyaDbEBuzTr7pWU1U0F7ILGBFjJmHaj3N7IAhQgeXdpdySg=="], - - "@oxc-resolver/binding-linux-arm64-gnu": ["@oxc-resolver/binding-linux-arm64-gnu@11.11.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-CiyufPFIOJrW/HovAMGsH0AbV7BSCb0oE0KDtt7z1+e+qsDo7HRlTSnqE3JbNuhJRg3Cz/j7qEYzgGqco9SE4Q=="], - - "@oxc-resolver/binding-linux-arm64-musl": ["@oxc-resolver/binding-linux-arm64-musl@11.11.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-w07MfGtDLZV0rISdXl2cGASxD/sRrrR93Qd4q27O2Hsky4MGbLw94trbzhmAkc7OKoJI0iDg1217i3jfxmVk1Q=="], - - "@oxc-resolver/binding-linux-ppc64-gnu": ["@oxc-resolver/binding-linux-ppc64-gnu@11.11.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-gzM+ZfIjfcCofwX/m1eLCoTT+3T70QLWaKDOW5Hf3+ddLlxMEVRIQtUoRsp0e/VFanr7u7VKS57TxhkRubseNg=="], - - "@oxc-resolver/binding-linux-riscv64-gnu": ["@oxc-resolver/binding-linux-riscv64-gnu@11.11.0", "", { "os": "linux", "cpu": "none" }, "sha512-oCR0ImJQhIwmqwNShsRT0tGIgKF5/H4nhtIEkQAQ9bLzMgjtRqIrZ3DtGHqd7w58zhXWfIZdyPNF9IrSm+J/fQ=="], - - "@oxc-resolver/binding-linux-riscv64-musl": ["@oxc-resolver/binding-linux-riscv64-musl@11.11.0", "", { "os": "linux", "cpu": "none" }, "sha512-MjCEqsUzXMfWPfsEUX+UXttzXz6xiNU11r7sj00C5og/UCyqYw1OjrbC/B1f/dloDpTn0rd4xy6c/LTvVQl2tg=="], - - "@oxc-resolver/binding-linux-s390x-gnu": ["@oxc-resolver/binding-linux-s390x-gnu@11.11.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-4TaTX7gT3357vWQsTe3IfDtWyJNe0FejypQ4ngwxB3v1IVaW6KAUt0huSvx/tmj+YWxd3zzXdWd8AzW0jo6dpg=="], - - "@oxc-resolver/binding-linux-x64-gnu": ["@oxc-resolver/binding-linux-x64-gnu@11.11.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ch1o3+tBra9vmrgXqrufVmYnvRPFlyUb7JWs/VXndBmyNSuP2KP+guAUrC0fr2aSGoOQOasAiZza7MTFU7Vrxg=="], - - "@oxc-resolver/binding-linux-x64-musl": ["@oxc-resolver/binding-linux-x64-musl@11.11.0", "", { "os": "linux", "cpu": "x64" }, "sha512-llTdl2gJAqXaGV7iV1w5BVlqXACcoT1YD3o840pCQx1ZmKKAAz7ydPnTjYVdkGImXNWPOIWJixHW0ryDm4Mx7w=="], - - "@oxc-resolver/binding-wasm32-wasi": ["@oxc-resolver/binding-wasm32-wasi@11.11.0", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.0.7" }, "cpu": "none" }, "sha512-cROavohP0nX91NtIVVgOTugqoxlUSNxI9j7MD+B7fmD3gEFl8CVyTamR0/p6loDxLv51bQYTHRKn/ZYTd3ENzw=="], - - "@oxc-resolver/binding-win32-arm64-msvc": ["@oxc-resolver/binding-win32-arm64-msvc@11.11.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-6amVs34yHmxE6Q3CtTPXnSvIYGqwQJ/lVVRYccLzg9smge3WJ1knyBV5jpKKayp0n316uPYzB4EgEbgcuRvrPw=="], - - "@oxc-resolver/binding-win32-ia32-msvc": ["@oxc-resolver/binding-win32-ia32-msvc@11.11.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-v/IZ5s2/3auHUoi0t6Ea1CDsWxrE9BvgvbDcJ04QX+nEbmTBazWPZeLsH8vWkRAh8EUKCZHXxjQsPhEH5Yk5pQ=="], - - "@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@11.11.0", "", { "os": "win32", "cpu": "x64" }, "sha512-qvm+IQ6r2q4HZitSV69O+OmvCD1y4pH7SbhR6lPwLsfZS5QRHS8V20VHxmG1jJzSPPw7S8Bb1rdNcxDSqc4bYA=="], - "@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="], "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="], @@ -1135,12 +1085,6 @@ "@sqltools/formatter": ["@sqltools/formatter@1.2.5", "", {}, "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw=="], - "@swc-node/core": ["@swc-node/core@1.14.1", "", { "peerDependencies": { "@swc/core": ">= 1.13.3", "@swc/types": ">= 0.1" } }, "sha512-jrt5GUaZUU6cmMS+WTJEvGvaB6j1YNKPHPzC2PUi2BjaFbtxURHj6641Az6xN7b665hNniAIdvjxWcRml5yCnw=="], - - "@swc-node/register": ["@swc-node/register@1.11.1", "", { "dependencies": { "@swc-node/core": "^1.14.1", "@swc-node/sourcemap-support": "^0.6.1", "colorette": "^2.0.20", "debug": "^4.4.1", "oxc-resolver": "^11.6.1", "pirates": "^4.0.7", "tslib": "^2.8.1" }, "peerDependencies": { "@swc/core": ">= 1.4.13", "typescript": ">= 4.3" } }, "sha512-VQ0hJ5jX31TVv/fhZx4xJRzd8pwn6VvzYd2tGOHHr2TfXGCBixZoqdPDXTiEoJLCTS2MmvBf6zyQZZ0M8aGQCQ=="], - - "@swc-node/sourcemap-support": ["@swc-node/sourcemap-support@0.6.1", "", { "dependencies": { "source-map-support": "^0.5.21", "tslib": "^2.8.1" } }, "sha512-ovltDVH5QpdHXZkW138vG4+dgcNsxfwxHVoV6BtmTbz2KKl1A8ZSlbdtxzzfNjCjbpayda8Us9eMtcHobm38dA=="], - "@swc/cli": ["@swc/cli@0.7.8", "", { "dependencies": { "@swc/counter": "^0.1.3", "@xhmikosr/bin-wrapper": "^13.0.5", "commander": "^8.3.0", "minimatch": "^9.0.3", "piscina": "^4.3.1", "semver": "^7.3.8", "slash": "3.0.0", "source-map": "^0.7.3", "tinyglobby": "^0.2.13" }, "peerDependencies": { "@swc/core": "^1.2.66", "chokidar": "^4.0.1" }, "optionalPeers": ["chokidar"], "bin": { "swc": "bin/swc.js", "swcx": "bin/swcx.js", "spack": "bin/spack.js" } }, "sha512-27Ov4rm0s2C6LLX+NDXfDVB69LGs8K94sXtFhgeUyQ4DBywZuCgTBu2loCNHRr8JhT9DeQvJM5j9FAu/THbo4w=="], "@swc/core": ["@swc/core@1.13.5", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.24" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.13.5", "@swc/core-darwin-x64": "1.13.5", "@swc/core-linux-arm-gnueabihf": "1.13.5", "@swc/core-linux-arm64-gnu": "1.13.5", "@swc/core-linux-arm64-musl": "1.13.5", "@swc/core-linux-x64-gnu": "1.13.5", "@swc/core-linux-x64-musl": "1.13.5", "@swc/core-win32-arm64-msvc": "1.13.5", "@swc/core-win32-ia32-msvc": "1.13.5", "@swc/core-win32-x64-msvc": "1.13.5" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ=="], @@ -1189,8 +1133,6 @@ "@tsconfig/node16": ["@tsconfig/node16@1.0.4", "", {}, "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="], - "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], - "@types/accepts": ["@types/accepts@1.3.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ=="], "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], @@ -2851,8 +2793,6 @@ "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], - "oxc-resolver": ["oxc-resolver@11.11.0", "", { "optionalDependencies": { "@oxc-resolver/binding-android-arm-eabi": "11.11.0", "@oxc-resolver/binding-android-arm64": "11.11.0", "@oxc-resolver/binding-darwin-arm64": "11.11.0", "@oxc-resolver/binding-darwin-x64": "11.11.0", "@oxc-resolver/binding-freebsd-x64": "11.11.0", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.11.0", "@oxc-resolver/binding-linux-arm-musleabihf": "11.11.0", "@oxc-resolver/binding-linux-arm64-gnu": "11.11.0", "@oxc-resolver/binding-linux-arm64-musl": "11.11.0", "@oxc-resolver/binding-linux-ppc64-gnu": "11.11.0", "@oxc-resolver/binding-linux-riscv64-gnu": "11.11.0", "@oxc-resolver/binding-linux-riscv64-musl": "11.11.0", "@oxc-resolver/binding-linux-s390x-gnu": "11.11.0", "@oxc-resolver/binding-linux-x64-gnu": "11.11.0", "@oxc-resolver/binding-linux-x64-musl": "11.11.0", "@oxc-resolver/binding-wasm32-wasi": "11.11.0", "@oxc-resolver/binding-win32-arm64-msvc": "11.11.0", "@oxc-resolver/binding-win32-ia32-msvc": "11.11.0", "@oxc-resolver/binding-win32-x64-msvc": "11.11.0" } }, "sha512-vVeBJf77zBeqOA/LBCTO/pr0/ETHGSleCRsI5Kmsf2OsfB5opzhhZptt6VxkqjKWZH+eF1se88fYDG5DGRLjkg=="], - "p-cancelable": ["p-cancelable@3.0.0", "", {}, "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw=="], "p-event": ["p-event@4.2.0", "", { "dependencies": { "p-timeout": "^3.1.0" } }, "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ=="], diff --git a/database/package.json b/database/package.json index 2adcd10af..b22f9d95f 100644 --- a/database/package.json +++ b/database/package.json @@ -33,10 +33,6 @@ }, "devDependencies": { "@biomejs/biome": "2.0.0", - "@swc-node/register": "^1.10.10", - "@swc/cli": "^0.7.3", - "@swc/core": "^1.11.24", - "@swc/helpers": "^0.5.17", "@types/faker": "^5.5.9", "@types/geojson": "^7946.0.13", "@types/mysql": "^2.15.27", diff --git a/database/tsconfig.json b/database/tsconfig.json index ec6769fa2..862e9bf94 100644 --- a/database/tsconfig.json +++ b/database/tsconfig.json @@ -72,8 +72,5 @@ "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ }, "references": [], /* Any project that is referenced must itself have a `references` array (which may be empty). */ - "ts-node": { - "swc": true - }, "exclude": ["**/*.test.ts", "**/*.spec.ts", "test/*"] } From 2d85b8b55bff7162d3e9088686ed4f531f4f5e88 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sat, 22 Nov 2025 13:28:46 +0100 Subject: [PATCH 04/22] update readme that database will be now tests by bun --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9abb4eb90..2bc9c6a32 100644 --- a/README.md +++ b/README.md @@ -189,11 +189,11 @@ describe('test', () => { ```ts import { clearLogs, printLogs } from 'config-schema/test/testSetup' ``` -- vitest (frontend, admin, database): +- vitest (frontend, admin): ```ts import { clearLogs, printLogs } from 'config-schema/test/testSetup.vitest' ``` -- bun (shared, core): +- bun (shared, core, database): ```ts import { clearLogs, printLogs } from 'config-schema/test/testSetup.bun' ``` From 99effdc2f35841cb453b373fb3497a2125f96df5 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 24 Nov 2025 14:42:28 +0100 Subject: [PATCH 05/22] move email into core, update packege, usw for it --- core/esbuild.config.ts | 16 + core/package.json | 7 +- core/src/config/index.ts | 26 + core/src/config/schema.ts | 87 + core/src/emails/README.md | 50 + .../sendEmailVariants.test.ts.snap | 1736 +++++++++++++++++ core/src/emails/index.ts | 1 + core/src/emails/locales/de.json | 101 + core/src/emails/locales/en.json | 104 + core/src/emails/localization.test.ts | 7 + core/src/emails/localization.ts | 31 + core/src/emails/sendEmailTranslated.test.ts | 153 ++ core/src/emails/sendEmailTranslated.ts | 122 ++ core/src/emails/sendEmailVariants.test.ts | 609 ++++++ core/src/emails/sendEmailVariants.ts | 179 ++ .../templates/accountActivation/html.pug | 18 + .../templates/accountActivation/subject.pug | 1 + .../accountMultiRegistration/html.pug | 22 + .../accountMultiRegistration/subject.pug | 1 + .../addedContributionMessage/html.pug | 16 + .../addedContributionMessage/subject.pug | 1 + .../contributionChangedByModerator/html.pug | 10 + .../subject.pug | 1 + .../templates/contributionConfirmed/html.pug | 10 + .../contributionConfirmed/subject.pug | 1 + .../templates/contributionDeleted/html.pug | 10 + .../templates/contributionDeleted/subject.pug | 1 + .../templates/contributionDenied/html.pug | 10 + .../templates/contributionDenied/subject.pug | 1 + .../src/emails/templates/includes/Chatbox.svg | 1 + .../templates/includes/answear_button.svg | 22 + .../templates/includes/chatbox-icon.png | Bin 0 -> 323 bytes .../templates/includes/chatbox-icon.pug | 6 + .../includes/contributionDetailsCTA.pug | 7 + .../emails/templates/includes/doNotReply.pug | 1 + core/src/emails/templates/includes/email.css | 216 ++ .../templates/includes/facebook-icon.png | Bin 0 -> 1449 bytes core/src/emails/templates/includes/footer.pug | 77 + .../templates/includes/gradido-header.jpeg | Bin 0 -> 9427 bytes .../emails/templates/includes/greeting.pug | 6 + core/src/emails/templates/includes/header.pug | 13 + .../templates/includes/requestNewLink.pug | 10 + .../emails/templates/includes/salutation.pug | 1 + .../templates/includes/telegram-icon.png | Bin 0 -> 2159 bytes .../templates/includes/twitter-icon.png | Bin 0 -> 2002 bytes .../src/emails/templates/includes/webflow.css | 166 ++ .../templates/includes/youtube-icon.png | Bin 0 -> 1802 bytes core/src/emails/templates/layout.pug | 26 + .../emails/templates/resetPassword/html.pug | 16 + .../templates/resetPassword/subject.pug | 1 + .../transactionLinkRedeemed/html.pug | 18 + .../transactionLinkRedeemed/subject.pug | 1 + .../templates/transactionReceived/html.pug | 30 + .../templates/transactionReceived/subject.pug | 1 + core/src/index.ts | 1 + core/src/types/images.d.ts | 14 + core/src/validation/user.test.ts | 27 +- core/tsconfig.json | 7 +- 58 files changed, 3989 insertions(+), 11 deletions(-) create mode 100644 core/esbuild.config.ts create mode 100644 core/src/emails/README.md create mode 100644 core/src/emails/__snapshots__/sendEmailVariants.test.ts.snap create mode 100644 core/src/emails/index.ts create mode 100644 core/src/emails/locales/de.json create mode 100644 core/src/emails/locales/en.json create mode 100644 core/src/emails/localization.test.ts create mode 100644 core/src/emails/localization.ts create mode 100644 core/src/emails/sendEmailTranslated.test.ts create mode 100644 core/src/emails/sendEmailTranslated.ts create mode 100644 core/src/emails/sendEmailVariants.test.ts create mode 100644 core/src/emails/sendEmailVariants.ts create mode 100644 core/src/emails/templates/accountActivation/html.pug create mode 100644 core/src/emails/templates/accountActivation/subject.pug create mode 100644 core/src/emails/templates/accountMultiRegistration/html.pug create mode 100644 core/src/emails/templates/accountMultiRegistration/subject.pug create mode 100644 core/src/emails/templates/addedContributionMessage/html.pug create mode 100644 core/src/emails/templates/addedContributionMessage/subject.pug create mode 100644 core/src/emails/templates/contributionChangedByModerator/html.pug create mode 100644 core/src/emails/templates/contributionChangedByModerator/subject.pug create mode 100644 core/src/emails/templates/contributionConfirmed/html.pug create mode 100644 core/src/emails/templates/contributionConfirmed/subject.pug create mode 100644 core/src/emails/templates/contributionDeleted/html.pug create mode 100644 core/src/emails/templates/contributionDeleted/subject.pug create mode 100644 core/src/emails/templates/contributionDenied/html.pug create mode 100644 core/src/emails/templates/contributionDenied/subject.pug create mode 100644 core/src/emails/templates/includes/Chatbox.svg create mode 100644 core/src/emails/templates/includes/answear_button.svg create mode 100644 core/src/emails/templates/includes/chatbox-icon.png create mode 100644 core/src/emails/templates/includes/chatbox-icon.pug create mode 100644 core/src/emails/templates/includes/contributionDetailsCTA.pug create mode 100644 core/src/emails/templates/includes/doNotReply.pug create mode 100644 core/src/emails/templates/includes/email.css create mode 100644 core/src/emails/templates/includes/facebook-icon.png create mode 100644 core/src/emails/templates/includes/footer.pug create mode 100644 core/src/emails/templates/includes/gradido-header.jpeg create mode 100644 core/src/emails/templates/includes/greeting.pug create mode 100644 core/src/emails/templates/includes/header.pug create mode 100644 core/src/emails/templates/includes/requestNewLink.pug create mode 100644 core/src/emails/templates/includes/salutation.pug create mode 100644 core/src/emails/templates/includes/telegram-icon.png create mode 100644 core/src/emails/templates/includes/twitter-icon.png create mode 100644 core/src/emails/templates/includes/webflow.css create mode 100644 core/src/emails/templates/includes/youtube-icon.png create mode 100644 core/src/emails/templates/layout.pug create mode 100644 core/src/emails/templates/resetPassword/html.pug create mode 100644 core/src/emails/templates/resetPassword/subject.pug create mode 100644 core/src/emails/templates/transactionLinkRedeemed/html.pug create mode 100644 core/src/emails/templates/transactionLinkRedeemed/subject.pug create mode 100644 core/src/emails/templates/transactionReceived/html.pug create mode 100644 core/src/emails/templates/transactionReceived/subject.pug create mode 100644 core/src/types/images.d.ts diff --git a/core/esbuild.config.ts b/core/esbuild.config.ts new file mode 100644 index 000000000..17435cef1 --- /dev/null +++ b/core/esbuild.config.ts @@ -0,0 +1,16 @@ +import { build } from 'esbuild' + +build({ + entryPoints: ['src/index.ts'], + outdir: 'build', + platform: 'node', + target: 'node18.20.7', + loader: { + '.png': 'dataurl', + '.jpeg': 'dataurl', + '.jpg': 'dataurl', + }, + bundle: true, + sourcemap: true, + packages: 'external', +}) diff --git a/core/package.json b/core/package.json index 3e444d5d5..06f6e7885 100644 --- a/core/package.json +++ b/core/package.json @@ -15,7 +15,7 @@ "license": "Apache-2.0", "private": true, "scripts": { - "build": "esbuild src/index.ts --outdir=build --platform=node --target=node18.20.7 --bundle --packages=external", + "build": "bun esbuild.config.ts", "build:bun": "bun build src/index.ts --outdir=build --target=bun --packages=external", "test": "bun test", "test:debug": "bun test --inspect-brk", @@ -26,20 +26,25 @@ }, "dependencies": { "database": "*", + "email-templates": "^10.0.1", "esbuild": "^0.25.2", "i18n": "^0.15.1", "joi": "^17.13.3", "jose": "^4.14.4", "log4js": "^6.9.1", + "nodemailer": "^6.6.5", + "pug": "^3.0.2", "shared": "*", "sodium-native": "^3.4.1", "zod": "^3.25.61" }, "devDependencies": { "@biomejs/biome": "2.0.0", + "@types/email-templates": "^10.0.4", "@types/i18n": "^0.13.4", "@types/minimatch": "6.0.0", "@types/node": "^17.0.21", + "@types/nodemailer": "^6.4.4", "@types/sodium-native": "^2.3.5", "config-schema": "*", "decimal.js-light": "^2.5.1", diff --git a/core/src/config/index.ts b/core/src/config/index.ts index 31b8cf033..be3179236 100644 --- a/core/src/config/index.ts +++ b/core/src/config/index.ts @@ -20,8 +20,34 @@ const federation = { ), } +const COMMUNITY_HOST = process.env.COMMUNITY_HOST ?? 'localhost' +const URL_PROTOCOL = process.env.URL_PROTOCOL ?? 'http' +const COMMUNITY_URL = process.env.COMMUNITY_URL ?? `${URL_PROTOCOL}://${COMMUNITY_HOST}` + +const community = { + COMMUNITY_SUPPORT_MAIL: process.env.COMMUNITY_SUPPORT_MAIL ?? 'support@supportmail.com', + COMMUNITY_URL, +} + + +const email = { + EMAIL: process.env.EMAIL === 'true', + EMAIL_LINK_FORGOTPASSWORD: + COMMUNITY_URL + (process.env.EMAIL_LINK_FORGOTPASSWORD_PATH ?? '/forgot-password'), + EMAIL_TLS: process.env.EMAIL_TLS !== 'false', + EMAIL_TEST_MODUS: process.env.EMAIL_TEST_MODUS === 'true', + EMAIL_TEST_RECEIVER: process.env.EMAIL_TEST_RECEIVER ?? 'stage1@gradido.net', + EMAIL_USERNAME: process.env.EMAIL_USERNAME ?? '', + EMAIL_SENDER: process.env.EMAIL_SENDER ?? 'info@gradido.net', + EMAIL_PASSWORD: process.env.EMAIL_PASSWORD ?? '', + EMAIL_SMTP_HOST: process.env.EMAIL_SMTP_HOST ?? 'mailserver', + EMAIL_SMTP_PORT: Number(process.env.EMAIL_SMTP_PORT) || 1025, +} + export const CONFIG = { ...federation, + ...community, + ...email, } validate(schema, CONFIG) diff --git a/core/src/config/schema.ts b/core/src/config/schema.ts index dbf6478de..6f8e2d6a8 100644 --- a/core/src/config/schema.ts +++ b/core/src/config/schema.ts @@ -1,6 +1,93 @@ + import Joi from 'joi' +import { COMMUNITY_SUPPORT_MAIL, COMMUNITY_URL, NODE_ENV } from 'config-schema' export const schema = Joi.object({ + COMMUNITY_SUPPORT_MAIL, + COMMUNITY_URL, + NODE_ENV, + + EMAIL: Joi.boolean() + .default(false) + .description('Enable or disable email functionality') + .required(), + + EMAIL_LINK_FORGOTPASSWORD: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .custom((value: string, helpers: Joi.CustomHelpers): string | Joi.ErrorReport => { + if (!value.startsWith(helpers.state.ancestors[0].COMMUNITY_URL)) { + return helpers.error('string.pattern.base', { value, communityUrl: COMMUNITY_URL }) + } + return value + }) + .description('Email Verification link for set new Password, when old Password was forgotten.') + .required(), + + EMAIL_TEST_MODUS: Joi.boolean() + .default(false) + .description('When enabled, all emails are sended to EMAIL_TEST_RECEIVER') + .optional(), + + EMAIL_TEST_RECEIVER: Joi.string() + .email() + .default('stage1@gradido.net') + .when('EMAIL_TEST_MODUS', { is: true, then: Joi.required() }) + .description('Email address used in test mode'), + + EMAIL_USERNAME: Joi.alternatives().conditional(Joi.ref('EMAIL'), { + is: true, + then: Joi.alternatives().conditional(Joi.ref('NODE_ENV'), { + is: 'development', + then: Joi.string() + .allow('') + .description('Username for SMTP authentication (optional in development)'), + otherwise: Joi.string() + .pattern(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/) + .description('Valid SMTP username required in production') + .required(), + }), + otherwise: Joi.string().allow('').optional(), + }), + + EMAIL_SENDER: Joi.string() + .email() + .when('EMAIL', { is: true, then: Joi.required() }) + .default('info@gradido.net') + .description('Email address used as sender'), + + EMAIL_PASSWORD: Joi.alternatives().conditional(Joi.ref('EMAIL'), { + is: true, + then: Joi.alternatives().conditional(Joi.ref('NODE_ENV'), { + is: 'development', + then: Joi.string() + .allow('') + .description('Password for SMTP authentication (optional in development)'), + otherwise: Joi.string() + .min(8) + .pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&#]).{8,}$/) + .description( + 'Password must be at least 8 characters long, include uppercase and lowercase letters, a number, and a special character', + ) + .required(), + }), + otherwise: Joi.string().allow('').optional(), + }), + + EMAIL_SMTP_HOST: Joi.string() + .hostname() + .when('EMAIL', { is: true, then: Joi.required() }) + .default('mailserver') + .description('SMTP server hostname'), + + EMAIL_SMTP_PORT: Joi.number() + .integer() + .positive() + .when('EMAIL', { is: true, then: Joi.required() }) + .default(1025) + .description('SMTP server port'), + + EMAIL_TLS: Joi.boolean().default(true).description('Enable or disable TLS for SMTP').optional(), + FEDERATION_BACKEND_SEND_ON_API: Joi.string() .pattern(/^\d+_\d+$/) .default('1_0') diff --git a/core/src/emails/README.md b/core/src/emails/README.md new file mode 100644 index 000000000..9ab1d1124 --- /dev/null +++ b/core/src/emails/README.md @@ -0,0 +1,50 @@ +# Using `forwardemail`–`email-templates` With `pug` Package + +You'll find the GitHub repository of the `email-templates` package and the `pug` package here: + +- [email-templates](https://github.com/forwardemail/email-templates) +- [pug](https://www.npmjs.com/package/pug) + +## `pug` Documentation + +The full `pug` documentation you'll find here: + +- [pugjs.org](https://pugjs.org/) + +### Caching Possibility + +In case we are sending many emails in the future there is the possibility to cache the `pug` templates: + +- [cache-pug-templates](https://github.com/ladjs/cache-pug-templates) + +## Testing + +To test your send emails you have different possibilities: + +### In General + +To send emails to yourself while developing set in `.env` the value `EMAIL_TEST_MODUS=true` and `EMAIL_TEST_RECEIVER` to your preferred email address. + +### Unit Or Integration Tests + +To change the behavior to show previews etc. you have the following options to be set in `sendEmailTranslated.ts` on creating the email object: + +```js + const email = new Email({ + … + // send emails in development/test env: + send: true, + … + // to open send emails in the browser + preview: true, + // or + // to open send emails in a specific the browser + preview: { + open: { + app: 'firefox', + wait: false, + }, + }, + … + }) +``` diff --git a/core/src/emails/__snapshots__/sendEmailVariants.test.ts.snap b/core/src/emails/__snapshots__/sendEmailVariants.test.ts.snap new file mode 100644 index 000000000..0bf88e84d --- /dev/null +++ b/core/src/emails/__snapshots__/sendEmailVariants.test.ts.snap @@ -0,0 +1,1736 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`sendEmailVariants sendAccountActivationEmail result has the correct html as snapshot 1`] = ` +" + + + + + + + + +
+
+
\"Gradido
+
+
+

Email Verification

+
+

Hello Peter Lustig,

+

Your email address has just been registered with Gradido.

+
+
+

Complete registration

+
Please click here to complete the registration and activate your Gradido account.
Activate account +
Or copy the link into your browser window.
http://localhost/checkEmail/6627633878930542284 + +

Request new valid link

+
The link has a validity of 23 hours and 30 minutes. +If the validity of the link has already expired, you can have a new link sent to you here.
New link +
+
+
+

Kind regards,
your Gradido team +

+
+
+
+
+
\"facebook\"\"Telegram\"\"Twitter\"\"youtube\"
+
+
+
If you have any further questions, please contact our support.
support@gradido.net +
\"Gradido
+
Privacy Policy +
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


+
+
+
+
+ +" +`; + +exports[`sendEmailVariants sendAccountMultiRegistrationEmail calls "sendEmailTranslated" result has the correct html as snapshot 1`] = ` +" + + + + + + + + +
+
+
\"Gradido
+
+
+

Try To Register Again With Your Email

+
+

Hello Peter Lustig,

+

Your email address has just been used again to register an account with Gradido.
However, an account already exists for your email address. +

+
+
+

Reset password

+
If you have forgotten your password, please click here.
reset +
Or copy the link into your browser window.
http://localhost/forgot-password +

Contact support

+
If you did not try to register again, please contact our support:
support@supportmail.com +
+
+

Kind regards,
your Gradido team +

+
+
+
+
+
\"facebook\"\"Telegram\"\"Twitter\"\"youtube\"
+
+
+
If you have any further questions, please contact our support.
support@gradido.net +
\"Gradido
+
Privacy Policy +
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


+
+
+
+
+ +" +`; + +exports[`sendEmailVariants sendAddedContributionMessageEmail result has the correct html as snapshot 1`] = ` +" + + + + + + + + +
+
+
\"Gradido
+
+
+

Message about your common good contribution

+
+

Hello Peter Lustig,

+

You have received a message from Bibi Bloxberg regarding your common good contribution “My contribution.”.

+
+
+

Read and reply to message

+
+

„My message.“

+

To reply to the message, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab.

+
To account +
Please do not reply to this email.
+
+
+

Kind regards,
your Gradido team +

+
+
+
+
+
\"facebook\"\"Telegram\"\"Twitter\"\"youtube\"
+
+
+
If you have any further questions, please contact our support.
support@gradido.net +
\"Gradido
+
Privacy Policy +
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


+
+
+
+
+ +" +`; + +exports[`sendEmailVariants sendContributionChangedByModeratorEmail result has the correct html as snapshot 1`] = ` +" + + + + + + + + +
+
+
\"Gradido
+
+
+

Your common good contribution has been changed

+
+

Hello Peter Lustig,

+

your common good contribution 'My contribution.' has just been changed by Bibi Bloxberg and now reads as 'This is a better contribution memo.'

+
+
+

Contribution details

+
To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab.
To account +
Or copy the link into your browser window.
https://gradido.net/contributions/own-contributions/1#contributionListItem-1 +
Please do not reply to this email.
+
+
+

Kind regards,
your Gradido team +

+
+
+
+
+
\"facebook\"\"Telegram\"\"Twitter\"\"youtube\"
+
+
+
If you have any further questions, please contact our support.
support@gradido.net +
\"Gradido
+
Privacy Policy +
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


+
+
+
+
+ +" +`; + +exports[`sendEmailVariants sendContributionConfirmedEmail result has the correct html as snapshot 1`] = ` +" + + + + + + + + +
+
+
\"Gradido
+
+
+

Your contribution to the common good was confirmed

+
+

Hello Peter Lustig,

+

Your common good contribution “My contribution.” has just been approved by Bibi Bloxberg. Your Gradido account has been credited with 23.54 GDD.

+
+
+

Contribution details

+
To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab.
To account +
Or copy the link into your browser window.
https://gradido.net/contributions/own-contributions/1#contributionListItem-1 +
Please do not reply to this email.
+
+
+

Kind regards,
your Gradido team +

+
+
+
+
+
\"facebook\"\"Telegram\"\"Twitter\"\"youtube\"
+
+
+
If you have any further questions, please contact our support.
support@gradido.net +
\"Gradido
+
Privacy Policy +
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


+
+
+
+
+ +" +`; + +exports[`sendEmailVariants sendContributionDeletedEmail result has the correct html as snapshot 1`] = ` +" + + + + + + + + +
+
+
\"Gradido
+
+
+

Your common good contribution was deleted

+
+

Hello Peter Lustig,

+

Your common good contribution “My contribution.” was deleted by Bibi Bloxberg.

+
+
+

Contribution details

+
To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab.
To account +
Or copy the link into your browser window.
https://gradido.net/contributions/own-contributions/1#contributionListItem-1 +
Please do not reply to this email.
+
+
+

Kind regards,
your Gradido team +

+
+
+
+
+
\"facebook\"\"Telegram\"\"Twitter\"\"youtube\"
+
+
+
If you have any further questions, please contact our support.
support@gradido.net +
\"Gradido
+
Privacy Policy +
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


+
+
+
+
+ +" +`; + +exports[`sendEmailVariants sendContributionDeniedEmail result has the correct html as snapshot 1`] = ` +" + + + + + + + + +
+
+
\"Gradido
+
+
+

Your common good contribution was rejected

+
+

Hello Peter Lustig,

+

Your common good contribution “My contribution.” was rejected by Bibi Bloxberg.

+
+
+

Contribution details

+
To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab.
To account +
Or copy the link into your browser window.
https://gradido.net/contributions/own-contributions/1#contributionListItem-1 +
Please do not reply to this email.
+
+
+

Kind regards,
your Gradido team +

+
+
+
+
+
\"facebook\"\"Telegram\"\"Twitter\"\"youtube\"
+
+
+
If you have any further questions, please contact our support.
support@gradido.net +
\"Gradido
+
Privacy Policy +
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


+
+
+
+
+ +" +`; + +exports[`sendEmailVariants sendResetPasswordEmail result has the correct html as snapshot 1`] = ` +" + + + + + + + + +
+
+
\"Gradido
+
+
+

Reset password

+
+

Hello Peter Lustig,

+

You, or someone else, requested a password reset for this account.

+
+
+

Reset password

+
If it was you, please click here.
reset +
Or copy the link into your browser window.
http://localhost/reset-password/3762660021544901417 + +

Request new valid link

+
The link has a validity of 23 hours and 30 minutes. +If the validity of the link has already expired, you can have a new link sent to you here.
New link +
+
+
+

Kind regards,
your Gradido team +

+
+
+
+
+
\"facebook\"\"Telegram\"\"Twitter\"\"youtube\"
+
+
+
If you have any further questions, please contact our support.
support@gradido.net +
\"Gradido
+
Privacy Policy +
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


+
+
+
+
+ +" +`; + +exports[`sendEmailVariants sendTransactionLinkRedeemedEmail result has the correct html as snapshot 1`] = ` +" + + + + + + + + +
+
+
\"Gradido
+
+
+

Bibi Bloxberg has redeemed your Gradido link

+
+

Hello Peter Lustig,

+

Bibi Bloxberg (bibi@bloxberg.de) has just redeemed your link.

+
+
+

Transaction details

+
Amount: 17.65 GDD
Message: You deserve it! 🙏🏼
You can find transaction details in your Gradido account. +
To account +
Please do not reply to this email.
+
+
+

Kind regards,
your Gradido team +

+
+
+
+
+
\"facebook\"\"Telegram\"\"Twitter\"\"youtube\"
+
+
+
If you have any further questions, please contact our support.
support@gradido.net +
\"Gradido
+
Privacy Policy +
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


+
+
+
+
+ +" +`; + +exports[`sendEmailVariants sendTransactionReceivedEmail result has the correct html as snapshot 1`] = ` +" + + + + + + + + +
+
+
\"Gradido
+
+
+

Bibi Bloxberg has sent you 37.40 Gradido

+
+

Hello Peter Lustig,

+

You have just received 37.40 GDD from Bibi Bloxberg (bibi@bloxberg.de). +

+
+
+

Message

+
+
Du bist schon lustiger ;)
+
+ +
To account +
+

Kind regards,
your Gradido team +

+
+
+
+
+
\"facebook\"\"Telegram\"\"Twitter\"\"youtube\"
+
+
+
If you have any further questions, please contact our support.
support@gradido.net +
\"Gradido
+
Privacy Policy +
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


+
+
+
+
+ +" +`; diff --git a/core/src/emails/index.ts b/core/src/emails/index.ts new file mode 100644 index 000000000..e7b39331a --- /dev/null +++ b/core/src/emails/index.ts @@ -0,0 +1 @@ +export * from './sendEmailVariants' \ No newline at end of file diff --git a/core/src/emails/locales/de.json b/core/src/emails/locales/de.json new file mode 100644 index 000000000..8b34f61f4 --- /dev/null +++ b/core/src/emails/locales/de.json @@ -0,0 +1,101 @@ +{ + "emails": { + "accountActivation": { + "activateAccount": "Konto aktivieren", + "emailRegistered": "deine E-Mail-Adresse wurde soeben bei Gradido registriert.", + "pleaseClickLink": "Klicke bitte hier, um die Registrierung abzuschließen und dein Gradido-Konto zu aktivieren.", + "subject": "E-Mail Überprüfung", + "title": "E-Mail Überprüfung" + }, + "accountMultiRegistration": { + "contactSupport": "Support kontaktieren", + "emailExists": "Es existiert jedoch zu deiner E-Mail-Adresse schon ein Konto.", + "emailReused": "deine E-Mail-Adresse wurde soeben erneut benutzt, um bei Gradido ein Konto zu registrieren.", + "ifYouAreNotTheOne": "Wenn du nicht versucht hast dich erneut zu registrieren, wende dich bitte an unseren Support:", + "onForgottenPasswordClickLink": "Solltest du dein Passwort vergessen haben, klicke bitte hier.", + "subject": "Erneuter Registrierungsversuch mit deiner E-Mail", + "title": "Erneuter Registrierungsversuch mit deiner E-Mail" + }, + "addedContributionMessage": { + "commonGoodContributionMessage": "du hast zu deinem Gemeinwohl-Beitrag „{contributionMemo}“ eine Nachricht von {senderFirstName} {senderLastName} erhalten.", + "readMessage": "Nachricht lesen und beantworten", + "subject": "Nachricht zu deinem Gemeinwohl-Beitrag", + "title": "Nachricht zu deinem Gemeinwohl-Beitrag", + "message": "„{message}“", + "toSeeAndAnswerMessage": "Um auf die Nachricht zu antworten, gehe in deinem Gradido-Konto ins Menü „Schöpfen“ auf den Tab „Meine Beiträge“." + }, + "contribution": { + "toSeeContributionsAndMessages": "Um deine Gemeinwohl-Beiträge und dazugehörige Nachrichten zu sehen, gehe in deinem Gradido-Konto ins Menü „Schöpfen“ auf den Tab „Meine Beiträge“." + }, + "contributionChangedByModerator": { + "subject": "Dein Gemeinwohl-Beitrag wurde geändert", + "text": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde soeben von {senderFirstName} {senderLastName} geändert und lautet jetzt „{contributionMemoUpdated}“", + "title": "Dein Gemeinwohl-Beitrag wurde geändert" + }, + "contributionConfirmed": { + "commonGoodContributionConfirmed": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde soeben von {senderFirstName} {senderLastName} bestätigt. Es wurden deinem Gradido-Konto {amountGDD} GDD gutgeschrieben.", + "subject": "Dein Gemeinwohl-Beitrag wurde bestätigt", + "title": "Dein Gemeinwohl-Beitrag wurde bestätigt" + }, + "contributionDeleted": { + "commonGoodContributionDeleted": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde von {senderFirstName} {senderLastName} gelöscht.", + "subject": "Dein Gemeinwohl-Beitrag wurde gelöscht", + "title": "Dein Gemeinwohl-Beitrag wurde gelöscht" + }, + "contributionDenied": { + "commonGoodContributionDenied": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde von {senderFirstName} {senderLastName} abgelehnt.", + "subject": "Dein Gemeinwohl-Beitrag wurde abgelehnt", + "title": "Dein Gemeinwohl-Beitrag wurde abgelehnt" + }, + "footer": { + "contactOurSupport": "Bei weiteren Fragen kontaktiere bitte unseren Support.", + "imprint": "Impressum", + "imprintAddress": "Gradido-Akademie\nInstitut für Wirtschaftsbionik\nPfarrweg 2\n74653 Künzelsau\nDeutschland", + "imprintImageAlt": "Gradido-Akademie Logo", + "privacyPolicy": "Datenschutzerklärung", + "supportEmail": "support@gradido.net" + }, + "general": { + "amountGDD": "Betrag: {amountGDD} GDD", + "answerNow": "Jetzt antworten", + "completeRegistration": "Registrierung abschließen", + "contribution": "Gemeinwohl-Beitrag: {contributionMemo}", + "contributionDetails": "Beitragsdetails", + "detailsYouFindOnLinkToYourAccount": "Details zur Transaktion findest du in deinem Gradido-Konto.", + "helloName": "Hallo {firstName} {lastName},", + "linkValidity": "Der Link hat eine Gültigkeit von {hours} Stunden.\nSollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen.", + "linkValidityWithMinutes": "Der Link hat eine Gültigkeit von {hours} Stunden und {minutes} Minuten.\nSollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen.", + "message": "Nachricht", + "newLink": "Neuer Link", + "orCopyLink": "Oder kopiere den Link in dein Browserfenster.", + "pleaseDoNotReply": "Bitte antworte nicht auf diese E-Mail.", + "requestNewLink": "Neuen gültigen Link anfordern", + "reset": "zurücksetzen", + "sincerelyYours": "Liebe Grüße", + "toAccount": "Zum Konto", + "transactionDetails": "Transaktionsdetails", + "yourGradidoTeam": "dein Gradido-Team" + }, + "resetPassword": { + "pleaseClickLink": "Wenn du es warst, klicke bitte hier.", + "subject": "Passwort zurücksetzen", + "title": "Passwort zurücksetzen", + "youOrSomeoneResetPassword": "du oder jemand anderes, hast für dieses Konto ein Zurücksetzen des Passworts angefordert." + }, + "transactionLinkRedeemed": { + "hasRedeemedYourLink": "{senderFirstName} {senderLastName} ({senderEmail}) hat soeben deinen Link eingelöst.", + "memo": "Nachricht: {transactionMemo}", + "subject": "{senderFirstName} {senderLastName} hat deinen Gradido-Link eingelöst", + "title": "{senderFirstName} {senderLastName} hat deinen Gradido-Link eingelöst" + }, + "transactionReceived": { + "haveReceivedAmountGDDFrom": "du hast soeben {transactionAmount} GDD erhalten von {senderFirstName} {senderLastName}", + "subject": "{senderFirstName} {senderLastName} hat dir {transactionAmount} Gradido gesendet", + "replySubject": "Re: {senderFirstName} {senderLastName} hat dir {transactionAmount} Gradido gesendet", + "title": "{senderFirstName} {senderLastName} hat dir {transactionAmount} Gradido gesendet" + } + }, + "general": { + "decimalSeparator": "," + } +} diff --git a/core/src/emails/locales/en.json b/core/src/emails/locales/en.json new file mode 100644 index 000000000..cc8d59c75 --- /dev/null +++ b/core/src/emails/locales/en.json @@ -0,0 +1,104 @@ +{ + "emails": { + "accountActivation": { + "activateAccount": "Activate account", + "emailRegistered": "Your email address has just been registered with Gradido.", + "pleaseClickLink": "Please click here to complete the registration and activate your Gradido account.", + "subject": "Email Verification", + "title": "Email Verification" + }, + "accountMultiRegistration": { + "contactSupport": "Contact support", + "emailExists": "However, an account already exists for your email address.", + "emailReused": "Your email address has just been used again to register an account with Gradido.", + "ifYouAreNotTheOne": "If you did not try to register again, please contact our support:", + "onForgottenPasswordClickLink": "If you have forgotten your password, please click here.", + "subject": "Try To Register Again With Your Email", + "title": "Try To Register Again With Your Email" + }, + "addedContributionMessage": { + "commonGoodContributionMessage": "You have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.", + "readMessage": "Read and reply to message", + "subject": "Message about your common good contribution", + "title": "Message about your common good contribution", + "message": "„{message}“", + "toSeeAndAnswerMessage": "To reply to the message, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab." + }, + "contribution": { + "toSeeContributionsAndMessages": "To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab." + }, + "contributionChangedByModerator": { + "subject": "Your common good contribution has been changed", + "text": "your common good contribution '{contributionMemo}' has just been changed by {senderFirstName} {senderLastName} and now reads as '{contributionMemoUpdated}'", + "title": "Your common good contribution has been changed" + }, + "contributionConfirmed": { + "commonGoodContributionConfirmed": "Your common good contribution “{contributionMemo}” has just been approved by {senderFirstName} {senderLastName}. Your Gradido account has been credited with {amountGDD} GDD.", + "subject": "Your contribution to the common good was confirmed", + "title": "Your contribution to the common good was confirmed" + }, + "contributionDeleted": { + "commonGoodContributionDeleted": "Your common good contribution “{contributionMemo}” was deleted by {senderFirstName} {senderLastName}.", + "subject": "Your common good contribution was deleted", + "title": "Your common good contribution was deleted" + }, + "contributionDenied": { + "commonGoodContributionDenied": "Your common good contribution “{contributionMemo}” was rejected by {senderFirstName} {senderLastName}.", + "subject": "Your common good contribution was rejected", + "title": "Your common good contribution was rejected" + }, + "footer": { + "contactOurSupport": "If you have any further questions, please contact our support.", + "imprint": "Impressum", + "imprintAddress": "Gradido-Akademie\nInstitut für Wirtschaftsbionik\nPfarrweg 2\n74653 Künzelsau\nDeutschland", + "imprintImageAlt": "Gradido-Akademie Logo", + "privacyPolicy": "Privacy Policy", + "supportEmail": "support@gradido.net" + }, + "general": { + "amountGDD": "Amount: {amountGDD} GDD", + "answerNow": "Reply", + "completeRegistration": "Complete registration", + "contribution": "Contribution: : {contributionMemo}", + "contributionDetails": "Contribution details", + "detailsYouFindOnLinkToYourAccount": "You can find transaction details in your Gradido account.", + "helloName": "Hello {firstName} {lastName},", + "linkValidity": "The link has a validity of {hours} hours.\nIf the validity of the link has already expired, you can have a new link sent to you here.", + "linkValidityWithMinutes": "The link has a validity of {hours} hours and {minutes} minutes.\nIf the validity of the link has already expired, you can have a new link sent to you here.", + "message": "Message", + "newLink": "New link", + "orCopyLink": "Or copy the link into your browser window.", + "pleaseDoNotReply": "Please do not reply to this email.", + "requestNewLink": "Request new valid link", + "reset": "reset", + "sincerelyYours": "Kind regards,", + "toAccount": "To account", + "transactionDetails": "Transaction details", + "yourGradidoTeam": "your Gradido team" + }, + "resetPassword": { + "pleaseClickLink": "If it was you, please click here.", + "subject": "Reset password", + "title": "Reset password", + "youOrSomeoneResetPassword": "You, or someone else, requested a password reset for this account." + }, + "transactionLinkRedeemed": { + "hasRedeemedYourLink": "{senderFirstName} {senderLastName} ({senderEmail}) has just redeemed your link.", + "memo": "Message: {transactionMemo}", + "subject": "{senderFirstName} {senderLastName} has redeemed your Gradido link", + "title": "{senderFirstName} {senderLastName} has redeemed your Gradido link" + }, + "transactionReceived": { + "haveReceivedAmountGDDFrom": "You have just received {transactionAmount} GDD from {senderFirstName} {senderLastName}", + "replySubject": "RE: {senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido", + "subject": "{senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido", + "title": "{senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido" + } + }, + "general": { + "decimalSeparator": "." + }, + "accountMultiRegistration": { + "contactSupport": "accountMultiRegistration.contactSupport" + } +} \ No newline at end of file diff --git a/core/src/emails/localization.test.ts b/core/src/emails/localization.test.ts new file mode 100644 index 000000000..77651c4e4 --- /dev/null +++ b/core/src/emails/localization.test.ts @@ -0,0 +1,7 @@ +import { i18n } from './localization' + +describe('localization', () => { + it('translate emails.accountMultiRegistration.contactSupport with Contact support', () => { + expect(i18n.__('emails.accountMultiRegistration.contactSupport')).toBe('Contact support') + }) +}) \ No newline at end of file diff --git a/core/src/emails/localization.ts b/core/src/emails/localization.ts new file mode 100644 index 000000000..488ae5411 --- /dev/null +++ b/core/src/emails/localization.ts @@ -0,0 +1,31 @@ +import en from './locales/en.json' +import de from './locales/de.json' +import { I18n } from 'i18n' + +function flatten(obj: any, prefix: string = ''): any { + const result: any = {} + for (const key in obj) { + if (typeof obj[key] === 'object' && obj[key] !== null) { + Object.assign(result, flatten(obj[key], prefix + key + '.')) + } else { + result[prefix + key] = obj[key] + } + } + return result +} + +export const i18n = new I18n({ + locales: ['en', 'de'], + defaultLocale: 'en', + staticCatalog: { en: flatten(en), de: flatten(de) }, + api: { + __: 't', // now req.__ becomes req.t + __n: 'tn', // and req.__n can be called as req.tn + }, + register: global, + mustacheConfig: { + tags: ['{', '}'], + disable: false, + }, +}) + diff --git a/core/src/emails/sendEmailTranslated.test.ts b/core/src/emails/sendEmailTranslated.test.ts new file mode 100644 index 000000000..3f8b5680e --- /dev/null +++ b/core/src/emails/sendEmailTranslated.test.ts @@ -0,0 +1,153 @@ +import { createTransport } from 'nodemailer' +import { CONFIG } from '../config' +import { i18n } from './localization' +import { getLogger } from '../../../config-schema/test/testSetup.bun' +import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const' +import { sendEmailTranslated } from './sendEmailTranslated' +import { mock, jest, describe, it, expect, beforeEach, afterAll } from 'bun:test' + +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.emails.sendEmailTranslated`) + +const testMailServerHost = 'localhost' +const testMailServerPort = 1025 + +CONFIG.EMAIL = false +CONFIG.EMAIL_SMTP_HOST = testMailServerHost +CONFIG.EMAIL_SMTP_PORT = testMailServerPort +CONFIG.EMAIL_SENDER = 'info@gradido.net' +CONFIG.EMAIL_USERNAME = 'user' +CONFIG.EMAIL_PASSWORD = 'pwd' +CONFIG.EMAIL_TLS = true + +mock.module('nodemailer', () => { + return { + createTransport: jest.fn(() => { + return { + sendMail: () => { + return { + messageId: 'message', + } + }, + } + }), + } +}) + +afterAll(() => { + jest.restoreAllMocks() +}) + +const spySetLocale = jest.spyOn(i18n, 'setLocale') +const spyTranslate = jest.spyOn(i18n, '__') + +describe('sendEmailTranslated', () => { + let result: Record | boolean | null + + describe('config email is false', () => { + beforeEach(async () => { + result = await sendEmailTranslated({ + receiver: { + to: 'receiver@mail.org', + cc: 'support@gradido.net', + }, + template: 'accountMultiRegistration', + locals: { + language: 'en', + }, + }) + }) + + it('logs warning', () => { + expect(logger.info).toBeCalledWith('Emails are disabled via config...') + }) + + it('returns false', () => { + expect(result).toBeFalsy() + }) + }) + + describe('config email is true', () => { + beforeEach(async () => { + CONFIG.EMAIL = true + result = await sendEmailTranslated({ + receiver: { + to: 'receiver@mail.org', + cc: 'support@gradido.net', + }, + template: 'accountMultiRegistration', + locals: { + language: 'en', + }, + }) + }) + + it('calls the transporter', () => { + expect(createTransport).toBeCalledWith({ + host: testMailServerHost, + port: testMailServerPort, + secure: false, + requireTLS: true, + auth: { + user: 'user', + pass: 'pwd', + }, + }) + }) + + describe('call of "sendEmailTranslated"', () => { + it('has expected result', () => { + expect(result).toMatchObject({ + originalMessage: expect.objectContaining({ + to: 'receiver@mail.org', + cc: 'support@gradido.net', + from: 'Gradido (emails.general.doNotAnswer) ', + attachments: expect.any(Array), + subject: 'Try To Register Again With Your Email', + html: expect.stringContaining('Try To Register Again With Your Email'), + text: expect.stringContaining('TRY TO REGISTER AGAIN WITH YOUR EMAIL'), + }), + }) + }) + }) + + it('calls "i18n.setLocale" with "en"', async () => { + expect(spySetLocale).toBeCalledWith('en') + }) + + it('calls "i18n.__" for translation', () => { + expect(spyTranslate).toBeCalled() + }) + }) + + describe('with email EMAIL_TEST_MODUS true', () => { + beforeEach(async () => { + jest.clearAllMocks() + CONFIG.EMAIL = true + CONFIG.EMAIL_TEST_MODUS = true + result = await sendEmailTranslated({ + receiver: { + to: 'receiver@mail.org', + cc: 'support@gradido.net', + }, + template: 'accountMultiRegistration', + locals: { + language: 'en', + }, + }) + }) + + it('call of "sendEmailTranslated" with faked "to"', () => { + expect(result).toMatchObject({ + originalMessage: expect.objectContaining({ + to: CONFIG.EMAIL_TEST_RECEIVER, + cc: 'support@gradido.net', + from: `Gradido (emails.general.doNotAnswer) <${CONFIG.EMAIL_SENDER}>`, + attachments: expect.any(Array), + subject: 'Try To Register Again With Your Email', + html: expect.stringContaining('Try To Register Again With Your Email'), + text: expect.stringContaining('TRY TO REGISTER AGAIN WITH YOUR EMAIL'), + }), + }) + }) + }) +}) diff --git a/core/src/emails/sendEmailTranslated.ts b/core/src/emails/sendEmailTranslated.ts new file mode 100644 index 000000000..02d6de130 --- /dev/null +++ b/core/src/emails/sendEmailTranslated.ts @@ -0,0 +1,122 @@ +import path from 'path' + +import Email from 'email-templates' +import { i18n } from './localization' +import { createTransport } from 'nodemailer' +import { CONFIG } from '../config' +import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const' +import { getLogger } from 'log4js' +import gradidoHeader from './templates/includes/gradido-header.jpeg' +import facebookIcon from './templates/includes/facebook-icon.png' +import telegramIcon from './templates/includes/telegram-icon.png' +import twitterIcon from './templates/includes/twitter-icon.png' +import youtubeIcon from './templates/includes/youtube-icon.png' +import chatboxIcon from './templates/includes/chatbox-icon.png' + +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.emails.sendEmailTranslated`) + +export const sendEmailTranslated = async ({ + receiver, + template, + locals, +}: { + receiver: { + to: string + cc?: string + } + template: string + locals: Record +}): Promise | boolean | null> => { + // TODO: test the calling order of 'i18n.setLocale' for example: language of logging 'en', language of email receiver 'es', reset language of current user 'de' + + if (!CONFIG.EMAIL) { + logger.info(`Emails are disabled via config...`) + return null + } + + // because language of receiver can differ from language of current user who triggers the sending + // const rememberLocaleToRestore = i18n.getLocale() + i18n.setLocale('en') // for logging + logger.info( + `send Email: language=${locals.locale as string} to=${receiver.to.substring(0, 3)}...` + + (receiver.cc ? `, cc=${receiver.cc.substring(0, 3)}...` : '') + + `, subject=${i18n.__('emails.' + template + '.subject')}`, + ) + + if (CONFIG.EMAIL_TEST_MODUS) { + logger.info( + `Testmodus=ON: change receiver from ${receiver.to} to ${CONFIG.EMAIL_TEST_RECEIVER}`, + ) + receiver.to = CONFIG.EMAIL_TEST_RECEIVER + } + + const transport = createTransport({ + host: CONFIG.EMAIL_SMTP_HOST, + port: CONFIG.EMAIL_SMTP_PORT, + secure: false, // true for 465, false for other ports + requireTLS: CONFIG.EMAIL_TLS, + auth: { + user: CONFIG.EMAIL_USERNAME, + pass: CONFIG.EMAIL_PASSWORD, + }, + }) + i18n.setLocale(locals.language as string) // for email + + // TESTING: see 'README.md' + const email = new Email({ + message: { + from: `Gradido (${i18n.__('emails.general.doNotAnswer')}) <${CONFIG.EMAIL_SENDER}>`, + }, + send: CONFIG.EMAIL, + transport, + preview: false, + }) + + const resultSend = await email + .send({ + template: path.join(__dirname, 'templates', template), + message: { + ...receiver, + attachments: [ + { + // filename: 'gradido-header.jpeg', + content: gradidoHeader, + cid: 'gradidoheader', + }, + { + // filename: 'facebook-icon.png', + content: facebookIcon, + cid: 'facebookicon', + }, + { + // filename: 'telegram-icon.png', + content: telegramIcon, + cid: 'telegramicon', + }, + { + // filename: 'twitter-icon.png', + content: twitterIcon, + cid: 'twittericon', + }, + { + // filename: 'youtube-icon.png', + content: youtubeIcon, + cid: 'youtubeicon', + }, + { + // filename: 'chatbox-icon.png', + content: chatboxIcon, + cid: 'chatboxicon', + }, + ], + }, + locals, // the 'locale' in here seems not to be used by 'email-template', because it doesn't work if the language isn't set before by 'i18n.setLocale' + // t: i18n.__.bind(i18n), + }) + .catch((error: unknown) => { + logger.error('Error sending notification email', error) + return error + }) + + return resultSend +} diff --git a/core/src/emails/sendEmailVariants.test.ts b/core/src/emails/sendEmailVariants.test.ts new file mode 100644 index 000000000..74743dbd9 --- /dev/null +++ b/core/src/emails/sendEmailVariants.test.ts @@ -0,0 +1,609 @@ +import { Decimal } from 'decimal.js-light' +import { CONFIG } from '../config' + +import * as sendEmailTranslatedApi from './sendEmailTranslated' +import { + sendAccountActivationEmail, + sendAccountMultiRegistrationEmail, + sendAddedContributionMessageEmail, + sendContributionChangedByModeratorEmail, + sendContributionConfirmedEmail, + sendContributionDeletedEmail, + sendContributionDeniedEmail, + sendResetPasswordEmail, + sendTransactionLinkRedeemedEmail, + sendTransactionReceivedEmail, +} from './sendEmailVariants' + +const testMailServerHost = 'localhost' +const testMailServerPort = 1025 +const testMailTLS = false + +CONFIG.EMAIL = true +CONFIG.EMAIL_SENDER = 'info@gradido.net' +CONFIG.EMAIL_SMTP_HOST = testMailServerHost +CONFIG.EMAIL_SMTP_PORT = testMailServerPort +CONFIG.EMAIL_TLS = testMailTLS + +jest.mock('nodemailer', () => { + return { + __esModule: true, + createTransport: jest.fn(() => { + return { + sendMail: () => { + return { + messageId: 'message', + } + }, + } + }), + } +}) + +const sendEmailTranslatedSpy = jest.spyOn(sendEmailTranslatedApi, 'sendEmailTranslated') + +describe('sendEmailVariants', () => { + let result: any + const contributionFrontendLink = + 'https://gradido.net/contributions/own-contributions/1#contributionListItem-1' + + describe('sendAddedContributionMessageEmail', () => { + beforeAll(async () => { + result = await sendAddedContributionMessageEmail({ + firstName: 'Peter', + lastName: 'Lustig', + email: 'peter@lustig.de', + language: 'en', + senderFirstName: 'Bibi', + senderLastName: 'Bloxberg', + contributionMemo: 'My contribution.', + contributionFrontendLink, + message: 'My message.', + }) + }) + + describe('calls "sendEmailTranslated"', () => { + it('with expected parameters', () => { + expect(sendEmailTranslatedSpy).toBeCalledWith({ + receiver: { + to: 'Peter Lustig ', + }, + template: 'addedContributionMessage', + locals: expect.objectContaining({ + firstName: 'Peter', + lastName: 'Lustig', + language: 'en', + senderFirstName: 'Bibi', + senderLastName: 'Bloxberg', + contributionMemo: 'My contribution.', + contributionFrontendLink, + message: 'My message.', + supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, + }), + }) + }) + }) + + describe('result', () => { + it('is the expected object', () => { + // bun testrunner bug, toMatchObject mess with 'result' + const resultClone = JSON.parse(JSON.stringify(result)) + expect(resultClone).toMatchObject({ + originalMessage: expect.objectContaining({ + to: 'Peter Lustig ', + from: 'Gradido (emails.general.doNotAnswer) ', + attachments: expect.any(Array), + subject: 'Message about your common good contribution', + html: expect.any(String), + text: expect.stringContaining('MESSAGE ABOUT YOUR COMMON GOOD CONTRIBUTION'), + }), + }) + }) + + it('has the correct html as snapshot', () => { + expect(result.originalMessage.html).toMatchSnapshot() + }) + }) + }) + + describe('sendAccountActivationEmail', () => { + beforeAll(async () => { + result = await sendAccountActivationEmail({ + firstName: 'Peter', + lastName: 'Lustig', + email: 'peter@lustig.de', + language: 'en', + activationLink: 'http://localhost/checkEmail/6627633878930542284', + timeDurationObject: { hours: 23, minutes: 30 }, + }) + }) + + describe('calls "sendEmailTranslated"', () => { + it('with expected parameters', () => { + expect(sendEmailTranslatedSpy).toBeCalledWith({ + receiver: { + to: 'Peter Lustig ', + }, + template: 'accountActivation', + locals: expect.objectContaining({ + firstName: 'Peter', + lastName: 'Lustig', + language: 'en', + activationLink: 'http://localhost/checkEmail/6627633878930542284', + timeDurationObject: { hours: 23, minutes: 30 }, + resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD, + supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, + communityURL: CONFIG.COMMUNITY_URL, + }), + }) + }) + }) + + + describe('result', () => { + it('is the expected object', () => { + // bun testrunner bug, toMatchObject mess with 'result' + const resultClone = JSON.parse(JSON.stringify(result)) + expect(resultClone).toMatchObject({ + originalMessage: expect.objectContaining({ + to: 'Peter Lustig ', + from: 'Gradido (emails.general.doNotAnswer) ', + attachments: expect.any(Array), + subject: 'Email Verification', + html: expect.any(String), + text: expect.stringContaining('EMAIL VERIFICATION'), + }), + }) + }) + + it('has the correct html as snapshot', () => { + expect(result.originalMessage.html).toMatchSnapshot() + }) + }) + }) + + /* + + describe('sendAccountMultiRegistrationEmail', () => { + beforeAll(async () => { + result = await sendAccountMultiRegistrationEmail({ + firstName: 'Peter', + lastName: 'Lustig', + email: 'peter@lustig.de', + language: 'en', + }) + }) + + describe('calls "sendEmailTranslated"', () => { + it('with expected parameters', () => { + expect(sendEmailTranslatedSpy).toBeCalledWith({ + receiver: { + to: 'Peter Lustig ', + }, + template: 'accountMultiRegistration', + locals: { + firstName: 'Peter', + lastName: 'Lustig', + locale: 'en', + resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD, + supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, + communityURL: CONFIG.COMMUNITY_URL, + }, + }) + }) + + describe('result', () => { + it('is the expected object', () => { + expect(result).toMatchObject({ + originalMessage: expect.objectContaining({ + to: 'Peter Lustig ', + from: 'Gradido (emails.general.doNotAnswer) ', + attachments: expect.any(Array), + subject: 'Try To Register Again With Your Email', + html: expect.any(String), + text: expect.stringContaining('TRY TO REGISTER AGAIN WITH YOUR EMAIL'), + }), + }) + }) + + it('has the correct html as snapshot', () => { + expect(result.originalMessage.html).toMatchSnapshot() + }) + }) + }) + }) + + describe('sendContributionConfirmedEmail', () => { + beforeAll(async () => { + result = await sendContributionConfirmedEmail({ + firstName: 'Peter', + lastName: 'Lustig', + email: 'peter@lustig.de', + language: 'en', + senderFirstName: 'Bibi', + senderLastName: 'Bloxberg', + contributionMemo: 'My contribution.', + contributionAmount: new Decimal(23.54), + contributionFrontendLink, + }) + }) + + describe('calls "sendEmailTranslated"', () => { + it('with expected parameters', () => { + expect(sendEmailTranslatedSpy).toBeCalledWith({ + receiver: { + to: 'Peter Lustig ', + }, + template: 'contributionConfirmed', + locals: { + firstName: 'Peter', + lastName: 'Lustig', + locale: 'en', + senderFirstName: 'Bibi', + senderLastName: 'Bloxberg', + contributionMemo: 'My contribution.', + contributionAmount: '23.54', + supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, + contributionFrontendLink, + }, + }) + }) + }) + + describe('result', () => { + it('is the expected object', () => { + expect(result).toMatchObject({ + originalMessage: expect.objectContaining({ + to: 'Peter Lustig ', + from: 'Gradido (emails.general.doNotAnswer) ', + attachments: expect.any(Array), + subject: 'Your contribution to the common good was confirmed', + html: expect.any(String), + text: expect.stringContaining('YOUR CONTRIBUTION TO THE COMMON GOOD WAS CONFIRMED'), + }), + }) + }) + + it('has the correct html as snapshot', () => { + expect(result.originalMessage.html).toMatchSnapshot() + }) + }) + }) + + describe('sendContributionChangedByModeratorEmail', () => { + beforeAll(async () => { + result = await sendContributionChangedByModeratorEmail({ + firstName: 'Peter', + lastName: 'Lustig', + email: 'peter@lustig.de', + language: 'en', + senderFirstName: 'Bibi', + senderLastName: 'Bloxberg', + contributionMemo: 'My contribution.', + contributionMemoUpdated: 'This is a better contribution memo.', + contributionFrontendLink, + }) + }) + + describe('calls "sendEmailTranslated"', () => { + it('with expected parameters', () => { + expect(sendEmailTranslatedSpy).toBeCalledWith({ + receiver: { + to: 'Peter Lustig ', + }, + template: 'contributionChangedByModerator', + locals: { + firstName: 'Peter', + lastName: 'Lustig', + locale: 'en', + senderFirstName: 'Bibi', + senderLastName: 'Bloxberg', + contributionMemo: 'My contribution.', + contributionMemoUpdated: 'This is a better contribution memo.', + contributionFrontendLink, + supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, + }, + }) + }) + }) + + describe('result', () => { + it('is the expected object', () => { + expect(result).toMatchObject({ + originalMessage: expect.objectContaining({ + to: 'Peter Lustig ', + from: 'Gradido (emails.general.doNotAnswer) ', + attachments: expect.any(Array), + subject: 'Your common good contribution has been changed', + html: expect.any(String), + text: expect.stringContaining('YOUR COMMON GOOD CONTRIBUTION HAS BEEN CHANGED'), + }), + }) + }) + + it('has the correct html as snapshot', () => { + expect(result.originalMessage.html).toMatchSnapshot() + }) + }) + }) + + describe('sendContributionDeniedEmail', () => { + beforeAll(async () => { + result = await sendContributionDeniedEmail({ + firstName: 'Peter', + lastName: 'Lustig', + email: 'peter@lustig.de', + language: 'en', + senderFirstName: 'Bibi', + senderLastName: 'Bloxberg', + contributionMemo: 'My contribution.', + contributionFrontendLink, + }) + }) + + describe('calls "sendEmailTranslated"', () => { + it('with expected parameters', () => { + expect(sendEmailTranslatedSpy).toBeCalledWith({ + receiver: { + to: 'Peter Lustig ', + }, + template: 'contributionDenied', + locals: { + firstName: 'Peter', + lastName: 'Lustig', + locale: 'en', + senderFirstName: 'Bibi', + senderLastName: 'Bloxberg', + contributionMemo: 'My contribution.', + contributionFrontendLink, + supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, + }, + }) + }) + }) + + describe('result', () => { + it('has expected result', () => { + expect(result).toMatchObject({ + originalMessage: expect.objectContaining({ + to: 'Peter Lustig ', + from: 'Gradido (emails.general.doNotAnswer) ', + attachments: expect.any(Array), + subject: 'Your common good contribution was rejected', + html: expect.any(String), + text: expect.stringContaining('YOUR COMMON GOOD CONTRIBUTION WAS REJECTED'), + }), + }) + }) + + it('has the correct html as snapshot', () => { + expect(result.originalMessage.html).toMatchSnapshot() + }) + }) + }) + + describe('sendContributionDeletedEmail', () => { + beforeAll(async () => { + result = await sendContributionDeletedEmail({ + firstName: 'Peter', + lastName: 'Lustig', + email: 'peter@lustig.de', + language: 'en', + senderFirstName: 'Bibi', + senderLastName: 'Bloxberg', + contributionMemo: 'My contribution.', + contributionFrontendLink, + }) + }) + + describe('calls "sendEmailTranslated"', () => { + it('with expected parameters', () => { + expect(sendEmailTranslatedSpy).toBeCalledWith({ + receiver: { + to: 'Peter Lustig ', + }, + template: 'contributionDeleted', + locals: { + firstName: 'Peter', + lastName: 'Lustig', + locale: 'en', + senderFirstName: 'Bibi', + senderLastName: 'Bloxberg', + contributionMemo: 'My contribution.', + contributionFrontendLink, + supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, + }, + }) + }) + }) + + describe('result', () => { + it('is the expected object', () => { + expect(result).toMatchObject({ + originalMessage: expect.objectContaining({ + to: 'Peter Lustig ', + from: 'Gradido (emails.general.doNotAnswer) ', + attachments: expect.any(Array), + subject: 'Your common good contribution was deleted', + html: expect.any(String), + text: expect.stringContaining('YOUR COMMON GOOD CONTRIBUTION WAS DELETED'), + }), + }) + }) + + it('has the correct html as snapshot', () => { + expect(result.originalMessage.html).toMatchSnapshot() + }) + }) + }) + + describe('sendResetPasswordEmail', () => { + beforeAll(async () => { + result = await sendResetPasswordEmail({ + firstName: 'Peter', + lastName: 'Lustig', + email: 'peter@lustig.de', + language: 'en', + resetLink: 'http://localhost/reset-password/3762660021544901417', + timeDurationObject: { hours: 23, minutes: 30 }, + }) + }) + + describe('calls "sendEmailTranslated"', () => { + it('with expected parameters', () => { + expect(sendEmailTranslatedSpy).toBeCalledWith({ + receiver: { + to: 'Peter Lustig ', + }, + template: 'resetPassword', + locals: { + firstName: 'Peter', + lastName: 'Lustig', + locale: 'en', + resetLink: 'http://localhost/reset-password/3762660021544901417', + timeDurationObject: { hours: 23, minutes: 30 }, + resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD, + supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, + communityURL: CONFIG.COMMUNITY_URL, + }, + }) + }) + }) + + describe('result', () => { + it('is the expected object', () => { + expect(result).toMatchObject({ + originalMessage: expect.objectContaining({ + to: 'Peter Lustig ', + from: 'Gradido (emails.general.doNotAnswer) ', + attachments: expect.any(Array), + subject: 'Reset password', + html: expect.any(String), + text: expect.stringContaining('RESET PASSWORD'), + }), + }) + }) + + it('has the correct html as snapshot', () => { + expect(result.originalMessage.html).toMatchSnapshot() + }) + }) + }) + + describe('sendTransactionLinkRedeemedEmail', () => { + beforeAll(async () => { + result = await sendTransactionLinkRedeemedEmail({ + firstName: 'Peter', + lastName: 'Lustig', + email: 'peter@lustig.de', + language: 'en', + senderFirstName: 'Bibi', + senderLastName: 'Bloxberg', + senderEmail: 'bibi@bloxberg.de', + transactionMemo: 'You deserve it! 🙏🏼', + transactionAmount: new Decimal(17.65), + }) + }) + + describe('calls "sendEmailTranslated"', () => { + it('with expected parameters', () => { + expect(sendEmailTranslatedSpy).toBeCalledWith({ + receiver: { + to: 'Peter Lustig ', + }, + template: 'transactionLinkRedeemed', + locals: { + firstName: 'Peter', + lastName: 'Lustig', + locale: 'en', + senderFirstName: 'Bibi', + senderLastName: 'Bloxberg', + senderEmail: 'bibi@bloxberg.de', + transactionMemo: 'You deserve it! 🙏🏼', + transactionAmount: '17.65', + supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, + communityURL: CONFIG.COMMUNITY_URL, + }, + }) + }) + }) + + describe('result', () => { + it('is the expected object', () => { + expect(result).toMatchObject({ + originalMessage: expect.objectContaining({ + to: 'Peter Lustig ', + from: 'Gradido (emails.general.doNotAnswer) ', + attachments: expect.any(Array), + subject: 'Bibi Bloxberg has redeemed your Gradido link', + html: expect.any(String), + text: expect.stringContaining('BIBI BLOXBERG HAS REDEEMED YOUR GRADIDO LINK'), + }), + }) + }) + + it('has the correct html as snapshot', () => { + expect(result.originalMessage.html).toMatchSnapshot() + }) + }) + }) + + describe('sendTransactionReceivedEmail', () => { + beforeAll(async () => { + result = await sendTransactionReceivedEmail({ + firstName: 'Peter', + lastName: 'Lustig', + email: 'peter@lustig.de', + language: 'en', + memo: 'Du bist schon lustiger ;)', + senderFirstName: 'Bibi', + senderLastName: 'Bloxberg', + senderEmail: 'bibi@bloxberg.de', + transactionAmount: new Decimal(37.4), + }) + }) + + describe('calls "sendEmailTranslated"', () => { + it('with expected parameters', () => { + expect(sendEmailTranslatedSpy).toBeCalledWith({ + receiver: { + to: 'Peter Lustig ', + }, + template: 'transactionReceived', + locals: { + firstName: 'Peter', + lastName: 'Lustig', + locale: 'en', + memo: 'Du bist schon lustiger ;)', + senderFirstName: 'Bibi', + senderLastName: 'Bloxberg', + senderEmail: 'bibi@bloxberg.de', + transactionAmount: '37.40', + supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, + communityURL: CONFIG.COMMUNITY_URL, + }, + }) + }) + }) + + describe('result', () => { + it('is the expected object', () => { + expect(result).toMatchObject({ + originalMessage: expect.objectContaining({ + to: 'Peter Lustig ', + from: 'Gradido (emails.general.doNotAnswer) ', + attachments: expect.any(Array), + subject: 'Bibi Bloxberg has sent you 37.40 Gradido', + html: expect.any(String), + text: expect.stringContaining('BIBI BLOXBERG HAS SENT YOU 37.40 GRADIDO'), + }), + }) + }) + + it('has the correct html as snapshot', () => { + expect(result.originalMessage.html).toMatchSnapshot() + }) + }) + }) + */ +}) diff --git a/core/src/emails/sendEmailVariants.ts b/core/src/emails/sendEmailVariants.ts new file mode 100644 index 000000000..75f5c468d --- /dev/null +++ b/core/src/emails/sendEmailVariants.ts @@ -0,0 +1,179 @@ +import { Decimal } from 'decimal.js-light' + +import { CONFIG } from '../config' +import { decimalSeparatorByLanguage } from 'core' + +import { sendEmailTranslated } from './sendEmailTranslated' + +export interface EmailCommonData { + firstName: string + lastName: string + email: string + language: string +} + +export interface ContributionEmailCommonData { + senderFirstName: string + senderLastName: string + contributionMemo: string + contributionFrontendLink: string +} + +function getEmailCommonLocales(): Record { + return { + supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, + resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD, + communityURL: CONFIG.COMMUNITY_URL, + } +} + +export const sendAddedContributionMessageEmail = ( + data: EmailCommonData & ContributionEmailCommonData & { + message: string + }, +): Promise | boolean | null> => { + return sendEmailTranslated({ + receiver: { + to: `${data.firstName} ${data.lastName} <${data.email}>`, + }, + template: 'addedContributionMessage', + locals: { + ...data, + ...getEmailCommonLocales(), + }, + }) +} + +export const sendAccountActivationEmail = (data: EmailCommonData & { + activationLink: string + timeDurationObject: Record + logoUrl?: string | null +}): Promise | boolean | null> => { + return sendEmailTranslated({ + receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, + template: 'accountActivation', + locals: { + ...data, + ...getEmailCommonLocales(), + }, + }) +} + +export const sendAccountMultiRegistrationEmail = (data: EmailCommonData): Promise | boolean | null> => { + return sendEmailTranslated({ + receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, + template: 'accountMultiRegistration', + locals: { + ...data, + ...getEmailCommonLocales(), + }, + }) +} + +export const sendContributionConfirmedEmail = ( + data: EmailCommonData & ContributionEmailCommonData & { + contributionAmount: Decimal + }, +): Promise | boolean | null> => { + return sendEmailTranslated({ + receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, + template: 'contributionConfirmed', + locals: { + ...data, + ...getEmailCommonLocales(), + contributionAmount: decimalSeparatorByLanguage(data.contributionAmount, data.language), + }, + }) +} + +export const sendContributionChangedByModeratorEmail = ( + data: EmailCommonData & ContributionEmailCommonData & { + contributionMemoUpdated: string + }, +): Promise | boolean | null> => { + return sendEmailTranslated({ + receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, + template: 'contributionChangedByModerator', + locals: { + ...data, + ...getEmailCommonLocales(), + contributionMemoUpdated: data.contributionMemoUpdated, + }, + }) +} + +export const sendContributionDeletedEmail = ( + data: EmailCommonData & ContributionEmailCommonData, +): Promise | boolean | null> => { + return sendEmailTranslated({ + receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, + template: 'contributionDeleted', + locals: { + ...data, + ...getEmailCommonLocales(), + }, + }) +} + +export const sendContributionDeniedEmail = ( + data: EmailCommonData & ContributionEmailCommonData, +): Promise | boolean | null> => { + return sendEmailTranslated({ + receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, + template: 'contributionDenied', + locals: { + ...data, + ...getEmailCommonLocales(), + }, + }) +} + +export const sendResetPasswordEmail = (data: EmailCommonData & { + resetLink: string + timeDurationObject: Record +}): Promise | boolean | null> => { + return sendEmailTranslated({ + receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, + template: 'resetPassword', + locals: { + ...data, + ...getEmailCommonLocales(), + }, + }) +} + +export const sendTransactionLinkRedeemedEmail = (data: EmailCommonData & { + senderFirstName: string + senderLastName: string + senderEmail: string + transactionMemo: string + transactionAmount: Decimal +}): Promise | boolean | null> => { + return sendEmailTranslated({ + receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, + template: 'transactionLinkRedeemed', + locals: { + ...data, + transactionAmount: decimalSeparatorByLanguage(data.transactionAmount, data.language), + ...getEmailCommonLocales(), + }, + }) +} + +export const sendTransactionReceivedEmail = (data: EmailCommonData & { + senderFirstName: string + senderLastName: string + senderEmail: string + memo: string + transactionAmount: Decimal +}): Promise | boolean | null> => { + return sendEmailTranslated({ + receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, + template: 'transactionReceived', + locals: { + ...data, + transactionAmount: decimalSeparatorByLanguage(data.transactionAmount, data.language), + ...getEmailCommonLocales(), + }, + }) +} diff --git a/core/src/emails/templates/accountActivation/html.pug b/core/src/emails/templates/accountActivation/html.pug new file mode 100644 index 000000000..6b5dd41c3 --- /dev/null +++ b/core/src/emails/templates/accountActivation/html.pug @@ -0,0 +1,18 @@ +extend ../layout.pug + +block content + if logoUrl + img(src=logoUrl, alt="Banner", style="max-width: 680px; max-height: 250px;border-radius:20px") + h2= t('emails.accountActivation.title') + .text-block + include ../includes/salutation.pug + p= t('emails.accountActivation.emailRegistered') + .content + h2= t('emails.general.completeRegistration') + div(class="p_content")= t('emails.accountActivation.pleaseClickLink') + a.button-3(href=activationLink) #{t('emails.accountActivation.activateAccount')} + div(class="p_content")= t('emails.general.orCopyLink') + + a.clink(href=activationLink) #{activationLink} + + include ../includes/requestNewLink.pug diff --git a/core/src/emails/templates/accountActivation/subject.pug b/core/src/emails/templates/accountActivation/subject.pug new file mode 100644 index 000000000..81749a38e --- /dev/null +++ b/core/src/emails/templates/accountActivation/subject.pug @@ -0,0 +1 @@ += t('emails.accountActivation.subject') diff --git a/core/src/emails/templates/accountMultiRegistration/html.pug b/core/src/emails/templates/accountMultiRegistration/html.pug new file mode 100644 index 000000000..4d568261b --- /dev/null +++ b/core/src/emails/templates/accountMultiRegistration/html.pug @@ -0,0 +1,22 @@ +extend ../layout.pug + +block content + h2= t('emails.accountMultiRegistration.title') + .text-block + include ../includes/salutation.pug + p + = t('emails.accountMultiRegistration.emailReused') + br + = t('emails.accountMultiRegistration.emailExists') + .content + h2= t('emails.resetPassword.title') + div(class="p_content")= t('emails.accountMultiRegistration.onForgottenPasswordClickLink') + a.button-3(href=resendLink) #{t('emails.general.reset')} + div(class="p_content")= t('emails.general.orCopyLink') + + a.clink(href=resendLink) #{resendLink} + + h2(style="color: red")= t('emails.accountMultiRegistration.contactSupport') + div(class="p_content")= t('emails.accountMultiRegistration.ifYouAreNotTheOne') + + a.clink(href='mailto:' + supportEmail)= supportEmail diff --git a/core/src/emails/templates/accountMultiRegistration/subject.pug b/core/src/emails/templates/accountMultiRegistration/subject.pug new file mode 100644 index 000000000..fb130f0e4 --- /dev/null +++ b/core/src/emails/templates/accountMultiRegistration/subject.pug @@ -0,0 +1 @@ += t('emails.accountMultiRegistration.subject') diff --git a/core/src/emails/templates/addedContributionMessage/html.pug b/core/src/emails/templates/addedContributionMessage/html.pug new file mode 100644 index 000000000..df5ba35c4 --- /dev/null +++ b/core/src/emails/templates/addedContributionMessage/html.pug @@ -0,0 +1,16 @@ +extend ../layout.pug + +block content + h2= t('emails.addedContributionMessage.title') + .text-block + include ../includes/salutation.pug + p= t('emails.addedContributionMessage.commonGoodContributionMessage', { senderFirstName, senderLastName, contributionMemo }) + .content + h2= t('emails.addedContributionMessage.readMessage') + div(class="p_content") + p= t('emails.addedContributionMessage.message', { message }) + p= t('emails.addedContributionMessage.toSeeAndAnswerMessage') + + a.button-3(href=`${contributionFrontendLink}`) #{t('emails.general.toAccount')} + + include ../includes/doNotReply.pug diff --git a/core/src/emails/templates/addedContributionMessage/subject.pug b/core/src/emails/templates/addedContributionMessage/subject.pug new file mode 100644 index 000000000..4ac85fa23 --- /dev/null +++ b/core/src/emails/templates/addedContributionMessage/subject.pug @@ -0,0 +1 @@ += t('emails.addedContributionMessage.subject') diff --git a/core/src/emails/templates/contributionChangedByModerator/html.pug b/core/src/emails/templates/contributionChangedByModerator/html.pug new file mode 100644 index 000000000..46bcd4ae1 --- /dev/null +++ b/core/src/emails/templates/contributionChangedByModerator/html.pug @@ -0,0 +1,10 @@ +extend ../layout.pug + +block content + h2= t('emails.contributionChangedByModerator.title') + .text-block + include ../includes/salutation.pug + p= t('emails.contributionChangedByModerator.text', { contributionMemo, senderFirstName, senderLastName, contributionMemoUpdated }) + .content + include ../includes/contributionDetailsCTA.pug + include ../includes/doNotReply.pug \ No newline at end of file diff --git a/core/src/emails/templates/contributionChangedByModerator/subject.pug b/core/src/emails/templates/contributionChangedByModerator/subject.pug new file mode 100644 index 000000000..791cee555 --- /dev/null +++ b/core/src/emails/templates/contributionChangedByModerator/subject.pug @@ -0,0 +1 @@ += t('emails.contributionChangedByModerator.subject') diff --git a/core/src/emails/templates/contributionConfirmed/html.pug b/core/src/emails/templates/contributionConfirmed/html.pug new file mode 100644 index 000000000..310993d97 --- /dev/null +++ b/core/src/emails/templates/contributionConfirmed/html.pug @@ -0,0 +1,10 @@ +extend ../layout.pug + +block content + h2= t('emails.contributionConfirmed.title') + .text-block + include ../includes/salutation.pug + p= t('emails.contributionConfirmed.commonGoodContributionConfirmed', { contributionMemo, senderFirstName, senderLastName, amountGDD: contributionAmount }) + .content + include ../includes/contributionDetailsCTA.pug + include ../includes/doNotReply.pug \ No newline at end of file diff --git a/core/src/emails/templates/contributionConfirmed/subject.pug b/core/src/emails/templates/contributionConfirmed/subject.pug new file mode 100644 index 000000000..c5bd41421 --- /dev/null +++ b/core/src/emails/templates/contributionConfirmed/subject.pug @@ -0,0 +1 @@ += t('emails.contributionConfirmed.subject') diff --git a/core/src/emails/templates/contributionDeleted/html.pug b/core/src/emails/templates/contributionDeleted/html.pug new file mode 100644 index 000000000..daf54227d --- /dev/null +++ b/core/src/emails/templates/contributionDeleted/html.pug @@ -0,0 +1,10 @@ +extend ../layout.pug + +block content + h2= t('emails.contributionDeleted.title') + .text-block + include ../includes/salutation.pug + p= t('emails.contributionDeleted.commonGoodContributionDeleted', { contributionMemo, senderFirstName, senderLastName }) + .content + include ../includes/contributionDetailsCTA.pug + include ../includes/doNotReply.pug diff --git a/core/src/emails/templates/contributionDeleted/subject.pug b/core/src/emails/templates/contributionDeleted/subject.pug new file mode 100644 index 000000000..024588472 --- /dev/null +++ b/core/src/emails/templates/contributionDeleted/subject.pug @@ -0,0 +1 @@ += t('emails.contributionDeleted.subject') diff --git a/core/src/emails/templates/contributionDenied/html.pug b/core/src/emails/templates/contributionDenied/html.pug new file mode 100644 index 000000000..d30653acd --- /dev/null +++ b/core/src/emails/templates/contributionDenied/html.pug @@ -0,0 +1,10 @@ +extend ../layout.pug + +block content + h2= t('emails.contributionDenied.title') + .text-block + include ../includes/salutation.pug + p= t('emails.contributionDenied.commonGoodContributionDenied', { contributionMemo, senderFirstName, senderLastName }) + .content + include ../includes/contributionDetailsCTA.pug + include ../includes/doNotReply.pug diff --git a/core/src/emails/templates/contributionDenied/subject.pug b/core/src/emails/templates/contributionDenied/subject.pug new file mode 100644 index 000000000..57431e56e --- /dev/null +++ b/core/src/emails/templates/contributionDenied/subject.pug @@ -0,0 +1 @@ += t('emails.contributionDenied.subject') diff --git a/core/src/emails/templates/includes/Chatbox.svg b/core/src/emails/templates/includes/Chatbox.svg new file mode 100644 index 000000000..4eb68e0bb --- /dev/null +++ b/core/src/emails/templates/includes/Chatbox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/core/src/emails/templates/includes/answear_button.svg b/core/src/emails/templates/includes/answear_button.svg new file mode 100644 index 000000000..f45fedc90 --- /dev/null +++ b/core/src/emails/templates/includes/answear_button.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + Jetzt antworten + + + diff --git a/core/src/emails/templates/includes/chatbox-icon.png b/core/src/emails/templates/includes/chatbox-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a25c7c209cb79912e5adab4b7732ca9858955e17 GIT binary patch literal 323 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9F5he4R}c>anMprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt&fkT^vI^I^Rxpt*$!I@bcilmcXRrOkP_`0>HiiLiq(xj+>07kDqMfHY{##Q zlh#;pR2gl4c_ETL@xfeC%dqcrr^TpWs_@NqndEL~zIij>tP8)bE11vjc5$h#TT}z| O8-u5-pUXO@geCw$1$z7d literal 0 HcmV?d00001 diff --git a/core/src/emails/templates/includes/chatbox-icon.pug b/core/src/emails/templates/includes/chatbox-icon.pug new file mode 100644 index 000000000..62775b0f0 --- /dev/null +++ b/core/src/emails/templates/includes/chatbox-icon.pug @@ -0,0 +1,6 @@ +span.chatbox-wrapper + img.bi-chatbox( + alt="chatbox" + loading="lazy" + src="cid:chatboxicon" + ) \ No newline at end of file diff --git a/core/src/emails/templates/includes/contributionDetailsCTA.pug b/core/src/emails/templates/includes/contributionDetailsCTA.pug new file mode 100644 index 000000000..ae3b77e6a --- /dev/null +++ b/core/src/emails/templates/includes/contributionDetailsCTA.pug @@ -0,0 +1,7 @@ +//- +h2= t('emails.general.contributionDetails') +div(class="p_content")= t('emails.contribution.toSeeContributionsAndMessages') +a.button-3(href=`${contributionFrontendLink}`) #{t('emails.general.toAccount')} +div(class="p_content")= t('emails.general.orCopyLink') + +a.clink(href=`${contributionFrontendLink}`) #{`${contributionFrontendLink}`} \ No newline at end of file diff --git a/core/src/emails/templates/includes/doNotReply.pug b/core/src/emails/templates/includes/doNotReply.pug new file mode 100644 index 000000000..506a5d0db --- /dev/null +++ b/core/src/emails/templates/includes/doNotReply.pug @@ -0,0 +1 @@ +div(class="p_content")= t('emails.general.pleaseDoNotReply') \ No newline at end of file diff --git a/core/src/emails/templates/includes/email.css b/core/src/emails/templates/includes/email.css new file mode 100644 index 000000000..5110c3882 --- /dev/null +++ b/core/src/emails/templates/includes/email.css @@ -0,0 +1,216 @@ +/* vietnamese */ +/* @font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 100; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} */ +/* latin-ext */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 100; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 100; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* vietnamese */ +/* @font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 200; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} */ +/* latin-ext */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 200; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 200; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* vietnamese */ +/* @font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 300; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} */ +/* latin-ext */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 300; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 300; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* vietnamese */ +/* @font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 400; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} */ +/* latin-ext */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 400; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 400; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* vietnamese */ +/* @font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 500; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} */ +/* latin-ext */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 500; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 500; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* vietnamese */ +/* @font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 600; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} */ +/* latin-ext */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 600; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 600; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* vietnamese */ +/* @font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 700; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} */ +/* latin-ext */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 700; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 700; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* vietnamese */ +/* @font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 800; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} */ +/* latin-ext */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 800; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 800; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* vietnamese */ +/* @font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 900; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} */ +/* latin-ext */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 900; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Work Sans'; + font-style: normal; + font-weight: 900; + src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/core/src/emails/templates/includes/facebook-icon.png b/core/src/emails/templates/includes/facebook-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1d880cf3e21f0eac022181f0540859e65c9d2635 GIT binary patch literal 1449 zcmV;a1y=frP)WmhDt1z%7D7m zQj!Z6h7TAE@2=H})G ztJUgd0AnOiq$Rf=08dEX&gb(tj~_q2l#0x)3YeXp9a>&q{!~P+kQ_?Erj`I$6p=}# z)P(Q*e{V@vrwvKM7&A)pnurW<#fUAi0N`p61a|D)Tqo_*hU7GW zQn6V4z1_BL2ViDq=3Ep-cS#;;x2;P6%zK`9sZc0Px6@9wQ`s0Z7DdthUUObVgyfMZ zitZa@#@cDOsQ{4lwAMGQwYNz2cV?n{NcLN6Z)vS>kn}dwrsWVI=^110lDxQe6SD)r zy&woK0f?H_YuN*gG1s@7^CBW7FB)U6x2o5)2N+|>P0`~LH!=f;>1NRC@; z-&9KNtIgWh+Sh^fPVWGWiO5$XaR9W|p{rx8b^Mm}z!+1s*4~Q^l=>edP3J{KNS@GI zhhZ4%+7KwEsv()bni%zdMmE$aiNtdN$qN}NT~2yH^0ka?sOh^PBBCml%3%PfvQ*v( zq}|V3>pNAcR1T~7rA4O7dx3b#e_oM1*==LG1<8{+lE+*K2k=FMvPVto|0smDSX1t5Myt$I2D0GtItDK*&Ei;*tjatMgB)~52B*&!~6z*0^rwd7(Ms! zi^b7K^^GxKlKiAWn|#`~)lf<;}yhgz@@VZEGUwhp3C(TB%dh& zPrGeMw*c@|0eIYPL%Ick#|prMPSZ=aGXMzSf%1L-1S9^n<%7RjAqP}2F08XDiy$IlHwl-x4 zfUE11t2Z2o1VM0zWLxQ`+X0f(K@i+oU%nwl1^~+%K6v_OK-_m)JHgAj=W5!5+)2kCBfR_NqTXp+2ZQ50`tFO$6y!v+_dA64p zOC+aBo^988k-ckkdlx77uFj?*WIem`@^litetrIbG9Al@qI{v(00000NkvXXu0mjf DhkUKj literal 0 HcmV?d00001 diff --git a/core/src/emails/templates/includes/footer.pug b/core/src/emails/templates/includes/footer.pug new file mode 100644 index 000000000..91a0e4543 --- /dev/null +++ b/core/src/emails/templates/includes/footer.pug @@ -0,0 +1,77 @@ +footer + .w-container(class="footer_01") + .socialmedia + a.slink( + target="_blank" + href="https://www.facebook.com/groups/Gradido/" + ) + img.bi-facebook( + alt="facebook" + loading="lazy" + src="cid:facebookicon" + ) + a.slink( + target="_blank" + href="https://t.me/GradidoGruppe" + ) + img.bi-telegram( + alt="Telegram" + loading="lazy" + src="cid:telegramicon" + ) + a.slink( + target="_blank" + href="https://twitter.com/gradido" + ) + img.bi-twitter( + alt="Twitter" + loading="lazy" + src="cid:twittericon" + ) + a.slink( + target="_blank" + href="https://www.youtube.com/c/GradidoNet" + ) + img.bi-youtube( + alt="youtube" + loading="lazy" + src="cid:youtubeicon" + ) + .line + .footer + div(class="footer_p1")= t("emails.footer.contactOurSupport") + a( + class="footer_p2" + href='mailto:' + t("emails.footer.supportEmail") + )= t("emails.footer.supportEmail") + div + img.image( + alt="Gradido Logo" + src="https://gdd.gradido.net/img/brand/green.png" + ) + div + a( + class="terms_of_use" + href="https://gradido.net/de/impressum/" + target="_blank" + )= t("emails.footer.imprint") + br + a( + class="terms_of_use" + href="https://gradido.net/de/datenschutz/" + target="_blank" + )= t("emails.footer.privacyPolicy") + div(class="footer_p1") + | Gradido-Akademie + br + | Institut für Wirtschaftsbionik + br + | Pfarrweg 2 + br + | 74653 Künzelsau + br + | Deutschland + br + br + br + diff --git a/core/src/emails/templates/includes/gradido-header.jpeg b/core/src/emails/templates/includes/gradido-header.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..38a5c3a80f090b8973db6d603d406249f7af7f04 GIT binary patch literal 9427 zcmbVx2UrtZ*Y1QAKoRL6ND-wicjw8>J8P}|uD#y9XZAeF;rqi$fJ{|MMG1gF0007hfWv9R z_bQhzS!n4Xl~mL(e|HeJTDiG75wHM&vx_HINBJVlRU=~-!XbbFI0YOB=z-%_C=a&_ zT3YG=*zmFV^WWR%0G=HHdU^ld|M%#BTphPTd!WDp5`lJC8xNE-h@VTR3Fd;4zIHGd{50A{AFm5`Rso)u>hCAD;#LE1OA=o#|Y+s^0>liVFEB=@GA{dhsnYuV3Gg} zOdh5HlY=ROR!NxjZ*HwW>cEd@9?bHC#~sXw{vKxlR{RRA%oDHzk5gcTE8y{);cr^p zqsMXn(C7x%0)y&`UTq1eraOx5hgF#NlC%W;p&16`a{L|fch!*pO?&t({1dz%JPyG} ze{-Y63TSj^YId1(pQ*1s)4z2`oTkp=Hn-=u2oN^bNoQt%tsX)@&7*OKi1CgIsaY<-iEINzaD?p`=_Gd`Xs$b%0POSRFRaKl#NvVcN7aLEvX>s z6;ck+!|;1{j#e|v-*mtLMxbWD)cW^a_&Wphn+en!asr%Z%u&X_-5>hkh%taS$cY8j z$FDONz!!}D^FI4o5sV2Y2vhmTs+Pd7_21k-?uTFV@k@!n*MLS`MkpdK04xY@gd{=` zp^C@9?r#JK=#vDG;8V*J?E~&D0C36G%@>QYv-e~X5)cq)xd3i;XciS06dw!O(ph66V!+QPT12^Wk0N`OOEcE>vLUIv&c8wn%u7l4o!WaP9zkhhRn{jx!p9%7h z0zkdf;RtXGfD#Z85fLHqUj&>GK}d)Izi>i)0}%p__})N_w;uzoM8w3z2>9_6$B0fK zh=_?GP#6r1B0|84i16P3=XO{RkP`tB1W%w4a)5vw0wsqWHUf;GI0R4#*nUKgPEq&fR7`B<>t2w70nZHqphx`Q84!eU&?!I;dcbQK3=W0=h=34~Ln+R{;DXu+ z)(cib7lZG=qI7>ubynv_x5(izKmrnh5#)e8aPTzf_n7|;AkM*P9%r`5=2XQ;mE~$y za6VIG4%<7IkHBGIKr@($yE{Q zjl#iCZa0^W&-5m3U-J~n@0;WDlfSty$KKe~R9{iifNnY+Y1urJWjO$tLL!z;O)V?n zO$dnF@gOapljZ(lJpqiu2x(`oSHp+EvPPTyzBH4*nR()c#{331nWySPzteaz-*M_=`8$v5Ljk5*JnAsRFu+8pVoY>Sj z1Q=wPT=)wljf*1bWjLs?mtZ>4gT2Sz8ZNbM?A5J$l#yeE6NK8_#5nsS=u7RD&(Zhk z+$|=VbbwI=KcUorB04y35k2w2fx$GNOD*%`>iTY7aRo)Z+wov`0e;mlCm z-c(<6KG^MQZ!u$nKKyNG>nr}RD*SzY3I{jrCAjnEp9L=I55f;xIS)ebla16wo$bRF zCw?HbBZrJOm;p=Ed_g!)1=9jztyBM_&VMz~m%@D;Nxd>}pK5kXK}qGFy66_8DHIW_ z6A?Qm(axKbrq?FBd*#4!SIb`psnq7JeM(KSt$3T_!@*nBShci;Y|Mv+#HJ{ z=kBj66x9u@x9JzW3yo!oz%0Tf5P4-#O7%2|$ops}* zi_+$P9C!Q%mtw`4loP0{k&SuL&Xq1T28Y1I0?NUs8op}*hd`Z0W5!#*!De}nedaB` zyHoiWcFNX~8>8cexgn+&D#|MsPxZOcL3}{HXpq1O(dhJR@gmIr!fb^q7w@R`*K#d= z(GI&VtojW_?g`BgCzIN~|B29pLKaxIRwV5df@Z4{yaD3=SVgZ8bfd~2{JqDONFO4j zAKf?L)hL6DjU{v(JfeD~I+A-}JjGEi-tn*@F*QZI-(T@kIWzT3j-28mqJG&*L7$n^ z&eCGrs-}V^yPQqavlCQ~7WQSzHWPJ3B9&P?OhsL594FZQ&gSpQ+l9C7IB)0X?$v1z zAsv!jV+^fL!fAU{-;X91)S(U9uIttpHKphV)JUNwaKi^GZRBO8$oaI6VUb4;`IEVN zQ#Q;g#@7>-{6j<5s^!>=WxVbVPJUUP2pjTwV6L1p4YSGnqpTy?oqi?-$h~G6s9bJ# zx23D4ik7=#EZoT}&O+}H2x(O-;JLY#$@W}5AyOrD-M=ny66~lzt*Dy##%e!?b|Iq^aQTmxfI7 zqXxH%+RBEgokg`qZVgiT?L5C@tt(jyIVmHvl7 zeC{C-yOBh2AnrNrZBe3g2yDr7$Of&MJO5QIzFqm4zgblwuuG-a@tND;vCpCA z^iy=oljHBSO_t^>827FQ_csMGMyP&+j5Gu>zPYjiEsA6c@;t9#6b5xW(h#amw5J>( z`^hlNawJh=`^9ozV++zQP`!8c>4aSavzKnlW`OAHmaf|zlRjOv$P+CxiTg~*(8*NC zjfS1we6+2Dk#0vvN=eyq^R}9P+=ZH%@JN#}-RP5Z(UQ(wD(xE2XHu;`B(Bjue3-Hc z7Iu3zeyUJWUU!J}b#oa*Hr)uSxanz5Urj&Pa;%239Z?}olJe9#)(B z&$}0d>F9pQ*qXSlNwLMA{oIt-WoOZQ^?)O-!I-}V6JeJe9i1e_PtN~%H+$ujgT%6K zSgzDV?k}Gv;_P0my*Rz)ImYbA-!Ty_j=0v)w$~}+N^AHry6I%9&kC^|b1X;ir~U3l z`n`dRK}S*^A%S<~oh*hWELyEuE+^k?=02yI({+)%>{dtfzG6skU@c~^FZM%Ll6yd} zdMv#huo<*zTBjEOP$JLQSZ?>FNBsMzbz?Pb)p=&rZCH!y*bjS-?lY7pVfJ$o87XQv zPSsn6xzn-65#Mz9lFckT_-fy!_KRh?K~CC=f6t< z9P}cW0evI3V1Q^2dR4@oPQ;Dx#OX^{8^Ar18}&)v&g^6;{ch4n+^FF7xn~{+DZ1C) zD?(B`^bxlg{ebxy9YPbDds6R9g9n?lNMGfCt5yZwM>CoHeFi%=V*@pspBPf@#XZ|D4Jeu}VwCTXqT8!2^7x{8 zGRAdfPyW<KXGKde>K{q1WR>6+1`58K{jY;TuJ zWPG|zG%pn_`^jsBK2~j?Vy3S`>!c)MCnv6DvuE;V*6x{>Pq)wsNz%|H(@pGCyAacy-AXJD+RN@NTKsDKf<`{9KJ>j>~XXNrqat3 z_Z|D|gqj;`;@{VvFYHKKSlxMKb|#uhdUb_KwtQ;L-Fa=Ka6D7@^P8NQvC_n3f9LhF zFDfT5OfybxB|b!A{P}|~7aO|-^5xoU$WXPJ$OQOmhE7+gwkip$?8cbvEW0No^#tpL zxAyxTq_Q8R_%+e__$5YTSBrVQdMCLKUc0#HZq1SAhvy!5;H@Z(UtzG#ms_dd`Z#WD z&Yu1Lv!F|aAdc7EXQ*2%r^%U{%;6X|B1J4VaO1Sa)9_(0)>Fwg&9Wt5cflgl0 z=NdTVJ!aOeuZ#W+j1T?PQD$AfCb`&1k3ejbE828opG%Z>c6vunET=d+8pkNro-4I< z>vJW=R7w6>PE86k6lZDD^>=d-(^(ldjY7c%f+QINjXHNz$SX+5D|VjsZl|*q=_IXB zo7asw9jx*+7@1>=^q=oBHNBF-(aFo!`WJO2PV3*wQ`x}mz1Tlh<}l5QhzWL6=6-vE z?P^nV6ug1FvK?hX^h~a8`Q!6c1P!B!Gn+bE{_FdSk9B!*FAhRN7aW@izS5<878fgg zoj7V?RIA~c~qO9e&wcXJqI8 zX{8t&^SI4zuxUo6=G)VDfpC*`ckQvu9@zOcdDT=iOxSq$myTEE;Za|Ux`$idoweia zd>}g<>80|t=H*I>4l>+pN3JTnb$xzv!G6cNchjeQjnb&Y&@}M1!;*spS+qF1Cz^jw z-$=49CPLiq{wyx-YqdL5GLLWQAs~7PJfDwUTHV>5oj^qzSMwYU%%8$WO3e(G2%lvz zj(q4dccZaeqNWib6K)-Nqe^7)g>&f z-Gg(s9%H7So#pZ4Yu22TC!o}N;cdeC^gLA}2}_J=r)OkA2RUPzT~*Ya$YSSPli|}5 zi!H;_f2|Ai4dz&1aE43``-6{_%d!%ksyG@Pq=Gg!NqMw4c%i%O(w%I6sq zdY0N-8+&k3eV;uY9+JJ#@5JfjTnd;OYo<=$HDY_QD{ks5lH_xDiA>oiG}}9Ebl_3O z!dOVY=A%A+ufIOZiusCL7{{Faz^X2X`Jc?ssk> zZx2VW?JJu+=it!IH+<7j#E72NW<1Z#n5~G(t2#qJS57&TVrsJxJ5($7^=i2K;+8|5 zaNqvjDwEjwtl5jZi=9P;`fuuAcNV`ya!@j9t@6f4U#CQi!+9q-n_RXvQ_QAumB{Xm zQ)BOrZVwRE)3-rjlW>zfXxjZKLXPuP_Fn?GszP4%Ki>86F}iP}pR5yB$xQTisjMt_ zk8+~BXsrKj=L;+SZJE?ht8CzoKy(OX7&I(vCs_o$n|4%1 z)L%OrAZMs{2$b9x&`W&2%rGyLeF$L7clrdJ4WH*QKYHl8-+z6Fgx0+#ML=fUwAQOB zP(OD&&TCaRZKJiKj=;kmlPg`AD6(ae&Bj!l44D_1@`GRtSonHF2-O&fz{d?4V}O%@ z)PrqHoZuR``J*7+E$O6@qQPDj!PZ2?6i}8UWf55i4UIL~yZknJxk81!#oPI9+Rmc- z$!<2XvEw4ciL{lQw-aPv+?2b%hD=2Ao=v?!o_2RJRb$#ZF;&{nq6(Z{~XX*eZ&MfWfFN^C5hKUQ(>Bc~Dn+%22IwwFuP zTgchKG(uWNImgcZR6^dD9A*C3CNd5Irb4%=GHkfZQ-jQEN)-u-nz)QjWa0d}JXQwn z%hla>ihMh;s}p?QIgAv>)~+q@B~%S_UJ4|YR0&+E%$zG1n_CT3p7OvVbD5X-<)jol z$NYynp6j%>S82t7Z?Mg`*4)|VoWEeuW`rF+y>xk0m6FIa9LvlK+jbpG%q!#4EmH^P zRUetyrQHn=CK^8Vep=D{>f7R1+a;w`x{w-p&Z+&Wvw@jzIWs%?1djR|Wnl}|mV+=h z%thne$i$sw{EzI z96AI_O}vL(^-Uy=WZ;R3r%%$TtT;3naBpW!P@#5<9e6K0-H)DE6nwX`klHaUrj#6- zZ4@mYd}E|OsrGAZBnOvDXF;tvHZGf^GA=87c{%ME_sHQT}(@h0L; zRng2XQ^!J&_+-^P?3=lvH@&Mg*4ca(auvmvR!Z3KKAAH~be0rBTa=G5F&Sv`2<_EP z=jUkOjx5wvQPNp=UZDU{>IpnGEh}sfXrA-XJt&fRPr;kU)+D9GRXeHM?2j@?ny55r|VzH1`)R|~Q$yr7o+t3CL`+`>a^=MI~M z(U_6MSLa@yOH_2$mQfJ7YUz5Kg%?~epG796s4(>w`AC8>V;`FQR~rON5-gsAtH5WJ z<-QZ4K?IAGs5QiauHHBj(4Y@WASciZ8e{o~1TflZC5sxmj(zR$y@^(;^<%^*9M|XGYxbtU#=L2+y`AZT!j~ecMBMcgVK*w{3mKqQ05FKnS?uH2qNE4 zdXxw%e6X?hgLJgMhCYlBuAR^7o$dW}z?EC+xC(8Pc3&LI{vfH+y=fXcuxOCyd3cKpvn`ja5b?|y6VI?eKu&JG4kBrGXe3a(N7feek+eQ_~ z>yiea~#|g)FAFoav2J_bRMdPh8V(tkeA118 zx6c#!^ncw5iV=X~0P!m)V*cEO@k&@efGb_RRS*7Q{U4iE`q?;zFgJpl(qPSDATvJudDOoPTykaL3dsZ_x-=LziV!MBt@e&qU<=)ayJkwN{9L~~ckRsUisxyL;e1y^$q zdI}Ea+3O90M-LPX6KHb>rx9mw!M;vG{J^D7Weo`|u;jf&N30KAdHU^}0=0ruKGdiF zM8BoWJ%)cS`~RB%XFz|tXq-Zn7Qv}L7>yR(cP7~2jpg|+G9vUH1PyaK#o%}CUusIw zOleUn0V?$MT_*>7AoU0zx!Q6lnvCJvH36~zZLWXDK^Y)XWFl?^k_trGTI8xhXA&gH zWh{@=otTM&t!5EV0qnPcc$WWf`k(2bC%$KaDqp3Ss_*YZP+M4BIT3H^lnx66uG~BI j&(;0ULH_q^a3&iR9NrNEg7pYJptBzRpN33$IP`x25HL#K literal 0 HcmV?d00001 diff --git a/core/src/emails/templates/includes/greeting.pug b/core/src/emails/templates/includes/greeting.pug new file mode 100644 index 000000000..6693e23ca --- /dev/null +++ b/core/src/emails/templates/includes/greeting.pug @@ -0,0 +1,6 @@ +//- This sets the greeting at the end of every e-mail +.text-block + p + = t('emails.general.sincerelyYours') + br + = t('emails.general.yourGradidoTeam') \ No newline at end of file diff --git a/core/src/emails/templates/includes/header.pug b/core/src/emails/templates/includes/header.pug new file mode 100644 index 000000000..3160d3e2b --- /dev/null +++ b/core/src/emails/templates/includes/header.pug @@ -0,0 +1,13 @@ +header + .head + //- TODO + //- when https://gdd.gradido.net/img/gradido-email-header.jpg is on production, + //- replace this URL by https://gdd.gradido.net/img/brand/gradido-email-header.png + img.head-logo( + alt="Gradido Logo" + loading="lazy" + src="cid:gradidoheader" + ) + + + diff --git a/core/src/emails/templates/includes/requestNewLink.pug b/core/src/emails/templates/includes/requestNewLink.pug new file mode 100644 index 000000000..8baa3d6df --- /dev/null +++ b/core/src/emails/templates/includes/requestNewLink.pug @@ -0,0 +1,10 @@ +//- +requestNewLink + h2= t('emails.general.requestNewLink') + + if timeDurationObject.minutes == 0 + div(class="p_content")= t('emails.general.linkValidity', { hours: timeDurationObject.hours }) + else + div(class="p_content")= t('emails.general.linkValidityWithMinutes', { hours: timeDurationObject.hours, minutes: timeDurationObject.minutes }) + + a.button-4(href=resendLink) #{t('emails.general.newLink')} \ No newline at end of file diff --git a/core/src/emails/templates/includes/salutation.pug b/core/src/emails/templates/includes/salutation.pug new file mode 100644 index 000000000..3e2591951 --- /dev/null +++ b/core/src/emails/templates/includes/salutation.pug @@ -0,0 +1 @@ +p= t('emails.general.helloName', { firstName, lastName }) diff --git a/core/src/emails/templates/includes/telegram-icon.png b/core/src/emails/templates/includes/telegram-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4c36ff66148cb69d8c8f9373b33bca573c251ed8 GIT binary patch literal 2159 zcmV-#2$1)QP)bMFuniA}Vr(Ws3f zX=+J~xF)q|Qi+NFAht>4#}8A5HmO9usx}fq(cTEGv}W7Iq&=4^ZB0l5?w~ISX?6ME=fa@mXo|4z+&fI z))k{%kk(V>d{nC<*|qmvj%wRg&6jhomIEO!9dE zdx9W{($zFY2b?%@B0DxV_8`e&Nq3~hTMv@QCGGC-@87;+#fl55aCS;iDwP&pzI^#> z03MdKCtfU*0%61bZ&v>4o zDqCxZtJUg(`DQ!-NV>6Ftsbz}4kwXo$pMnQD2lc?=XOcDu00jI4@uWK=XOO=v_+EF zicHfXAjz}VJ}c?o&Q-j6Ai3Z7{dFX(bJc6w1FW@=zuSxl07>^+Yaee`Z_XZItsQpG z?MhFrs}Ez$mweyf-ROBk2gI}ef%!blvcpRgB-dJNH_Rxn|5GZJ7M088Q+Vh0 z_T9!g&+`WB4^UV1fcR85uZ<`90?98LV{#-nClp`ooZEB-U+u?<6DP9cX^4$H}e)h-ZN0TL7K|u*3KLgL8$l!Z7RsurZU#{3Vymy;T3KR4Uz6E|=Th ztVy~D05Zj5afx$osN3V8Q)em60^m2~h)B06PZ;27cZ^`B@Q$;ioQMyts|zG626E7#R5c;Naj}v(=TTr#7hqwkxTn0SAC8ov#aw`Ty{ zkW^688^)Mt;;rfNb{ej)TaZS<&thJx4R4SvAHq{#sV06KP1s_goe6d(u z0^p{kddvpr+=YxW=7OZA6QQIv)oS&nD2iSMun)lAAP7oxDe9bi!a4WAELeZ{yCsjJtw{Oga41jY0KA1p(+W>3^u=()e!zW4Z%VaWpbGh7Ttxgz*U#M2AKLqgJ z>GCA^FI~FybIX=3n`l$-)EofdTt?C<0EKp>NiGNQNTpJFB#NTr0QLg7OVZs9M0WbV z|4@SC?hK@{1FYjZl_7aDjU^?HCp-qAk#XNH6bg@a+)#==faFO-ay{O;nJl`sdaynB6-N<^ZB!qUQUtN)E{C< zo*o(+`gf^R>Wu>&Z6oFJ|8hQ`KWhLW&cjQKn7VROISl}J-g)OZFcsx#3(mPT$N%%d z+fm^?0KT3U`7}&jNEQl(%dvyZTu0qGkXjGGq|K8zLqQNkB-=XG%?3`Q)(cKgdnRjb zJ`BSjg<-g`RnB6ucs+nyQ_}BvE$g&?VtP6aasZHIB)Pb;D;!|0y<5^j02?L!wNxs# z6p(Swr8)km4s}xw=;)fJm25n zzrFT<$^r3R*^_BBQrDPzdU{?50Bh|-B!4kW#z_{yz9@>eG?^gNngE}y&ojPaZSLvm zc>7Nx}umvm_tzegBWOvM>x6$7wDdh3`oY2SISfTsMPI62mb3^E;U(5-*XDEnK+p zsqyjg>q%~_-Q=_jNh5_q;Um*`z1NbYW)mnB3a35KTSv0oef7GFI8AO{qfGLKlTt31 z8zH%|d#YU>klZ*|2Kk&PG~f4kliWI2o%sxsTbo6)uO&;jKva#1-ZecTe)CO@2NHfU zVyzvPv~9lT50fmD+?eD7=h`PTNgK-LawC$~NJ`XDHz8?6(wd~k1J^#AyY}JawNGc$ lAeoN;%a5lUVZMJp|6hafLt8=zP{RNK002ovPDHLkV1k2FAsYYy literal 0 HcmV?d00001 diff --git a/core/src/emails/templates/includes/twitter-icon.png b/core/src/emails/templates/includes/twitter-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fffb9b59939b564a552879830d1244e319c9ba8e GIT binary patch literal 2002 zcmV;@2QB!CP)2v^jL`{t)V1_VYpM$PXVmRA|eAWko=M7c?U=;4MC~~@O}SQ zNry;oNn@WkBpoBUqg*b(o76UO0~|kod`le1uSmK%scj=c@_eCC*xuFEbu5XUNm4ir z!|phak1RIl0YK8taU35B!>~Jv-Mkt=QXvR}mz;BZCAFlJ*my{4an9`xg5V`dh56Xj z{RAWx!Z3VA(zfgp7Y-zU?|I&KlJQ*S>h^#z3}5`PIS&AmwuNE%V!d*6_JA-9yPb1; z^OLpkFvdLQdEUO+exJ1gs-yjp#r)0B2PDS|g~GkF%?>j*fbaXal6-41j`nFl^1P(Y zGp!S*jRBG<>CpA!JaA=-tok(VA3m)C!Z6&qSXOv>g5;Jk40ld@U;9w0RF;p8jh(>< zx3+IIE))ud&(&_AuId5RUETHSJaFY8=KHlTrg}hMUtj0Q$jCbjwWY0tnB*XUk4ajR zCo##39UUFFO`a!A^?;F)k?(wv&P$pgxu;MlY_ZlZvDV&Vt!*{N+)478>KVWkNM0tn z2f*tI>sdZLJpB0NbL}UPXcJK*L;xNlc{q)I6@PDOX(?H2pD2||f301YeNl6qoe(4dM?SbwKkaS0S_l7s3H0-0IXWI>VIu*ZQDq`k;L8@ z^KcsH0bq1=^z#6|LGsrr%y$6QPe9U_(g*+ml7E~0yI3p^d!F}Y06!t==3)N2wf4=l zbz5uy(9zNHmUHf|q_&c_0)UC4XeG(@X+;1St$iTr%H{IY0JZ`+F_)n+=64zD1pthU zj2s}jCaE3C^-&b9G}WL*>cdgzTyduN*4iW1+Ad?vBLL3KV&0RsUf=hdgCO{or0r?! zt4scSnk3EWzfP(-IhGAHC&V7^Qea^YnB%7Rb7iY7bN4+mg`g&S>0FYEP zk`i~HS~%x+Mp3k~UOCV6{BpVcLu+lZy}i8yz{a+=wp4>307$ZFZEHBzG?82*r8-gv z0FZp8+70*2*QQu3j-k%@P!Ezi$ZT;PlB=5l+?0W1-1EH0(gr2cqIyWXEC;F;#yQuS zfuQ-si4&j7K}aI>^z_^TpgAi~H9~E&GyolD#?c6-e&y?d@$yBq<1j2PEB}Cnw2X zL-J@|D>+GbU%q_#U{6m^M;?5Ws8lMScFrBhlar)K9yO&>X+YA+yojB14~>tHhfx%L zRZ>${zW)CHC1Yb_zag1r$vzE|PL@ig0RsTlJiI)y+BtV1ilRreko5QWFBuvddKJJI za?&-0NuMVNltVd@UL<+g81wCA%a+~dd0y(!dT(#<(xIWDS0&w-hqf7T2&f5+1VPZ7 zB*Bs7JI0u;aU8ccH8r`3iHY~EweRIIoH_p!OTx58#Ca zJfCsSJ=)yd{QRa(n?~{w*3;9|F)=am1LxdR0CJ58)k4x#!bIlKn56fNF+Xo_Z{M?S z-MYavf}$w;cpS%%0C)<(EonIBi)%w;R~@MYLGY}kXVMT$`mZtOu%y>oT3Y_HapT5+ z%vHWpsoWmN@mW=_SvcDYeeN49)F)?ucSc${~=kBw4CH8s)wSv zPHXeTwD9<}L$x3XzE%5;Tu&g`ZLQsZ)w^p8NuuxjZ(eU%B9e}k%jLVTou#I2EUHVy z9VE{$oV><@N-YpSL38D z3`p*pD}#K_y|w3g`$+E2U)I8ahzpWd^u1J4o6rDN#j@grsAVHYar+xN&Xn#>L4SS7-Ae kxfUPE%hR)Av0tD6KRO$}cDgFm=l}o!07*qoM6N<$f{G#L5&!@I literal 0 HcmV?d00001 diff --git a/core/src/emails/templates/includes/webflow.css b/core/src/emails/templates/includes/webflow.css new file mode 100644 index 000000000..45e4420c4 --- /dev/null +++ b/core/src/emails/templates/includes/webflow.css @@ -0,0 +1,166 @@ +body{ + display: block; + font-family: "Work Sans", sans-serif; + font-size: 17px; + text-align: center; + text-align: -webkit-center; + justify-content: center; + padding: 0px; + margin: 0px; +} + +h2 { + margin-top: 15px; + color: #383838; +} + +.container { + max-width: 680px; + margin: 0 auto; + display: block; +} + +.head-logo { + width: 100%; + height: auto; +} + +.text-block { + margin-top: 20px; + color: #9ca0a8; +} + +.content { + display: block; + width: 78%; + margin: 40px 1% 40px 1%; + padding: 20px 10% 40px 10%; + border-radius: 24px; + background-image: linear-gradient(180deg, #f5f5f5, #f5f5f5); +} + +.p_content{ + margin: 15px 0 15px 0; + line-height: 26px; + color: #696c72; +} + +.clink { + line-break: anywhere; + margin-bottom: 40px; +} + +.button-3, +.button-4, +.button-5 { + display: inline-block; + padding: 9px 15px; + color: white; + border: 0; + line-height: inherit; + text-decoration: none; + cursor: pointer; + border-radius: 20px; + background-image: radial-gradient(circle farthest-corner at 0% 0%, #f9cd69, #c58d38); + box-shadow: 16px 13px 35px 0 rgba(56, 56, 56, 0.3); + margin: 25px 0 25px 0; + width: 50%; +} + +.button-4 { + background-image: radial-gradient(circle farthest-corner at 0% 0%, #616161, #c2c2c2); +} + +.button-5 { + background: linear-gradient(135deg, #53900c, #6e6e6e); + font-size: 20px; + font-weight: 600; + color: #f5f5f5; + width: auto; + box-shadow: 20px 20px 25px; + transition: all 0.3s ease; +} + +.button-5:hover { + transform: translateY(-5px); + box-shadow: 20px 25px 30px rgba(56, 56, 56, 0.4); +} + +.chatbox-wrapper { + margin-right: 8px; +} +.bi-chatbox { + margin-bottom: -5px; +} + +.child-right { + text-align: right; +} +.child-left { + text-align: left; +} + +.socialmedia { + display: flex; + margin-top: 40px; + max-width: 600px; + +} +.slink{ + width: 150px; +} + +.footer { + padding-bottom: 20px; +} + +.footer_p1 { + margin-top: 30px; + color: #9ca0a8; + margin-bottom: 30px; +} + +.footer_p2 { + color: #383838; + font-weight: bold; +} + + +.image { + width: 200px; + margin-top: 30px; + margin-bottom: 30px; +} + +.div-block { + display: table; + margin-top: 20px; + margin-bottom: 40px; + flex-direction: row; + justify-content: center; + align-items: center; +} + +.terms_of_use { + color: #9ca0a8; +} + +.text-block-3 { + color: #9ca0a8; + margin-bottom: 30px; +} + +.line_image, +.line { + width: 100%; + height: 13px; + margin-top: 40px; +} + +.line_image { + background-image: linear-gradient(90deg, #c58d38, #c58d38 0%, #f3cd7c 35%, #dbb056 54%, #eec05f 63%, #cc9d3d); +} + +.line { + background-image: linear-gradient(90deg, #c58d38, #f3cd7c 40%, #dbb056 55%, #eec05f 71%, #cc9d3d); +} diff --git a/core/src/emails/templates/includes/youtube-icon.png b/core/src/emails/templates/includes/youtube-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d135ba625d2749adad59d87623b082797418f5ca GIT binary patch literal 1802 zcmV+l2le=gP)A6O75=_ElNjVNA;O7-5TOgwnA%F_y_qafL{%!fVbMi4 zNR|-6MXj=dt|DavsEewq3JqC+h?I~8@`6PdEXo4mNTPBc>FVd_svY4G~Z@4@4fGwbI-f)-h0kHm*_I8)#?FB&q^AWbcE#700y0N z1ICyE0C3JN8Do|J%uAXfd7I=7l2?KtxZ6$p*bSjk6qNvsN_v6hh$JsPBqixO$v*=a zFO^DBXD#iZ1E!~^2bPzYUnV&w>6uQ+Z3fAkk}eerg$qMNLrWcznN>ioR@=Y2y806U zKbEw=12(k<$$J3K8)MD|L9mcXmM>8ft+l5ly$N72ixC+xPx4F{hL=goM}U_DDwWD1 zN#i6>`0zUoNs}Z;%jNP1X}PH#aP{id6LB2BBk5pTZZ|=4E|<%lEEbECX?P~h!`9ld zIF2WFTjK#h(!n^6C# zhcV`*Fbpqkb$?3-B%}SrZvN(X0+K7aT<-a;W`|83P^nZ7k^IMQ9PNETa!%6m&DIGU z#sEo_H2%0W9$1?qlQwO5;Ts%atv$6{R(PER$rIMvQyZ=~|Ebk#`&U+0X7KRV_T9#O zE|)vp96+t>0ZCN%xHcYGOT@g|eBnV4n4X>`h?_tl1`G78c%g(vyCA}u; zDIYl`$B;OHz*-JZQpHo)ElJ-jm&=Pjd|%AY&VFHWaq)cs!#;eHHM-NNuOY^m zAN9KN0C4#5;s56I`Bb5A3r2xt7LfFUj|7r;f*^Q5gAtifEEfMx@}EB3Mw|21YW09~ zZbb4*sa;F+u(kFWfbSV&E*Fc%86SnMkaQiu*VFPyj#R7F118zD@Xg!bPV>B^UpnW` z#&KMWqUdL{v$K6ZdRuIm_2DH;{%4IOuR#O=oOAz5skA@&?UVHLg@uKwYPI^Dul_{V zzcX-$jie*K0!V)B%T4%4;y8ZKTKl_Ow{CsOQ_Q@teo03R$t(`&$nK1D?o_>ApRQCY zKS;|p#w`2lCHb@g;OlviT=nH{fR;%s!rDU{W&^YG?eA&*qj<5J6J-52L zIu%9H*>2mA>`)tT2gq~o+=1LaNv~hKcI~U(w80oNkSVYr*_G|}ME37TMn*pDwhhKP z=RJ^h!vQ4k7-PO$DwV#!lb|Y#4M|~!$ug4f$-rF)N%u*u?>$s4{7NGiT|3}eh2aU6eajJc5VJKqjN zzC@BY@+7Y$)mW)VQ%R|m2SM;IfOpgCX@hV0kVuN;6%z!(-6WwW(wAPN7n% z4Ea{1NxB{c!CeCYNgbXSB>iTm#R#25qVw0j{6?D_E5Y#$hJ43b`6aY8+_V7>T0jrX;WLsR@fDKLjw>*cXtrO!9w{R!DwA@_u7az!>8Iz&U4{ z_6G8j`bh4T^m&qnrhS2flD-09Ult=t-Yk_$!$8x%fTW86+E!Gw!9Gd*06de(MC)(! zjME&*wcgu|+bKwTP%!Zz7%CJB7m^Ch#~LK>6$*t5jsFiEknEM6e<)V<7U!GmjMs(3 zd-m-46*A=59s$f7W6m{SSm%JFM~~hkd8W5E^$sM@v=&!yn6@ObQmOp)ajO!MG+8c} zztvh)y&)r%ED=XZ&h^f`?ta=H9LE|)t=a;5v`br(sQ+{vvf$+yf2 z7K_D6lBc_8T5mw|^mY~G+a^WBFuX+a+;-3GW{^C$U6m;Cq*%Iru{6mz5%kFPgyhY) zZ9I_j#E7+aED3*iW&JS86_Tghb^CRkv`RkHM`nPv*+J5ANvRZd6Otw+9Z%bM;K{MM sCkH2=9G&fikXC#q4^MA}-F|%jQ$FLp<@n27i2wiq07*qoM6N<$f^Fe$FaQ7m literal 0 HcmV?d00001 diff --git a/core/src/emails/templates/layout.pug b/core/src/emails/templates/layout.pug new file mode 100644 index 000000000..0995b2647 --- /dev/null +++ b/core/src/emails/templates/layout.pug @@ -0,0 +1,26 @@ +doctype html +html(lang=locale) + head + meta( + content="multipart/html; charset=UTF-8" + http-equiv="content-type" + ) + meta( + name="viewport" + content="width=device-width, initial-scale=1" + ) + style. + .wf-force-outline-none[tabindex="-1"]:focus{outline:none;} + style + include includes/email.css + include includes/webflow.css + + body + div.container + include includes/header.pug + + .wrapper + block content + include includes/greeting.pug + + include includes/footer.pug diff --git a/core/src/emails/templates/resetPassword/html.pug b/core/src/emails/templates/resetPassword/html.pug new file mode 100644 index 000000000..4d4ed3f50 --- /dev/null +++ b/core/src/emails/templates/resetPassword/html.pug @@ -0,0 +1,16 @@ +extends ../layout.pug + +block content + h2= t('emails.resetPassword.title') + .text-block + include ../includes/salutation.pug + p= t('emails.resetPassword.youOrSomeoneResetPassword') + .content + h2= t('emails.resetPassword.title') + div(class="p_content")= t('emails.resetPassword.pleaseClickLink') + a.button-3(href=resetLink) #{t('emails.general.reset')} + div(class="p_content")= t('emails.general.orCopyLink') + + a.clink(href=resetLink) #{resetLink} + + include ../includes/requestNewLink.pug diff --git a/core/src/emails/templates/resetPassword/subject.pug b/core/src/emails/templates/resetPassword/subject.pug new file mode 100644 index 000000000..21f277316 --- /dev/null +++ b/core/src/emails/templates/resetPassword/subject.pug @@ -0,0 +1 @@ += t('emails.resetPassword.subject') diff --git a/core/src/emails/templates/transactionLinkRedeemed/html.pug b/core/src/emails/templates/transactionLinkRedeemed/html.pug new file mode 100644 index 000000000..281ee9205 --- /dev/null +++ b/core/src/emails/templates/transactionLinkRedeemed/html.pug @@ -0,0 +1,18 @@ +extend ../layout.pug + +block content + h2= t('emails.transactionLinkRedeemed.title', { senderFirstName, senderLastName }) + .text-block + include ../includes/salutation.pug + p= t('emails.transactionLinkRedeemed.hasRedeemedYourLink', { senderFirstName, senderLastName, senderEmail }) + .content + h2= t('emails.general.transactionDetails') + div(class="p_content")= t('emails.general.amountGDD', { amountGDD: transactionAmount }) + br + = t('emails.transactionLinkRedeemed.memo', { transactionMemo }) + br + = t('emails.general.detailsYouFindOnLinkToYourAccount') + + a.button-3(href=`${communityURL}/transactions`) #{t('emails.general.toAccount')} + + include ../includes/doNotReply.pug diff --git a/core/src/emails/templates/transactionLinkRedeemed/subject.pug b/core/src/emails/templates/transactionLinkRedeemed/subject.pug new file mode 100644 index 000000000..9070b60b3 --- /dev/null +++ b/core/src/emails/templates/transactionLinkRedeemed/subject.pug @@ -0,0 +1 @@ += t('emails.transactionLinkRedeemed.subject', { senderFirstName, senderLastName }) diff --git a/core/src/emails/templates/transactionReceived/html.pug b/core/src/emails/templates/transactionReceived/html.pug new file mode 100644 index 000000000..4f9aa9c31 --- /dev/null +++ b/core/src/emails/templates/transactionReceived/html.pug @@ -0,0 +1,30 @@ +extend ../layout.pug + +block content + mixin mailto(email, subject) + - var formattedSubject = encodeURIComponent(subject) + a(class!=attributes.class href=`mailto:${email}?subject=${formattedSubject}`) + block + + - var subject= t('emails.transactionReceived.replySubject', { senderFirstName, senderLastName, transactionAmount }) + h2= t('emails.transactionReceived.title', { senderFirstName, senderLastName, transactionAmount }) + .text-block + include ../includes/salutation.pug + p + = t('emails.transactionReceived.haveReceivedAmountGDDFrom', { transactionAmount, senderFirstName, senderLastName }) + | ( + +mailto(senderEmail, subject)=senderEmail + |). + .content + h2= t('emails.general.message') + .child-left + div(class="p_content")= memo + .child-right + +mailto(senderEmail, subject)(class="button-5") + include ../includes/chatbox-icon.pug + span #{t('emails.general.answerNow')} + + a.button-3(href=`${communityURL}/transactions`) #{t('emails.general.toAccount')} + + + diff --git a/core/src/emails/templates/transactionReceived/subject.pug b/core/src/emails/templates/transactionReceived/subject.pug new file mode 100644 index 000000000..872806ebc --- /dev/null +++ b/core/src/emails/templates/transactionReceived/subject.pug @@ -0,0 +1 @@ += t('emails.transactionReceived.subject', { senderFirstName, senderLastName, transactionAmount }) diff --git a/core/src/index.ts b/core/src/index.ts index a10eb6caa..a429bdc3b 100644 --- a/core/src/index.ts +++ b/core/src/index.ts @@ -18,6 +18,7 @@ export * from './graphql/logic/settlePendingSenderTransaction' export * from './graphql/logic/storeForeignUser' export * from './graphql/model/Decay' export * from './graphql/model/EncryptedTransferArgs' +export * from './emails' export * from './util/calculateSenderBalance' export * from './util/utilities' export * from './validation/user' diff --git a/core/src/types/images.d.ts b/core/src/types/images.d.ts new file mode 100644 index 000000000..ef7006d32 --- /dev/null +++ b/core/src/types/images.d.ts @@ -0,0 +1,14 @@ +declare module '*.jpg' { + const value: string + export default value +} +declare module '*.jpeg' { + const value: string + export default value +} +declare module '*.png' { + const value: string + export default value +} + + \ No newline at end of file diff --git a/core/src/validation/user.test.ts b/core/src/validation/user.test.ts index cd3bd1925..fb9cf7b94 100644 --- a/core/src/validation/user.test.ts +++ b/core/src/validation/user.test.ts @@ -1,22 +1,31 @@ import { validateAlias } from './user' import { getLogger } from '../../../config-schema/test/testSetup.bun' -import { describe, it, expect, beforeEach, mock, jest } from 'bun:test' -import { aliasExists } from 'database' +import { describe, it, expect, mock, jest, afterAll, beforeEach } from 'bun:test' import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const' +import { aliasExists, AbstractLoggingView, AppDatabase } from 'database' const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.validation.user`) -mock.module('database', () => ({ - aliasExists: jest.fn(), -})) mock.module('shared/src/schema/user.schema', () => ({ aliasSchema: { parse: jest.fn(), }, })) -describe('validate alias', () => { - beforeEach(() => { +// bun mock module currently cannot be restored, so we must mock compatible with all tests! +mock.module('database', () => ({ + aliasExists: jest.fn(), + AbstractLoggingView, + AppDatabase, +})) + + +afterAll(() => { + mock.restore() +}) + +describe('validate alias', () => { + beforeEach(() => { jest.clearAllMocks() }) @@ -52,7 +61,7 @@ describe('validate alias', () => { describe('test against existing alias in database', () => { describe('alias exists in database', () => { it('throws and logs an error', () => { - (aliasExists as jest.Mock).mockResolvedValue(true) + (aliasExists as jest.Mock).mockReturnValue(true) expect(validateAlias('b-b')).rejects.toEqual(new Error('Given alias is already in use')) expect(logger.warn.mock.calls[0]).toEqual(['alias already in use', 'b-b']) }) @@ -60,7 +69,7 @@ describe('validate alias', () => { describe('valid alias', () => { it('resolves to true', async () => { - (aliasExists as jest.Mock).mockResolvedValue(false) + (aliasExists as jest.Mock).mockReturnValue(false) expect(validateAlias('bibi')).resolves.toEqual(true) }) }) diff --git a/core/tsconfig.json b/core/tsconfig.json index f4fef84fa..155a31a7b 100644 --- a/core/tsconfig.json +++ b/core/tsconfig.json @@ -5,6 +5,7 @@ // "incremental": true, /* Enable incremental compilation */ "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "resolveJsonModule": true, // "lib": [], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ @@ -69,6 +70,10 @@ "skipLibCheck": true, /* Skip type checking of declaration files. */ "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ }, + "include": [ + "src/**/*.ts", + "src/**/*.json", + ], "references": [], /* Any project that is referenced must itself have a `references` array (which may be empty). */ - "exclude": ["**/*.test.ts", "**/*.spec.ts", "test/*", "**/bun.d.ts"], + "exclude": ["**/*.test.ts", "**/*.spec.ts", "test/*", "**/bun.d.ts", "esbuild.config.ts"], } From 9d4bda53dabf8f8bc72400f0a71e1a296a6f98ed Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 24 Nov 2025 14:42:56 +0100 Subject: [PATCH 06/22] remove email from backend --- backend/src/emails/README.md | 50 - .../sendEmailVariants.test.ts.snap | 1736 ----------------- .../src/emails/sendEmailTranslated.test.ts | 149 -- backend/src/emails/sendEmailTranslated.ts | 118 -- backend/src/emails/sendEmailVariants.test.ts | 623 ------ backend/src/emails/sendEmailVariants.ts | 225 --- .../templates/accountActivation/html.pug | 18 - .../templates/accountActivation/subject.pug | 1 - .../accountMultiRegistration/html.pug | 22 - .../accountMultiRegistration/subject.pug | 1 - .../addedContributionMessage/html.pug | 16 - .../addedContributionMessage/subject.pug | 1 - .../contributionChangedByModerator/html.pug | 10 - .../subject.pug | 1 - .../templates/contributionConfirmed/html.pug | 10 - .../contributionConfirmed/subject.pug | 1 - .../templates/contributionDeleted/html.pug | 10 - .../templates/contributionDeleted/subject.pug | 1 - .../templates/contributionDenied/html.pug | 10 - .../templates/contributionDenied/subject.pug | 1 - .../src/emails/templates/includes/Chatbox.svg | 1 - .../templates/includes/answear_button.svg | 22 - .../templates/includes/chatbox-icon.png | Bin 323 -> 0 bytes .../templates/includes/chatbox-icon.pug | 6 - .../includes/contributionDetailsCTA.pug | 7 - .../emails/templates/includes/doNotReply.pug | 1 - .../src/emails/templates/includes/email.css | 216 -- .../templates/includes/facebook-icon.png | Bin 1449 -> 0 bytes .../src/emails/templates/includes/footer.pug | 77 - .../templates/includes/gradido-header.jpeg | Bin 9427 -> 0 bytes .../emails/templates/includes/greeting.pug | 6 - .../src/emails/templates/includes/header.pug | 13 - .../templates/includes/requestNewLink.pug | 10 - .../emails/templates/includes/salutation.pug | 1 - .../templates/includes/telegram-icon.png | Bin 2159 -> 0 bytes .../templates/includes/twitter-icon.png | Bin 2002 -> 0 bytes .../src/emails/templates/includes/webflow.css | 166 -- .../templates/includes/youtube-icon.png | Bin 1802 -> 0 bytes backend/src/emails/templates/layout.pug | 26 - .../emails/templates/resetPassword/html.pug | 16 - .../templates/resetPassword/subject.pug | 1 - .../transactionLinkRedeemed/html.pug | 18 - .../transactionLinkRedeemed/subject.pug | 1 - .../templates/transactionReceived/html.pug | 30 - .../templates/transactionReceived/subject.pug | 1 - 45 files changed, 3623 deletions(-) delete mode 100644 backend/src/emails/README.md delete mode 100644 backend/src/emails/__snapshots__/sendEmailVariants.test.ts.snap delete mode 100644 backend/src/emails/sendEmailTranslated.test.ts delete mode 100644 backend/src/emails/sendEmailTranslated.ts delete mode 100644 backend/src/emails/sendEmailVariants.test.ts delete mode 100644 backend/src/emails/sendEmailVariants.ts delete mode 100644 backend/src/emails/templates/accountActivation/html.pug delete mode 100644 backend/src/emails/templates/accountActivation/subject.pug delete mode 100644 backend/src/emails/templates/accountMultiRegistration/html.pug delete mode 100644 backend/src/emails/templates/accountMultiRegistration/subject.pug delete mode 100644 backend/src/emails/templates/addedContributionMessage/html.pug delete mode 100644 backend/src/emails/templates/addedContributionMessage/subject.pug delete mode 100644 backend/src/emails/templates/contributionChangedByModerator/html.pug delete mode 100644 backend/src/emails/templates/contributionChangedByModerator/subject.pug delete mode 100644 backend/src/emails/templates/contributionConfirmed/html.pug delete mode 100644 backend/src/emails/templates/contributionConfirmed/subject.pug delete mode 100644 backend/src/emails/templates/contributionDeleted/html.pug delete mode 100644 backend/src/emails/templates/contributionDeleted/subject.pug delete mode 100644 backend/src/emails/templates/contributionDenied/html.pug delete mode 100644 backend/src/emails/templates/contributionDenied/subject.pug delete mode 100644 backend/src/emails/templates/includes/Chatbox.svg delete mode 100644 backend/src/emails/templates/includes/answear_button.svg delete mode 100644 backend/src/emails/templates/includes/chatbox-icon.png delete mode 100644 backend/src/emails/templates/includes/chatbox-icon.pug delete mode 100644 backend/src/emails/templates/includes/contributionDetailsCTA.pug delete mode 100644 backend/src/emails/templates/includes/doNotReply.pug delete mode 100644 backend/src/emails/templates/includes/email.css delete mode 100644 backend/src/emails/templates/includes/facebook-icon.png delete mode 100644 backend/src/emails/templates/includes/footer.pug delete mode 100644 backend/src/emails/templates/includes/gradido-header.jpeg delete mode 100644 backend/src/emails/templates/includes/greeting.pug delete mode 100644 backend/src/emails/templates/includes/header.pug delete mode 100644 backend/src/emails/templates/includes/requestNewLink.pug delete mode 100644 backend/src/emails/templates/includes/salutation.pug delete mode 100644 backend/src/emails/templates/includes/telegram-icon.png delete mode 100644 backend/src/emails/templates/includes/twitter-icon.png delete mode 100644 backend/src/emails/templates/includes/webflow.css delete mode 100644 backend/src/emails/templates/includes/youtube-icon.png delete mode 100644 backend/src/emails/templates/layout.pug delete mode 100644 backend/src/emails/templates/resetPassword/html.pug delete mode 100644 backend/src/emails/templates/resetPassword/subject.pug delete mode 100644 backend/src/emails/templates/transactionLinkRedeemed/html.pug delete mode 100644 backend/src/emails/templates/transactionLinkRedeemed/subject.pug delete mode 100644 backend/src/emails/templates/transactionReceived/html.pug delete mode 100644 backend/src/emails/templates/transactionReceived/subject.pug diff --git a/backend/src/emails/README.md b/backend/src/emails/README.md deleted file mode 100644 index 9ab1d1124..000000000 --- a/backend/src/emails/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# Using `forwardemail`–`email-templates` With `pug` Package - -You'll find the GitHub repository of the `email-templates` package and the `pug` package here: - -- [email-templates](https://github.com/forwardemail/email-templates) -- [pug](https://www.npmjs.com/package/pug) - -## `pug` Documentation - -The full `pug` documentation you'll find here: - -- [pugjs.org](https://pugjs.org/) - -### Caching Possibility - -In case we are sending many emails in the future there is the possibility to cache the `pug` templates: - -- [cache-pug-templates](https://github.com/ladjs/cache-pug-templates) - -## Testing - -To test your send emails you have different possibilities: - -### In General - -To send emails to yourself while developing set in `.env` the value `EMAIL_TEST_MODUS=true` and `EMAIL_TEST_RECEIVER` to your preferred email address. - -### Unit Or Integration Tests - -To change the behavior to show previews etc. you have the following options to be set in `sendEmailTranslated.ts` on creating the email object: - -```js - const email = new Email({ - … - // send emails in development/test env: - send: true, - … - // to open send emails in the browser - preview: true, - // or - // to open send emails in a specific the browser - preview: { - open: { - app: 'firefox', - wait: false, - }, - }, - … - }) -``` diff --git a/backend/src/emails/__snapshots__/sendEmailVariants.test.ts.snap b/backend/src/emails/__snapshots__/sendEmailVariants.test.ts.snap deleted file mode 100644 index aec3510fe..000000000 --- a/backend/src/emails/__snapshots__/sendEmailVariants.test.ts.snap +++ /dev/null @@ -1,1736 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`sendEmailVariants sendAccountActivationEmail result has the correct html as snapshot 1`] = ` -" - - - - - - - - -
-
-
\\"Gradido
-
-
-

Email Verification

-
-

Hello Peter Lustig,

-

Your email address has just been registered with Gradido.

-
-
-

Complete registration

-
Please click here to complete the registration and activate your Gradido account.
Activate account -
Or copy the link into your browser window.
http://localhost/checkEmail/6627633878930542284 - -

Request new valid link

-
The link has a validity of 23 hours and 30 minutes. -If the validity of the link has already expired, you can have a new link sent to you here.
New link -
-
-
-

Kind regards,
your Gradido team -

-
-
-
-
-
\\"facebook\\"\\"Telegram\\"\\"Twitter\\"\\"youtube\\"
-
-
-
If you have any further questions, please contact our support.
support@gradido.net -
\\"Gradido
-
Privacy Policy -
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


-
-
-
-
- -" -`; - -exports[`sendEmailVariants sendAccountMultiRegistrationEmail calls "sendEmailTranslated" result has the correct html as snapshot 1`] = ` -" - - - - - - - - -
-
-
\\"Gradido
-
-
-

Try To Register Again With Your Email

-
-

Hello Peter Lustig,

-

Your email address has just been used again to register an account with Gradido.
However, an account already exists for your email address. -

-
-
-

Reset password

-
If you have forgotten your password, please click here.
reset -
Or copy the link into your browser window.
http://localhost/forgot-password -

Contact support

-
If you did not try to register again, please contact our support:
support@supportmail.com -
-
-

Kind regards,
your Gradido team -

-
-
-
-
-
\\"facebook\\"\\"Telegram\\"\\"Twitter\\"\\"youtube\\"
-
-
-
If you have any further questions, please contact our support.
support@gradido.net -
\\"Gradido
-
Privacy Policy -
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


-
-
-
-
- -" -`; - -exports[`sendEmailVariants sendAddedContributionMessageEmail result has the correct html as snapshot 1`] = ` -" - - - - - - - - -
-
-
\\"Gradido
-
-
-

Message about your common good contribution

-
-

Hello Peter Lustig,

-

You have received a message from Bibi Bloxberg regarding your common good contribution “My contribution.”.

-
-
-

Read and reply to message

-
-

„My message.“

-

To reply to the message, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab.

-
To account -
Please do not reply to this email.
-
-
-

Kind regards,
your Gradido team -

-
-
-
-
-
\\"facebook\\"\\"Telegram\\"\\"Twitter\\"\\"youtube\\"
-
-
-
If you have any further questions, please contact our support.
support@gradido.net -
\\"Gradido
-
Privacy Policy -
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


-
-
-
-
- -" -`; - -exports[`sendEmailVariants sendContributionChangedByModeratorEmail result has the correct html as snapshot 1`] = ` -" - - - - - - - - -
-
-
\\"Gradido
-
-
-

Your common good contribution has been changed

-
-

Hello Peter Lustig,

-

your common good contribution 'My contribution.' has just been changed by Bibi Bloxberg and now reads as 'This is a better contribution memo.'

-
-
-

Contribution details

-
To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab.
To account -
Or copy the link into your browser window.
https://gradido.net/contributions/own-contributions/1#contributionListItem-1 -
Please do not reply to this email.
-
-
-

Kind regards,
your Gradido team -

-
-
-
-
-
\\"facebook\\"\\"Telegram\\"\\"Twitter\\"\\"youtube\\"
-
-
-
If you have any further questions, please contact our support.
support@gradido.net -
\\"Gradido
-
Privacy Policy -
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


-
-
-
-
- -" -`; - -exports[`sendEmailVariants sendContributionConfirmedEmail result has the correct html as snapshot 1`] = ` -" - - - - - - - - -
-
-
\\"Gradido
-
-
-

Your contribution to the common good was confirmed

-
-

Hello Peter Lustig,

-

Your common good contribution “My contribution.” has just been approved by Bibi Bloxberg. Your Gradido account has been credited with 23.54 GDD.

-
-
-

Contribution details

-
To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab.
To account -
Or copy the link into your browser window.
https://gradido.net/contributions/own-contributions/1#contributionListItem-1 -
Please do not reply to this email.
-
-
-

Kind regards,
your Gradido team -

-
-
-
-
-
\\"facebook\\"\\"Telegram\\"\\"Twitter\\"\\"youtube\\"
-
-
-
If you have any further questions, please contact our support.
support@gradido.net -
\\"Gradido
-
Privacy Policy -
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


-
-
-
-
- -" -`; - -exports[`sendEmailVariants sendContributionDeletedEmail result has the correct html as snapshot 1`] = ` -" - - - - - - - - -
-
-
\\"Gradido
-
-
-

Your common good contribution was deleted

-
-

Hello Peter Lustig,

-

Your common good contribution “My contribution.” was deleted by Bibi Bloxberg.

-
-
-

Contribution details

-
To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab.
To account -
Or copy the link into your browser window.
https://gradido.net/contributions/own-contributions/1#contributionListItem-1 -
Please do not reply to this email.
-
-
-

Kind regards,
your Gradido team -

-
-
-
-
-
\\"facebook\\"\\"Telegram\\"\\"Twitter\\"\\"youtube\\"
-
-
-
If you have any further questions, please contact our support.
support@gradido.net -
\\"Gradido
-
Privacy Policy -
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


-
-
-
-
- -" -`; - -exports[`sendEmailVariants sendContributionDeniedEmail result has the correct html as snapshot 1`] = ` -" - - - - - - - - -
-
-
\\"Gradido
-
-
-

Your common good contribution was rejected

-
-

Hello Peter Lustig,

-

Your common good contribution “My contribution.” was rejected by Bibi Bloxberg.

-
-
-

Contribution details

-
To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab.
To account -
Or copy the link into your browser window.
https://gradido.net/contributions/own-contributions/1#contributionListItem-1 -
Please do not reply to this email.
-
-
-

Kind regards,
your Gradido team -

-
-
-
-
-
\\"facebook\\"\\"Telegram\\"\\"Twitter\\"\\"youtube\\"
-
-
-
If you have any further questions, please contact our support.
support@gradido.net -
\\"Gradido
-
Privacy Policy -
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


-
-
-
-
- -" -`; - -exports[`sendEmailVariants sendResetPasswordEmail result has the correct html as snapshot 1`] = ` -" - - - - - - - - -
-
-
\\"Gradido
-
-
-

Reset password

-
-

Hello Peter Lustig,

-

You, or someone else, requested a password reset for this account.

-
-
-

Reset password

-
If it was you, please click here.
reset -
Or copy the link into your browser window.
http://localhost/reset-password/3762660021544901417 - -

Request new valid link

-
The link has a validity of 23 hours and 30 minutes. -If the validity of the link has already expired, you can have a new link sent to you here.
New link -
-
-
-

Kind regards,
your Gradido team -

-
-
-
-
-
\\"facebook\\"\\"Telegram\\"\\"Twitter\\"\\"youtube\\"
-
-
-
If you have any further questions, please contact our support.
support@gradido.net -
\\"Gradido
-
Privacy Policy -
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


-
-
-
-
- -" -`; - -exports[`sendEmailVariants sendTransactionLinkRedeemedEmail result has the correct html as snapshot 1`] = ` -" - - - - - - - - -
-
-
\\"Gradido
-
-
-

Bibi Bloxberg has redeemed your Gradido link

-
-

Hello Peter Lustig,

-

Bibi Bloxberg (bibi@bloxberg.de) has just redeemed your link.

-
-
-

Transaction details

-
Amount: 17.65 GDD
Message: You deserve it! 🙏🏼
You can find transaction details in your Gradido account. -
To account -
Please do not reply to this email.
-
-
-

Kind regards,
your Gradido team -

-
-
-
-
-
\\"facebook\\"\\"Telegram\\"\\"Twitter\\"\\"youtube\\"
-
-
-
If you have any further questions, please contact our support.
support@gradido.net -
\\"Gradido
-
Privacy Policy -
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


-
-
-
-
- -" -`; - -exports[`sendEmailVariants sendTransactionReceivedEmail result has the correct html as snapshot 1`] = ` -" - - - - - - - - -
-
-
\\"Gradido
-
-
-

Bibi Bloxberg has sent you 37.40 Gradido

-
-

Hello Peter Lustig,

-

You have just received 37.40 GDD from Bibi Bloxberg (bibi@bloxberg.de). -

-
-
-

Message

-
-
Du bist schon lustiger ;)
-
- -
To account -
-

Kind regards,
your Gradido team -

-
-
-
-
-
\\"facebook\\"\\"Telegram\\"\\"Twitter\\"\\"youtube\\"
-
-
-
If you have any further questions, please contact our support.
support@gradido.net -
\\"Gradido
-
Privacy Policy -
Gradido-Akademie
Institut für Wirtschaftsbionik
Pfarrweg 2
74653 Künzelsau
Deutschland


-
-
-
-
- -" -`; diff --git a/backend/src/emails/sendEmailTranslated.test.ts b/backend/src/emails/sendEmailTranslated.test.ts deleted file mode 100644 index 103296080..000000000 --- a/backend/src/emails/sendEmailTranslated.test.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { createTransport } from 'nodemailer' - -import { i18n } from '@test/testSetup' - -import { CONFIG } from '@/config' - -import { getLogger } from 'config-schema/test/testSetup' -import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' -import { sendEmailTranslated } from './sendEmailTranslated' - -const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.emails.sendEmailTranslated`) - -const testMailServerHost = 'localhost' -const testMailServerPort = 1025 - -CONFIG.EMAIL = false -CONFIG.EMAIL_SMTP_HOST = testMailServerHost -CONFIG.EMAIL_SMTP_PORT = testMailServerPort -CONFIG.EMAIL_SENDER = 'info@gradido.net' -CONFIG.EMAIL_USERNAME = 'user' -CONFIG.EMAIL_PASSWORD = 'pwd' -CONFIG.EMAIL_TLS = true - -jest.mock('nodemailer', () => { - return { - __esModule: true, - createTransport: jest.fn(() => { - return { - sendMail: () => { - return { - messageId: 'message', - } - }, - } - }), - } -}) - -describe('sendEmailTranslated', () => { - let result: Record | boolean | null - - describe('config email is false', () => { - beforeEach(async () => { - result = await sendEmailTranslated({ - receiver: { - to: 'receiver@mail.org', - cc: 'support@gradido.net', - }, - template: 'accountMultiRegistration', - locals: { - locale: 'en', - }, - }) - }) - - it('logs warning', () => { - expect(logger.info).toBeCalledWith('Emails are disabled via config...') - }) - - it('returns false', () => { - expect(result).toBeFalsy() - }) - }) - - describe('config email is true', () => { - beforeEach(async () => { - CONFIG.EMAIL = true - result = await sendEmailTranslated({ - receiver: { - to: 'receiver@mail.org', - cc: 'support@gradido.net', - }, - template: 'accountMultiRegistration', - locals: { - locale: 'en', - }, - }) - }) - - it('calls the transporter', () => { - expect(createTransport).toBeCalledWith({ - host: testMailServerHost, - port: testMailServerPort, - secure: false, - requireTLS: true, - auth: { - user: 'user', - pass: 'pwd', - }, - }) - }) - - describe('call of "sendEmailTranslated"', () => { - it('has expected result', () => { - expect(result).toMatchObject({ - originalMessage: expect.objectContaining({ - to: 'receiver@mail.org', - cc: 'support@gradido.net', - from: 'Gradido (emails.general.doNotAnswer) ', - attachments: expect.any(Array), - subject: 'Try To Register Again With Your Email', - html: expect.stringContaining('Try To Register Again With Your Email'), - text: expect.stringContaining('TRY TO REGISTER AGAIN WITH YOUR EMAIL'), - }), - }) - }) - }) - - it.skip('calls "i18n.setLocale" with "en"', () => { - expect(i18n.setLocale).toBeCalledWith('en') - }) - - it.skip('calls "i18n.__" for translation', () => { - expect(i18n.__).toBeCalled() - }) - }) - - describe('with email EMAIL_TEST_MODUS true', () => { - beforeEach(async () => { - jest.clearAllMocks() - CONFIG.EMAIL = true - CONFIG.EMAIL_TEST_MODUS = true - result = await sendEmailTranslated({ - receiver: { - to: 'receiver@mail.org', - cc: 'support@gradido.net', - }, - template: 'accountMultiRegistration', - locals: { - locale: 'en', - }, - }) - }) - - it('call of "sendEmailTranslated" with faked "to"', () => { - expect(result).toMatchObject({ - originalMessage: expect.objectContaining({ - to: CONFIG.EMAIL_TEST_RECEIVER, - cc: 'support@gradido.net', - from: `Gradido (emails.general.doNotAnswer) <${CONFIG.EMAIL_SENDER}>`, - attachments: expect.any(Array), - subject: 'Try To Register Again With Your Email', - html: expect.stringContaining('Try To Register Again With Your Email'), - text: expect.stringContaining('TRY TO REGISTER AGAIN WITH YOUR EMAIL'), - }), - }) - }) - }) -}) diff --git a/backend/src/emails/sendEmailTranslated.ts b/backend/src/emails/sendEmailTranslated.ts deleted file mode 100644 index 5b95cd7ad..000000000 --- a/backend/src/emails/sendEmailTranslated.ts +++ /dev/null @@ -1,118 +0,0 @@ -import path from 'path' - -import Email from 'email-templates' -import i18n from 'i18n' -import { createTransport } from 'nodemailer' - -import { CONFIG } from '@/config' -import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' -import { getLogger } from 'log4js' - -const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.emails.sendEmailTranslated`) - -export const sendEmailTranslated = async ({ - receiver, - template, - locals, -}: { - receiver: { - to: string - cc?: string - } - template: string - locals: Record -}): Promise | boolean | null> => { - // TODO: test the calling order of 'i18n.setLocale' for example: language of logging 'en', language of email receiver 'es', reset language of current user 'de' - - if (!CONFIG.EMAIL) { - logger.info(`Emails are disabled via config...`) - return null - } - - // because language of receiver can differ from language of current user who triggers the sending - // const rememberLocaleToRestore = i18n.getLocale() - - i18n.setLocale('en') // for logging - logger.info( - `send Email: language=${locals.locale as string} to=${receiver.to.substring(0, 3)}...` + - (receiver.cc ? `, cc=${receiver.cc.substring(0, 3)}...` : '') + - `, subject=${i18n.__('emails.' + template + '.subject')}`, - ) - - if (CONFIG.EMAIL_TEST_MODUS) { - logger.info( - `Testmodus=ON: change receiver from ${receiver.to} to ${CONFIG.EMAIL_TEST_RECEIVER}`, - ) - receiver.to = CONFIG.EMAIL_TEST_RECEIVER - } - const transport = createTransport({ - host: CONFIG.EMAIL_SMTP_HOST, - port: CONFIG.EMAIL_SMTP_PORT, - secure: false, // true for 465, false for other ports - requireTLS: CONFIG.EMAIL_TLS, - auth: { - user: CONFIG.EMAIL_USERNAME, - pass: CONFIG.EMAIL_PASSWORD, - }, - }) - - i18n.setLocale(locals.locale as string) // for email - - // TESTING: see 'README.md' - const email = new Email({ - message: { - from: `Gradido (${i18n.__('emails.general.doNotAnswer')}) <${CONFIG.EMAIL_SENDER}>`, - }, - send: CONFIG.EMAIL, - transport, - preview: false, - // i18n, // is only needed if you don't install i18n - }) - - const resultSend = await email - .send({ - template: path.join(__dirname, 'templates', template), - message: { - ...receiver, - attachments: [ - { - filename: 'gradido-header.jpeg', - path: path.join(__dirname, 'templates/includes/gradido-header.jpeg'), - cid: 'gradidoheader', - }, - { - filename: 'facebook-icon.png', - path: path.join(__dirname, 'templates/includes/facebook-icon.png'), - cid: 'facebookicon', - }, - { - filename: 'telegram-icon.png', - path: path.join(__dirname, 'templates/includes/telegram-icon.png'), - cid: 'telegramicon', - }, - { - filename: 'twitter-icon.png', - path: path.join(__dirname, 'templates/includes/twitter-icon.png'), - cid: 'twittericon', - }, - { - filename: 'youtube-icon.png', - path: path.join(__dirname, 'templates/includes/youtube-icon.png'), - cid: 'youtubeicon', - }, - { - filename: 'chatbox-icon.png', - path: path.join(__dirname, 'templates/includes/chatbox-icon.png'), - cid: 'chatboxicon', - }, - ], - }, - locals, // the 'locale' in here seems not to be used by 'email-template', because it doesn't work if the language isn't set before by 'i18n.setLocale' - }) - .catch((error: unknown) => { - logger.error('Error sending notification email', error) - return error - }) - - return resultSend -} diff --git a/backend/src/emails/sendEmailVariants.test.ts b/backend/src/emails/sendEmailVariants.test.ts deleted file mode 100644 index 74eb940ed..000000000 --- a/backend/src/emails/sendEmailVariants.test.ts +++ /dev/null @@ -1,623 +0,0 @@ -import { ApolloServerTestClient } from 'apollo-server-testing' -import { Decimal } from 'decimal.js-light' -import { DataSource } from 'typeorm' - -import { testEnvironment } from '@test/helpers' -import { i18n as localization } from '@test/testSetup' -import { getLogger } from 'config-schema/test/testSetup' - -import { CONFIG } from '@/config' - -import * as sendEmailTranslatedApi from './sendEmailTranslated' -import { - sendAccountActivationEmail, - sendAccountMultiRegistrationEmail, - sendAddedContributionMessageEmail, - sendContributionChangedByModeratorEmail, - sendContributionConfirmedEmail, - sendContributionDeletedEmail, - sendContributionDeniedEmail, - sendResetPasswordEmail, - sendTransactionLinkRedeemedEmail, - sendTransactionReceivedEmail, -} from './sendEmailVariants' - -const testMailServerHost = 'localhost' -const testMailServerPort = 1025 -const testMailTLS = false - -CONFIG.EMAIL_SENDER = 'info@gradido.net' -CONFIG.EMAIL_SMTP_HOST = testMailServerHost -CONFIG.EMAIL_SMTP_PORT = testMailServerPort -CONFIG.EMAIL_TLS = testMailTLS - -jest.mock('nodemailer', () => { - return { - __esModule: true, - createTransport: jest.fn(() => { - return { - sendMail: () => { - return { - messageId: 'message', - } - }, - } - }), - } -}) - -let con: DataSource -let testEnv: { - mutate: ApolloServerTestClient['mutate'] - query: ApolloServerTestClient['query'] - con: DataSource -} - -beforeAll(async () => { - testEnv = await testEnvironment(getLogger('apollo'), localization) - con = testEnv.con -}) - -afterAll(async () => { - await con.destroy() -}) - -const sendEmailTranslatedSpy = jest.spyOn(sendEmailTranslatedApi, 'sendEmailTranslated') - -describe('sendEmailVariants', () => { - let result: any - const contributionFrontendLink = - 'https://gradido.net/contributions/own-contributions/1#contributionListItem-1' - - describe('sendAddedContributionMessageEmail', () => { - beforeAll(async () => { - result = await sendAddedContributionMessageEmail({ - firstName: 'Peter', - lastName: 'Lustig', - email: 'peter@lustig.de', - language: 'en', - senderFirstName: 'Bibi', - senderLastName: 'Bloxberg', - contributionMemo: 'My contribution.', - contributionFrontendLink, - message: 'My message.', - }) - }) - - describe('calls "sendEmailTranslated"', () => { - it('with expected parameters', () => { - expect(sendEmailTranslatedSpy).toBeCalledWith({ - receiver: { - to: 'Peter Lustig ', - }, - template: 'addedContributionMessage', - locals: { - firstName: 'Peter', - lastName: 'Lustig', - locale: 'en', - senderFirstName: 'Bibi', - senderLastName: 'Bloxberg', - contributionMemo: 'My contribution.', - contributionFrontendLink, - message: 'My message.', - supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, - }, - }) - }) - }) - - describe('result', () => { - it('is the expected object', () => { - expect(result).toMatchObject({ - originalMessage: expect.objectContaining({ - to: 'Peter Lustig ', - from: 'Gradido (emails.general.doNotAnswer) ', - attachments: expect.any(Array), - subject: 'Message about your common good contribution', - html: expect.any(String), - text: expect.stringContaining('MESSAGE ABOUT YOUR COMMON GOOD CONTRIBUTION'), - }), - }) - }) - - it('has the correct html as snapshot', () => { - expect(result.originalMessage.html).toMatchSnapshot() - }) - }) - }) - - describe('sendAccountActivationEmail', () => { - beforeAll(async () => { - result = await sendAccountActivationEmail({ - firstName: 'Peter', - lastName: 'Lustig', - email: 'peter@lustig.de', - language: 'en', - activationLink: 'http://localhost/checkEmail/6627633878930542284', - timeDurationObject: { hours: 23, minutes: 30 }, - }) - }) - - describe('calls "sendEmailTranslated"', () => { - it('with expected parameters', () => { - expect(sendEmailTranslatedSpy).toBeCalledWith({ - receiver: { - to: 'Peter Lustig ', - }, - template: 'accountActivation', - locals: { - firstName: 'Peter', - lastName: 'Lustig', - locale: 'en', - activationLink: 'http://localhost/checkEmail/6627633878930542284', - timeDurationObject: { hours: 23, minutes: 30 }, - resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD, - supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, - communityURL: CONFIG.COMMUNITY_URL, - }, - }) - }) - }) - - describe('result', () => { - it('is the expected object', () => { - expect(result).toMatchObject({ - originalMessage: expect.objectContaining({ - to: 'Peter Lustig ', - from: 'Gradido (emails.general.doNotAnswer) ', - attachments: expect.any(Array), - subject: 'Email Verification', - html: expect.any(String), - text: expect.stringContaining('EMAIL VERIFICATION'), - }), - }) - }) - - it('has the correct html as snapshot', () => { - expect(result.originalMessage.html).toMatchSnapshot() - }) - }) - }) - - describe('sendAccountMultiRegistrationEmail', () => { - beforeAll(async () => { - result = await sendAccountMultiRegistrationEmail({ - firstName: 'Peter', - lastName: 'Lustig', - email: 'peter@lustig.de', - language: 'en', - }) - }) - - describe('calls "sendEmailTranslated"', () => { - it('with expected parameters', () => { - expect(sendEmailTranslatedSpy).toBeCalledWith({ - receiver: { - to: 'Peter Lustig ', - }, - template: 'accountMultiRegistration', - locals: { - firstName: 'Peter', - lastName: 'Lustig', - locale: 'en', - resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD, - supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, - communityURL: CONFIG.COMMUNITY_URL, - }, - }) - }) - - describe('result', () => { - it('is the expected object', () => { - expect(result).toMatchObject({ - originalMessage: expect.objectContaining({ - to: 'Peter Lustig ', - from: 'Gradido (emails.general.doNotAnswer) ', - attachments: expect.any(Array), - subject: 'Try To Register Again With Your Email', - html: expect.any(String), - text: expect.stringContaining('TRY TO REGISTER AGAIN WITH YOUR EMAIL'), - }), - }) - }) - - it('has the correct html as snapshot', () => { - expect(result.originalMessage.html).toMatchSnapshot() - }) - }) - }) - }) - - describe('sendContributionConfirmedEmail', () => { - beforeAll(async () => { - result = await sendContributionConfirmedEmail({ - firstName: 'Peter', - lastName: 'Lustig', - email: 'peter@lustig.de', - language: 'en', - senderFirstName: 'Bibi', - senderLastName: 'Bloxberg', - contributionMemo: 'My contribution.', - contributionAmount: new Decimal(23.54), - contributionFrontendLink, - }) - }) - - describe('calls "sendEmailTranslated"', () => { - it('with expected parameters', () => { - expect(sendEmailTranslatedSpy).toBeCalledWith({ - receiver: { - to: 'Peter Lustig ', - }, - template: 'contributionConfirmed', - locals: { - firstName: 'Peter', - lastName: 'Lustig', - locale: 'en', - senderFirstName: 'Bibi', - senderLastName: 'Bloxberg', - contributionMemo: 'My contribution.', - contributionAmount: '23.54', - supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, - contributionFrontendLink, - }, - }) - }) - }) - - describe('result', () => { - it('is the expected object', () => { - expect(result).toMatchObject({ - originalMessage: expect.objectContaining({ - to: 'Peter Lustig ', - from: 'Gradido (emails.general.doNotAnswer) ', - attachments: expect.any(Array), - subject: 'Your contribution to the common good was confirmed', - html: expect.any(String), - text: expect.stringContaining('YOUR CONTRIBUTION TO THE COMMON GOOD WAS CONFIRMED'), - }), - }) - }) - - it('has the correct html as snapshot', () => { - expect(result.originalMessage.html).toMatchSnapshot() - }) - }) - }) - - describe('sendContributionChangedByModeratorEmail', () => { - beforeAll(async () => { - result = await sendContributionChangedByModeratorEmail({ - firstName: 'Peter', - lastName: 'Lustig', - email: 'peter@lustig.de', - language: 'en', - senderFirstName: 'Bibi', - senderLastName: 'Bloxberg', - contributionMemo: 'My contribution.', - contributionMemoUpdated: 'This is a better contribution memo.', - contributionFrontendLink, - }) - }) - - describe('calls "sendEmailTranslated"', () => { - it('with expected parameters', () => { - expect(sendEmailTranslatedSpy).toBeCalledWith({ - receiver: { - to: 'Peter Lustig ', - }, - template: 'contributionChangedByModerator', - locals: { - firstName: 'Peter', - lastName: 'Lustig', - locale: 'en', - senderFirstName: 'Bibi', - senderLastName: 'Bloxberg', - contributionMemo: 'My contribution.', - contributionMemoUpdated: 'This is a better contribution memo.', - contributionFrontendLink, - supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, - }, - }) - }) - }) - - describe('result', () => { - it('is the expected object', () => { - expect(result).toMatchObject({ - originalMessage: expect.objectContaining({ - to: 'Peter Lustig ', - from: 'Gradido (emails.general.doNotAnswer) ', - attachments: expect.any(Array), - subject: 'Your common good contribution has been changed', - html: expect.any(String), - text: expect.stringContaining('YOUR COMMON GOOD CONTRIBUTION HAS BEEN CHANGED'), - }), - }) - }) - - it('has the correct html as snapshot', () => { - expect(result.originalMessage.html).toMatchSnapshot() - }) - }) - }) - - describe('sendContributionDeniedEmail', () => { - beforeAll(async () => { - result = await sendContributionDeniedEmail({ - firstName: 'Peter', - lastName: 'Lustig', - email: 'peter@lustig.de', - language: 'en', - senderFirstName: 'Bibi', - senderLastName: 'Bloxberg', - contributionMemo: 'My contribution.', - contributionFrontendLink, - }) - }) - - describe('calls "sendEmailTranslated"', () => { - it('with expected parameters', () => { - expect(sendEmailTranslatedSpy).toBeCalledWith({ - receiver: { - to: 'Peter Lustig ', - }, - template: 'contributionDenied', - locals: { - firstName: 'Peter', - lastName: 'Lustig', - locale: 'en', - senderFirstName: 'Bibi', - senderLastName: 'Bloxberg', - contributionMemo: 'My contribution.', - contributionFrontendLink, - supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, - }, - }) - }) - }) - - describe('result', () => { - it('has expected result', () => { - expect(result).toMatchObject({ - originalMessage: expect.objectContaining({ - to: 'Peter Lustig ', - from: 'Gradido (emails.general.doNotAnswer) ', - attachments: expect.any(Array), - subject: 'Your common good contribution was rejected', - html: expect.any(String), - text: expect.stringContaining('YOUR COMMON GOOD CONTRIBUTION WAS REJECTED'), - }), - }) - }) - - it('has the correct html as snapshot', () => { - expect(result.originalMessage.html).toMatchSnapshot() - }) - }) - }) - - describe('sendContributionDeletedEmail', () => { - beforeAll(async () => { - result = await sendContributionDeletedEmail({ - firstName: 'Peter', - lastName: 'Lustig', - email: 'peter@lustig.de', - language: 'en', - senderFirstName: 'Bibi', - senderLastName: 'Bloxberg', - contributionMemo: 'My contribution.', - contributionFrontendLink, - }) - }) - - describe('calls "sendEmailTranslated"', () => { - it('with expected parameters', () => { - expect(sendEmailTranslatedSpy).toBeCalledWith({ - receiver: { - to: 'Peter Lustig ', - }, - template: 'contributionDeleted', - locals: { - firstName: 'Peter', - lastName: 'Lustig', - locale: 'en', - senderFirstName: 'Bibi', - senderLastName: 'Bloxberg', - contributionMemo: 'My contribution.', - contributionFrontendLink, - supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, - }, - }) - }) - }) - - describe('result', () => { - it('is the expected object', () => { - expect(result).toMatchObject({ - originalMessage: expect.objectContaining({ - to: 'Peter Lustig ', - from: 'Gradido (emails.general.doNotAnswer) ', - attachments: expect.any(Array), - subject: 'Your common good contribution was deleted', - html: expect.any(String), - text: expect.stringContaining('YOUR COMMON GOOD CONTRIBUTION WAS DELETED'), - }), - }) - }) - - it('has the correct html as snapshot', () => { - expect(result.originalMessage.html).toMatchSnapshot() - }) - }) - }) - - describe('sendResetPasswordEmail', () => { - beforeAll(async () => { - result = await sendResetPasswordEmail({ - firstName: 'Peter', - lastName: 'Lustig', - email: 'peter@lustig.de', - language: 'en', - resetLink: 'http://localhost/reset-password/3762660021544901417', - timeDurationObject: { hours: 23, minutes: 30 }, - }) - }) - - describe('calls "sendEmailTranslated"', () => { - it('with expected parameters', () => { - expect(sendEmailTranslatedSpy).toBeCalledWith({ - receiver: { - to: 'Peter Lustig ', - }, - template: 'resetPassword', - locals: { - firstName: 'Peter', - lastName: 'Lustig', - locale: 'en', - resetLink: 'http://localhost/reset-password/3762660021544901417', - timeDurationObject: { hours: 23, minutes: 30 }, - resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD, - supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, - communityURL: CONFIG.COMMUNITY_URL, - }, - }) - }) - }) - - describe('result', () => { - it('is the expected object', () => { - expect(result).toMatchObject({ - originalMessage: expect.objectContaining({ - to: 'Peter Lustig ', - from: 'Gradido (emails.general.doNotAnswer) ', - attachments: expect.any(Array), - subject: 'Reset password', - html: expect.any(String), - text: expect.stringContaining('RESET PASSWORD'), - }), - }) - }) - - it('has the correct html as snapshot', () => { - expect(result.originalMessage.html).toMatchSnapshot() - }) - }) - }) - - describe('sendTransactionLinkRedeemedEmail', () => { - beforeAll(async () => { - result = await sendTransactionLinkRedeemedEmail({ - firstName: 'Peter', - lastName: 'Lustig', - email: 'peter@lustig.de', - language: 'en', - senderFirstName: 'Bibi', - senderLastName: 'Bloxberg', - senderEmail: 'bibi@bloxberg.de', - transactionMemo: 'You deserve it! 🙏🏼', - transactionAmount: new Decimal(17.65), - }) - }) - - describe('calls "sendEmailTranslated"', () => { - it('with expected parameters', () => { - expect(sendEmailTranslatedSpy).toBeCalledWith({ - receiver: { - to: 'Peter Lustig ', - }, - template: 'transactionLinkRedeemed', - locals: { - firstName: 'Peter', - lastName: 'Lustig', - locale: 'en', - senderFirstName: 'Bibi', - senderLastName: 'Bloxberg', - senderEmail: 'bibi@bloxberg.de', - transactionMemo: 'You deserve it! 🙏🏼', - transactionAmount: '17.65', - supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, - communityURL: CONFIG.COMMUNITY_URL, - }, - }) - }) - }) - - describe('result', () => { - it('is the expected object', () => { - expect(result).toMatchObject({ - originalMessage: expect.objectContaining({ - to: 'Peter Lustig ', - from: 'Gradido (emails.general.doNotAnswer) ', - attachments: expect.any(Array), - subject: 'Bibi Bloxberg has redeemed your Gradido link', - html: expect.any(String), - text: expect.stringContaining('BIBI BLOXBERG HAS REDEEMED YOUR GRADIDO LINK'), - }), - }) - }) - - it('has the correct html as snapshot', () => { - expect(result.originalMessage.html).toMatchSnapshot() - }) - }) - }) - - describe('sendTransactionReceivedEmail', () => { - beforeAll(async () => { - result = await sendTransactionReceivedEmail({ - firstName: 'Peter', - lastName: 'Lustig', - email: 'peter@lustig.de', - language: 'en', - memo: 'Du bist schon lustiger ;)', - senderFirstName: 'Bibi', - senderLastName: 'Bloxberg', - senderEmail: 'bibi@bloxberg.de', - transactionAmount: new Decimal(37.4), - }) - }) - - describe('calls "sendEmailTranslated"', () => { - it('with expected parameters', () => { - expect(sendEmailTranslatedSpy).toBeCalledWith({ - receiver: { - to: 'Peter Lustig ', - }, - template: 'transactionReceived', - locals: { - firstName: 'Peter', - lastName: 'Lustig', - locale: 'en', - memo: 'Du bist schon lustiger ;)', - senderFirstName: 'Bibi', - senderLastName: 'Bloxberg', - senderEmail: 'bibi@bloxberg.de', - transactionAmount: '37.40', - supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, - communityURL: CONFIG.COMMUNITY_URL, - }, - }) - }) - }) - - describe('result', () => { - it('is the expected object', () => { - expect(result).toMatchObject({ - originalMessage: expect.objectContaining({ - to: 'Peter Lustig ', - from: 'Gradido (emails.general.doNotAnswer) ', - attachments: expect.any(Array), - subject: 'Bibi Bloxberg has sent you 37.40 Gradido', - html: expect.any(String), - text: expect.stringContaining('BIBI BLOXBERG HAS SENT YOU 37.40 GRADIDO'), - }), - }) - }) - - it('has the correct html as snapshot', () => { - expect(result.originalMessage.html).toMatchSnapshot() - }) - }) - }) -}) diff --git a/backend/src/emails/sendEmailVariants.ts b/backend/src/emails/sendEmailVariants.ts deleted file mode 100644 index aac726002..000000000 --- a/backend/src/emails/sendEmailVariants.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { Decimal } from 'decimal.js-light' - -import { CONFIG } from '@/config' -import { decimalSeparatorByLanguage } from 'core' - -import { sendEmailTranslated } from './sendEmailTranslated' - -export interface ContributionEmailCommonData { - firstName: string - lastName: string - email: string - language: string - senderFirstName: string - senderLastName: string - contributionMemo: string - contributionFrontendLink: string -} - -function toContributionEmailLocales(data: ContributionEmailCommonData): Record { - return { - firstName: data.firstName, - lastName: data.lastName, - locale: data.language, - senderFirstName: data.senderFirstName, - senderLastName: data.senderLastName, - contributionMemo: data.contributionMemo, - contributionFrontendLink: data.contributionFrontendLink, - supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, - } -} - -export const sendAddedContributionMessageEmail = ( - data: ContributionEmailCommonData & { - message: string - }, -): Promise | boolean | null> => { - return sendEmailTranslated({ - receiver: { - to: `${data.firstName} ${data.lastName} <${data.email}>`, - }, - template: 'addedContributionMessage', - locals: { - ...toContributionEmailLocales(data), - message: data.message, - }, - }) -} - -export const sendAccountActivationEmail = (data: { - firstName: string - lastName: string - email: string - language: string - activationLink: string - timeDurationObject: Record - logoUrl?: string | null -}): Promise | boolean | null> => { - return sendEmailTranslated({ - receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, - template: 'accountActivation', - locals: { - firstName: data.firstName, - lastName: data.lastName, - locale: data.language, - activationLink: data.activationLink, - timeDurationObject: data.timeDurationObject, - logoUrl: data.logoUrl, - resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD, - supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, - communityURL: CONFIG.COMMUNITY_URL, - }, - }) -} - -export const sendAccountMultiRegistrationEmail = (data: { - firstName: string - lastName: string - email: string - language: string -}): Promise | boolean | null> => { - return sendEmailTranslated({ - receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, - template: 'accountMultiRegistration', - locals: { - firstName: data.firstName, - lastName: data.lastName, - locale: data.language, - resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD, - supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, - communityURL: CONFIG.COMMUNITY_URL, - }, - }) -} - -export const sendContributionConfirmedEmail = ( - data: ContributionEmailCommonData & { - contributionAmount: Decimal - }, -): Promise | boolean | null> => { - return sendEmailTranslated({ - receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, - template: 'contributionConfirmed', - locals: { - ...toContributionEmailLocales(data), - contributionAmount: decimalSeparatorByLanguage(data.contributionAmount, data.language), - }, - }) -} - -export const sendContributionChangedByModeratorEmail = ( - data: ContributionEmailCommonData & { - contributionMemoUpdated: string - }, -): Promise | boolean | null> => { - return sendEmailTranslated({ - receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, - template: 'contributionChangedByModerator', - locals: { - ...toContributionEmailLocales(data), - contributionMemoUpdated: data.contributionMemoUpdated, - }, - }) -} - -export const sendContributionDeletedEmail = ( - data: ContributionEmailCommonData, -): Promise | boolean | null> => { - return sendEmailTranslated({ - receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, - template: 'contributionDeleted', - locals: toContributionEmailLocales(data), - }) -} - -export const sendContributionDeniedEmail = ( - data: ContributionEmailCommonData, -): Promise | boolean | null> => { - return sendEmailTranslated({ - receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, - template: 'contributionDenied', - locals: toContributionEmailLocales(data), - }) -} - -export const sendResetPasswordEmail = (data: { - firstName: string - lastName: string - email: string - language: string - resetLink: string - timeDurationObject: Record -}): Promise | boolean | null> => { - return sendEmailTranslated({ - receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, - template: 'resetPassword', - locals: { - firstName: data.firstName, - lastName: data.lastName, - locale: data.language, - resetLink: data.resetLink, - timeDurationObject: data.timeDurationObject, - resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD, - supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, - communityURL: CONFIG.COMMUNITY_URL, - }, - }) -} - -export const sendTransactionLinkRedeemedEmail = (data: { - firstName: string - lastName: string - email: string - language: string - senderFirstName: string - senderLastName: string - senderEmail: string - transactionMemo: string - transactionAmount: Decimal -}): Promise | boolean | null> => { - return sendEmailTranslated({ - receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, - template: 'transactionLinkRedeemed', - locals: { - firstName: data.firstName, - lastName: data.lastName, - locale: data.language, - senderFirstName: data.senderFirstName, - senderLastName: data.senderLastName, - senderEmail: data.senderEmail, - transactionMemo: data.transactionMemo, - transactionAmount: decimalSeparatorByLanguage(data.transactionAmount, data.language), - supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, - communityURL: CONFIG.COMMUNITY_URL, - }, - }) -} - -export const sendTransactionReceivedEmail = (data: { - firstName: string - lastName: string - email: string - language: string - senderFirstName: string - senderLastName: string - senderEmail: string - memo: string - transactionAmount: Decimal -}): Promise | boolean | null> => { - return sendEmailTranslated({ - receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, - template: 'transactionReceived', - locals: { - firstName: data.firstName, - lastName: data.lastName, - locale: data.language, - memo: data.memo, - senderFirstName: data.senderFirstName, - senderLastName: data.senderLastName, - senderEmail: data.senderEmail, - transactionAmount: decimalSeparatorByLanguage(data.transactionAmount, data.language), - supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, - communityURL: CONFIG.COMMUNITY_URL, - }, - }) -} diff --git a/backend/src/emails/templates/accountActivation/html.pug b/backend/src/emails/templates/accountActivation/html.pug deleted file mode 100644 index 6b5dd41c3..000000000 --- a/backend/src/emails/templates/accountActivation/html.pug +++ /dev/null @@ -1,18 +0,0 @@ -extend ../layout.pug - -block content - if logoUrl - img(src=logoUrl, alt="Banner", style="max-width: 680px; max-height: 250px;border-radius:20px") - h2= t('emails.accountActivation.title') - .text-block - include ../includes/salutation.pug - p= t('emails.accountActivation.emailRegistered') - .content - h2= t('emails.general.completeRegistration') - div(class="p_content")= t('emails.accountActivation.pleaseClickLink') - a.button-3(href=activationLink) #{t('emails.accountActivation.activateAccount')} - div(class="p_content")= t('emails.general.orCopyLink') - - a.clink(href=activationLink) #{activationLink} - - include ../includes/requestNewLink.pug diff --git a/backend/src/emails/templates/accountActivation/subject.pug b/backend/src/emails/templates/accountActivation/subject.pug deleted file mode 100644 index 81749a38e..000000000 --- a/backend/src/emails/templates/accountActivation/subject.pug +++ /dev/null @@ -1 +0,0 @@ -= t('emails.accountActivation.subject') diff --git a/backend/src/emails/templates/accountMultiRegistration/html.pug b/backend/src/emails/templates/accountMultiRegistration/html.pug deleted file mode 100644 index 4d568261b..000000000 --- a/backend/src/emails/templates/accountMultiRegistration/html.pug +++ /dev/null @@ -1,22 +0,0 @@ -extend ../layout.pug - -block content - h2= t('emails.accountMultiRegistration.title') - .text-block - include ../includes/salutation.pug - p - = t('emails.accountMultiRegistration.emailReused') - br - = t('emails.accountMultiRegistration.emailExists') - .content - h2= t('emails.resetPassword.title') - div(class="p_content")= t('emails.accountMultiRegistration.onForgottenPasswordClickLink') - a.button-3(href=resendLink) #{t('emails.general.reset')} - div(class="p_content")= t('emails.general.orCopyLink') - - a.clink(href=resendLink) #{resendLink} - - h2(style="color: red")= t('emails.accountMultiRegistration.contactSupport') - div(class="p_content")= t('emails.accountMultiRegistration.ifYouAreNotTheOne') - - a.clink(href='mailto:' + supportEmail)= supportEmail diff --git a/backend/src/emails/templates/accountMultiRegistration/subject.pug b/backend/src/emails/templates/accountMultiRegistration/subject.pug deleted file mode 100644 index fb130f0e4..000000000 --- a/backend/src/emails/templates/accountMultiRegistration/subject.pug +++ /dev/null @@ -1 +0,0 @@ -= t('emails.accountMultiRegistration.subject') diff --git a/backend/src/emails/templates/addedContributionMessage/html.pug b/backend/src/emails/templates/addedContributionMessage/html.pug deleted file mode 100644 index df5ba35c4..000000000 --- a/backend/src/emails/templates/addedContributionMessage/html.pug +++ /dev/null @@ -1,16 +0,0 @@ -extend ../layout.pug - -block content - h2= t('emails.addedContributionMessage.title') - .text-block - include ../includes/salutation.pug - p= t('emails.addedContributionMessage.commonGoodContributionMessage', { senderFirstName, senderLastName, contributionMemo }) - .content - h2= t('emails.addedContributionMessage.readMessage') - div(class="p_content") - p= t('emails.addedContributionMessage.message', { message }) - p= t('emails.addedContributionMessage.toSeeAndAnswerMessage') - - a.button-3(href=`${contributionFrontendLink}`) #{t('emails.general.toAccount')} - - include ../includes/doNotReply.pug diff --git a/backend/src/emails/templates/addedContributionMessage/subject.pug b/backend/src/emails/templates/addedContributionMessage/subject.pug deleted file mode 100644 index 4ac85fa23..000000000 --- a/backend/src/emails/templates/addedContributionMessage/subject.pug +++ /dev/null @@ -1 +0,0 @@ -= t('emails.addedContributionMessage.subject') diff --git a/backend/src/emails/templates/contributionChangedByModerator/html.pug b/backend/src/emails/templates/contributionChangedByModerator/html.pug deleted file mode 100644 index 46bcd4ae1..000000000 --- a/backend/src/emails/templates/contributionChangedByModerator/html.pug +++ /dev/null @@ -1,10 +0,0 @@ -extend ../layout.pug - -block content - h2= t('emails.contributionChangedByModerator.title') - .text-block - include ../includes/salutation.pug - p= t('emails.contributionChangedByModerator.text', { contributionMemo, senderFirstName, senderLastName, contributionMemoUpdated }) - .content - include ../includes/contributionDetailsCTA.pug - include ../includes/doNotReply.pug \ No newline at end of file diff --git a/backend/src/emails/templates/contributionChangedByModerator/subject.pug b/backend/src/emails/templates/contributionChangedByModerator/subject.pug deleted file mode 100644 index 791cee555..000000000 --- a/backend/src/emails/templates/contributionChangedByModerator/subject.pug +++ /dev/null @@ -1 +0,0 @@ -= t('emails.contributionChangedByModerator.subject') diff --git a/backend/src/emails/templates/contributionConfirmed/html.pug b/backend/src/emails/templates/contributionConfirmed/html.pug deleted file mode 100644 index 310993d97..000000000 --- a/backend/src/emails/templates/contributionConfirmed/html.pug +++ /dev/null @@ -1,10 +0,0 @@ -extend ../layout.pug - -block content - h2= t('emails.contributionConfirmed.title') - .text-block - include ../includes/salutation.pug - p= t('emails.contributionConfirmed.commonGoodContributionConfirmed', { contributionMemo, senderFirstName, senderLastName, amountGDD: contributionAmount }) - .content - include ../includes/contributionDetailsCTA.pug - include ../includes/doNotReply.pug \ No newline at end of file diff --git a/backend/src/emails/templates/contributionConfirmed/subject.pug b/backend/src/emails/templates/contributionConfirmed/subject.pug deleted file mode 100644 index c5bd41421..000000000 --- a/backend/src/emails/templates/contributionConfirmed/subject.pug +++ /dev/null @@ -1 +0,0 @@ -= t('emails.contributionConfirmed.subject') diff --git a/backend/src/emails/templates/contributionDeleted/html.pug b/backend/src/emails/templates/contributionDeleted/html.pug deleted file mode 100644 index daf54227d..000000000 --- a/backend/src/emails/templates/contributionDeleted/html.pug +++ /dev/null @@ -1,10 +0,0 @@ -extend ../layout.pug - -block content - h2= t('emails.contributionDeleted.title') - .text-block - include ../includes/salutation.pug - p= t('emails.contributionDeleted.commonGoodContributionDeleted', { contributionMemo, senderFirstName, senderLastName }) - .content - include ../includes/contributionDetailsCTA.pug - include ../includes/doNotReply.pug diff --git a/backend/src/emails/templates/contributionDeleted/subject.pug b/backend/src/emails/templates/contributionDeleted/subject.pug deleted file mode 100644 index 024588472..000000000 --- a/backend/src/emails/templates/contributionDeleted/subject.pug +++ /dev/null @@ -1 +0,0 @@ -= t('emails.contributionDeleted.subject') diff --git a/backend/src/emails/templates/contributionDenied/html.pug b/backend/src/emails/templates/contributionDenied/html.pug deleted file mode 100644 index d30653acd..000000000 --- a/backend/src/emails/templates/contributionDenied/html.pug +++ /dev/null @@ -1,10 +0,0 @@ -extend ../layout.pug - -block content - h2= t('emails.contributionDenied.title') - .text-block - include ../includes/salutation.pug - p= t('emails.contributionDenied.commonGoodContributionDenied', { contributionMemo, senderFirstName, senderLastName }) - .content - include ../includes/contributionDetailsCTA.pug - include ../includes/doNotReply.pug diff --git a/backend/src/emails/templates/contributionDenied/subject.pug b/backend/src/emails/templates/contributionDenied/subject.pug deleted file mode 100644 index 57431e56e..000000000 --- a/backend/src/emails/templates/contributionDenied/subject.pug +++ /dev/null @@ -1 +0,0 @@ -= t('emails.contributionDenied.subject') diff --git a/backend/src/emails/templates/includes/Chatbox.svg b/backend/src/emails/templates/includes/Chatbox.svg deleted file mode 100644 index 4eb68e0bb..000000000 --- a/backend/src/emails/templates/includes/Chatbox.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/backend/src/emails/templates/includes/answear_button.svg b/backend/src/emails/templates/includes/answear_button.svg deleted file mode 100644 index f45fedc90..000000000 --- a/backend/src/emails/templates/includes/answear_button.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - Jetzt antworten - - - diff --git a/backend/src/emails/templates/includes/chatbox-icon.png b/backend/src/emails/templates/includes/chatbox-icon.png deleted file mode 100644 index a25c7c209cb79912e5adab4b7732ca9858955e17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 323 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9F5he4R}c>anMprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt&fkT^vI^I^Rxpt*$!I@bcilmcXRrOkP_`0>HiiLiq(xj+>07kDqMfHY{##Q zlh#;pR2gl4c_ETL@xfeC%dqcrr^TpWs_@NqndEL~zIij>tP8)bE11vjc5$h#TT}z| O8-u5-pUXO@geCw$1$z7d diff --git a/backend/src/emails/templates/includes/chatbox-icon.pug b/backend/src/emails/templates/includes/chatbox-icon.pug deleted file mode 100644 index 62775b0f0..000000000 --- a/backend/src/emails/templates/includes/chatbox-icon.pug +++ /dev/null @@ -1,6 +0,0 @@ -span.chatbox-wrapper - img.bi-chatbox( - alt="chatbox" - loading="lazy" - src="cid:chatboxicon" - ) \ No newline at end of file diff --git a/backend/src/emails/templates/includes/contributionDetailsCTA.pug b/backend/src/emails/templates/includes/contributionDetailsCTA.pug deleted file mode 100644 index ae3b77e6a..000000000 --- a/backend/src/emails/templates/includes/contributionDetailsCTA.pug +++ /dev/null @@ -1,7 +0,0 @@ -//- -h2= t('emails.general.contributionDetails') -div(class="p_content")= t('emails.contribution.toSeeContributionsAndMessages') -a.button-3(href=`${contributionFrontendLink}`) #{t('emails.general.toAccount')} -div(class="p_content")= t('emails.general.orCopyLink') - -a.clink(href=`${contributionFrontendLink}`) #{`${contributionFrontendLink}`} \ No newline at end of file diff --git a/backend/src/emails/templates/includes/doNotReply.pug b/backend/src/emails/templates/includes/doNotReply.pug deleted file mode 100644 index 506a5d0db..000000000 --- a/backend/src/emails/templates/includes/doNotReply.pug +++ /dev/null @@ -1 +0,0 @@ -div(class="p_content")= t('emails.general.pleaseDoNotReply') \ No newline at end of file diff --git a/backend/src/emails/templates/includes/email.css b/backend/src/emails/templates/includes/email.css deleted file mode 100644 index 5110c3882..000000000 --- a/backend/src/emails/templates/includes/email.css +++ /dev/null @@ -1,216 +0,0 @@ -/* vietnamese */ -/* @font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 100; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} */ -/* latin-ext */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 100; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 100; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -/* @font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 200; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} */ -/* latin-ext */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 200; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 200; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -/* @font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 300; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} */ -/* latin-ext */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 300; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 300; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -/* @font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 400; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} */ -/* latin-ext */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 400; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 400; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -/* @font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 500; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} */ -/* latin-ext */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 500; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 500; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -/* @font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 600; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} */ -/* latin-ext */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 600; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 600; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -/* @font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 700; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} */ -/* latin-ext */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 700; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 700; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -/* @font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 800; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} */ -/* latin-ext */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 800; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 800; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -/* @font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 900; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} */ -/* latin-ext */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 900; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 900; - src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} diff --git a/backend/src/emails/templates/includes/facebook-icon.png b/backend/src/emails/templates/includes/facebook-icon.png deleted file mode 100644 index 1d880cf3e21f0eac022181f0540859e65c9d2635..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1449 zcmV;a1y=frP)WmhDt1z%7D7m zQj!Z6h7TAE@2=H})G ztJUgd0AnOiq$Rf=08dEX&gb(tj~_q2l#0x)3YeXp9a>&q{!~P+kQ_?Erj`I$6p=}# z)P(Q*e{V@vrwvKM7&A)pnurW<#fUAi0N`p61a|D)Tqo_*hU7GW zQn6V4z1_BL2ViDq=3Ep-cS#;;x2;P6%zK`9sZc0Px6@9wQ`s0Z7DdthUUObVgyfMZ zitZa@#@cDOsQ{4lwAMGQwYNz2cV?n{NcLN6Z)vS>kn}dwrsWVI=^110lDxQe6SD)r zy&woK0f?H_YuN*gG1s@7^CBW7FB)U6x2o5)2N+|>P0`~LH!=f;>1NRC@; z-&9KNtIgWh+Sh^fPVWGWiO5$XaR9W|p{rx8b^Mm}z!+1s*4~Q^l=>edP3J{KNS@GI zhhZ4%+7KwEsv()bni%zdMmE$aiNtdN$qN}NT~2yH^0ka?sOh^PBBCml%3%PfvQ*v( zq}|V3>pNAcR1T~7rA4O7dx3b#e_oM1*==LG1<8{+lE+*K2k=FMvPVto|0smDSX1t5Myt$I2D0GtItDK*&Ei;*tjatMgB)~52B*&!~6z*0^rwd7(Ms! zi^b7K^^GxKlKiAWn|#`~)lf<;}yhgz@@VZEGUwhp3C(TB%dh& zPrGeMw*c@|0eIYPL%Ick#|prMPSZ=aGXMzSf%1L-1S9^n<%7RjAqP}2F08XDiy$IlHwl-x4 zfUE11t2Z2o1VM0zWLxQ`+X0f(K@i+oU%nwl1^~+%K6v_OK-_m)JHgAj=W5!5+)2kCBfR_NqTXp+2ZQ50`tFO$6y!v+_dA64p zOC+aBo^988k-ckkdlx77uFj?*WIem`@^litetrIbG9Al@qI{v(00000NkvXXu0mjf DhkUKj diff --git a/backend/src/emails/templates/includes/footer.pug b/backend/src/emails/templates/includes/footer.pug deleted file mode 100644 index 91a0e4543..000000000 --- a/backend/src/emails/templates/includes/footer.pug +++ /dev/null @@ -1,77 +0,0 @@ -footer - .w-container(class="footer_01") - .socialmedia - a.slink( - target="_blank" - href="https://www.facebook.com/groups/Gradido/" - ) - img.bi-facebook( - alt="facebook" - loading="lazy" - src="cid:facebookicon" - ) - a.slink( - target="_blank" - href="https://t.me/GradidoGruppe" - ) - img.bi-telegram( - alt="Telegram" - loading="lazy" - src="cid:telegramicon" - ) - a.slink( - target="_blank" - href="https://twitter.com/gradido" - ) - img.bi-twitter( - alt="Twitter" - loading="lazy" - src="cid:twittericon" - ) - a.slink( - target="_blank" - href="https://www.youtube.com/c/GradidoNet" - ) - img.bi-youtube( - alt="youtube" - loading="lazy" - src="cid:youtubeicon" - ) - .line - .footer - div(class="footer_p1")= t("emails.footer.contactOurSupport") - a( - class="footer_p2" - href='mailto:' + t("emails.footer.supportEmail") - )= t("emails.footer.supportEmail") - div - img.image( - alt="Gradido Logo" - src="https://gdd.gradido.net/img/brand/green.png" - ) - div - a( - class="terms_of_use" - href="https://gradido.net/de/impressum/" - target="_blank" - )= t("emails.footer.imprint") - br - a( - class="terms_of_use" - href="https://gradido.net/de/datenschutz/" - target="_blank" - )= t("emails.footer.privacyPolicy") - div(class="footer_p1") - | Gradido-Akademie - br - | Institut für Wirtschaftsbionik - br - | Pfarrweg 2 - br - | 74653 Künzelsau - br - | Deutschland - br - br - br - diff --git a/backend/src/emails/templates/includes/gradido-header.jpeg b/backend/src/emails/templates/includes/gradido-header.jpeg deleted file mode 100644 index 38a5c3a80f090b8973db6d603d406249f7af7f04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9427 zcmbVx2UrtZ*Y1QAKoRL6ND-wicjw8>J8P}|uD#y9XZAeF;rqi$fJ{|MMG1gF0007hfWv9R z_bQhzS!n4Xl~mL(e|HeJTDiG75wHM&vx_HINBJVlRU=~-!XbbFI0YOB=z-%_C=a&_ zT3YG=*zmFV^WWR%0G=HHdU^ld|M%#BTphPTd!WDp5`lJC8xNE-h@VTR3Fd;4zIHGd{50A{AFm5`Rso)u>hCAD;#LE1OA=o#|Y+s^0>liVFEB=@GA{dhsnYuV3Gg} zOdh5HlY=ROR!NxjZ*HwW>cEd@9?bHC#~sXw{vKxlR{RRA%oDHzk5gcTE8y{);cr^p zqsMXn(C7x%0)y&`UTq1eraOx5hgF#NlC%W;p&16`a{L|fch!*pO?&t({1dz%JPyG} ze{-Y63TSj^YId1(pQ*1s)4z2`oTkp=Hn-=u2oN^bNoQt%tsX)@&7*OKi1CgIsaY<-iEINzaD?p`=_Gd`Xs$b%0POSRFRaKl#NvVcN7aLEvX>s z6;ck+!|;1{j#e|v-*mtLMxbWD)cW^a_&Wphn+en!asr%Z%u&X_-5>hkh%taS$cY8j z$FDONz!!}D^FI4o5sV2Y2vhmTs+Pd7_21k-?uTFV@k@!n*MLS`MkpdK04xY@gd{=` zp^C@9?r#JK=#vDG;8V*J?E~&D0C36G%@>QYv-e~X5)cq)xd3i;XciS06dw!O(ph66V!+QPT12^Wk0N`OOEcE>vLUIv&c8wn%u7l4o!WaP9zkhhRn{jx!p9%7h z0zkdf;RtXGfD#Z85fLHqUj&>GK}d)Izi>i)0}%p__})N_w;uzoM8w3z2>9_6$B0fK zh=_?GP#6r1B0|84i16P3=XO{RkP`tB1W%w4a)5vw0wsqWHUf;GI0R4#*nUKgPEq&fR7`B<>t2w70nZHqphx`Q84!eU&?!I;dcbQK3=W0=h=34~Ln+R{;DXu+ z)(cib7lZG=qI7>ubynv_x5(izKmrnh5#)e8aPTzf_n7|;AkM*P9%r`5=2XQ;mE~$y za6VIG4%<7IkHBGIKr@($yE{Q zjl#iCZa0^W&-5m3U-J~n@0;WDlfSty$KKe~R9{iifNnY+Y1urJWjO$tLL!z;O)V?n zO$dnF@gOapljZ(lJpqiu2x(`oSHp+EvPPTyzBH4*nR()c#{331nWySPzteaz-*M_=`8$v5Ljk5*JnAsRFu+8pVoY>Sj z1Q=wPT=)wljf*1bWjLs?mtZ>4gT2Sz8ZNbM?A5J$l#yeE6NK8_#5nsS=u7RD&(Zhk z+$|=VbbwI=KcUorB04y35k2w2fx$GNOD*%`>iTY7aRo)Z+wov`0e;mlCm z-c(<6KG^MQZ!u$nKKyNG>nr}RD*SzY3I{jrCAjnEp9L=I55f;xIS)ebla16wo$bRF zCw?HbBZrJOm;p=Ed_g!)1=9jztyBM_&VMz~m%@D;Nxd>}pK5kXK}qGFy66_8DHIW_ z6A?Qm(axKbrq?FBd*#4!SIb`psnq7JeM(KSt$3T_!@*nBShci;Y|Mv+#HJ{ z=kBj66x9u@x9JzW3yo!oz%0Tf5P4-#O7%2|$ops}* zi_+$P9C!Q%mtw`4loP0{k&SuL&Xq1T28Y1I0?NUs8op}*hd`Z0W5!#*!De}nedaB` zyHoiWcFNX~8>8cexgn+&D#|MsPxZOcL3}{HXpq1O(dhJR@gmIr!fb^q7w@R`*K#d= z(GI&VtojW_?g`BgCzIN~|B29pLKaxIRwV5df@Z4{yaD3=SVgZ8bfd~2{JqDONFO4j zAKf?L)hL6DjU{v(JfeD~I+A-}JjGEi-tn*@F*QZI-(T@kIWzT3j-28mqJG&*L7$n^ z&eCGrs-}V^yPQqavlCQ~7WQSzHWPJ3B9&P?OhsL594FZQ&gSpQ+l9C7IB)0X?$v1z zAsv!jV+^fL!fAU{-;X91)S(U9uIttpHKphV)JUNwaKi^GZRBO8$oaI6VUb4;`IEVN zQ#Q;g#@7>-{6j<5s^!>=WxVbVPJUUP2pjTwV6L1p4YSGnqpTy?oqi?-$h~G6s9bJ# zx23D4ik7=#EZoT}&O+}H2x(O-;JLY#$@W}5AyOrD-M=ny66~lzt*Dy##%e!?b|Iq^aQTmxfI7 zqXxH%+RBEgokg`qZVgiT?L5C@tt(jyIVmHvl7 zeC{C-yOBh2AnrNrZBe3g2yDr7$Of&MJO5QIzFqm4zgblwuuG-a@tND;vCpCA z^iy=oljHBSO_t^>827FQ_csMGMyP&+j5Gu>zPYjiEsA6c@;t9#6b5xW(h#amw5J>( z`^hlNawJh=`^9ozV++zQP`!8c>4aSavzKnlW`OAHmaf|zlRjOv$P+CxiTg~*(8*NC zjfS1we6+2Dk#0vvN=eyq^R}9P+=ZH%@JN#}-RP5Z(UQ(wD(xE2XHu;`B(Bjue3-Hc z7Iu3zeyUJWUU!J}b#oa*Hr)uSxanz5Urj&Pa;%239Z?}olJe9#)(B z&$}0d>F9pQ*qXSlNwLMA{oIt-WoOZQ^?)O-!I-}V6JeJe9i1e_PtN~%H+$ujgT%6K zSgzDV?k}Gv;_P0my*Rz)ImYbA-!Ty_j=0v)w$~}+N^AHry6I%9&kC^|b1X;ir~U3l z`n`dRK}S*^A%S<~oh*hWELyEuE+^k?=02yI({+)%>{dtfzG6skU@c~^FZM%Ll6yd} zdMv#huo<*zTBjEOP$JLQSZ?>FNBsMzbz?Pb)p=&rZCH!y*bjS-?lY7pVfJ$o87XQv zPSsn6xzn-65#Mz9lFckT_-fy!_KRh?K~CC=f6t< z9P}cW0evI3V1Q^2dR4@oPQ;Dx#OX^{8^Ar18}&)v&g^6;{ch4n+^FF7xn~{+DZ1C) zD?(B`^bxlg{ebxy9YPbDds6R9g9n?lNMGfCt5yZwM>CoHeFi%=V*@pspBPf@#XZ|D4Jeu}VwCTXqT8!2^7x{8 zGRAdfPyW<KXGKde>K{q1WR>6+1`58K{jY;TuJ zWPG|zG%pn_`^jsBK2~j?Vy3S`>!c)MCnv6DvuE;V*6x{>Pq)wsNz%|H(@pGCyAacy-AXJD+RN@NTKsDKf<`{9KJ>j>~XXNrqat3 z_Z|D|gqj;`;@{VvFYHKKSlxMKb|#uhdUb_KwtQ;L-Fa=Ka6D7@^P8NQvC_n3f9LhF zFDfT5OfybxB|b!A{P}|~7aO|-^5xoU$WXPJ$OQOmhE7+gwkip$?8cbvEW0No^#tpL zxAyxTq_Q8R_%+e__$5YTSBrVQdMCLKUc0#HZq1SAhvy!5;H@Z(UtzG#ms_dd`Z#WD z&Yu1Lv!F|aAdc7EXQ*2%r^%U{%;6X|B1J4VaO1Sa)9_(0)>Fwg&9Wt5cflgl0 z=NdTVJ!aOeuZ#W+j1T?PQD$AfCb`&1k3ejbE828opG%Z>c6vunET=d+8pkNro-4I< z>vJW=R7w6>PE86k6lZDD^>=d-(^(ldjY7c%f+QINjXHNz$SX+5D|VjsZl|*q=_IXB zo7asw9jx*+7@1>=^q=oBHNBF-(aFo!`WJO2PV3*wQ`x}mz1Tlh<}l5QhzWL6=6-vE z?P^nV6ug1FvK?hX^h~a8`Q!6c1P!B!Gn+bE{_FdSk9B!*FAhRN7aW@izS5<878fgg zoj7V?RIA~c~qO9e&wcXJqI8 zX{8t&^SI4zuxUo6=G)VDfpC*`ckQvu9@zOcdDT=iOxSq$myTEE;Za|Ux`$idoweia zd>}g<>80|t=H*I>4l>+pN3JTnb$xzv!G6cNchjeQjnb&Y&@}M1!;*spS+qF1Cz^jw z-$=49CPLiq{wyx-YqdL5GLLWQAs~7PJfDwUTHV>5oj^qzSMwYU%%8$WO3e(G2%lvz zj(q4dccZaeqNWib6K)-Nqe^7)g>&f z-Gg(s9%H7So#pZ4Yu22TC!o}N;cdeC^gLA}2}_J=r)OkA2RUPzT~*Ya$YSSPli|}5 zi!H;_f2|Ai4dz&1aE43``-6{_%d!%ksyG@Pq=Gg!NqMw4c%i%O(w%I6sq zdY0N-8+&k3eV;uY9+JJ#@5JfjTnd;OYo<=$HDY_QD{ks5lH_xDiA>oiG}}9Ebl_3O z!dOVY=A%A+ufIOZiusCL7{{Faz^X2X`Jc?ssk> zZx2VW?JJu+=it!IH+<7j#E72NW<1Z#n5~G(t2#qJS57&TVrsJxJ5($7^=i2K;+8|5 zaNqvjDwEjwtl5jZi=9P;`fuuAcNV`ya!@j9t@6f4U#CQi!+9q-n_RXvQ_QAumB{Xm zQ)BOrZVwRE)3-rjlW>zfXxjZKLXPuP_Fn?GszP4%Ki>86F}iP}pR5yB$xQTisjMt_ zk8+~BXsrKj=L;+SZJE?ht8CzoKy(OX7&I(vCs_o$n|4%1 z)L%OrAZMs{2$b9x&`W&2%rGyLeF$L7clrdJ4WH*QKYHl8-+z6Fgx0+#ML=fUwAQOB zP(OD&&TCaRZKJiKj=;kmlPg`AD6(ae&Bj!l44D_1@`GRtSonHF2-O&fz{d?4V}O%@ z)PrqHoZuR``J*7+E$O6@qQPDj!PZ2?6i}8UWf55i4UIL~yZknJxk81!#oPI9+Rmc- z$!<2XvEw4ciL{lQw-aPv+?2b%hD=2Ao=v?!o_2RJRb$#ZF;&{nq6(Z{~XX*eZ&MfWfFN^C5hKUQ(>Bc~Dn+%22IwwFuP zTgchKG(uWNImgcZR6^dD9A*C3CNd5Irb4%=GHkfZQ-jQEN)-u-nz)QjWa0d}JXQwn z%hla>ihMh;s}p?QIgAv>)~+q@B~%S_UJ4|YR0&+E%$zG1n_CT3p7OvVbD5X-<)jol z$NYynp6j%>S82t7Z?Mg`*4)|VoWEeuW`rF+y>xk0m6FIa9LvlK+jbpG%q!#4EmH^P zRUetyrQHn=CK^8Vep=D{>f7R1+a;w`x{w-p&Z+&Wvw@jzIWs%?1djR|Wnl}|mV+=h z%thne$i$sw{EzI z96AI_O}vL(^-Uy=WZ;R3r%%$TtT;3naBpW!P@#5<9e6K0-H)DE6nwX`klHaUrj#6- zZ4@mYd}E|OsrGAZBnOvDXF;tvHZGf^GA=87c{%ME_sHQT}(@h0L; zRng2XQ^!J&_+-^P?3=lvH@&Mg*4ca(auvmvR!Z3KKAAH~be0rBTa=G5F&Sv`2<_EP z=jUkOjx5wvQPNp=UZDU{>IpnGEh}sfXrA-XJt&fRPr;kU)+D9GRXeHM?2j@?ny55r|VzH1`)R|~Q$yr7o+t3CL`+`>a^=MI~M z(U_6MSLa@yOH_2$mQfJ7YUz5Kg%?~epG796s4(>w`AC8>V;`FQR~rON5-gsAtH5WJ z<-QZ4K?IAGs5QiauHHBj(4Y@WASciZ8e{o~1TflZC5sxmj(zR$y@^(;^<%^*9M|XGYxbtU#=L2+y`AZT!j~ecMBMcgVK*w{3mKqQ05FKnS?uH2qNE4 zdXxw%e6X?hgLJgMhCYlBuAR^7o$dW}z?EC+xC(8Pc3&LI{vfH+y=fXcuxOCyd3cKpvn`ja5b?|y6VI?eKu&JG4kBrGXe3a(N7feek+eQ_~ z>yiea~#|g)FAFoav2J_bRMdPh8V(tkeA118 zx6c#!^ncw5iV=X~0P!m)V*cEO@k&@efGb_RRS*7Q{U4iE`q?;zFgJpl(qPSDATvJudDOoPTykaL3dsZ_x-=LziV!MBt@e&qU<=)ayJkwN{9L~~ckRsUisxyL;e1y^$q zdI}Ea+3O90M-LPX6KHb>rx9mw!M;vG{J^D7Weo`|u;jf&N30KAdHU^}0=0ruKGdiF zM8BoWJ%)cS`~RB%XFz|tXq-Zn7Qv}L7>yR(cP7~2jpg|+G9vUH1PyaK#o%}CUusIw zOleUn0V?$MT_*>7AoU0zx!Q6lnvCJvH36~zZLWXDK^Y)XWFl?^k_trGTI8xhXA&gH zWh{@=otTM&t!5EV0qnPcc$WWf`k(2bC%$KaDqp3Ss_*YZP+M4BIT3H^lnx66uG~BI j&(;0ULH_q^a3&iR9NrNEg7pYJptBzRpN33$IP`x25HL#K diff --git a/backend/src/emails/templates/includes/greeting.pug b/backend/src/emails/templates/includes/greeting.pug deleted file mode 100644 index 6693e23ca..000000000 --- a/backend/src/emails/templates/includes/greeting.pug +++ /dev/null @@ -1,6 +0,0 @@ -//- This sets the greeting at the end of every e-mail -.text-block - p - = t('emails.general.sincerelyYours') - br - = t('emails.general.yourGradidoTeam') \ No newline at end of file diff --git a/backend/src/emails/templates/includes/header.pug b/backend/src/emails/templates/includes/header.pug deleted file mode 100644 index 3160d3e2b..000000000 --- a/backend/src/emails/templates/includes/header.pug +++ /dev/null @@ -1,13 +0,0 @@ -header - .head - //- TODO - //- when https://gdd.gradido.net/img/gradido-email-header.jpg is on production, - //- replace this URL by https://gdd.gradido.net/img/brand/gradido-email-header.png - img.head-logo( - alt="Gradido Logo" - loading="lazy" - src="cid:gradidoheader" - ) - - - diff --git a/backend/src/emails/templates/includes/requestNewLink.pug b/backend/src/emails/templates/includes/requestNewLink.pug deleted file mode 100644 index 8baa3d6df..000000000 --- a/backend/src/emails/templates/includes/requestNewLink.pug +++ /dev/null @@ -1,10 +0,0 @@ -//- -requestNewLink - h2= t('emails.general.requestNewLink') - - if timeDurationObject.minutes == 0 - div(class="p_content")= t('emails.general.linkValidity', { hours: timeDurationObject.hours }) - else - div(class="p_content")= t('emails.general.linkValidityWithMinutes', { hours: timeDurationObject.hours, minutes: timeDurationObject.minutes }) - - a.button-4(href=resendLink) #{t('emails.general.newLink')} \ No newline at end of file diff --git a/backend/src/emails/templates/includes/salutation.pug b/backend/src/emails/templates/includes/salutation.pug deleted file mode 100644 index 3e2591951..000000000 --- a/backend/src/emails/templates/includes/salutation.pug +++ /dev/null @@ -1 +0,0 @@ -p= t('emails.general.helloName', { firstName, lastName }) diff --git a/backend/src/emails/templates/includes/telegram-icon.png b/backend/src/emails/templates/includes/telegram-icon.png deleted file mode 100644 index 4c36ff66148cb69d8c8f9373b33bca573c251ed8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2159 zcmV-#2$1)QP)bMFuniA}Vr(Ws3f zX=+J~xF)q|Qi+NFAht>4#}8A5HmO9usx}fq(cTEGv}W7Iq&=4^ZB0l5?w~ISX?6ME=fa@mXo|4z+&fI z))k{%kk(V>d{nC<*|qmvj%wRg&6jhomIEO!9dE zdx9W{($zFY2b?%@B0DxV_8`e&Nq3~hTMv@QCGGC-@87;+#fl55aCS;iDwP&pzI^#> z03MdKCtfU*0%61bZ&v>4o zDqCxZtJUg(`DQ!-NV>6Ftsbz}4kwXo$pMnQD2lc?=XOcDu00jI4@uWK=XOO=v_+EF zicHfXAjz}VJ}c?o&Q-j6Ai3Z7{dFX(bJc6w1FW@=zuSxl07>^+Yaee`Z_XZItsQpG z?MhFrs}Ez$mweyf-ROBk2gI}ef%!blvcpRgB-dJNH_Rxn|5GZJ7M088Q+Vh0 z_T9!g&+`WB4^UV1fcR85uZ<`90?98LV{#-nClp`ooZEB-U+u?<6DP9cX^4$H}e)h-ZN0TL7K|u*3KLgL8$l!Z7RsurZU#{3Vymy;T3KR4Uz6E|=Th ztVy~D05Zj5afx$osN3V8Q)em60^m2~h)B06PZ;27cZ^`B@Q$;ioQMyts|zG626E7#R5c;Naj}v(=TTr#7hqwkxTn0SAC8ov#aw`Ty{ zkW^688^)Mt;;rfNb{ej)TaZS<&thJx4R4SvAHq{#sV06KP1s_goe6d(u z0^p{kddvpr+=YxW=7OZA6QQIv)oS&nD2iSMun)lAAP7oxDe9bi!a4WAELeZ{yCsjJtw{Oga41jY0KA1p(+W>3^u=()e!zW4Z%VaWpbGh7Ttxgz*U#M2AKLqgJ z>GCA^FI~FybIX=3n`l$-)EofdTt?C<0EKp>NiGNQNTpJFB#NTr0QLg7OVZs9M0WbV z|4@SC?hK@{1FYjZl_7aDjU^?HCp-qAk#XNH6bg@a+)#==faFO-ay{O;nJl`sdaynB6-N<^ZB!qUQUtN)E{C< zo*o(+`gf^R>Wu>&Z6oFJ|8hQ`KWhLW&cjQKn7VROISl}J-g)OZFcsx#3(mPT$N%%d z+fm^?0KT3U`7}&jNEQl(%dvyZTu0qGkXjGGq|K8zLqQNkB-=XG%?3`Q)(cKgdnRjb zJ`BSjg<-g`RnB6ucs+nyQ_}BvE$g&?VtP6aasZHIB)Pb;D;!|0y<5^j02?L!wNxs# z6p(Swr8)km4s}xw=;)fJm25n zzrFT<$^r3R*^_BBQrDPzdU{?50Bh|-B!4kW#z_{yz9@>eG?^gNngE}y&ojPaZSLvm zc>7Nx}umvm_tzegBWOvM>x6$7wDdh3`oY2SISfTsMPI62mb3^E;U(5-*XDEnK+p zsqyjg>q%~_-Q=_jNh5_q;Um*`z1NbYW)mnB3a35KTSv0oef7GFI8AO{qfGLKlTt31 z8zH%|d#YU>klZ*|2Kk&PG~f4kliWI2o%sxsTbo6)uO&;jKva#1-ZecTe)CO@2NHfU zVyzvPv~9lT50fmD+?eD7=h`PTNgK-LawC$~NJ`XDHz8?6(wd~k1J^#AyY}JawNGc$ lAeoN;%a5lUVZMJp|6hafLt8=zP{RNK002ovPDHLkV1k2FAsYYy diff --git a/backend/src/emails/templates/includes/twitter-icon.png b/backend/src/emails/templates/includes/twitter-icon.png deleted file mode 100644 index fffb9b59939b564a552879830d1244e319c9ba8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2002 zcmV;@2QB!CP)2v^jL`{t)V1_VYpM$PXVmRA|eAWko=M7c?U=;4MC~~@O}SQ zNry;oNn@WkBpoBUqg*b(o76UO0~|kod`le1uSmK%scj=c@_eCC*xuFEbu5XUNm4ir z!|phak1RIl0YK8taU35B!>~Jv-Mkt=QXvR}mz;BZCAFlJ*my{4an9`xg5V`dh56Xj z{RAWx!Z3VA(zfgp7Y-zU?|I&KlJQ*S>h^#z3}5`PIS&AmwuNE%V!d*6_JA-9yPb1; z^OLpkFvdLQdEUO+exJ1gs-yjp#r)0B2PDS|g~GkF%?>j*fbaXal6-41j`nFl^1P(Y zGp!S*jRBG<>CpA!JaA=-tok(VA3m)C!Z6&qSXOv>g5;Jk40ld@U;9w0RF;p8jh(>< zx3+IIE))ud&(&_AuId5RUETHSJaFY8=KHlTrg}hMUtj0Q$jCbjwWY0tnB*XUk4ajR zCo##39UUFFO`a!A^?;F)k?(wv&P$pgxu;MlY_ZlZvDV&Vt!*{N+)478>KVWkNM0tn z2f*tI>sdZLJpB0NbL}UPXcJK*L;xNlc{q)I6@PDOX(?H2pD2||f301YeNl6qoe(4dM?SbwKkaS0S_l7s3H0-0IXWI>VIu*ZQDq`k;L8@ z^KcsH0bq1=^z#6|LGsrr%y$6QPe9U_(g*+ml7E~0yI3p^d!F}Y06!t==3)N2wf4=l zbz5uy(9zNHmUHf|q_&c_0)UC4XeG(@X+;1St$iTr%H{IY0JZ`+F_)n+=64zD1pthU zj2s}jCaE3C^-&b9G}WL*>cdgzTyduN*4iW1+Ad?vBLL3KV&0RsUf=hdgCO{or0r?! zt4scSnk3EWzfP(-IhGAHC&V7^Qea^YnB%7Rb7iY7bN4+mg`g&S>0FYEP zk`i~HS~%x+Mp3k~UOCV6{BpVcLu+lZy}i8yz{a+=wp4>307$ZFZEHBzG?82*r8-gv z0FZp8+70*2*QQu3j-k%@P!Ezi$ZT;PlB=5l+?0W1-1EH0(gr2cqIyWXEC;F;#yQuS zfuQ-si4&j7K}aI>^z_^TpgAi~H9~E&GyolD#?c6-e&y?d@$yBq<1j2PEB}Cnw2X zL-J@|D>+GbU%q_#U{6m^M;?5Ws8lMScFrBhlar)K9yO&>X+YA+yojB14~>tHhfx%L zRZ>${zW)CHC1Yb_zag1r$vzE|PL@ig0RsTlJiI)y+BtV1ilRreko5QWFBuvddKJJI za?&-0NuMVNltVd@UL<+g81wCA%a+~dd0y(!dT(#<(xIWDS0&w-hqf7T2&f5+1VPZ7 zB*Bs7JI0u;aU8ccH8r`3iHY~EweRIIoH_p!OTx58#Ca zJfCsSJ=)yd{QRa(n?~{w*3;9|F)=am1LxdR0CJ58)k4x#!bIlKn56fNF+Xo_Z{M?S z-MYavf}$w;cpS%%0C)<(EonIBi)%w;R~@MYLGY}kXVMT$`mZtOu%y>oT3Y_HapT5+ z%vHWpsoWmN@mW=_SvcDYeeN49)F)?ucSc${~=kBw4CH8s)wSv zPHXeTwD9<}L$x3XzE%5;Tu&g`ZLQsZ)w^p8NuuxjZ(eU%B9e}k%jLVTou#I2EUHVy z9VE{$oV><@N-YpSL38D z3`p*pD}#K_y|w3g`$+E2U)I8ahzpWd^u1J4o6rDN#j@grsAVHYar+xN&Xn#>L4SS7-Ae kxfUPE%hR)Av0tD6KRO$}cDgFm=l}o!07*qoM6N<$f{G#L5&!@I diff --git a/backend/src/emails/templates/includes/webflow.css b/backend/src/emails/templates/includes/webflow.css deleted file mode 100644 index 45e4420c4..000000000 --- a/backend/src/emails/templates/includes/webflow.css +++ /dev/null @@ -1,166 +0,0 @@ -body{ - display: block; - font-family: "Work Sans", sans-serif; - font-size: 17px; - text-align: center; - text-align: -webkit-center; - justify-content: center; - padding: 0px; - margin: 0px; -} - -h2 { - margin-top: 15px; - color: #383838; -} - -.container { - max-width: 680px; - margin: 0 auto; - display: block; -} - -.head-logo { - width: 100%; - height: auto; -} - -.text-block { - margin-top: 20px; - color: #9ca0a8; -} - -.content { - display: block; - width: 78%; - margin: 40px 1% 40px 1%; - padding: 20px 10% 40px 10%; - border-radius: 24px; - background-image: linear-gradient(180deg, #f5f5f5, #f5f5f5); -} - -.p_content{ - margin: 15px 0 15px 0; - line-height: 26px; - color: #696c72; -} - -.clink { - line-break: anywhere; - margin-bottom: 40px; -} - -.button-3, -.button-4, -.button-5 { - display: inline-block; - padding: 9px 15px; - color: white; - border: 0; - line-height: inherit; - text-decoration: none; - cursor: pointer; - border-radius: 20px; - background-image: radial-gradient(circle farthest-corner at 0% 0%, #f9cd69, #c58d38); - box-shadow: 16px 13px 35px 0 rgba(56, 56, 56, 0.3); - margin: 25px 0 25px 0; - width: 50%; -} - -.button-4 { - background-image: radial-gradient(circle farthest-corner at 0% 0%, #616161, #c2c2c2); -} - -.button-5 { - background: linear-gradient(135deg, #53900c, #6e6e6e); - font-size: 20px; - font-weight: 600; - color: #f5f5f5; - width: auto; - box-shadow: 20px 20px 25px; - transition: all 0.3s ease; -} - -.button-5:hover { - transform: translateY(-5px); - box-shadow: 20px 25px 30px rgba(56, 56, 56, 0.4); -} - -.chatbox-wrapper { - margin-right: 8px; -} -.bi-chatbox { - margin-bottom: -5px; -} - -.child-right { - text-align: right; -} -.child-left { - text-align: left; -} - -.socialmedia { - display: flex; - margin-top: 40px; - max-width: 600px; - -} -.slink{ - width: 150px; -} - -.footer { - padding-bottom: 20px; -} - -.footer_p1 { - margin-top: 30px; - color: #9ca0a8; - margin-bottom: 30px; -} - -.footer_p2 { - color: #383838; - font-weight: bold; -} - - -.image { - width: 200px; - margin-top: 30px; - margin-bottom: 30px; -} - -.div-block { - display: table; - margin-top: 20px; - margin-bottom: 40px; - flex-direction: row; - justify-content: center; - align-items: center; -} - -.terms_of_use { - color: #9ca0a8; -} - -.text-block-3 { - color: #9ca0a8; - margin-bottom: 30px; -} - -.line_image, -.line { - width: 100%; - height: 13px; - margin-top: 40px; -} - -.line_image { - background-image: linear-gradient(90deg, #c58d38, #c58d38 0%, #f3cd7c 35%, #dbb056 54%, #eec05f 63%, #cc9d3d); -} - -.line { - background-image: linear-gradient(90deg, #c58d38, #f3cd7c 40%, #dbb056 55%, #eec05f 71%, #cc9d3d); -} diff --git a/backend/src/emails/templates/includes/youtube-icon.png b/backend/src/emails/templates/includes/youtube-icon.png deleted file mode 100644 index d135ba625d2749adad59d87623b082797418f5ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1802 zcmV+l2le=gP)A6O75=_ElNjVNA;O7-5TOgwnA%F_y_qafL{%!fVbMi4 zNR|-6MXj=dt|DavsEewq3JqC+h?I~8@`6PdEXo4mNTPBc>FVd_svY4G~Z@4@4fGwbI-f)-h0kHm*_I8)#?FB&q^AWbcE#700y0N z1ICyE0C3JN8Do|J%uAXfd7I=7l2?KtxZ6$p*bSjk6qNvsN_v6hh$JsPBqixO$v*=a zFO^DBXD#iZ1E!~^2bPzYUnV&w>6uQ+Z3fAkk}eerg$qMNLrWcznN>ioR@=Y2y806U zKbEw=12(k<$$J3K8)MD|L9mcXmM>8ft+l5ly$N72ixC+xPx4F{hL=goM}U_DDwWD1 zN#i6>`0zUoNs}Z;%jNP1X}PH#aP{id6LB2BBk5pTZZ|=4E|<%lEEbECX?P~h!`9ld zIF2WFTjK#h(!n^6C# zhcV`*Fbpqkb$?3-B%}SrZvN(X0+K7aT<-a;W`|83P^nZ7k^IMQ9PNETa!%6m&DIGU z#sEo_H2%0W9$1?qlQwO5;Ts%atv$6{R(PER$rIMvQyZ=~|Ebk#`&U+0X7KRV_T9#O zE|)vp96+t>0ZCN%xHcYGOT@g|eBnV4n4X>`h?_tl1`G78c%g(vyCA}u; zDIYl`$B;OHz*-JZQpHo)ElJ-jm&=Pjd|%AY&VFHWaq)cs!#;eHHM-NNuOY^m zAN9KN0C4#5;s56I`Bb5A3r2xt7LfFUj|7r;f*^Q5gAtifEEfMx@}EB3Mw|21YW09~ zZbb4*sa;F+u(kFWfbSV&E*Fc%86SnMkaQiu*VFPyj#R7F118zD@Xg!bPV>B^UpnW` z#&KMWqUdL{v$K6ZdRuIm_2DH;{%4IOuR#O=oOAz5skA@&?UVHLg@uKwYPI^Dul_{V zzcX-$jie*K0!V)B%T4%4;y8ZKTKl_Ow{CsOQ_Q@teo03R$t(`&$nK1D?o_>ApRQCY zKS;|p#w`2lCHb@g;OlviT=nH{fR;%s!rDU{W&^YG?eA&*qj<5J6J-52L zIu%9H*>2mA>`)tT2gq~o+=1LaNv~hKcI~U(w80oNkSVYr*_G|}ME37TMn*pDwhhKP z=RJ^h!vQ4k7-PO$DwV#!lb|Y#4M|~!$ug4f$-rF)N%u*u?>$s4{7NGiT|3}eh2aU6eajJc5VJKqjN zzC@BY@+7Y$)mW)VQ%R|m2SM;IfOpgCX@hV0kVuN;6%z!(-6WwW(wAPN7n% z4Ea{1NxB{c!CeCYNgbXSB>iTm#R#25qVw0j{6?D_E5Y#$hJ43b`6aY8+_V7>T0jrX;WLsR@fDKLjw>*cXtrO!9w{R!DwA@_u7az!>8Iz&U4{ z_6G8j`bh4T^m&qnrhS2flD-09Ult=t-Yk_$!$8x%fTW86+E!Gw!9Gd*06de(MC)(! zjME&*wcgu|+bKwTP%!Zz7%CJB7m^Ch#~LK>6$*t5jsFiEknEM6e<)V<7U!GmjMs(3 zd-m-46*A=59s$f7W6m{SSm%JFM~~hkd8W5E^$sM@v=&!yn6@ObQmOp)ajO!MG+8c} zztvh)y&)r%ED=XZ&h^f`?ta=H9LE|)t=a;5v`br(sQ+{vvf$+yf2 z7K_D6lBc_8T5mw|^mY~G+a^WBFuX+a+;-3GW{^C$U6m;Cq*%Iru{6mz5%kFPgyhY) zZ9I_j#E7+aED3*iW&JS86_Tghb^CRkv`RkHM`nPv*+J5ANvRZd6Otw+9Z%bM;K{MM sCkH2=9G&fikXC#q4^MA}-F|%jQ$FLp<@n27i2wiq07*qoM6N<$f^Fe$FaQ7m diff --git a/backend/src/emails/templates/layout.pug b/backend/src/emails/templates/layout.pug deleted file mode 100644 index 0995b2647..000000000 --- a/backend/src/emails/templates/layout.pug +++ /dev/null @@ -1,26 +0,0 @@ -doctype html -html(lang=locale) - head - meta( - content="multipart/html; charset=UTF-8" - http-equiv="content-type" - ) - meta( - name="viewport" - content="width=device-width, initial-scale=1" - ) - style. - .wf-force-outline-none[tabindex="-1"]:focus{outline:none;} - style - include includes/email.css - include includes/webflow.css - - body - div.container - include includes/header.pug - - .wrapper - block content - include includes/greeting.pug - - include includes/footer.pug diff --git a/backend/src/emails/templates/resetPassword/html.pug b/backend/src/emails/templates/resetPassword/html.pug deleted file mode 100644 index 4d4ed3f50..000000000 --- a/backend/src/emails/templates/resetPassword/html.pug +++ /dev/null @@ -1,16 +0,0 @@ -extends ../layout.pug - -block content - h2= t('emails.resetPassword.title') - .text-block - include ../includes/salutation.pug - p= t('emails.resetPassword.youOrSomeoneResetPassword') - .content - h2= t('emails.resetPassword.title') - div(class="p_content")= t('emails.resetPassword.pleaseClickLink') - a.button-3(href=resetLink) #{t('emails.general.reset')} - div(class="p_content")= t('emails.general.orCopyLink') - - a.clink(href=resetLink) #{resetLink} - - include ../includes/requestNewLink.pug diff --git a/backend/src/emails/templates/resetPassword/subject.pug b/backend/src/emails/templates/resetPassword/subject.pug deleted file mode 100644 index 21f277316..000000000 --- a/backend/src/emails/templates/resetPassword/subject.pug +++ /dev/null @@ -1 +0,0 @@ -= t('emails.resetPassword.subject') diff --git a/backend/src/emails/templates/transactionLinkRedeemed/html.pug b/backend/src/emails/templates/transactionLinkRedeemed/html.pug deleted file mode 100644 index 281ee9205..000000000 --- a/backend/src/emails/templates/transactionLinkRedeemed/html.pug +++ /dev/null @@ -1,18 +0,0 @@ -extend ../layout.pug - -block content - h2= t('emails.transactionLinkRedeemed.title', { senderFirstName, senderLastName }) - .text-block - include ../includes/salutation.pug - p= t('emails.transactionLinkRedeemed.hasRedeemedYourLink', { senderFirstName, senderLastName, senderEmail }) - .content - h2= t('emails.general.transactionDetails') - div(class="p_content")= t('emails.general.amountGDD', { amountGDD: transactionAmount }) - br - = t('emails.transactionLinkRedeemed.memo', { transactionMemo }) - br - = t('emails.general.detailsYouFindOnLinkToYourAccount') - - a.button-3(href=`${communityURL}/transactions`) #{t('emails.general.toAccount')} - - include ../includes/doNotReply.pug diff --git a/backend/src/emails/templates/transactionLinkRedeemed/subject.pug b/backend/src/emails/templates/transactionLinkRedeemed/subject.pug deleted file mode 100644 index 9070b60b3..000000000 --- a/backend/src/emails/templates/transactionLinkRedeemed/subject.pug +++ /dev/null @@ -1 +0,0 @@ -= t('emails.transactionLinkRedeemed.subject', { senderFirstName, senderLastName }) diff --git a/backend/src/emails/templates/transactionReceived/html.pug b/backend/src/emails/templates/transactionReceived/html.pug deleted file mode 100644 index 4f9aa9c31..000000000 --- a/backend/src/emails/templates/transactionReceived/html.pug +++ /dev/null @@ -1,30 +0,0 @@ -extend ../layout.pug - -block content - mixin mailto(email, subject) - - var formattedSubject = encodeURIComponent(subject) - a(class!=attributes.class href=`mailto:${email}?subject=${formattedSubject}`) - block - - - var subject= t('emails.transactionReceived.replySubject', { senderFirstName, senderLastName, transactionAmount }) - h2= t('emails.transactionReceived.title', { senderFirstName, senderLastName, transactionAmount }) - .text-block - include ../includes/salutation.pug - p - = t('emails.transactionReceived.haveReceivedAmountGDDFrom', { transactionAmount, senderFirstName, senderLastName }) - | ( - +mailto(senderEmail, subject)=senderEmail - |). - .content - h2= t('emails.general.message') - .child-left - div(class="p_content")= memo - .child-right - +mailto(senderEmail, subject)(class="button-5") - include ../includes/chatbox-icon.pug - span #{t('emails.general.answerNow')} - - a.button-3(href=`${communityURL}/transactions`) #{t('emails.general.toAccount')} - - - diff --git a/backend/src/emails/templates/transactionReceived/subject.pug b/backend/src/emails/templates/transactionReceived/subject.pug deleted file mode 100644 index 872806ebc..000000000 --- a/backend/src/emails/templates/transactionReceived/subject.pug +++ /dev/null @@ -1 +0,0 @@ -= t('emails.transactionReceived.subject', { senderFirstName, senderLastName, transactionAmount }) From d9647183c5f76eec9dd32da9d7423466136cc112 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 24 Nov 2025 14:45:34 +0100 Subject: [PATCH 07/22] better error message for validation error, fix jest error --- config-schema/src/validate.ts | 27 ++++++++++++++++----------- config-schema/test/testSetup.bun.ts | 3 ++- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/config-schema/src/validate.ts b/config-schema/src/validate.ts index c566dfab7..83cf097b3 100644 --- a/config-schema/src/validate.ts +++ b/config-schema/src/validate.ts @@ -17,17 +17,22 @@ export function validate(schema: ObjectSchema, data: any) { throw new Error('missing key in config validation with joi: ' + details) } const value = err.context.value - const description = schemaJson.keys[key] - ? schema.describe().keys[key].flags.description - : 'No description available' - if (data[key] === undefined) { - throw new Error( - `Environment Variable '${key}' is missing. ${description}, details: ${details}`, - ) - } else { - throw new Error( - `Error on Environment Variable ${key} with value = ${value}: ${err.message}. ${description}`, - ) + try { + const description = schemaJson.keys[key] + ? schema.describe().keys[key].flags.description + : 'No description available' + if (data[key] === undefined) { + throw new Error( + `Environment Variable '${key}' is missing. ${description}, details: ${details}`, + ) + } else { + throw new Error( + `Error on Environment Variable ${key} with value = ${value}: ${err.message}. ${description}`, + ) + } + } catch (e) { + console.error('Error getting description for key ' + key + ': ' + e) + throw e } }) } diff --git a/config-schema/test/testSetup.bun.ts b/config-schema/test/testSetup.bun.ts index f64b81279..fd7153133 100644 --- a/config-schema/test/testSetup.bun.ts +++ b/config-schema/test/testSetup.bun.ts @@ -91,7 +91,8 @@ const getLoggerMocked = mock().mockImplementation((param: any) => { }) mock.module('log4js', () => ({ - getLogger: getLoggerMocked + getLogger: getLoggerMocked, + addLayout: jest.fn() })) export function getLogger(name: string) { From d07e9ba4139aeeff31f95c409913cc2f80b4ed0c Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 24 Nov 2025 14:45:50 +0100 Subject: [PATCH 08/22] prepare federation for using emails --- federation/esbuild.config.ts | 3 ++- federation/package.json | 6 +++++- federation/tsconfig.json | 6 +++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/federation/esbuild.config.ts b/federation/esbuild.config.ts index 2ef5f88e5..879d96cbd 100644 --- a/federation/esbuild.config.ts +++ b/federation/esbuild.config.ts @@ -8,8 +8,9 @@ build({ target: 'node18.20.7', bundle: true, keepNames: true, + entryNames: '[name]', // legalComments: 'inline', - external: ['sodium-native'], + external: ['sodium-native', 'email-templates'], plugins: [esbuildDecorators()], minify: true, sourcemap: true, diff --git a/federation/package.json b/federation/package.json index 20176e3f9..057b22a98 100644 --- a/federation/package.json +++ b/federation/package.json @@ -8,13 +8,14 @@ "license": "Apache-2.0", "private": false, "scripts": { - "build": "ts-node ./esbuild.config.ts", + "build": "ts-node ./esbuild.config.ts && mkdirp build/templates/ && ncp ../core/src/emails/templates build/templates", "start": "cross-env TZ=UTC NODE_ENV=production node build/index.js", "start:bun": "cross-env TZ=UTC NODE_ENV=production bun build/index.js", "dev": "cross-env TZ=UTC nodemon -w src --ext ts,json,css -r tsconfig-paths/register src/index.ts", "dev:bun": "cross-env TZ=UTC bun --hot src/index.ts", "typecheck": "tsc --noEmit", "test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation jest --runInBand --forceExit --detectOpenHandles", + "test:bun": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation bun test", "test:debug": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation node --inspect-brk node_modules/.bin/jest --bail --runInBand --forceExit --detectOpenHandles", "test:coverage": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation jest --coverage --runInBand --forceExit --detectOpenHandles", "lint": "biome check --error-on-warnings .", @@ -23,6 +24,7 @@ }, "dependencies": { "cross-env": "^7.0.3", + "email-templates": "^10.0.1", "sodium-native": "^3.4.1" }, "devDependencies": { @@ -60,6 +62,8 @@ "joi": "17.13.3", "lodash.clonedeep": "^4.5.0", "log4js": "^6.7.1", + "mkdirp": "^3.0.1", + "ncp": "^2.0.0", "nodemon": "^2.0.7", "prettier": "^3.5.3", "reflect-metadata": "^0.1.13", diff --git a/federation/tsconfig.json b/federation/tsconfig.json index 86b102fa6..c42e39505 100644 --- a/federation/tsconfig.json +++ b/federation/tsconfig.json @@ -6,6 +6,7 @@ // "incremental": true, /* Enable incremental compilation */ "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "resolveJsonModule": true, // "lib": [], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ @@ -62,7 +63,7 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ "typeRoots": [ /* List of folders to include type definitions from. */ "node_modules/@types", - "../node_modules/@types" + "../node_modules/@types", ], // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ @@ -84,6 +85,9 @@ "skipLibCheck": true, /* Skip type checking of declaration files. */ "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ }, + "include": [ + "../core/src/types", + ], "ts-node": { "swc": true } From 8bb05d172132c889dff90ef1cbb02df84e0067f1 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 24 Nov 2025 14:46:48 +0100 Subject: [PATCH 09/22] remove email locales, simplify i18n initalization --- backend/src/locales/de.json | 101 ----------------------------- backend/src/locales/en.json | 101 ----------------------------- backend/src/server/createServer.ts | 8 +-- backend/src/server/localization.ts | 3 +- 4 files changed, 5 insertions(+), 208 deletions(-) delete mode 100644 backend/src/locales/de.json delete mode 100644 backend/src/locales/en.json diff --git a/backend/src/locales/de.json b/backend/src/locales/de.json deleted file mode 100644 index 8b34f61f4..000000000 --- a/backend/src/locales/de.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "emails": { - "accountActivation": { - "activateAccount": "Konto aktivieren", - "emailRegistered": "deine E-Mail-Adresse wurde soeben bei Gradido registriert.", - "pleaseClickLink": "Klicke bitte hier, um die Registrierung abzuschließen und dein Gradido-Konto zu aktivieren.", - "subject": "E-Mail Überprüfung", - "title": "E-Mail Überprüfung" - }, - "accountMultiRegistration": { - "contactSupport": "Support kontaktieren", - "emailExists": "Es existiert jedoch zu deiner E-Mail-Adresse schon ein Konto.", - "emailReused": "deine E-Mail-Adresse wurde soeben erneut benutzt, um bei Gradido ein Konto zu registrieren.", - "ifYouAreNotTheOne": "Wenn du nicht versucht hast dich erneut zu registrieren, wende dich bitte an unseren Support:", - "onForgottenPasswordClickLink": "Solltest du dein Passwort vergessen haben, klicke bitte hier.", - "subject": "Erneuter Registrierungsversuch mit deiner E-Mail", - "title": "Erneuter Registrierungsversuch mit deiner E-Mail" - }, - "addedContributionMessage": { - "commonGoodContributionMessage": "du hast zu deinem Gemeinwohl-Beitrag „{contributionMemo}“ eine Nachricht von {senderFirstName} {senderLastName} erhalten.", - "readMessage": "Nachricht lesen und beantworten", - "subject": "Nachricht zu deinem Gemeinwohl-Beitrag", - "title": "Nachricht zu deinem Gemeinwohl-Beitrag", - "message": "„{message}“", - "toSeeAndAnswerMessage": "Um auf die Nachricht zu antworten, gehe in deinem Gradido-Konto ins Menü „Schöpfen“ auf den Tab „Meine Beiträge“." - }, - "contribution": { - "toSeeContributionsAndMessages": "Um deine Gemeinwohl-Beiträge und dazugehörige Nachrichten zu sehen, gehe in deinem Gradido-Konto ins Menü „Schöpfen“ auf den Tab „Meine Beiträge“." - }, - "contributionChangedByModerator": { - "subject": "Dein Gemeinwohl-Beitrag wurde geändert", - "text": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde soeben von {senderFirstName} {senderLastName} geändert und lautet jetzt „{contributionMemoUpdated}“", - "title": "Dein Gemeinwohl-Beitrag wurde geändert" - }, - "contributionConfirmed": { - "commonGoodContributionConfirmed": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde soeben von {senderFirstName} {senderLastName} bestätigt. Es wurden deinem Gradido-Konto {amountGDD} GDD gutgeschrieben.", - "subject": "Dein Gemeinwohl-Beitrag wurde bestätigt", - "title": "Dein Gemeinwohl-Beitrag wurde bestätigt" - }, - "contributionDeleted": { - "commonGoodContributionDeleted": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde von {senderFirstName} {senderLastName} gelöscht.", - "subject": "Dein Gemeinwohl-Beitrag wurde gelöscht", - "title": "Dein Gemeinwohl-Beitrag wurde gelöscht" - }, - "contributionDenied": { - "commonGoodContributionDenied": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde von {senderFirstName} {senderLastName} abgelehnt.", - "subject": "Dein Gemeinwohl-Beitrag wurde abgelehnt", - "title": "Dein Gemeinwohl-Beitrag wurde abgelehnt" - }, - "footer": { - "contactOurSupport": "Bei weiteren Fragen kontaktiere bitte unseren Support.", - "imprint": "Impressum", - "imprintAddress": "Gradido-Akademie\nInstitut für Wirtschaftsbionik\nPfarrweg 2\n74653 Künzelsau\nDeutschland", - "imprintImageAlt": "Gradido-Akademie Logo", - "privacyPolicy": "Datenschutzerklärung", - "supportEmail": "support@gradido.net" - }, - "general": { - "amountGDD": "Betrag: {amountGDD} GDD", - "answerNow": "Jetzt antworten", - "completeRegistration": "Registrierung abschließen", - "contribution": "Gemeinwohl-Beitrag: {contributionMemo}", - "contributionDetails": "Beitragsdetails", - "detailsYouFindOnLinkToYourAccount": "Details zur Transaktion findest du in deinem Gradido-Konto.", - "helloName": "Hallo {firstName} {lastName},", - "linkValidity": "Der Link hat eine Gültigkeit von {hours} Stunden.\nSollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen.", - "linkValidityWithMinutes": "Der Link hat eine Gültigkeit von {hours} Stunden und {minutes} Minuten.\nSollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen.", - "message": "Nachricht", - "newLink": "Neuer Link", - "orCopyLink": "Oder kopiere den Link in dein Browserfenster.", - "pleaseDoNotReply": "Bitte antworte nicht auf diese E-Mail.", - "requestNewLink": "Neuen gültigen Link anfordern", - "reset": "zurücksetzen", - "sincerelyYours": "Liebe Grüße", - "toAccount": "Zum Konto", - "transactionDetails": "Transaktionsdetails", - "yourGradidoTeam": "dein Gradido-Team" - }, - "resetPassword": { - "pleaseClickLink": "Wenn du es warst, klicke bitte hier.", - "subject": "Passwort zurücksetzen", - "title": "Passwort zurücksetzen", - "youOrSomeoneResetPassword": "du oder jemand anderes, hast für dieses Konto ein Zurücksetzen des Passworts angefordert." - }, - "transactionLinkRedeemed": { - "hasRedeemedYourLink": "{senderFirstName} {senderLastName} ({senderEmail}) hat soeben deinen Link eingelöst.", - "memo": "Nachricht: {transactionMemo}", - "subject": "{senderFirstName} {senderLastName} hat deinen Gradido-Link eingelöst", - "title": "{senderFirstName} {senderLastName} hat deinen Gradido-Link eingelöst" - }, - "transactionReceived": { - "haveReceivedAmountGDDFrom": "du hast soeben {transactionAmount} GDD erhalten von {senderFirstName} {senderLastName}", - "subject": "{senderFirstName} {senderLastName} hat dir {transactionAmount} Gradido gesendet", - "replySubject": "Re: {senderFirstName} {senderLastName} hat dir {transactionAmount} Gradido gesendet", - "title": "{senderFirstName} {senderLastName} hat dir {transactionAmount} Gradido gesendet" - } - }, - "general": { - "decimalSeparator": "," - } -} diff --git a/backend/src/locales/en.json b/backend/src/locales/en.json deleted file mode 100644 index fb90a0334..000000000 --- a/backend/src/locales/en.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "emails": { - "accountActivation": { - "activateAccount": "Activate account", - "emailRegistered": "Your email address has just been registered with Gradido.", - "pleaseClickLink": "Please click here to complete the registration and activate your Gradido account.", - "subject": "Email Verification", - "title": "Email Verification" - }, - "accountMultiRegistration": { - "contactSupport": "Contact support", - "emailExists": "However, an account already exists for your email address.", - "emailReused": "Your email address has just been used again to register an account with Gradido.", - "ifYouAreNotTheOne": "If you did not try to register again, please contact our support:", - "onForgottenPasswordClickLink": "If you have forgotten your password, please click here.", - "subject": "Try To Register Again With Your Email", - "title": "Try To Register Again With Your Email" - }, - "addedContributionMessage": { - "commonGoodContributionMessage": "You have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.", - "readMessage": "Read and reply to message", - "subject": "Message about your common good contribution", - "title": "Message about your common good contribution", - "message": "„{message}“", - "toSeeAndAnswerMessage": "To reply to the message, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab." - }, - "contribution": { - "toSeeContributionsAndMessages": "To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab." - }, - "contributionChangedByModerator": { - "subject": "Your common good contribution has been changed", - "text": "your common good contribution '{contributionMemo}' has just been changed by {senderFirstName} {senderLastName} and now reads as '{contributionMemoUpdated}'", - "title": "Your common good contribution has been changed" - }, - "contributionConfirmed": { - "commonGoodContributionConfirmed": "Your common good contribution “{contributionMemo}” has just been approved by {senderFirstName} {senderLastName}. Your Gradido account has been credited with {amountGDD} GDD.", - "subject": "Your contribution to the common good was confirmed", - "title": "Your contribution to the common good was confirmed" - }, - "contributionDeleted": { - "commonGoodContributionDeleted": "Your common good contribution “{contributionMemo}” was deleted by {senderFirstName} {senderLastName}.", - "subject": "Your common good contribution was deleted", - "title": "Your common good contribution was deleted" - }, - "contributionDenied": { - "commonGoodContributionDenied": "Your common good contribution “{contributionMemo}” was rejected by {senderFirstName} {senderLastName}.", - "subject": "Your common good contribution was rejected", - "title": "Your common good contribution was rejected" - }, - "footer": { - "contactOurSupport": "If you have any further questions, please contact our support.", - "imprint": "Impressum", - "imprintAddress": "Gradido-Akademie\nInstitut für Wirtschaftsbionik\nPfarrweg 2\n74653 Künzelsau\nDeutschland", - "imprintImageAlt": "Gradido-Akademie Logo", - "privacyPolicy": "Privacy Policy", - "supportEmail": "support@gradido.net" - }, - "general": { - "amountGDD": "Amount: {amountGDD} GDD", - "answerNow": "Reply", - "completeRegistration": "Complete registration", - "contribution": "Contribution: : {contributionMemo}", - "contributionDetails": "Contribution details", - "detailsYouFindOnLinkToYourAccount": "You can find transaction details in your Gradido account.", - "helloName": "Hello {firstName} {lastName},", - "linkValidity": "The link has a validity of {hours} hours.\nIf the validity of the link has already expired, you can have a new link sent to you here.", - "linkValidityWithMinutes": "The link has a validity of {hours} hours and {minutes} minutes.\nIf the validity of the link has already expired, you can have a new link sent to you here.", - "message": "Message", - "newLink": "New link", - "orCopyLink": "Or copy the link into your browser window.", - "pleaseDoNotReply": "Please do not reply to this email.", - "requestNewLink": "Request new valid link", - "reset": "reset", - "sincerelyYours": "Kind regards,", - "toAccount": "To account", - "transactionDetails": "Transaction details", - "yourGradidoTeam": "your Gradido team" - }, - "resetPassword": { - "pleaseClickLink": "If it was you, please click here.", - "subject": "Reset password", - "title": "Reset password", - "youOrSomeoneResetPassword": "You, or someone else, requested a password reset for this account." - }, - "transactionLinkRedeemed": { - "hasRedeemedYourLink": "{senderFirstName} {senderLastName} ({senderEmail}) has just redeemed your link.", - "memo": "Message: {transactionMemo}", - "subject": "{senderFirstName} {senderLastName} has redeemed your Gradido link", - "title": "{senderFirstName} {senderLastName} has redeemed your Gradido link" - }, - "transactionReceived": { - "haveReceivedAmountGDDFrom": "You have just received {transactionAmount} GDD from {senderFirstName} {senderLastName}", - "replySubject": "RE: {senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido", - "subject": "{senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido", - "title": "{senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido" - } - }, - "general": { - "decimalSeparator": "." - } -} diff --git a/backend/src/server/createServer.ts b/backend/src/server/createServer.ts index 5f3bb02ef..eaa2ea5b4 100644 --- a/backend/src/server/createServer.ts +++ b/backend/src/server/createServer.ts @@ -1,4 +1,5 @@ import { CONFIG } from '@/config' +import { CONFIG as CORE_CONFIG } from 'core' import { schema } from '@/graphql/schema' import { elopageWebhook } from '@/webhook/elopage' import { gmsWebhook } from '@/webhook/gms' @@ -28,7 +29,6 @@ interface ServerDef { export const createServer = async ( apolloLogger: Logger, context: any = serverContext, - localization: i18n.I18n = i18n, ): Promise => { const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.createServer`) logger.debug('createServer...') @@ -73,9 +73,9 @@ export const createServer = async ( app.use(json()) // bodyparser urlencoded for elopage app.use(urlencoded({ extended: true })) - + // i18n - app.use(localization.init) + app.use(i18n.init) // Elopage Webhook @@ -100,7 +100,7 @@ export const createServer = async ( }) apollo.applyMiddleware({ app, path: '/' }) logger.info( - `running with PRODUCTION=${CONFIG.PRODUCTION}, sending EMAIL enabled=${CONFIG.EMAIL} and EMAIL_TEST_MODUS=${CONFIG.EMAIL_TEST_MODUS} ...`, + `running with PRODUCTION=${CONFIG.PRODUCTION}, sending EMAIL enabled=${CORE_CONFIG.EMAIL} and EMAIL_TEST_MODUS=${CORE_CONFIG.EMAIL_TEST_MODUS} ...`, ) logger.debug('createServer...successful') diff --git a/backend/src/server/localization.ts b/backend/src/server/localization.ts index 1e587104a..16f9d54b6 100644 --- a/backend/src/server/localization.ts +++ b/backend/src/server/localization.ts @@ -1,4 +1,3 @@ -import path from 'node:path' import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import i18n from 'i18n' import { getLogger } from 'log4js' @@ -9,7 +8,7 @@ i18n.configure({ locales: ['en', 'de'], defaultLocale: 'en', retryInDefaultLocale: false, - directory: path.join(__dirname, '..', 'locales'), + staticCatalog: {'en': {}, 'de': {}}, // autoReload: true, // if this is activated the seeding hangs at the very end updateFiles: false, objectNotation: true, From 11147e81a91f5695e2f719a514d6d1a1e20bde3f Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 24 Nov 2025 14:47:34 +0100 Subject: [PATCH 10/22] move mails config to core --- backend/src/config/index.ts | 12 ------ backend/src/config/schema.ts | 83 +----------------------------------- 2 files changed, 1 insertion(+), 94 deletions(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index b583156b5..8af6e739a 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -64,22 +64,10 @@ const loginServer = { } const email = { - EMAIL: process.env.EMAIL === 'true', - EMAIL_TEST_MODUS: process.env.EMAIL_TEST_MODUS === 'true', - EMAIL_TEST_RECEIVER: process.env.EMAIL_TEST_RECEIVER ?? 'stage1@gradido.net', - EMAIL_USERNAME: process.env.EMAIL_USERNAME ?? '', - EMAIL_SENDER: process.env.EMAIL_SENDER ?? 'info@gradido.net', - EMAIL_PASSWORD: process.env.EMAIL_PASSWORD ?? '', - EMAIL_SMTP_HOST: process.env.EMAIL_SMTP_HOST ?? 'mailserver', - EMAIL_SMTP_PORT: Number(process.env.EMAIL_SMTP_PORT) || 1025, - - EMAIL_TLS: process.env.EMAIL_TLS !== 'false', EMAIL_LINK_VERIFICATION: COMMUNITY_URL + (process.env.EMAIL_LINK_VERIFICATION_PATH ?? '/checkEmail/'), EMAIL_LINK_SETPASSWORD: COMMUNITY_URL + (process.env.EMAIL_LINK_SETPASSWORD_PATH ?? '/reset-password/'), - EMAIL_LINK_FORGOTPASSWORD: - COMMUNITY_URL + (process.env.EMAIL_LINK_FORGOTPASSWORD_PATH ?? '/forgot-password'), EMAIL_LINK_OVERVIEW: COMMUNITY_URL + (process.env.EMAIL_LINK_OVERVIEW_PATH ?? '/overview'), // time in minutes a optin code is valid EMAIL_CODE_VALID_TIME: process.env.EMAIL_CODE_VALID_TIME diff --git a/backend/src/config/schema.ts b/backend/src/config/schema.ts index f4e6033ea..64871715c 100644 --- a/backend/src/config/schema.ts +++ b/backend/src/config/schema.ts @@ -83,76 +83,6 @@ export const schema = Joi.object({ .default('~/.gradido') .description('The home folder for the gradido dlt node server'), - EMAIL: Joi.boolean() - .default(false) - .description('Enable or disable email functionality') - .required(), - - EMAIL_TEST_MODUS: Joi.boolean() - .default(false) - .description('When enabled, all emails are sended to EMAIL_TEST_RECEIVER') - .optional(), - - EMAIL_TEST_RECEIVER: Joi.string() - .email() - .default('stage1@gradido.net') - .when('EMAIL_TEST_MODUS', { is: true, then: Joi.required() }) - .description('Email address used in test mode'), - - EMAIL_USERNAME: Joi.alternatives().conditional(Joi.ref('EMAIL'), { - is: true, - then: Joi.alternatives().conditional(Joi.ref('NODE_ENV'), { - is: 'development', - then: Joi.string() - .allow('') - .description('Username for SMTP authentication (optional in development)'), - otherwise: Joi.string() - .pattern(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/) - .description('Valid SMTP username required in production') - .required(), - }), - otherwise: Joi.string().allow('').optional(), - }), - - EMAIL_SENDER: Joi.string() - .email() - .when('EMAIL', { is: true, then: Joi.required() }) - .default('info@gradido.net') - .description('Email address used as sender'), - - EMAIL_PASSWORD: Joi.alternatives().conditional(Joi.ref('EMAIL'), { - is: true, - then: Joi.alternatives().conditional(Joi.ref('NODE_ENV'), { - is: 'development', - then: Joi.string() - .allow('') - .description('Password for SMTP authentication (optional in development)'), - otherwise: Joi.string() - .min(8) - .pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&#]).{8,}$/) - .description( - 'Password must be at least 8 characters long, include uppercase and lowercase letters, a number, and a special character', - ) - .required(), - }), - otherwise: Joi.string().allow('').optional(), - }), - - EMAIL_SMTP_HOST: Joi.string() - .hostname() - .when('EMAIL', { is: true, then: Joi.required() }) - .default('mailserver') - .description('SMTP server hostname'), - - EMAIL_SMTP_PORT: Joi.number() - .integer() - .positive() - .when('EMAIL', { is: true, then: Joi.required() }) - .default(1025) - .description('SMTP server port'), - - EMAIL_TLS: Joi.boolean().default(true).description('Enable or disable TLS for SMTP').optional(), - EMAIL_LINK_VERIFICATION: Joi.string() .uri({ scheme: ['http', 'https'] }) .custom((value: string, helpers: Joi.CustomHelpers): string | Joi.ErrorReport => { @@ -175,17 +105,6 @@ export const schema = Joi.object({ .description('Email Verification link for set initial Password.') .required(), - EMAIL_LINK_FORGOTPASSWORD: Joi.string() - .uri({ scheme: ['http', 'https'] }) - .custom((value: string, helpers: Joi.CustomHelpers): string | Joi.ErrorReport => { - if (!value.startsWith(helpers.state.ancestors[0].COMMUNITY_URL)) { - return helpers.error('string.pattern.base', { value, communityUrl: COMMUNITY_URL }) - } - return value - }) - .description('Email Verification link for set new Password, when old Password was forgotten.') - .required(), - EMAIL_LINK_OVERVIEW: Joi.string() .uri({ scheme: ['http', 'https'] }) .custom((value: string, helpers: Joi.CustomHelpers): string | Joi.ErrorReport => { @@ -213,7 +132,7 @@ export const schema = Joi.object({ .description('Time in minutes before a new code can be requested') .required(), - FEDERATION_VALIDATE_COMMUNITY_TIMER: Joi.number() + FEDERATION_VALIDATE_COMMUNITY_TIMER: Joi.number() .integer() .min(1000) .default(60000) From 4e08a182e8902f1a55d7b1eda24ccb37dd941f0d Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 24 Nov 2025 16:41:29 +0100 Subject: [PATCH 11/22] update code for moved emails, fix bugs --- backend/.env.org | 77 ------- backend/package.json | 7 +- backend/scripts/sort_filter.jq | 13 -- backend/src/apis/gms/ExportUsers.ts | 4 +- .../resolver/CommunityResolver.test.ts | 3 +- .../ContributionMessageResolver.test.ts | 9 +- .../resolver/ContributionMessageResolver.ts | 2 +- .../resolver/ContributionResolver.test.ts | 22 +- .../graphql/resolver/ContributionResolver.ts | 7 +- .../graphql/resolver/EmailOptinCodes.test.ts | 3 +- .../resolver/KlicktippResolver.test.ts | 3 +- .../resolver/TransactionResolver.test.ts | 3 +- .../graphql/resolver/TransactionResolver.ts | 18 +- .../src/graphql/resolver/UserResolver.test.ts | 9 +- backend/src/graphql/resolver/UserResolver.ts | 17 +- .../src/graphql/resolver/semaphore.test.ts | 3 +- backend/src/seeds/index.ts | 3 +- backend/src/server/localization.ts | 5 +- backend/test/helpers.ts | 6 +- backend/test/testSetup.ts | 22 +- backend/tsconfig.json | 5 +- backend/turbo.json | 2 - bun.lock | 133 +++++------ core/esbuild.config.ts | 6 +- core/package.json | 2 + {backend => core}/scripts/sort.sh | 4 +- core/src/emails/locales/en.json | 206 +++++++++--------- core/src/emails/sendEmailTranslated.ts | 11 +- core/turbo.json | 13 ++ 29 files changed, 269 insertions(+), 349 deletions(-) delete mode 100644 backend/.env.org delete mode 100644 backend/scripts/sort_filter.jq rename {backend => core}/scripts/sort.sh (83%) create mode 100644 core/turbo.json diff --git a/backend/.env.org b/backend/.env.org deleted file mode 100644 index 2267fdc50..000000000 --- a/backend/.env.org +++ /dev/null @@ -1,77 +0,0 @@ -# Server -PORT=4000 -JWT_SECRET=secret123 -JWT_EXPIRES_IN=10m -GRAPHIQL=false -GDT_API_URL=https://gdt.gradido.net - -# Database -DB_HOST=127.0.0.1 -DB_PORT=3306 -DB_USER=root -DB_PASSWORD= -DB_DATABASE=gradido_community -TYPEORM_LOGGING_RELATIVE_PATH=typeorm.backend.log - -# Klicktipp -KLICKTIPP=false -KLICKTTIPP_API_URL=https://api.klicktipp.com -KLICKTIPP_USER=gradido_test -KLICKTIPP_PASSWORD=secret321 -KLICKTIPP_APIKEY_DE=SomeFakeKeyDE -KLICKTIPP_APIKEY_EN=SomeFakeKeyEN - -# DltConnector -DLT_CONNECTOR=true -DLT_CONNECTOR_URL=http://localhost:6010 - -# Community -COMMUNITY_NAME=Gradido Entwicklung -COMMUNITY_URL=http://localhost -COMMUNITY_REGISTER_PATH=/register -COMMUNITY_REDEEM_PATH=/redeem/{code} -COMMUNITY_REDEEM_CONTRIBUTION_PATH=/redeem/CL-{code} -COMMUNITY_DESCRIPTION=Die lokale Entwicklungsumgebung von Gradido. -COMMUNITY_SUPPORT_MAIL=support@supportmail.com - -# Login Server -LOGIN_APP_SECRET=21ffbbc616fe -LOGIN_SERVER_KEY=a51ef8ac7ef1abf162fb7a65261acd7a - -# EMail -EMAIL=false -EMAIL_TEST_MODUS=false -EMAIL_TEST_RECEIVER=stage1@gradido.net -EMAIL_USERNAME=gradido_email -EMAIL_SENDER=info@gradido.net -EMAIL_PASSWORD=xxx -EMAIL_SMTP_HOST=gmail.com -EMAIL_SMTP_PORT=587 -EMAIL_LINK_VERIFICATION_PATH=/checkEmail/{optin}{code} -EMAIL_LINK_SETPASSWORD_PATH=/reset-password/{optin} -EMAIL_LINK_FORGOTPASSWORD_PATH=/forgot-password -EMAIL_LINK_OVERVIEW_PATH=/overview -EMAIL_CODE_VALID_TIME=1440 -EMAIL_CODE_REQUEST_TIME=10 - -# Webhook -WEBHOOK_ELOPAGE_SECRET=secret - -# SET LOG LEVEL AS NEEDED IN YOUR .ENV -# POSSIBLE VALUES: all | trace | debug | info | warn | error | fatal -LOG_LEVEL=INFO - -# Federation -FEDERATION_VALIDATE_COMMUNITY_TIMER=60000 - -# GMS -# GMS_ACTIVE=true -# Coordinates of Illuminz test instance -#GMS_API_URL=http://54.176.169.179:3071 -GMS_API_URL=http://localhost:4044 -GMS_DASHBOARD_URL=http://localhost:8080 - -# HUMHUB -HUMHUB_ACTIVE=true -HUMHUB_API_URL=https://community-test.gradido.net -HUMHUB_JWT_KEY=GwdkIKi-rkRS0mXC4Cg3MYc3ktZh89VFmntDpNKET_dUfcIdjL_957F3nCv3brNtDfbbV81NViKaktUsfExrkH diff --git a/backend/package.json b/backend/package.json index d758c5518..0a1bab487 100644 --- a/backend/package.json +++ b/backend/package.json @@ -8,7 +8,7 @@ "author": "Gradido Academy - https://www.gradido.net", "main": "src/index.ts", "scripts": { - "build": "ts-node ./esbuild.config.ts && mkdirp build/templates/ && ncp src/emails/templates build/templates && mkdirp locales/ && ncp src/locales locales", + "build": "ts-node ./esbuild.config.ts && mkdirp build/templates/ && ncp ../core/src/emails/templates build/templates", "dev": "cross-env TZ=UTC nodemon -w src --ext ts,pug,json,css -r tsconfig-paths/register src/index.ts", "test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_backend jest --runInBand --forceExit --detectOpenHandles", "test:coverage": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_backend jest --coverage --runInBand --forceExit --detectOpenHandles", @@ -19,8 +19,6 @@ "lint": "biome check --error-on-warnings .", "lint:fix": "biome check --error-on-warnings . --write", "lint:fix:unsafe": "biome check --fix --unsafe", - "locales": "scripts/sort.sh", - "locales:fix": "scripts/sort.sh --fix", "start": "cross-env TZ=UTC node build/index.js", "typecheck": "tsc --noEmit", "clear": "rm -rf node_modules && rm -rf build && rm -rf .turbo" @@ -49,7 +47,6 @@ "@types/jest": "27.0.2", "@types/lodash.clonedeep": "^4.5.6", "@types/node": "^17.0.21", - "@types/nodemailer": "^6.4.4", "@types/sodium-native": "^2.3.5", "@types/source-map-support": "^0.5.10", "@types/uuid": "^8.3.4", @@ -83,11 +80,9 @@ "log4js": "^6.7.1", "mkdirp": "^3.0.1", "ncp": "^2.0.0", - "nodemailer": "^6.6.5", "nodemon": "^2.0.7", "openai": "^4.87.3", "prettier": "^3.5.3", - "pug": "^3.0.2", "random-bigint": "^0.0.1", "reflect-metadata": "^0.1.13", "regenerator-runtime": "^0.14.1", diff --git a/backend/scripts/sort_filter.jq b/backend/scripts/sort_filter.jq deleted file mode 100644 index 9d108f8f0..000000000 --- a/backend/scripts/sort_filter.jq +++ /dev/null @@ -1,13 +0,0 @@ -def walk(f): - . as $in - | if type == "object" then - reduce keys_unsorted[] as $key - ( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f - elif type == "array" then map( walk(f) ) | f - else f - end; - -def keys_sort_by(f): - to_entries | sort_by(.key|f ) | from_entries; - -walk(if type == "object" then keys_sort_by(ascii_upcase) else . end) \ No newline at end of file diff --git a/backend/src/apis/gms/ExportUsers.ts b/backend/src/apis/gms/ExportUsers.ts index 03cc92fa4..5c5cb72d8 100644 --- a/backend/src/apis/gms/ExportUsers.ts +++ b/backend/src/apis/gms/ExportUsers.ts @@ -4,7 +4,7 @@ import { User as DbUser } from 'database' import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' // import { createGmsUser } from '@/apis/gms/GmsClient' // import { GmsUser } from '@/apis/gms/model/GmsUser' -import { CONFIG } from '@/config' +import { CONFIG as CORE_CONFIG } from 'core' import { sendUserToGms } from '@/graphql/resolver/util/sendUserToGms' import { LogError } from '@/server/LogError' import { initLogging } from '@/server/logger' @@ -13,7 +13,7 @@ import { getLogger } from 'log4js' const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.gms.ExportUsers`) -CONFIG.EMAIL = false +CORE_CONFIG.EMAIL = false // use force to copy over all user even if gmsRegistered is set to true const forceMode = process.argv.includes('--force') diff --git a/backend/src/graphql/resolver/CommunityResolver.test.ts b/backend/src/graphql/resolver/CommunityResolver.test.ts index 57173c9d1..0f48d356b 100644 --- a/backend/src/graphql/resolver/CommunityResolver.test.ts +++ b/backend/src/graphql/resolver/CommunityResolver.test.ts @@ -5,7 +5,6 @@ import { DataSource } from 'typeorm' import { v4 as uuidv4 } from 'uuid' import { cleanDB, testEnvironment } from '@test/helpers' -import { i18n as localization } from '@test/testSetup' import { userFactory } from '@/seeds/factory/user' import { login, updateHomeCommunityQuery } from '@/seeds/graphql/mutations' @@ -43,7 +42,7 @@ const peterLoginData = { } beforeAll(async () => { - testEnv = await testEnvironment(getLogger('apollo'), localization) + testEnv = await testEnvironment(getLogger('apollo')) mutate = testEnv.mutate query = testEnv.query con = testEnv.con diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index 962b77766..d29bf5235 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -5,10 +5,9 @@ import { DataSource } from 'typeorm' import { ContributionStatus } from '@enum/ContributionStatus' import { cleanDB, resetToken, testEnvironment } from '@test/helpers' -import { i18n as localization } from '@test/testSetup' import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' -import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants' +import { sendAddedContributionMessageEmail } from 'core' import { EventType } from '@/event/Events' import { userFactory } from '@/seeds/factory/user' import { @@ -30,8 +29,8 @@ const interactionLogger = getLogger( ) jest.mock('@/password/EncryptorUtils') -jest.mock('@/emails/sendEmailVariants', () => { - const originalModule = jest.requireActual('@/emails/sendEmailVariants') +jest.mock('core', () => { + const originalModule = jest.requireActual('core') return { __esModule: true, ...originalModule, @@ -51,7 +50,7 @@ let testEnv: { let result: any beforeAll(async () => { - testEnv = await testEnvironment(logger, localization) + testEnv = await testEnvironment(logger) mutate = testEnv.mutate con = testEnv.con await cleanDB() diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index 8ddf782b0..5926a1bc4 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -14,7 +14,7 @@ import { Order } from '@enum/Order' import { ContributionMessage, ContributionMessageListResult } from '@model/ContributionMessage' import { RIGHTS } from '@/auth/RIGHTS' -import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants' +import { sendAddedContributionMessageEmail } from 'core' import { EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE, EVENT_CONTRIBUTION_MESSAGE_CREATE, diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 6dee20592..fd31a5275 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -14,14 +14,13 @@ import { resetToken, testEnvironment, } from '@test/helpers' -import { i18n as localization } from '@test/testSetup' import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { sendContributionConfirmedEmail, sendContributionDeletedEmail, sendContributionDeniedEmail, -} from '@/emails/sendEmailVariants' +} from 'core' import { EventType } from '@/event/Events' import { creations } from '@/seeds/creation/index' import { creationFactory } from '@/seeds/factory/creation' @@ -54,7 +53,22 @@ import { getFirstDayOfPreviousNMonth } from 'core' import { getLogger } from 'config-schema/test/testSetup' import { getLogger as originalGetLogger } from 'log4js' -jest.mock('@/emails/sendEmailVariants') +jest.mock('core', () => { + const originalModule = jest.requireActual('core') + return { + __esModule: true, + ...originalModule, + sendContributionDeniedEmail: jest.fn((a) => + originalModule.sendContributionDeniedEmail(a), + ), + sendContributionConfirmedEmail: jest.fn((a) => + originalModule.sendContributionConfirmedEmail(a), + ), + sendContributionDeletedEmail: jest.fn((a) => + originalModule.sendContributionDeletedEmail(a), + ), + } +}) jest.mock('@/password/EncryptorUtils') const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`) @@ -77,7 +91,7 @@ let contributionToDelete: any let bibiCreatedContribution: Contribution beforeAll(async () => { - testEnv = await testEnvironment(originalGetLogger('apollo'), localization) + testEnv = await testEnvironment(originalGetLogger('apollo')) mutate = testEnv.mutate query = testEnv.query con = testEnv.con diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index ac06f012e..180c22cfb 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -21,15 +21,15 @@ import { AdminUpdateContribution } from '@model/AdminUpdateContribution' import { Contribution, ContributionListResult } from '@model/Contribution' import { OpenCreation } from '@model/OpenCreation' import { UnconfirmedContribution } from '@model/UnconfirmedContribution' -import { TransactionTypeId } from 'core' - import { RIGHTS } from '@/auth/RIGHTS' import { + fullName, sendContributionChangedByModeratorEmail, sendContributionConfirmedEmail, sendContributionDeletedEmail, sendContributionDeniedEmail, -} from '@/emails/sendEmailVariants' + TransactionTypeId +} from 'core' import { EVENT_ADMIN_CONTRIBUTION_CONFIRM, EVENT_ADMIN_CONTRIBUTION_CREATE, @@ -44,7 +44,6 @@ import { UpdateUnconfirmedContributionContext } from '@/interactions/updateUncon import { LogError } from '@/server/LogError' import { Context, getClientTimezoneOffset, getUser } from '@/server/context' import { TRANSACTIONS_LOCK } from 'database' -import { fullName } from 'core' import { calculateDecay, Decay } from 'shared' import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' diff --git a/backend/src/graphql/resolver/EmailOptinCodes.test.ts b/backend/src/graphql/resolver/EmailOptinCodes.test.ts index b916d23b6..18114dbb2 100644 --- a/backend/src/graphql/resolver/EmailOptinCodes.test.ts +++ b/backend/src/graphql/resolver/EmailOptinCodes.test.ts @@ -5,6 +5,7 @@ import { DataSource } from 'typeorm' import { cleanDB, testEnvironment } from '@test/helpers' +import { CONFIG as CORE_CONFIG } from 'core' import { CONFIG } from '@/config' import { writeHomeCommunityEntry } from '@/seeds/community' import { createUser, forgotPassword, setPassword } from '@/seeds/graphql/mutations' @@ -21,7 +22,7 @@ let testEnv: { CONFIG.EMAIL_CODE_VALID_TIME = 1440 CONFIG.EMAIL_CODE_REQUEST_TIME = 10 -CONFIG.EMAIL = false +CORE_CONFIG.EMAIL = false beforeAll(async () => { testEnv = await testEnvironment() diff --git a/backend/src/graphql/resolver/KlicktippResolver.test.ts b/backend/src/graphql/resolver/KlicktippResolver.test.ts index f3ac85ef4..1c285d440 100644 --- a/backend/src/graphql/resolver/KlicktippResolver.test.ts +++ b/backend/src/graphql/resolver/KlicktippResolver.test.ts @@ -2,7 +2,6 @@ import { Event as DbEvent, UserContact } from 'database' import { GraphQLError } from 'graphql' import { cleanDB, resetToken, testEnvironment } from '@test/helpers' -import { i18n as localization } from '@test/testSetup' import { getLogger } from 'config-schema/test/testSetup' import { EventType } from '@/event/Events' @@ -20,7 +19,7 @@ let mutate: any let con: any beforeAll(async () => { - testEnv = await testEnvironment(logger, localization) + testEnv = await testEnvironment(logger) mutate = testEnv.mutate con = testEnv.con await cleanDB() diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index a134ca84b..774502b28 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -34,12 +34,13 @@ import { peterLustig } from '@/seeds/users/peter-lustig' import { stephenHawking } from '@/seeds/users/stephen-hawking' import { getLogger } from 'config-schema/test/testSetup' import { CONFIG } from '@/config' +import { CONFIG as CORE_CONFIG} from 'core' jest.mock('@/password/EncryptorUtils') const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`) CONFIG.DLT_CONNECTOR = false -CONFIG.EMAIL = false +CORE_CONFIG.EMAIL = false let mutate: ApolloServerTestClient['mutate'] let query: ApolloServerTestClient['query'] diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 359e69b45..923971743 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -1,14 +1,11 @@ import { AppDatabase, countOpenPendingTransactions, - Community as DbCommunity, DltTransaction as DbDltTransaction, Transaction as dbTransaction, TransactionLink as dbTransactionLink, User as dbUser, findUserByIdentifier, - TransactionLoggingView, - UserLoggingView } from 'database' import { Decimal } from 'decimal.js-light' import { Args, Authorized, Ctx, Mutation, Query, Resolver } from 'type-graphql' @@ -20,21 +17,22 @@ import { Order } from '@enum/Order' import { Transaction } from '@model/Transaction' import { TransactionList } from '@model/TransactionList' import { User } from '@model/User' -import { processXComCompleteTransaction, TransactionTypeId } from 'core' - +import { + fullName, + processXComCompleteTransaction, + sendTransactionLinkRedeemedEmail, + sendTransactionReceivedEmail, + TransactionTypeId +} from 'core' import { RIGHTS } from '@/auth/RIGHTS' import { CONFIG } from '@/config' import { - sendTransactionLinkRedeemedEmail, - sendTransactionReceivedEmail, -} from '@/emails/sendEmailVariants' -import { EVENT_TRANSACTION_RECEIVE, EVENT_TRANSACTION_SEND } from '@/event/Events' + EVENT_TRANSACTION_RECEIVE, EVENT_TRANSACTION_SEND } from '@/event/Events' import { LogError } from '@/server/LogError' import { Context, getUser } from '@/server/context' import { communityUser } from '@/util/communityUser' import { calculateBalance } from '@/util/validate' import { virtualDecayTransaction, virtualLinkTransaction } from '@/util/virtualTransactions' -import { fullName } from 'core' import { TRANSACTIONS_LOCK } from 'database' import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 8b4faa443..d5bad3bc8 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -20,7 +20,6 @@ import { UserContactType } from '@enum/UserContactType' import { ContributionLink } from '@model/ContributionLink' import { Location } from '@model/Location' import { cleanDB, headerPushMock, resetToken, testEnvironment } from '@test/helpers' -import { i18n as localization } from '@test/testSetup' import { subscribe } from '@/apis/KlicktippController' import { CONFIG } from '@/config' @@ -28,7 +27,7 @@ import { sendAccountActivationEmail, sendAccountMultiRegistrationEmail, sendResetPasswordEmail, -} from '@/emails/sendEmailVariants' +} from 'core' import { EventType } from '@/event/Events' import { PublishNameType } from '@/graphql/enum/PublishNameType' import { SecretKeyCryptographyCreateKey } from '@/password/EncryptorUtils' @@ -74,8 +73,8 @@ import { Location2Point } from './util/Location2Point' jest.mock('@/apis/humhub/HumHubClient') jest.mock('@/password/EncryptorUtils') -jest.mock('@/emails/sendEmailVariants', () => { - const originalModule = jest.requireActual('@/emails/sendEmailVariants') +jest.mock('core', () => { + const originalModule = jest.requireActual('core') return { __esModule: true, ...originalModule, @@ -112,7 +111,7 @@ let testEnv: { } beforeAll(async () => { - testEnv = await testEnvironment(getLogger('apollo'), localization) + testEnv = await testEnvironment(getLogger('apollo')) mutate = testEnv.mutate query = testEnv.query con = testEnv.con diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 70b1642a8..4a2ca85ca 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -10,7 +10,6 @@ import { findUserByIdentifier } from 'database' import { GraphQLResolveInfo } from 'graphql' -import i18n from 'i18n' import { Arg, Args, @@ -57,11 +56,6 @@ import { encode } from '@/auth/JWT' import { RIGHTS } from '@/auth/RIGHTS' import { CONFIG } from '@/config' import { PublishNameLogic } from '@/data/PublishName.logic' -import { - sendAccountActivationEmail, - sendAccountMultiRegistrationEmail, - sendResetPasswordEmail, -} from '@/emails/sendEmailVariants' import { EVENT_ADMIN_USER_DELETE, EVENT_ADMIN_USER_ROLE_SET, @@ -85,8 +79,12 @@ import { Context, getClientTimezoneOffset, getUser } from '@/server/context' import { communityDbUser } from '@/util/communityUser' import { hasElopageBuys } from '@/util/hasElopageBuys' import { durationInMinutesFromDates, getTimeDurationObject, printTimeDuration } from '@/util/time' -import { delay } from 'core' - +import { + delay, + sendAccountActivationEmail, + sendAccountMultiRegistrationEmail, + sendResetPasswordEmail, +} from 'core' import random from 'random-bigint' import { randombytes_random } from 'sodium-native' @@ -233,7 +231,6 @@ export class UserResolver { logger.debug('validation of login credentials successful...') const user = new User(dbUser) - i18n.setLocale(user.language) // Elopage Status & Stored PublisherId user.hasElopage = await this.hasElopage({ ...context, user: dbUser }) @@ -322,7 +319,6 @@ export class UserResolver { if (!language || !isLanguage(language)) { language = DEFAULT_LANGUAGE } - i18n.setLocale(language) // check if user with email still exists? email = email.trim().toLowerCase() @@ -764,7 +760,6 @@ export class UserResolver { throw new LogError('Given language is not a valid language or not supported') } user.language = language - i18n.setLocale(language) updated = true } diff --git a/backend/src/graphql/resolver/semaphore.test.ts b/backend/src/graphql/resolver/semaphore.test.ts index d0bf08b7c..d88ed1857 100644 --- a/backend/src/graphql/resolver/semaphore.test.ts +++ b/backend/src/graphql/resolver/semaphore.test.ts @@ -22,12 +22,13 @@ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { bobBaumeister } from '@/seeds/users/bob-baumeister' import { peterLustig } from '@/seeds/users/peter-lustig' import { CONFIG } from '@/config' +import { CONFIG as CORE_CONFIG } from 'core' import { TRANSACTIONS_LOCK } from 'database' jest.mock('@/password/EncryptorUtils') CONFIG.DLT_CONNECTOR = false -CONFIG.EMAIL = false +CORE_CONFIG.EMAIL = false let mutate: ApolloServerTestClient['mutate'] let con: DataSource diff --git a/backend/src/seeds/index.ts b/backend/src/seeds/index.ts index 451f671ac..3a1504262 100644 --- a/backend/src/seeds/index.ts +++ b/backend/src/seeds/index.ts @@ -3,6 +3,7 @@ import { entities } from 'database' import { datatype, internet, name } from 'faker' import { CONFIG } from '@/config' +import { CONFIG as CORE_CONFIG } from 'core' import { createServer } from '@/server/createServer' import { initLogging } from '@/server/logger' @@ -17,7 +18,7 @@ import { userFactory } from './factory/user' import { transactionLinks } from './transactionLink/index' import { users } from './users/index' -CONFIG.EMAIL = false +CORE_CONFIG.EMAIL = false const logger = getLogger('seed') const context = { diff --git a/backend/src/server/localization.ts b/backend/src/server/localization.ts index 16f9d54b6..2332f0eaf 100644 --- a/backend/src/server/localization.ts +++ b/backend/src/server/localization.ts @@ -8,7 +8,10 @@ i18n.configure({ locales: ['en', 'de'], defaultLocale: 'en', retryInDefaultLocale: false, - staticCatalog: {'en': {}, 'de': {}}, + staticCatalog: { + en: { general: { decimalSeparator: "." } }, + de: { general: { decimalSeparator: "," } }, + }, // autoReload: true, // if this is activated the seeding hangs at the very end updateFiles: false, objectNotation: true, diff --git a/backend/test/helpers.ts b/backend/test/helpers.ts index c7f533931..7001e3067 100644 --- a/backend/test/helpers.ts +++ b/backend/test/helpers.ts @@ -3,8 +3,6 @@ import { entities } from 'database' import { createServer } from '@/server/createServer' -import { i18n } from './testSetup' - import { getLogger } from 'log4js' export const headerPushMock = jest.fn((t) => { @@ -29,8 +27,8 @@ export const cleanDB = async () => { } } -export const testEnvironment = async (testLogger = getLogger('apollo'), testI18n = i18n) => { - const server = await createServer( testLogger, context, testI18n) +export const testEnvironment = async (testLogger = getLogger('apollo')) => { + const server = await createServer( testLogger, context) const con = server.con const testClient = createTestClient(server.apollo) const mutate = testClient.mutate diff --git a/backend/test/testSetup.ts b/backend/test/testSetup.ts index c010ff705..6a009e652 100644 --- a/backend/test/testSetup.ts +++ b/backend/test/testSetup.ts @@ -1,27 +1,13 @@ import 'openai/shims/node' import { CONFIG } from '@/config' -import { i18n } from '@/server/localization' +import { CONFIG as CORE_CONFIG } from 'core' import { getLogger, printLogs, clearLogs } from 'config-schema/test/testSetup' -CONFIG.EMAIL = true -CONFIG.EMAIL_TEST_MODUS = false +CORE_CONFIG.EMAIL = true +CORE_CONFIG.EMAIL_TEST_MODUS = false CONFIG.HUMHUB_ACTIVE = false CONFIG.GMS_ACTIVE = false jest.setTimeout(1000000) -jest.mock('@/server/localization', () => { - const originalModule = jest.requireActual('@/server/localization') - return { - __esModule: true, - ...originalModule, - i18n: { - init: jest.fn(), - // configure: jest.fn(), - // __: jest.fn(), - // setLocale: jest.fn(), - }, - } -}) - -export { i18n, getLogger, printLogs, clearLogs as cleanLogs } +export { getLogger, printLogs, clearLogs as cleanLogs } diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 2152f4b79..d330e2b81 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -63,7 +63,7 @@ "typeRoots": [ /* List of folders to include type definitions from. */ "@types", "node_modules/@types", - "../node_modules/@types" + "../node_modules/@types", ], // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ @@ -86,6 +86,9 @@ "skipLibCheck": true, /* Skip type checking of declaration files. */ "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ }, + "include": [ + "../core/src/types", + ], "ts-node": { "swc": true } diff --git a/backend/turbo.json b/backend/turbo.json index 822b2765f..399bfa6ba 100644 --- a/backend/turbo.json +++ b/backend/turbo.json @@ -8,10 +8,8 @@ "locales": {}, "locales:fix": {}, "lint": { - "dependsOn": ["locales"] }, "lint:fix": { - "dependsOn": ["locales:fix"] }, "test": { "dependsOn": ["database#up:backend_test", "^build"] diff --git a/bun.lock b/bun.lock index 0f2ac4c20..9b449354c 100644 --- a/bun.lock +++ b/bun.lock @@ -109,7 +109,6 @@ "@types/jest": "27.0.2", "@types/lodash.clonedeep": "^4.5.6", "@types/node": "^17.0.21", - "@types/nodemailer": "^6.4.4", "@types/sodium-native": "^2.3.5", "@types/source-map-support": "^0.5.10", "@types/uuid": "^8.3.4", @@ -143,11 +142,9 @@ "log4js": "^6.7.1", "mkdirp": "^3.0.1", "ncp": "^2.0.0", - "nodemailer": "^6.6.5", "nodemon": "^2.0.7", "openai": "^4.87.3", "prettier": "^3.5.3", - "pug": "^3.0.2", "random-bigint": "^0.0.1", "reflect-metadata": "^0.1.13", "regenerator-runtime": "^0.14.1", @@ -188,20 +185,25 @@ "version": "2.7.1", "dependencies": { "database": "*", + "email-templates": "^10.0.1", "esbuild": "^0.25.2", "i18n": "^0.15.1", "joi": "^17.13.3", "jose": "^4.14.4", "log4js": "^6.9.1", + "nodemailer": "^6.6.5", + "pug": "^3.0.2", "shared": "*", "sodium-native": "^3.4.1", "zod": "^3.25.61", }, "devDependencies": { "@biomejs/biome": "2.0.0", + "@types/email-templates": "^10.0.4", "@types/i18n": "^0.13.4", "@types/minimatch": "6.0.0", "@types/node": "^17.0.21", + "@types/nodemailer": "^6.4.4", "@types/sodium-native": "^2.3.5", "config-schema": "*", "decimal.js-light": "^2.5.1", @@ -288,6 +290,7 @@ "version": "2.7.1", "dependencies": { "cross-env": "^7.0.3", + "email-templates": "^10.0.1", "sodium-native": "^3.4.1", }, "devDependencies": { @@ -325,6 +328,8 @@ "joi": "17.13.3", "lodash.clonedeep": "^4.5.0", "log4js": "^6.7.1", + "mkdirp": "^3.0.1", + "ncp": "^2.0.0", "nodemon": "^2.0.7", "prettier": "^3.5.3", "reflect-metadata": "^0.1.13", @@ -485,53 +490,55 @@ "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="], - "@aws-sdk/client-ses": ["@aws-sdk/client-ses@3.913.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.911.0", "@aws-sdk/credential-provider-node": "3.913.0", "@aws-sdk/middleware-host-header": "3.910.0", "@aws-sdk/middleware-logger": "3.910.0", "@aws-sdk/middleware-recursion-detection": "3.910.0", "@aws-sdk/middleware-user-agent": "3.911.0", "@aws-sdk/region-config-resolver": "3.910.0", "@aws-sdk/types": "3.910.0", "@aws-sdk/util-endpoints": "3.910.0", "@aws-sdk/util-user-agent-browser": "3.910.0", "@aws-sdk/util-user-agent-node": "3.911.0", "@smithy/config-resolver": "^4.3.2", "@smithy/core": "^3.16.1", "@smithy/fetch-http-handler": "^5.3.3", "@smithy/hash-node": "^4.2.2", "@smithy/invalid-dependency": "^4.2.2", "@smithy/middleware-content-length": "^4.2.2", "@smithy/middleware-endpoint": "^4.3.3", "@smithy/middleware-retry": "^4.4.3", "@smithy/middleware-serde": "^4.2.2", "@smithy/middleware-stack": "^4.2.2", "@smithy/node-config-provider": "^4.3.2", "@smithy/node-http-handler": "^4.4.1", "@smithy/protocol-http": "^5.3.2", "@smithy/smithy-client": "^4.8.1", "@smithy/types": "^4.7.1", "@smithy/url-parser": "^4.2.2", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.2", "@smithy/util-defaults-mode-node": "^4.2.3", "@smithy/util-endpoints": "^3.2.2", "@smithy/util-middleware": "^4.2.2", "@smithy/util-retry": "^4.2.2", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-jUF1mN+webeAgkNXS/tl6KpJyUbsAWxQGsQgsWoHwaNCSnxMDBEyPmgBnzbqf2CrybIa7zmzaqCO0z6FgKeZRg=="], + "@aws-sdk/client-ses": ["@aws-sdk/client-ses@3.936.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.936.0", "@aws-sdk/credential-provider-node": "3.936.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.936.0", "@aws-sdk/middleware-user-agent": "3.936.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.12", "@smithy/middleware-retry": "^4.4.12", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.11", "@smithy/util-defaults-mode-node": "^4.2.14", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-2toHYwRkcYGasPHYGwOwaIAa2Api/uFhmL3px0Tyt4bne2ilqhSwq+6a/0UVMd8JYwWaLMJolTbWKFt2jUlmGg=="], - "@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.911.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.911.0", "@aws-sdk/middleware-host-header": "3.910.0", "@aws-sdk/middleware-logger": "3.910.0", "@aws-sdk/middleware-recursion-detection": "3.910.0", "@aws-sdk/middleware-user-agent": "3.911.0", "@aws-sdk/region-config-resolver": "3.910.0", "@aws-sdk/types": "3.910.0", "@aws-sdk/util-endpoints": "3.910.0", "@aws-sdk/util-user-agent-browser": "3.910.0", "@aws-sdk/util-user-agent-node": "3.911.0", "@smithy/config-resolver": "^4.3.2", "@smithy/core": "^3.16.1", "@smithy/fetch-http-handler": "^5.3.3", "@smithy/hash-node": "^4.2.2", "@smithy/invalid-dependency": "^4.2.2", "@smithy/middleware-content-length": "^4.2.2", "@smithy/middleware-endpoint": "^4.3.3", "@smithy/middleware-retry": "^4.4.3", "@smithy/middleware-serde": "^4.2.2", "@smithy/middleware-stack": "^4.2.2", "@smithy/node-config-provider": "^4.3.2", "@smithy/node-http-handler": "^4.4.1", "@smithy/protocol-http": "^5.3.2", "@smithy/smithy-client": "^4.8.1", "@smithy/types": "^4.7.1", "@smithy/url-parser": "^4.2.2", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.2", "@smithy/util-defaults-mode-node": "^4.2.3", "@smithy/util-endpoints": "^3.2.2", "@smithy/util-middleware": "^4.2.2", "@smithy/util-retry": "^4.2.2", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-N9QAeMvN3D1ZyKXkQp4aUgC4wUMuA5E1HuVCkajc0bq1pnH4PIke36YlrDGGREqPlyLFrXCkws2gbL5p23vtlg=="], + "@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.936.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.936.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.936.0", "@aws-sdk/middleware-user-agent": "3.936.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.12", "@smithy/middleware-retry": "^4.4.12", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.11", "@smithy/util-defaults-mode-node": "^4.2.14", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-0G73S2cDqYwJVvqL08eakj79MZG2QRaB56Ul8/Ps9oQxllr7DMI1IQ/N3j3xjxgpq/U36pkoFZ8aK1n7Sbr3IQ=="], - "@aws-sdk/core": ["@aws-sdk/core@3.911.0", "", { "dependencies": { "@aws-sdk/types": "3.910.0", "@aws-sdk/xml-builder": "3.911.0", "@smithy/core": "^3.16.1", "@smithy/node-config-provider": "^4.3.2", "@smithy/property-provider": "^4.2.2", "@smithy/protocol-http": "^5.3.2", "@smithy/signature-v4": "^5.3.2", "@smithy/smithy-client": "^4.8.1", "@smithy/types": "^4.7.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.2", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-k4QG9A+UCq/qlDJFmjozo6R0eXXfe++/KnCDMmajehIE9kh+b/5DqlGvAmbl9w4e92LOtrY6/DN3mIX1xs4sXw=="], + "@aws-sdk/core": ["@aws-sdk/core@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-eGJ2ySUMvgtOziHhDRDLCrj473RJoL4J1vPjVM3NrKC/fF3/LoHjkut8AAnKmrW6a2uTzNKubigw8dEnpmpERw=="], - "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.911.0", "", { "dependencies": { "@aws-sdk/core": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/property-provider": "^4.2.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-6FWRwWn3LUZzLhqBXB+TPMW2ijCWUqGICSw8bVakEdODrvbiv1RT/MVUayzFwz/ek6e6NKZn6DbSWzx07N9Hjw=="], + "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.936.0", "", { "dependencies": { "@aws-sdk/core": "3.936.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-dKajFuaugEA5i9gCKzOaVy9uTeZcApE+7Z5wdcZ6j40523fY1a56khDAUYkCfwqa7sHci4ccmxBkAo+fW1RChA=="], - "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.911.0", "", { "dependencies": { "@aws-sdk/core": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/fetch-http-handler": "^5.3.3", "@smithy/node-http-handler": "^4.4.1", "@smithy/property-provider": "^4.2.2", "@smithy/protocol-http": "^5.3.2", "@smithy/smithy-client": "^4.8.1", "@smithy/types": "^4.7.1", "@smithy/util-stream": "^4.5.2", "tslib": "^2.6.2" } }, "sha512-xUlwKmIUW2fWP/eM3nF5u4CyLtOtyohlhGJ5jdsJokr3MrQ7w0tDITO43C9IhCn+28D5UbaiWnKw5ntkw7aVfA=="], + "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.936.0", "", { "dependencies": { "@aws-sdk/core": "3.936.0", "@aws-sdk/types": "3.936.0", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-5FguODLXG1tWx/x8fBxH+GVrk7Hey2LbXV5h9SFzYCx/2h50URBm0+9hndg0Rd23+xzYe14F6SI9HA9c1sPnjg=="], - "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.913.0", "", { "dependencies": { "@aws-sdk/core": "3.911.0", "@aws-sdk/credential-provider-env": "3.911.0", "@aws-sdk/credential-provider-http": "3.911.0", "@aws-sdk/credential-provider-process": "3.911.0", "@aws-sdk/credential-provider-sso": "3.911.0", "@aws-sdk/credential-provider-web-identity": "3.911.0", "@aws-sdk/nested-clients": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/credential-provider-imds": "^4.2.2", "@smithy/property-provider": "^4.2.2", "@smithy/shared-ini-file-loader": "^4.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-iR4c4NQ1OSRKQi0SxzpwD+wP1fCy+QNKtEyCajuVlD0pvmoIHdrm5THK9e+2/7/SsQDRhOXHJfLGxHapD74WJw=="], + "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.936.0", "", { "dependencies": { "@aws-sdk/core": "3.936.0", "@aws-sdk/credential-provider-env": "3.936.0", "@aws-sdk/credential-provider-http": "3.936.0", "@aws-sdk/credential-provider-login": "3.936.0", "@aws-sdk/credential-provider-process": "3.936.0", "@aws-sdk/credential-provider-sso": "3.936.0", "@aws-sdk/credential-provider-web-identity": "3.936.0", "@aws-sdk/nested-clients": "3.936.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-TbUv56ERQQujoHcLMcfL0Q6bVZfYF83gu/TjHkVkdSlHPOIKaG/mhE2XZSQzXv1cud6LlgeBbfzVAxJ+HPpffg=="], - "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.913.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.911.0", "@aws-sdk/credential-provider-http": "3.911.0", "@aws-sdk/credential-provider-ini": "3.913.0", "@aws-sdk/credential-provider-process": "3.911.0", "@aws-sdk/credential-provider-sso": "3.911.0", "@aws-sdk/credential-provider-web-identity": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/credential-provider-imds": "^4.2.2", "@smithy/property-provider": "^4.2.2", "@smithy/shared-ini-file-loader": "^4.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-HQPLkKDxS83Q/nZKqg9bq4igWzYQeOMqhpx5LYs4u1GwsKeCsYrrfz12Iu4IHNWPp9EnGLcmdfbfYuqZGrsaSQ=="], + "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.936.0", "", { "dependencies": { "@aws-sdk/core": "3.936.0", "@aws-sdk/nested-clients": "3.936.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8DVrdRqPyUU66gfV7VZNToh56ZuO5D6agWrkLQE/xbLJOm2RbeRgh6buz7CqV8ipRd6m+zCl9mM4F3osQLZn8Q=="], - "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.911.0", "", { "dependencies": { "@aws-sdk/core": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/property-provider": "^4.2.2", "@smithy/shared-ini-file-loader": "^4.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-mKshhV5jRQffZjbK9x7bs+uC2IsYKfpzYaBamFsEov3xtARCpOiKaIlM8gYKFEbHT2M+1R3rYYlhhl9ndVWS2g=="], + "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.936.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.936.0", "@aws-sdk/credential-provider-http": "3.936.0", "@aws-sdk/credential-provider-ini": "3.936.0", "@aws-sdk/credential-provider-process": "3.936.0", "@aws-sdk/credential-provider-sso": "3.936.0", "@aws-sdk/credential-provider-web-identity": "3.936.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-rk/2PCtxX9xDsQW8p5Yjoca3StqmQcSfkmD7nQ61AqAHL1YgpSQWqHE+HjfGGiHDYKG7PvE33Ku2GyA7lEIJAw=="], - "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.911.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.911.0", "@aws-sdk/core": "3.911.0", "@aws-sdk/token-providers": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/property-provider": "^4.2.2", "@smithy/shared-ini-file-loader": "^4.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-JAxd4uWe0Zc9tk6+N0cVxe9XtJVcOx6Ms0k933ZU9QbuRMH6xti/wnZxp/IvGIWIDzf5fhqiGyw5MSyDeI5b1w=="], + "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.936.0", "", { "dependencies": { "@aws-sdk/core": "3.936.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-GpA4AcHb96KQK2PSPUyvChvrsEKiLhQ5NWjeef2IZ3Jc8JoosiedYqp6yhZR+S8cTysuvx56WyJIJc8y8OTrLA=="], - "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.911.0", "", { "dependencies": { "@aws-sdk/core": "3.911.0", "@aws-sdk/nested-clients": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/property-provider": "^4.2.2", "@smithy/shared-ini-file-loader": "^4.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-urIbXWWG+cm54RwwTFQuRwPH0WPsMFSDF2/H9qO2J2fKoHRURuyblFCyYG3aVKZGvFBhOizJYexf5+5w3CJKBw=="], + "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.936.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.936.0", "@aws-sdk/core": "3.936.0", "@aws-sdk/token-providers": "3.936.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wHlEAJJvtnSyxTfNhN98JcU4taA1ED2JvuI2eePgawqBwS/Tzi0mhED1lvNIaWOkjfLd+nHALwszGrtJwEq4yQ=="], - "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.910.0", "", { "dependencies": { "@aws-sdk/types": "3.910.0", "@smithy/protocol-http": "^5.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-F9Lqeu80/aTM6S/izZ8RtwSmjfhWjIuxX61LX+/9mxJyEkgaECRxv0chsLQsLHJumkGnXRy/eIyMLBhcTPF5vg=="], + "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.936.0", "", { "dependencies": { "@aws-sdk/core": "3.936.0", "@aws-sdk/nested-clients": "3.936.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-v3qHAuoODkoRXsAF4RG+ZVO6q2P9yYBT4GMpMEfU9wXVNn7AIfwZgTwzSUfnjNiGva5BKleWVpRpJ9DeuLFbUg=="], - "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.910.0", "", { "dependencies": { "@aws-sdk/types": "3.910.0", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-3LJyyfs1USvRuRDla1pGlzGRtXJBXD1zC9F+eE9Iz/V5nkmhyv52A017CvKWmYoR0DM9dzjLyPOI0BSSppEaTw=="], + "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw=="], - "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.910.0", "", { "dependencies": { "@aws-sdk/types": "3.910.0", "@aws/lambda-invoke-store": "^0.0.1", "@smithy/protocol-http": "^5.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-m/oLz0EoCy+WoIVBnXRXJ4AtGpdl0kPE7U+VH9TsuUzHgxY1Re/176Q1HWLBRVlz4gr++lNsgsMWEC+VnAwMpw=="], + "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw=="], - "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.911.0", "", { "dependencies": { "@aws-sdk/core": "3.911.0", "@aws-sdk/types": "3.910.0", "@aws-sdk/util-endpoints": "3.910.0", "@smithy/core": "^3.16.1", "@smithy/protocol-http": "^5.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-rY3LvGvgY/UI0nmt5f4DRzjEh8135A2TeHcva1bgOmVfOI4vkkGfA20sNRqerOkSO6hPbkxJapO50UJHFzmmyA=="], + "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA=="], - "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.911.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.911.0", "@aws-sdk/middleware-host-header": "3.910.0", "@aws-sdk/middleware-logger": "3.910.0", "@aws-sdk/middleware-recursion-detection": "3.910.0", "@aws-sdk/middleware-user-agent": "3.911.0", "@aws-sdk/region-config-resolver": "3.910.0", "@aws-sdk/types": "3.910.0", "@aws-sdk/util-endpoints": "3.910.0", "@aws-sdk/util-user-agent-browser": "3.910.0", "@aws-sdk/util-user-agent-node": "3.911.0", "@smithy/config-resolver": "^4.3.2", "@smithy/core": "^3.16.1", "@smithy/fetch-http-handler": "^5.3.3", "@smithy/hash-node": "^4.2.2", "@smithy/invalid-dependency": "^4.2.2", "@smithy/middleware-content-length": "^4.2.2", "@smithy/middleware-endpoint": "^4.3.3", "@smithy/middleware-retry": "^4.4.3", "@smithy/middleware-serde": "^4.2.2", "@smithy/middleware-stack": "^4.2.2", "@smithy/node-config-provider": "^4.3.2", "@smithy/node-http-handler": "^4.4.1", "@smithy/protocol-http": "^5.3.2", "@smithy/smithy-client": "^4.8.1", "@smithy/types": "^4.7.1", "@smithy/url-parser": "^4.2.2", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.2", "@smithy/util-defaults-mode-node": "^4.2.3", "@smithy/util-endpoints": "^3.2.2", "@smithy/util-middleware": "^4.2.2", "@smithy/util-retry": "^4.2.2", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-lp/sXbdX/S0EYaMYPVKga0omjIUbNNdFi9IJITgKZkLC6CzspihIoHd5GIdl4esMJevtTQQfkVncXTFkf/a4YA=="], + "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.936.0", "", { "dependencies": { "@aws-sdk/core": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@smithy/core": "^3.18.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-YB40IPa7K3iaYX0lSnV9easDOLPLh+fJyUDF3BH8doX4i1AOSsYn86L4lVldmOaSX+DwiaqKHpvk4wPBdcIPWw=="], - "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.910.0", "", { "dependencies": { "@aws-sdk/types": "3.910.0", "@smithy/node-config-provider": "^4.3.2", "@smithy/types": "^4.7.1", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-gzQAkuHI3xyG6toYnH/pju+kc190XmvnB7X84vtN57GjgdQJICt9So/BD0U6h+eSfk9VBnafkVrAzBzWMEFZVw=="], + "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.936.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.936.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.936.0", "@aws-sdk/middleware-user-agent": "3.936.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.12", "@smithy/middleware-retry": "^4.4.12", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.11", "@smithy/util-defaults-mode-node": "^4.2.14", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-eyj2tz1XmDSLSZQ5xnB7cLTVKkSJnYAEoNDSUNhzWPxrBDYeJzIbatecOKceKCU8NBf8gWWZCK/CSY0mDxMO0A=="], - "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.911.0", "", { "dependencies": { "@aws-sdk/core": "3.911.0", "@aws-sdk/nested-clients": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/property-provider": "^4.2.2", "@smithy/shared-ini-file-loader": "^4.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-O1c5F1pbEImgEe3Vr8j1gpWu69UXWj3nN3vvLGh77hcrG5dZ8I27tSP5RN4Labm8Dnji/6ia+vqSYpN8w6KN5A=="], + "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw=="], - "@aws-sdk/types": ["@aws-sdk/types@3.910.0", "", { "dependencies": { "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-o67gL3vjf4nhfmuSUNNkit0d62QJEwwHLxucwVJkR/rw9mfUtAWsgBs8Tp16cdUbMgsyQtCQilL8RAJDoGtadQ=="], + "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.936.0", "", { "dependencies": { "@aws-sdk/core": "3.936.0", "@aws-sdk/nested-clients": "3.936.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-vvw8+VXk0I+IsoxZw0mX9TMJawUJvEsg3EF7zcCSetwhNPAU8Xmlhv7E/sN/FgSmm7b7DsqKoW6rVtQiCs1PWQ=="], - "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.910.0", "", { "dependencies": { "@aws-sdk/types": "3.910.0", "@smithy/types": "^4.7.1", "@smithy/url-parser": "^4.2.2", "@smithy/util-endpoints": "^3.2.2", "tslib": "^2.6.2" } }, "sha512-6XgdNe42ibP8zCQgNGDWoOF53RfEKzpU/S7Z29FTTJ7hcZv0SytC0ZNQQZSx4rfBl036YWYwJRoJMlT4AA7q9A=="], + "@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + + "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" } }, "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w=="], "@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.893.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg=="], - "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.910.0", "", { "dependencies": { "@aws-sdk/types": "3.910.0", "@smithy/types": "^4.7.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-iOdrRdLZHrlINk9pezNZ82P/VxO/UmtmpaOAObUN+xplCUJu31WNM2EE/HccC8PQw6XlAudpdA6HDTGiW6yVGg=="], + "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw=="], - "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.911.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/node-config-provider": "^4.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-3l+f6ooLF6Z6Lz0zGi7vSKSUYn/EePPizv88eZQpEAFunBHv+CSVNPtxhxHfkm7X9tTsV4QGZRIqo3taMLolmA=="], + "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.936.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.936.0", "@aws-sdk/types": "3.936.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-XOEc7PF9Op00pWV2AYCGDSu5iHgYjIO53Py2VUQTIvP7SRCaCsXmA33mjBvC2Ms6FhSyWNa4aK4naUGIz0hQcw=="], - "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.911.0", "", { "dependencies": { "@smithy/types": "^4.7.1", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-/yh3oe26bZfCVGrIMRM9Z4hvvGJD+qx5tOLlydOkuBkm72aXON7D9+MucjJXTAcI8tF2Yq+JHa0478eHQOhnLg=="], + "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], - "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.0.1", "", {}, "sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw=="], + "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.1", "", {}, "sha512-sIyFcoPZkTtNu9xFeEoynMef3bPJIAbOfUh+ueYcfhVl6xm2VRtMcMclSxmZCMnHHd4hlYKJeq/aggmBEWynww=="], "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], @@ -1001,55 +1008,55 @@ "@sinonjs/fake-timers": ["@sinonjs/fake-timers@8.1.0", "", { "dependencies": { "@sinonjs/commons": "^1.7.0" } }, "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg=="], - "@smithy/abort-controller": ["@smithy/abort-controller@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-xWL9Mf8b7tIFuAlpjKtRPnHrR8XVrwTj5NPYO/QwZPtc0SDLsPxb56V5tzi5yspSMytISHybifez+4jlrx0vkQ=="], + "@smithy/abort-controller": ["@smithy/abort-controller@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA=="], - "@smithy/config-resolver": ["@smithy/config-resolver@4.3.3", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.3", "@smithy/types": "^4.8.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.3", "tslib": "^2.6.2" } }, "sha512-xSql8A1Bl41O9JvGU/CtgiLBlwkvpHTSKRlvz9zOBvBCPjXghZ6ZkcVzmV2f7FLAA+80+aqKmIOmy8pEDrtCaw=="], + "@smithy/config-resolver": ["@smithy/config-resolver@4.4.3", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw=="], - "@smithy/core": ["@smithy/core@3.17.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.3", "@smithy/protocol-http": "^5.3.3", "@smithy/types": "^4.8.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.3", "@smithy/util-stream": "^4.5.3", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-Tir3DbfoTO97fEGUZjzGeoXgcQAUBRDTmuH9A8lxuP8ATrgezrAJ6cLuRvwdKN4ZbYNlHgKlBX69Hyu3THYhtg=="], + "@smithy/core": ["@smithy/core@3.18.5", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.6", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-6gnIz3h+PEPQGDj8MnRSjDvKBah042jEoPgjFGJ4iJLBE78L4lY/n98x14XyPF4u3lN179Ub/ZKFY5za9GeLQw=="], - "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.3", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.3", "@smithy/property-provider": "^4.2.3", "@smithy/types": "^4.8.0", "@smithy/url-parser": "^4.2.3", "tslib": "^2.6.2" } }, "sha512-hA1MQ/WAHly4SYltJKitEsIDVsNmXcQfYBRv2e+q04fnqtAX5qXaybxy/fhUeAMCnQIdAjaGDb04fMHQefWRhw=="], + "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.5", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ=="], - "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.3", "@smithy/querystring-builder": "^4.2.3", "@smithy/types": "^4.8.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-bwigPylvivpRLCm+YK9I5wRIYjFESSVwl8JQ1vVx/XhCw0PtCi558NwTnT2DaVCl5pYlImGuQTSwMsZ+pIavRw=="], + "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.6", "", { "dependencies": { "@smithy/protocol-http": "^5.3.5", "@smithy/querystring-builder": "^4.2.5", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg=="], - "@smithy/hash-node": ["@smithy/hash-node@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6+NOdZDbfuU6s1ISp3UOk5Rg953RJ2aBLNLLBEcamLjHAg1Po9Ha7QIB5ZWhdRUVuOUrT8BVFR+O2KIPmw027g=="], + "@smithy/hash-node": ["@smithy/hash-node@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA=="], - "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-Cc9W5DwDuebXEDMpOpl4iERo8I0KFjTnomK2RMdhhR87GwrSmUmwMxS4P5JdRf+LsjOdIqumcerwRgYMr/tZ9Q=="], + "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A=="], "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], - "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.3", "", { "dependencies": { "@smithy/protocol-http": "^5.3.3", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-/atXLsT88GwKtfp5Jr0Ks1CSa4+lB+IgRnkNrrYP0h1wL4swHNb0YONEvTceNKNdZGJsye+W2HH8W7olbcPUeA=="], + "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A=="], - "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.4", "", { "dependencies": { "@smithy/core": "^3.17.0", "@smithy/middleware-serde": "^4.2.3", "@smithy/node-config-provider": "^4.3.3", "@smithy/shared-ini-file-loader": "^4.3.3", "@smithy/types": "^4.8.0", "@smithy/url-parser": "^4.2.3", "@smithy/util-middleware": "^4.2.3", "tslib": "^2.6.2" } }, "sha512-/RJhpYkMOaUZoJEkddamGPPIYeKICKXOu/ojhn85dKDM0n5iDIhjvYAQLP3K5FPhgB203O3GpWzoK2OehEoIUw=="], + "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.12", "", { "dependencies": { "@smithy/core": "^3.18.5", "@smithy/middleware-serde": "^4.2.6", "@smithy/node-config-provider": "^4.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-middleware": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-9pAX/H+VQPzNbouhDhkW723igBMLgrI8OtX+++M7iKJgg/zY/Ig3i1e6seCcx22FWhE6Q/S61BRdi2wXBORT+A=="], - "@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.3", "@smithy/protocol-http": "^5.3.3", "@smithy/service-error-classification": "^4.2.3", "@smithy/smithy-client": "^4.9.0", "@smithy/types": "^4.8.0", "@smithy/util-middleware": "^4.2.3", "@smithy/util-retry": "^4.2.3", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-vSgABQAkuUHRO03AhR2rWxVQ1un284lkBn+NFawzdahmzksAoOeVMnXXsuPViL4GlhRHXqFaMlc8Mj04OfQk1w=="], + "@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.12", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/protocol-http": "^5.3.5", "@smithy/service-error-classification": "^4.2.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-S4kWNKFowYd0lID7/DBqWHOQxmxlsf0jBaos9chQZUWTVOjSW1Ogyh8/ib5tM+agFDJ/TCxuCTvrnlc+9cIBcQ=="], - "@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.3", "", { "dependencies": { "@smithy/protocol-http": "^5.3.3", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-8g4NuUINpYccxiCXM5s1/V+uLtts8NcX4+sPEbvYQDZk4XoJfDpq5y2FQxfmUL89syoldpzNzA0R9nhzdtdKnQ=="], + "@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.6", "", { "dependencies": { "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ=="], - "@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-iGuOJkH71faPNgOj/gWuEGS6xvQashpLwWB1HjHq1lNNiVfbiJLpZVbhddPuDbx9l4Cgl0vPLq5ltRfSaHfspA=="], + "@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ=="], - "@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.3", "", { "dependencies": { "@smithy/property-provider": "^4.2.3", "@smithy/shared-ini-file-loader": "^4.3.3", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-NzI1eBpBSViOav8NVy1fqOlSfkLgkUjUTlohUSgAEhHaFWA3XJiLditvavIP7OpvTjDp5u2LhtlBhkBlEisMwA=="], + "@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.5", "", { "dependencies": { "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg=="], - "@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.2", "", { "dependencies": { "@smithy/abort-controller": "^4.2.3", "@smithy/protocol-http": "^5.3.3", "@smithy/querystring-builder": "^4.2.3", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-MHFvTjts24cjGo1byXqhXrbqm7uznFD/ESFx8npHMWTFQVdBZjrT1hKottmp69LBTRm/JQzP/sn1vPt0/r6AYQ=="], + "@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.5", "", { "dependencies": { "@smithy/abort-controller": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/querystring-builder": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw=="], - "@smithy/property-provider": ["@smithy/property-provider@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-+1EZ+Y+njiefCohjlhyOcy1UNYjT+1PwGFHCxA/gYctjg3DQWAU19WigOXAco/Ql8hZokNehpzLd0/+3uCreqQ=="], + "@smithy/property-provider": ["@smithy/property-provider@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg=="], - "@smithy/protocol-http": ["@smithy/protocol-http@5.3.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-Mn7f/1aN2/jecywDcRDvWWWJF4uwg/A0XjFMJtj72DsgHTByfjRltSqcT9NyE9RTdBSN6X1RSXrhn/YWQl8xlw=="], + "@smithy/protocol-http": ["@smithy/protocol-http@5.3.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ=="], - "@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-LOVCGCmwMahYUM/P0YnU/AlDQFjcu+gWbFJooC417QRB/lDJlWSn8qmPSDp+s4YVAHOgtgbNG4sR+SxF/VOcJQ=="], + "@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg=="], - "@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-cYlSNHcTAX/wc1rpblli3aUlLMGgKZ/Oqn8hhjFASXMCXjIqeuQBei0cnq2JR8t4RtU9FpG6uyl6PxyArTiwKA=="], + "@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ=="], - "@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0" } }, "sha512-NkxsAxFWwsPsQiwFG2MzJ/T7uIR6AQNh1SzcxSUnmmIqIQMlLRQDKhc17M7IYjiuBXhrQRjQTo3CxX+DobS93g=="], + "@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0" } }, "sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ=="], - "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.3.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-9f9Ixej0hFhroOK2TxZfUUDR13WVa8tQzhSzPDgXe5jGL3KmaM9s8XN7RQwqtEypI82q9KHnKS71CJ+q/1xLtQ=="], + "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA=="], - "@smithy/signature-v4": ["@smithy/signature-v4@5.3.3", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.3", "@smithy/types": "^4.8.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.3", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-CmSlUy+eEYbIEYN5N3vvQTRfqt0lJlQkaQUIf+oizu7BbDut0pozfDjBGecfcfWf7c62Yis4JIEgqQ/TCfodaA=="], + "@smithy/signature-v4": ["@smithy/signature-v4@5.3.5", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w=="], - "@smithy/smithy-client": ["@smithy/smithy-client@4.9.0", "", { "dependencies": { "@smithy/core": "^3.17.0", "@smithy/middleware-endpoint": "^4.3.4", "@smithy/middleware-stack": "^4.2.3", "@smithy/protocol-http": "^5.3.3", "@smithy/types": "^4.8.0", "@smithy/util-stream": "^4.5.3", "tslib": "^2.6.2" } }, "sha512-qz7RTd15GGdwJ3ZCeBKLDQuUQ88m+skh2hJwcpPm1VqLeKzgZvXf6SrNbxvx7uOqvvkjCMXqx3YB5PDJyk00ww=="], + "@smithy/smithy-client": ["@smithy/smithy-client@4.9.8", "", { "dependencies": { "@smithy/core": "^3.18.5", "@smithy/middleware-endpoint": "^4.3.12", "@smithy/middleware-stack": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-8xgq3LgKDEFoIrLWBho/oYKyWByw9/corz7vuh1upv7ZBm0ZMjGYBhbn6v643WoIqA9UTcx5A5htEp/YatUwMA=="], - "@smithy/types": ["@smithy/types@4.8.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QpELEHLO8SsQVtqP+MkEgCYTFW0pleGozfs3cZ183ZBj9z3VC1CX1/wtFMK64p+5bhtZo41SeLK1rBRtd25nHQ=="], + "@smithy/types": ["@smithy/types@4.9.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA=="], - "@smithy/url-parser": ["@smithy/url-parser@4.2.3", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.3", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-I066AigYvY3d9VlU3zG9XzZg1yT10aNqvCaBTw9EPgu5GrsEl1aUkcMvhkIXascYH1A8W0LQo3B1Kr1cJNcQEw=="], + "@smithy/url-parser": ["@smithy/url-parser@4.2.5", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ=="], "@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], @@ -1061,25 +1068,25 @@ "@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="], - "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.3", "", { "dependencies": { "@smithy/property-provider": "^4.2.3", "@smithy/smithy-client": "^4.9.0", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-vqHoybAuZXbFXZqgzquiUXtdY+UT/aU33sxa4GBPkiYklmR20LlCn+d3Wc3yA5ZM13gQ92SZe/D8xh6hkjx+IQ=="], + "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.11", "", { "dependencies": { "@smithy/property-provider": "^4.2.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-yHv+r6wSQXEXTPVCIQTNmXVWs7ekBTpMVErjqZoWkYN75HIFN5y9+/+sYOejfAuvxWGvgzgxbTHa/oz61YTbKw=="], - "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.4", "", { "dependencies": { "@smithy/config-resolver": "^4.3.3", "@smithy/credential-provider-imds": "^4.2.3", "@smithy/node-config-provider": "^4.3.3", "@smithy/property-provider": "^4.2.3", "@smithy/smithy-client": "^4.9.0", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-X5/xrPHedifo7hJUUWKlpxVb2oDOiqPUXlvsZv1EZSjILoutLiJyWva3coBpn00e/gPSpH8Rn2eIbgdwHQdW7Q=="], + "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.14", "", { "dependencies": { "@smithy/config-resolver": "^4.4.3", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-ljZN3iRvaJUgulfvobIuG97q1iUuCMrvXAlkZ4msY+ZuVHQHDIqn7FKZCEj+bx8omz6kF5yQXms/xhzjIO5XiA=="], - "@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.3", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.3", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-aCfxUOVv0CzBIkU10TubdgKSx5uRvzH064kaiPEWfNIvKOtNpu642P4FP1hgOFkjQIkDObrfIDnKMKkeyrejvQ=="], + "@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.5", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A=="], "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], - "@smithy/util-middleware": ["@smithy/util-middleware@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-v5ObKlSe8PWUHCqEiX2fy1gNv6goiw6E5I/PN2aXg3Fb/hse0xeaAnSpXDiWl7x6LamVKq7senB+m5LOYHUAHw=="], + "@smithy/util-middleware": ["@smithy/util-middleware@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA=="], - "@smithy/util-retry": ["@smithy/util-retry@4.2.3", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.3", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-lLPWnakjC0q9z+OtiXk+9RPQiYPNAovt2IXD3CP4LkOnd9NpUsxOjMx1SnoUVB7Orb7fZp67cQMtTBKMFDvOGg=="], + "@smithy/util-retry": ["@smithy/util-retry@4.2.5", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg=="], - "@smithy/util-stream": ["@smithy/util-stream@4.5.3", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.4", "@smithy/node-http-handler": "^4.4.2", "@smithy/types": "^4.8.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-oZvn8a5bwwQBNYHT2eNo0EU8Kkby3jeIg1P2Lu9EQtqDxki1LIjGRJM6dJ5CZUig8QmLxWxqOKWvg3mVoOBs5A=="], + "@smithy/util-stream": ["@smithy/util-stream@4.5.6", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ=="], "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], "@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], - "@smithy/util-waiter": ["@smithy/util-waiter@4.2.3", "", { "dependencies": { "@smithy/abort-controller": "^4.2.3", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-5+nU///E5sAdD7t3hs4uwvCTWQtTR8JwKwOCSJtBRx0bY1isDo1QwH87vRK86vlFLBTISqoDA2V6xvP6nF1isQ=="], + "@smithy/util-waiter": ["@smithy/util-waiter@4.2.5", "", { "dependencies": { "@smithy/abort-controller": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Dbun99A3InifQdIrsXZ+QLcC0PGBPAdrl4cj1mTgJvyc9N2zf7QSxg8TBkzsCmGJdE3TLbO9ycwpY0EkWahQ/g=="], "@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], @@ -1223,7 +1230,7 @@ "@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="], - "@types/nodemailer": ["@types/nodemailer@6.4.20", "", { "dependencies": { "@aws-sdk/client-ses": "^3.731.1", "@types/node": "*" } }, "sha512-uj83z0GqwqMUE6RI4EKptPlav0FYE6vpIlqJAnxzu+/sSezRdbH69rSBCMsdW6DdsCAzoFQZ52c2UIlhRVQYDA=="], + "@types/nodemailer": ["@types/nodemailer@6.4.21", "", { "dependencies": { "@aws-sdk/client-ses": "^3.731.1", "@types/node": "*" } }, "sha512-Eix+sb/Nj28MNnWvO2X1OLrk5vuD4C9SMnb2Vf4itWnxphYeSceqkFX7IdmxTzn+dvmnNz7paMbg4Uc60wSfJg=="], "@types/prettier": ["@types/prettier@2.7.3", "", {}, "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA=="], @@ -1391,6 +1398,8 @@ "@xtuc/long": ["@xtuc/long@4.2.2", "", {}, "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="], + "@zone-eu/mailsplit": ["@zone-eu/mailsplit@5.4.7", "", { "dependencies": { "libbase64": "1.3.0", "libmime": "5.3.7", "libqp": "2.1.1" } }, "sha512-jApX86aDgolMz08pP20/J2zcns02NSK3zSiYouf01QQg4250L+GUAWSWicmS7eRvs+Z7wP7QfXrnkaTBGrIpwQ=="], + "abab": ["abab@2.0.6", "", {}, "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="], "abbrev": ["abbrev@2.0.0", "", {}, "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ=="], @@ -2621,9 +2630,7 @@ "magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], - "mailparser": ["mailparser@3.7.5", "", { "dependencies": { "encoding-japanese": "2.2.0", "he": "1.2.0", "html-to-text": "9.0.5", "iconv-lite": "0.7.0", "libmime": "5.3.7", "linkify-it": "5.0.0", "mailsplit": "5.4.6", "nodemailer": "7.0.9", "punycode.js": "2.3.1", "tlds": "1.260.0" } }, "sha512-o59RgZC+4SyCOn4xRH1mtRiZ1PbEmi6si6Ufnd3tbX/V9zmZN1qcqu8xbXY62H6CwIclOT3ppm5u/wV2nujn4g=="], - - "mailsplit": ["mailsplit@5.4.6", "", { "dependencies": { "libbase64": "1.3.0", "libmime": "5.3.7", "libqp": "2.1.1" } }, "sha512-M+cqmzaPG/mEiCDmqQUz8L177JZLZmXAUpq38owtpq2xlXlTSw+kntnxRt2xsxVFFV6+T8Mj/U0l5s7s6e0rNw=="], + "mailparser": ["mailparser@3.9.0", "", { "dependencies": { "@zone-eu/mailsplit": "5.4.7", "encoding-japanese": "2.2.0", "he": "1.2.0", "html-to-text": "9.0.5", "iconv-lite": "0.7.0", "libmime": "5.3.7", "linkify-it": "5.0.0", "nodemailer": "7.0.10", "punycode.js": "2.3.1", "tlds": "1.261.0" } }, "sha512-jpaNLhDjwy0w2f8sySOSRiWREjPqssSc0C2czV98btCXCRX3EyNloQ2IWirmMDj1Ies8Fkm0l96bZBZpDG7qkg=="], "make-dir": ["make-dir@4.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw=="], @@ -3285,7 +3292,7 @@ "titleize": ["titleize@2.1.0", "", {}, "sha512-m+apkYlfiQTKLW+sI4vqUkwMEzfgEUEYSqljx1voUE3Wz/z1ZsxyzSxvH2X8uKVrOp7QkByWt0rA6+gvhCKy6g=="], - "tlds": ["tlds@1.260.0", "", { "bin": { "tlds": "bin.js" } }, "sha512-78+28EWBhCEE7qlyaHA9OR3IPvbCLiDh3Ckla593TksfFc9vfTsgvH7eS+dr3o9qr31gwGbogcI16yN91PoRjQ=="], + "tlds": ["tlds@1.261.0", "", { "bin": { "tlds": "bin.js" } }, "sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA=="], "tldts": ["tldts@6.1.86", "", { "dependencies": { "tldts-core": "^6.1.86" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ=="], @@ -4041,7 +4048,7 @@ "mailparser/html-to-text": ["html-to-text@9.0.5", "", { "dependencies": { "@selderee/plugin-htmlparser2": "^0.11.0", "deepmerge": "^4.3.1", "dom-serializer": "^2.0.0", "htmlparser2": "^8.0.2", "selderee": "^0.11.0" } }, "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg=="], - "mailparser/nodemailer": ["nodemailer@7.0.9", "", {}, "sha512-9/Qm0qXIByEP8lEV2qOqcAW7bRpL8CR9jcTwk3NBnHJNmP9fIJ86g2fgmIXqHY+nj55ZEMwWqYAT2QTDpRUYiQ=="], + "mailparser/nodemailer": ["nodemailer@7.0.10", "", {}, "sha512-Us/Se1WtT0ylXgNFfyFSx4LElllVLJXQjWi2Xz17xWw7amDKO2MLtFnVp1WACy7GkVGs+oBlRopVNUzlrGSw1w=="], "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], diff --git a/core/esbuild.config.ts b/core/esbuild.config.ts index 17435cef1..dae89f49c 100644 --- a/core/esbuild.config.ts +++ b/core/esbuild.config.ts @@ -6,9 +6,9 @@ build({ platform: 'node', target: 'node18.20.7', loader: { - '.png': 'dataurl', - '.jpeg': 'dataurl', - '.jpg': 'dataurl', + '.png': 'binary', + '.jpeg': 'binary', + '.jpg': 'binary', }, bundle: true, sourcemap: true, diff --git a/core/package.json b/core/package.json index 06f6e7885..e56a57f43 100644 --- a/core/package.json +++ b/core/package.json @@ -22,6 +22,8 @@ "typecheck": "tsc --noEmit", "lint": "biome check --error-on-warnings .", "lint:fix": "biome check --error-on-warnings . --write", + "locales": "scripts/sort.sh src/emails/locales", + "locales:fix": "scripts/sort.sh src/emails/locales --fix", "clear": "rm -rf node_modules && rm -rf build && rm -rf .turbo" }, "dependencies": { diff --git a/backend/scripts/sort.sh b/core/scripts/sort.sh similarity index 83% rename from backend/scripts/sort.sh rename to core/scripts/sort.sh index d24307d7c..ba54e835a 100755 --- a/backend/scripts/sort.sh +++ b/core/scripts/sort.sh @@ -4,11 +4,11 @@ ROOT_DIR=$(dirname "$0")/.. exit_code=0 -for locale_file in $ROOT_DIR/src/locales/*.json +for locale_file in $ROOT_DIR/"$1"/*.json do jq -M 'to_entries | sort_by(.key) | from_entries' "$locale_file" > tmp.json - if [ "$*" == "--fix" ] + if [ "$2" == "--fix" ] then mv tmp.json "$locale_file" else diff --git a/core/src/emails/locales/en.json b/core/src/emails/locales/en.json index cc8d59c75..b76bc1497 100644 --- a/core/src/emails/locales/en.json +++ b/core/src/emails/locales/en.json @@ -1,104 +1,104 @@ { - "emails": { - "accountActivation": { - "activateAccount": "Activate account", - "emailRegistered": "Your email address has just been registered with Gradido.", - "pleaseClickLink": "Please click here to complete the registration and activate your Gradido account.", - "subject": "Email Verification", - "title": "Email Verification" - }, - "accountMultiRegistration": { - "contactSupport": "Contact support", - "emailExists": "However, an account already exists for your email address.", - "emailReused": "Your email address has just been used again to register an account with Gradido.", - "ifYouAreNotTheOne": "If you did not try to register again, please contact our support:", - "onForgottenPasswordClickLink": "If you have forgotten your password, please click here.", - "subject": "Try To Register Again With Your Email", - "title": "Try To Register Again With Your Email" - }, - "addedContributionMessage": { - "commonGoodContributionMessage": "You have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.", - "readMessage": "Read and reply to message", - "subject": "Message about your common good contribution", - "title": "Message about your common good contribution", - "message": "„{message}“", - "toSeeAndAnswerMessage": "To reply to the message, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab." - }, - "contribution": { - "toSeeContributionsAndMessages": "To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab." - }, - "contributionChangedByModerator": { - "subject": "Your common good contribution has been changed", - "text": "your common good contribution '{contributionMemo}' has just been changed by {senderFirstName} {senderLastName} and now reads as '{contributionMemoUpdated}'", - "title": "Your common good contribution has been changed" - }, - "contributionConfirmed": { - "commonGoodContributionConfirmed": "Your common good contribution “{contributionMemo}” has just been approved by {senderFirstName} {senderLastName}. Your Gradido account has been credited with {amountGDD} GDD.", - "subject": "Your contribution to the common good was confirmed", - "title": "Your contribution to the common good was confirmed" - }, - "contributionDeleted": { - "commonGoodContributionDeleted": "Your common good contribution “{contributionMemo}” was deleted by {senderFirstName} {senderLastName}.", - "subject": "Your common good contribution was deleted", - "title": "Your common good contribution was deleted" - }, - "contributionDenied": { - "commonGoodContributionDenied": "Your common good contribution “{contributionMemo}” was rejected by {senderFirstName} {senderLastName}.", - "subject": "Your common good contribution was rejected", - "title": "Your common good contribution was rejected" - }, - "footer": { - "contactOurSupport": "If you have any further questions, please contact our support.", - "imprint": "Impressum", - "imprintAddress": "Gradido-Akademie\nInstitut für Wirtschaftsbionik\nPfarrweg 2\n74653 Künzelsau\nDeutschland", - "imprintImageAlt": "Gradido-Akademie Logo", - "privacyPolicy": "Privacy Policy", - "supportEmail": "support@gradido.net" - }, - "general": { - "amountGDD": "Amount: {amountGDD} GDD", - "answerNow": "Reply", - "completeRegistration": "Complete registration", - "contribution": "Contribution: : {contributionMemo}", - "contributionDetails": "Contribution details", - "detailsYouFindOnLinkToYourAccount": "You can find transaction details in your Gradido account.", - "helloName": "Hello {firstName} {lastName},", - "linkValidity": "The link has a validity of {hours} hours.\nIf the validity of the link has already expired, you can have a new link sent to you here.", - "linkValidityWithMinutes": "The link has a validity of {hours} hours and {minutes} minutes.\nIf the validity of the link has already expired, you can have a new link sent to you here.", - "message": "Message", - "newLink": "New link", - "orCopyLink": "Or copy the link into your browser window.", - "pleaseDoNotReply": "Please do not reply to this email.", - "requestNewLink": "Request new valid link", - "reset": "reset", - "sincerelyYours": "Kind regards,", - "toAccount": "To account", - "transactionDetails": "Transaction details", - "yourGradidoTeam": "your Gradido team" - }, - "resetPassword": { - "pleaseClickLink": "If it was you, please click here.", - "subject": "Reset password", - "title": "Reset password", - "youOrSomeoneResetPassword": "You, or someone else, requested a password reset for this account." - }, - "transactionLinkRedeemed": { - "hasRedeemedYourLink": "{senderFirstName} {senderLastName} ({senderEmail}) has just redeemed your link.", - "memo": "Message: {transactionMemo}", - "subject": "{senderFirstName} {senderLastName} has redeemed your Gradido link", - "title": "{senderFirstName} {senderLastName} has redeemed your Gradido link" - }, - "transactionReceived": { - "haveReceivedAmountGDDFrom": "You have just received {transactionAmount} GDD from {senderFirstName} {senderLastName}", - "replySubject": "RE: {senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido", - "subject": "{senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido", - "title": "{senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido" - } - }, - "general": { - "decimalSeparator": "." - }, - "accountMultiRegistration": { - "contactSupport": "accountMultiRegistration.contactSupport" - } -} \ No newline at end of file + "accountMultiRegistration": { + "contactSupport": "accountMultiRegistration.contactSupport" + }, + "emails": { + "accountActivation": { + "activateAccount": "Activate account", + "emailRegistered": "Your email address has just been registered with Gradido.", + "pleaseClickLink": "Please click here to complete the registration and activate your Gradido account.", + "subject": "Email Verification", + "title": "Email Verification" + }, + "accountMultiRegistration": { + "contactSupport": "Contact support", + "emailExists": "However, an account already exists for your email address.", + "emailReused": "Your email address has just been used again to register an account with Gradido.", + "ifYouAreNotTheOne": "If you did not try to register again, please contact our support:", + "onForgottenPasswordClickLink": "If you have forgotten your password, please click here.", + "subject": "Try To Register Again With Your Email", + "title": "Try To Register Again With Your Email" + }, + "addedContributionMessage": { + "commonGoodContributionMessage": "You have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.", + "readMessage": "Read and reply to message", + "subject": "Message about your common good contribution", + "title": "Message about your common good contribution", + "message": "„{message}“", + "toSeeAndAnswerMessage": "To reply to the message, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab." + }, + "contribution": { + "toSeeContributionsAndMessages": "To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab." + }, + "contributionChangedByModerator": { + "subject": "Your common good contribution has been changed", + "text": "your common good contribution '{contributionMemo}' has just been changed by {senderFirstName} {senderLastName} and now reads as '{contributionMemoUpdated}'", + "title": "Your common good contribution has been changed" + }, + "contributionConfirmed": { + "commonGoodContributionConfirmed": "Your common good contribution “{contributionMemo}” has just been approved by {senderFirstName} {senderLastName}. Your Gradido account has been credited with {amountGDD} GDD.", + "subject": "Your contribution to the common good was confirmed", + "title": "Your contribution to the common good was confirmed" + }, + "contributionDeleted": { + "commonGoodContributionDeleted": "Your common good contribution “{contributionMemo}” was deleted by {senderFirstName} {senderLastName}.", + "subject": "Your common good contribution was deleted", + "title": "Your common good contribution was deleted" + }, + "contributionDenied": { + "commonGoodContributionDenied": "Your common good contribution “{contributionMemo}” was rejected by {senderFirstName} {senderLastName}.", + "subject": "Your common good contribution was rejected", + "title": "Your common good contribution was rejected" + }, + "footer": { + "contactOurSupport": "If you have any further questions, please contact our support.", + "imprint": "Impressum", + "imprintAddress": "Gradido-Akademie\nInstitut für Wirtschaftsbionik\nPfarrweg 2\n74653 Künzelsau\nDeutschland", + "imprintImageAlt": "Gradido-Akademie Logo", + "privacyPolicy": "Privacy Policy", + "supportEmail": "support@gradido.net" + }, + "general": { + "amountGDD": "Amount: {amountGDD} GDD", + "answerNow": "Reply", + "completeRegistration": "Complete registration", + "contribution": "Contribution: : {contributionMemo}", + "contributionDetails": "Contribution details", + "detailsYouFindOnLinkToYourAccount": "You can find transaction details in your Gradido account.", + "helloName": "Hello {firstName} {lastName},", + "linkValidity": "The link has a validity of {hours} hours.\nIf the validity of the link has already expired, you can have a new link sent to you here.", + "linkValidityWithMinutes": "The link has a validity of {hours} hours and {minutes} minutes.\nIf the validity of the link has already expired, you can have a new link sent to you here.", + "message": "Message", + "newLink": "New link", + "orCopyLink": "Or copy the link into your browser window.", + "pleaseDoNotReply": "Please do not reply to this email.", + "requestNewLink": "Request new valid link", + "reset": "reset", + "sincerelyYours": "Kind regards,", + "toAccount": "To account", + "transactionDetails": "Transaction details", + "yourGradidoTeam": "your Gradido team" + }, + "resetPassword": { + "pleaseClickLink": "If it was you, please click here.", + "subject": "Reset password", + "title": "Reset password", + "youOrSomeoneResetPassword": "You, or someone else, requested a password reset for this account." + }, + "transactionLinkRedeemed": { + "hasRedeemedYourLink": "{senderFirstName} {senderLastName} ({senderEmail}) has just redeemed your link.", + "memo": "Message: {transactionMemo}", + "subject": "{senderFirstName} {senderLastName} has redeemed your Gradido link", + "title": "{senderFirstName} {senderLastName} has redeemed your Gradido link" + }, + "transactionReceived": { + "haveReceivedAmountGDDFrom": "You have just received {transactionAmount} GDD from {senderFirstName} {senderLastName}", + "replySubject": "RE: {senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido", + "subject": "{senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido", + "title": "{senderFirstName} {senderLastName} has sent you {transactionAmount} Gradido" + } + }, + "general": { + "decimalSeparator": "." + } +} diff --git a/core/src/emails/sendEmailTranslated.ts b/core/src/emails/sendEmailTranslated.ts index 02d6de130..b05b93ca7 100644 --- a/core/src/emails/sendEmailTranslated.ts +++ b/core/src/emails/sendEmailTranslated.ts @@ -1,5 +1,4 @@ import path from 'path' - import Email from 'email-templates' import { i18n } from './localization' import { createTransport } from 'nodemailer' @@ -60,8 +59,8 @@ export const sendEmailTranslated = async ({ pass: CONFIG.EMAIL_PASSWORD, }, }) - i18n.setLocale(locals.language as string) // for email + i18n.setLocale(locals.language as string) // for email // TESTING: see 'README.md' const email = new Email({ message: { @@ -71,7 +70,6 @@ export const sendEmailTranslated = async ({ transport, preview: false, }) - const resultSend = await email .send({ template: path.join(__dirname, 'templates', template), @@ -79,8 +77,9 @@ export const sendEmailTranslated = async ({ ...receiver, attachments: [ { - // filename: 'gradido-header.jpeg', - content: gradidoHeader, + filename: 'gradido-header.jpeg', + // content: gradidoHeader, + path: path.join(__dirname, 'templates/includes/gradido-header.jpeg'), cid: 'gradidoheader', }, { @@ -115,7 +114,7 @@ export const sendEmailTranslated = async ({ }) .catch((error: unknown) => { logger.error('Error sending notification email', error) - return error + throw error }) return resultSend diff --git a/core/turbo.json b/core/turbo.json new file mode 100644 index 000000000..067806d37 --- /dev/null +++ b/core/turbo.json @@ -0,0 +1,13 @@ +{ + "extends": ["//"], + "tasks": { + "locales": {}, + "locales:fix": {}, + "lint": { + "dependsOn": ["locales"] + }, + "lint:fix": { + "dependsOn": ["locales:fix"] + } + } +} \ No newline at end of file From 0cb4f5fb695c8db6d522a51b884bd195db2bbf40 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 24 Nov 2025 16:48:28 +0100 Subject: [PATCH 12/22] fix --- core/src/emails/sendEmailTranslated.ts | 5 ++--- core/src/emails/sendEmailVariants.ts | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/src/emails/sendEmailTranslated.ts b/core/src/emails/sendEmailTranslated.ts index b05b93ca7..30ae03112 100644 --- a/core/src/emails/sendEmailTranslated.ts +++ b/core/src/emails/sendEmailTranslated.ts @@ -77,9 +77,8 @@ export const sendEmailTranslated = async ({ ...receiver, attachments: [ { - filename: 'gradido-header.jpeg', - // content: gradidoHeader, - path: path.join(__dirname, 'templates/includes/gradido-header.jpeg'), + // filename: 'gradido-header.jpeg', + content: gradidoHeader, cid: 'gradidoheader', }, { diff --git a/core/src/emails/sendEmailVariants.ts b/core/src/emails/sendEmailVariants.ts index 75f5c468d..da67beefb 100644 --- a/core/src/emails/sendEmailVariants.ts +++ b/core/src/emails/sendEmailVariants.ts @@ -1,7 +1,7 @@ import { Decimal } from 'decimal.js-light' import { CONFIG } from '../config' -import { decimalSeparatorByLanguage } from 'core' +import { decimalSeparatorByLanguage } from '../util/utilities' import { sendEmailTranslated } from './sendEmailTranslated' From 68e09d432fc596abc4ceb893c44e83481d7a6c3a Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 24 Nov 2025 17:03:44 +0100 Subject: [PATCH 13/22] fix ci errors --- .github/workflows/test_backend.yml | 17 +---------------- .github/workflows/test_core.yml | 15 +++++++++++++++ backend/Dockerfile | 3 +-- backend/package.json | 2 +- federation/Dockerfile | 2 ++ federation/package.json | 2 +- 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/.github/workflows/test_backend.yml b/.github/workflows/test_backend.yml index c0e65078f..f85fdfa4c 100644 --- a/.github/workflows/test_backend.yml +++ b/.github/workflows/test_backend.yml @@ -92,19 +92,4 @@ jobs: bun install --global --no-save turbo@^2 - name: Backend | Typecheck - run: turbo backend#typecheck backend#build - - locales: - if: needs.files-changed.outputs.backend == 'true' - name: Locales - Backend - needs: files-changed - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: install bun - uses: oven-sh/setup-bun@v2 - - - name: Backend | Locales - run: cd backend && bun locales \ No newline at end of file + run: turbo backend#typecheck backend#build \ No newline at end of file diff --git a/.github/workflows/test_core.yml b/.github/workflows/test_core.yml index ff5f29c1a..f3857e2ae 100644 --- a/.github/workflows/test_core.yml +++ b/.github/workflows/test_core.yml @@ -43,3 +43,18 @@ jobs: - name: typecheck && unit test run: turbo core#test core#typecheck + locales: + if: needs.files-changed.outputs.core == 'true' + name: Locales - Core + needs: files-changed + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: install bun + uses: oven-sh/setup-bun@v2 + + - name: Core | Locales + run: cd core && bun locales + diff --git a/backend/Dockerfile b/backend/Dockerfile index eeab1ac17..d3d549cab 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -114,8 +114,7 @@ COPY --chown=app:app --from=build ${DOCKER_WORKDIR}/backend/build/worker.js ./wo # add node_modules from production_node_modules COPY --chown=app:app --from=production-node-modules ${DOCKER_WORKDIR}/node_modules ./node_modules -# Copy locales -COPY --chown=app:app --from=build ${DOCKER_WORKDIR}/backend/locales ./locales +COPY --chown=app:app --from=build ${DOCKER_WORKDIR}/core/build/templates ./templates # Run command CMD ["node", "index.js"] diff --git a/backend/package.json b/backend/package.json index 0a1bab487..463683372 100644 --- a/backend/package.json +++ b/backend/package.json @@ -8,7 +8,7 @@ "author": "Gradido Academy - https://www.gradido.net", "main": "src/index.ts", "scripts": { - "build": "ts-node ./esbuild.config.ts && mkdirp build/templates/ && ncp ../core/src/emails/templates build/templates", + "build": "ts-node ./esbuild.config.ts && mkdirp build/templates/ && ncp ../core/build/templates build/templates", "dev": "cross-env TZ=UTC nodemon -w src --ext ts,pug,json,css -r tsconfig-paths/register src/index.ts", "test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_backend jest --runInBand --forceExit --detectOpenHandles", "test:coverage": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_backend jest --coverage --runInBand --forceExit --detectOpenHandles", diff --git a/federation/Dockerfile b/federation/Dockerfile index c3a45bf3d..c754eced6 100644 --- a/federation/Dockerfile +++ b/federation/Dockerfile @@ -116,5 +116,7 @@ COPY --chown=app:app --from=build ${DOCKER_WORKDIR}/federation/build/index.js ./ # add node_modules from production_node_modules COPY --chown=app:app --from=production-node-modules ${DOCKER_WORKDIR}/node_modules ./node_modules +COPY --chown=app:app --from=build ${DOCKER_WORKDIR}/core/build/templates ./templates + # Run command CMD ["node", "index.js"] \ No newline at end of file diff --git a/federation/package.json b/federation/package.json index 057b22a98..fcf1365ab 100644 --- a/federation/package.json +++ b/federation/package.json @@ -8,7 +8,7 @@ "license": "Apache-2.0", "private": false, "scripts": { - "build": "ts-node ./esbuild.config.ts && mkdirp build/templates/ && ncp ../core/src/emails/templates build/templates", + "build": "ts-node ./esbuild.config.ts && mkdirp build/templates/ && ncp ../core/build/templates build/templates", "start": "cross-env TZ=UTC NODE_ENV=production node build/index.js", "start:bun": "cross-env TZ=UTC NODE_ENV=production bun build/index.js", "dev": "cross-env TZ=UTC nodemon -w src --ext ts,json,css -r tsconfig-paths/register src/index.ts", From e6a191838cc4e565bab4a21b7138d94c85117b63 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 24 Nov 2025 17:07:18 +0100 Subject: [PATCH 14/22] core: copy email templates into build --- core/package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/package.json b/core/package.json index e56a57f43..93c7431ad 100644 --- a/core/package.json +++ b/core/package.json @@ -15,7 +15,7 @@ "license": "Apache-2.0", "private": true, "scripts": { - "build": "bun esbuild.config.ts", + "build": "bun esbuild.config.ts && mkdirp build/templates/ && ncp src/emails/templates build/templates", "build:bun": "bun build src/index.ts --outdir=build --target=bun --packages=external", "test": "bun test", "test:debug": "bun test --inspect-brk", @@ -53,6 +53,8 @@ "dotenv": "^10.0.0", "graphql-request": "5.0.0", "jest": "27.2.4", + "mkdirp": "^3.0.1", + "ncp": "^2.0.0", "type-graphql": "^1.1.1", "typescript": "^4.9.5" }, From c5bca631451e4d06c0aa25d40c7f7c23b0c82161 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 24 Nov 2025 17:15:11 +0100 Subject: [PATCH 15/22] increase bun version --- .bun-version | 2 +- bun.lock | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.bun-version b/.bun-version index 589268e6f..785cda807 100644 --- a/.bun-version +++ b/.bun-version @@ -1 +1 @@ -1.3.0 \ No newline at end of file +1.3.3 \ No newline at end of file diff --git a/bun.lock b/bun.lock index 9b449354c..6dd175b61 100644 --- a/bun.lock +++ b/bun.lock @@ -210,6 +210,8 @@ "dotenv": "^10.0.0", "graphql-request": "5.0.0", "jest": "27.2.4", + "mkdirp": "^3.0.1", + "ncp": "^2.0.0", "type-graphql": "^1.1.1", "typescript": "^4.9.5", }, From 7e793f13f79278a35c22beaa0be1dc4df55f5771 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 24 Nov 2025 18:03:42 +0100 Subject: [PATCH 16/22] fix backend unit test errors --- .bun-version | 2 +- .../resolver/ContributionMessageResolver.test.ts | 4 +--- .../graphql/resolver/ContributionResolver.test.ts | 13 ++++--------- backend/src/graphql/resolver/UserResolver.test.ts | 10 +++++----- backend/test/testSetup.ts | 2 +- backend/tsconfig.json | 2 +- 6 files changed, 13 insertions(+), 20 deletions(-) diff --git a/.bun-version b/.bun-version index 785cda807..589268e6f 100644 --- a/.bun-version +++ b/.bun-version @@ -1 +1 @@ -1.3.3 \ No newline at end of file +1.3.0 \ No newline at end of file diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index d29bf5235..483747b90 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -34,9 +34,7 @@ jest.mock('core', () => { return { __esModule: true, ...originalModule, - sendAddedContributionMessageEmail: jest.fn((a) => - originalModule.sendAddedContributionMessageEmail(a), - ), + sendAddedContributionMessageEmail: jest.fn(), } }) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index fd31a5275..31d405942 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -58,15 +58,10 @@ jest.mock('core', () => { return { __esModule: true, ...originalModule, - sendContributionDeniedEmail: jest.fn((a) => - originalModule.sendContributionDeniedEmail(a), - ), - sendContributionConfirmedEmail: jest.fn((a) => - originalModule.sendContributionConfirmedEmail(a), - ), - sendContributionDeletedEmail: jest.fn((a) => - originalModule.sendContributionDeletedEmail(a), - ), + sendContributionDeniedEmail: jest.fn(), + sendContributionConfirmedEmail: jest.fn(), + sendContributionDeletedEmail: jest.fn(), + sendEmailTranslated: jest.fn(), } }) jest.mock('@/password/EncryptorUtils') diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index d5bad3bc8..0fd06c0d1 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -78,11 +78,10 @@ jest.mock('core', () => { return { __esModule: true, ...originalModule, - sendAccountActivationEmail: jest.fn((a) => originalModule.sendAccountActivationEmail(a)), - sendAccountMultiRegistrationEmail: jest.fn((a) => - originalModule.sendAccountMultiRegistrationEmail(a), - ), - sendResetPasswordEmail: jest.fn((a) => originalModule.sendResetPasswordEmail(a)), + sendAccountActivationEmail: jest.fn(), + sendAccountMultiRegistrationEmail: jest.fn(), + sendResetPasswordEmail: jest.fn(), + sendEmailTranslated: jest.fn(), } }) @@ -154,6 +153,7 @@ describe('UserResolver', () => { expect(result).toEqual( expect.objectContaining({ data: { createUser: { id: expect.any(Number) } } }), ) + }) describe('valid input data', () => { diff --git a/backend/test/testSetup.ts b/backend/test/testSetup.ts index 6a009e652..841046e0f 100644 --- a/backend/test/testSetup.ts +++ b/backend/test/testSetup.ts @@ -3,7 +3,7 @@ import { CONFIG } from '@/config' import { CONFIG as CORE_CONFIG } from 'core' import { getLogger, printLogs, clearLogs } from 'config-schema/test/testSetup' -CORE_CONFIG.EMAIL = true +CORE_CONFIG.EMAIL = false CORE_CONFIG.EMAIL_TEST_MODUS = false CONFIG.HUMHUB_ACTIVE = false CONFIG.GMS_ACTIVE = false diff --git a/backend/tsconfig.json b/backend/tsconfig.json index d330e2b81..b71bf1938 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -87,7 +87,7 @@ "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ }, "include": [ - "../core/src/types", + "../core/src/types" ], "ts-node": { "swc": true From 07e2ba0bc8dcc568fad5d84b44534e81930ea98e Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 26 Nov 2025 12:36:08 +0100 Subject: [PATCH 17/22] fix wrong code --- core/src/emails/locales/en.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/src/emails/locales/en.json b/core/src/emails/locales/en.json index b76bc1497..fb90a0334 100644 --- a/core/src/emails/locales/en.json +++ b/core/src/emails/locales/en.json @@ -1,7 +1,4 @@ { - "accountMultiRegistration": { - "contactSupport": "accountMultiRegistration.contactSupport" - }, "emails": { "accountActivation": { "activateAccount": "Activate account", From c60569e9007586e322cd14740fa93ce84e3e2d7b Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 26 Nov 2025 12:41:01 +0100 Subject: [PATCH 18/22] remove wrong line --- core/src/emails/sendEmailTranslated.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/emails/sendEmailTranslated.ts b/core/src/emails/sendEmailTranslated.ts index 30ae03112..2e5bcca93 100644 --- a/core/src/emails/sendEmailTranslated.ts +++ b/core/src/emails/sendEmailTranslated.ts @@ -113,7 +113,6 @@ export const sendEmailTranslated = async ({ }) .catch((error: unknown) => { logger.error('Error sending notification email', error) - throw error }) return resultSend From bad4b69868210397e8d1e4b335c7b02aee89c291 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 26 Nov 2025 12:49:41 +0100 Subject: [PATCH 19/22] fix types without changing logic --- core/src/emails/sendEmailTranslated.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/emails/sendEmailTranslated.ts b/core/src/emails/sendEmailTranslated.ts index 2e5bcca93..205ca07c6 100644 --- a/core/src/emails/sendEmailTranslated.ts +++ b/core/src/emails/sendEmailTranslated.ts @@ -25,7 +25,7 @@ export const sendEmailTranslated = async ({ } template: string locals: Record -}): Promise | boolean | null> => { +}): Promise | boolean | null | Error> => { // TODO: test the calling order of 'i18n.setLocale' for example: language of logging 'en', language of email receiver 'es', reset language of current user 'de' if (!CONFIG.EMAIL) { @@ -113,6 +113,7 @@ export const sendEmailTranslated = async ({ }) .catch((error: unknown) => { logger.error('Error sending notification email', error) + return error }) return resultSend From 295eb525f0b7a247b21bd0739a212e1db3aa53b8 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 26 Nov 2025 12:51:57 +0100 Subject: [PATCH 20/22] adapt to change in sendEmailTranslated --- core/src/emails/sendEmailVariants.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/src/emails/sendEmailVariants.ts b/core/src/emails/sendEmailVariants.ts index da67beefb..8a4b6f9af 100644 --- a/core/src/emails/sendEmailVariants.ts +++ b/core/src/emails/sendEmailVariants.ts @@ -31,7 +31,7 @@ export const sendAddedContributionMessageEmail = ( data: EmailCommonData & ContributionEmailCommonData & { message: string }, -): Promise | boolean | null> => { +): Promise | boolean | null | Error> => { return sendEmailTranslated({ receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>`, @@ -48,7 +48,7 @@ export const sendAccountActivationEmail = (data: EmailCommonData & { activationLink: string timeDurationObject: Record logoUrl?: string | null -}): Promise | boolean | null> => { +}): Promise | boolean | null | Error> => { return sendEmailTranslated({ receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, template: 'accountActivation', @@ -59,7 +59,7 @@ export const sendAccountActivationEmail = (data: EmailCommonData & { }) } -export const sendAccountMultiRegistrationEmail = (data: EmailCommonData): Promise | boolean | null> => { +export const sendAccountMultiRegistrationEmail = (data: EmailCommonData): Promise | boolean | null | Error> => { return sendEmailTranslated({ receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, template: 'accountMultiRegistration', @@ -74,7 +74,7 @@ export const sendContributionConfirmedEmail = ( data: EmailCommonData & ContributionEmailCommonData & { contributionAmount: Decimal }, -): Promise | boolean | null> => { +): Promise | boolean | null | Error> => { return sendEmailTranslated({ receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, template: 'contributionConfirmed', @@ -90,7 +90,7 @@ export const sendContributionChangedByModeratorEmail = ( data: EmailCommonData & ContributionEmailCommonData & { contributionMemoUpdated: string }, -): Promise | boolean | null> => { +): Promise | boolean | null | Error> => { return sendEmailTranslated({ receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, template: 'contributionChangedByModerator', @@ -104,7 +104,7 @@ export const sendContributionChangedByModeratorEmail = ( export const sendContributionDeletedEmail = ( data: EmailCommonData & ContributionEmailCommonData, -): Promise | boolean | null> => { +): Promise | boolean | null | Error> => { return sendEmailTranslated({ receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, template: 'contributionDeleted', @@ -117,7 +117,7 @@ export const sendContributionDeletedEmail = ( export const sendContributionDeniedEmail = ( data: EmailCommonData & ContributionEmailCommonData, -): Promise | boolean | null> => { +): Promise | boolean | null | Error> => { return sendEmailTranslated({ receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, template: 'contributionDenied', @@ -131,7 +131,7 @@ export const sendContributionDeniedEmail = ( export const sendResetPasswordEmail = (data: EmailCommonData & { resetLink: string timeDurationObject: Record -}): Promise | boolean | null> => { +}): Promise | boolean | null | Error> => { return sendEmailTranslated({ receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, template: 'resetPassword', @@ -148,7 +148,7 @@ export const sendTransactionLinkRedeemedEmail = (data: EmailCommonData & { senderEmail: string transactionMemo: string transactionAmount: Decimal -}): Promise | boolean | null> => { +}): Promise | boolean | null | Error> => { return sendEmailTranslated({ receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, template: 'transactionLinkRedeemed', @@ -166,7 +166,7 @@ export const sendTransactionReceivedEmail = (data: EmailCommonData & { senderEmail: string memo: string transactionAmount: Decimal -}): Promise | boolean | null> => { +}): Promise | boolean | null | Error> => { return sendEmailTranslated({ receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, template: 'transactionReceived', From 8e990314a122cc1de8a790896c48d7781232e621 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 26 Nov 2025 14:04:26 +0100 Subject: [PATCH 21/22] fixed for future use of biome --- backend/src/apis/openai/OpenaiClient.ts | 1 + backend/src/graphql/resolver/TransactionResolver.test.ts | 1 - backend/src/seeds/factory/user.ts | 1 - config-schema/src/validate.ts | 1 + core/src/graphql/logic/storeLinkAsRedeemed.ts | 8 ++++++-- core/src/types/images.d.ts | 1 + database/src/queries/communityHandshakes.test.ts | 1 - .../graphql/api/1_0/resolver/AuthenticationResolver.ts | 2 ++ federation/src/server/cors.ts | 5 ++--- shared/src/helper/onShutdown.ts | 3 ++- 10 files changed, 15 insertions(+), 9 deletions(-) diff --git a/backend/src/apis/openai/OpenaiClient.ts b/backend/src/apis/openai/OpenaiClient.ts index eb37a04f8..5655020aa 100644 --- a/backend/src/apis/openai/OpenaiClient.ts +++ b/backend/src/apis/openai/OpenaiClient.ts @@ -92,6 +92,7 @@ export class OpenaiClient { if (openaiThreadEntity.updatedAt < new Date(Date.now() - OPENAI_AI_THREAD_DEFAULT_TIMEOUT_DAYS * 24 * 60 * 60 * 1000)) { logger.info(`Openai thread for user: ${user.id} is older than ${OPENAI_AI_THREAD_DEFAULT_TIMEOUT_DAYS} days, deleting...`) // let run async, because it could need some time, but we don't need to wait, because we create a new one nevertheless + // biome-ignore lint/complexity/noVoid: start it intentionally async without waiting for result void this.deleteThread(openaiThreadEntity.id) return [] } diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index 774502b28..636c4d63f 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -73,7 +73,6 @@ let peter: User let homeCom: DbCommunity let foreignCom: DbCommunity -let fedForeignCom: DbFederatedCommunity describe('send coins', () => { beforeAll(async () => { diff --git a/backend/src/seeds/factory/user.ts b/backend/src/seeds/factory/user.ts index 44db02c8f..36d9adfeb 100644 --- a/backend/src/seeds/factory/user.ts +++ b/backend/src/seeds/factory/user.ts @@ -18,7 +18,6 @@ export const userFactory = async ( // console.log('call createUser with', JSON.stringify(user, null, 2)) const response = await mutate({ mutation: createUser, variables: user }) if (!response?.data?.createUser) { - // biome-ignore lint/suspicious/noConsole: will be used in tests where logging is mocked // console.log(JSON.stringify(response, null, 2)) throw new Error('createUser mutation returned unexpected response') } diff --git a/config-schema/src/validate.ts b/config-schema/src/validate.ts index 83cf097b3..17ccea483 100644 --- a/config-schema/src/validate.ts +++ b/config-schema/src/validate.ts @@ -31,6 +31,7 @@ export function validate(schema: ObjectSchema, data: any) { ) } } catch (e) { + // biome-ignore lint/suspicious/noConsole: schema validation may be run before logger is initialized console.error('Error getting description for key ' + key + ': ' + e) throw e } diff --git a/core/src/graphql/logic/storeLinkAsRedeemed.ts b/core/src/graphql/logic/storeLinkAsRedeemed.ts index cccc97d50..efc3589de 100644 --- a/core/src/graphql/logic/storeLinkAsRedeemed.ts +++ b/core/src/graphql/logic/storeLinkAsRedeemed.ts @@ -1,4 +1,8 @@ -import { TransactionLink as DbTransactionLink, User as DbUser } from "database"; +import { TransactionLink as DbTransactionLink, User as DbUser } from 'database' +import { getLogger } from 'log4js' +import { LOG4JS_BASE_CATEGORY_NAME } from '../../config/const' + +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.logic.storeLinkAsRedeemed`) export async function storeLinkAsRedeemed( dbTransactionLink: DbTransactionLink, @@ -11,7 +15,7 @@ export async function storeLinkAsRedeemed( await DbTransactionLink.save(dbTransactionLink) return true } catch (err) { - console.error('error in storeLinkAsRedeemed;', err) + logger.error('error: ', err) return false } } \ No newline at end of file diff --git a/core/src/types/images.d.ts b/core/src/types/images.d.ts index ef7006d32..91c6fd6f9 100644 --- a/core/src/types/images.d.ts +++ b/core/src/types/images.d.ts @@ -1,3 +1,4 @@ +// biome-ignore lint/style/noDefaultExport: Asset modules use default export by convention declare module '*.jpg' { const value: string export default value diff --git a/database/src/queries/communityHandshakes.test.ts b/database/src/queries/communityHandshakes.test.ts index 09937cc66..c1b665bab 100644 --- a/database/src/queries/communityHandshakes.test.ts +++ b/database/src/queries/communityHandshakes.test.ts @@ -58,7 +58,6 @@ describe('communityHandshakes', () => { communityHandshakeState!.status = CommunityHandshakeStateType.START_OPEN_CONNECTION_CALLBACK await communityHandshakeState!.save() const communityHandshakeState2 = await findPendingCommunityHandshake(publicKey, '1_0') - const states = await DbCommunityHandshakeState.find() expect(communityHandshakeState2).toBeDefined() expect(communityHandshakeState2).toMatchObject({ publicKey: publicKey.asBuffer(), diff --git a/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts b/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts index d22c816ff..3bee91e3e 100644 --- a/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts @@ -62,6 +62,7 @@ export class AuthenticationResolver { // no await to respond immediately and invoke callback-request asynchronously // important: startOpenConnectionCallback must catch all exceptions them self, or server will crash! + // biome-ignore lint/complexity/noVoid: start it intentionally async without waiting for result void startOpenConnectionCallback(args.handshakeID, argsPublicKey, fedComA) methodLogger.debug('openConnection() successfully initiated callback and returns true immediately...') return true @@ -96,6 +97,7 @@ export class AuthenticationResolver { `found fedComB and start authentication: ${fedComB.endPoint}${fedComB.apiVersion}`, ) // no await to respond immediately and invoke authenticate-request asynchronously + // biome-ignore lint/complexity/noVoid: start it intentionally async without waiting for result void startAuthentication(args.handshakeID, openConnectionCallbackJwtPayload.oneTimeCode, fedComB) // methodLogger.debug('openConnectionCallback() successfully initiated authentication and returns true immediately...') return true diff --git a/federation/src/server/cors.ts b/federation/src/server/cors.ts index 873d6a2eb..95663695d 100644 --- a/federation/src/server/cors.ts +++ b/federation/src/server/cors.ts @@ -1,9 +1,8 @@ -import cors from 'cors' +import corsLib from 'cors' const corsOptions = { origin: '*', exposedHeaders: ['token'], } -// biome-ignore lint/style/noDefaultExport: -export default cors(corsOptions) +export const cors = corsLib(corsOptions) diff --git a/shared/src/helper/onShutdown.ts b/shared/src/helper/onShutdown.ts index a27c931a6..ea24bf2fc 100644 --- a/shared/src/helper/onShutdown.ts +++ b/shared/src/helper/onShutdown.ts @@ -1,4 +1,3 @@ -import { Logger } from 'log4js' import colors from 'yoctocolors-cjs' export enum ShutdownReason { @@ -44,8 +43,10 @@ export function onShutdown(shutdownHandler: (reason: ShutdownReason, error?: Err } export function printServerCrashAsciiArt(msg1: string, msg2: string, msg3: string) { + // biome-ignore-start lint/suspicious/noConsole: Server Crash Ascii Art is for console and stdout console.error(colors.redBright(` /\\_/\\ ${msg1}`)) console.error(colors.redBright(`( x.x ) ${msg2}`)) console.error(colors.redBright(` > < ${msg3}`)) console.error(colors.redBright('')) + // biome-ignore-end lint/suspicious/noConsole: Server Crash Ascii Art is for console and stdout } \ No newline at end of file From bcaf1cdbe2caeb3e802a9464abb218e39bcdfd22 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 26 Nov 2025 14:07:13 +0100 Subject: [PATCH 22/22] adapt to logic change --- federation/src/server/createServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/federation/src/server/createServer.ts b/federation/src/server/createServer.ts index 015fbba48..d8cb0f473 100644 --- a/federation/src/server/createServer.ts +++ b/federation/src/server/createServer.ts @@ -4,7 +4,7 @@ import { ApolloServer } from 'apollo-server-express' import express, { Express } from 'express' // server -import cors from './cors' +import { cors } from './cors' // import serverContext from './context' import { plugins } from './plugins'