diff --git a/.vscode/launch.json b/.vscode/launch.json index ef271c3c99..86cb60a23e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -95,7 +95,6 @@ "SECRET": "secret", "METRICS_CONSOLE": "true", "ACCOUNTS_URL": "http://localhost:3000", - "TRANSACTOR_URL": "ws://localhost:3333", "UPLOAD_URL": "/files", "MONGO_URL": "mongodb://localhost:27017", "MINIO_ACCESS_KEY": "minioadmin", @@ -124,7 +123,7 @@ "request": "launch", "args": ["src/index.ts", "gen-recruit", "ws1", "20"], "env": { - "TRANSACTOR_URL": "ws://localhost:3333", + "ACCOUNTS_URL": "ws://localhost:3000", "MINIO_ACCESS_KEY": "minioadmin", "MINIO_SECRET_KEY": "minioadmin", "MINIO_ENDPOINT": "localhost" @@ -144,7 +143,7 @@ "MINIO_SECRET_KEY": "minioadmin", "MINIO_ENDPOINT": "localhost", "MONGO_URL": "mongodb://localhost:27017", - "TRANSACTOR_URL": "ws://localhost:3333", + "ACCOUNTS_URL": "localhost:3000", "TELEGRAM_DATABASE": "telegram-service", "ELASTIC_URL": "http://localhost:9200" }, @@ -164,7 +163,7 @@ "MINIO_SECRET_KEY": "minioadmin", "MINIO_ENDPOINT": "localhost", "MONGO_URL": "mongodb://localhost:27017", - "TRANSACTOR_URL": "ws://localhost:3333", + "ACCOUNTS_URL": "http://localhost:3000", "TELEGRAM_DATABASE": "telegram-service", "ELASTIC_URL": "http://localhost:9200", "REKONI_URL": "http://localhost:4004" @@ -184,7 +183,6 @@ "MINIO_ACCESS_KEY": "minioadmin", "MINIO_SECRET_KEY": "minioadmin", "SECRET": "secret", - "TRANSACTOR_URL": "ws://localhost:3333", "BUCKET_NAME":"test_backups", "INTERVAL":"30" }, diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index c2d2e0f003..b5d369d193 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -25,16 +25,16 @@ dependencies: version: 8.4.1 '@hocuspocus/provider': specifier: ^2.11.0 - version: 2.11.2(bufferutil@4.0.8)(utf-8-validate@6.0.3)(y-protocols@1.0.6)(yjs@13.6.12) + version: 2.11.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6)(yjs@13.6.12) '@hocuspocus/server': specifier: ^2.9.0 - version: 2.11.2(bufferutil@4.0.8)(utf-8-validate@6.0.3)(y-protocols@1.0.6)(yjs@13.6.12) + version: 2.11.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6)(yjs@13.6.12) '@hocuspocus/transformer': specifier: ^2.9.0 version: 2.11.2(@tiptap/pm@2.2.4)(y-prosemirror@1.2.2)(yjs@13.6.12) '@koa/cors': - specifier: ^3.1.0 - version: 3.4.3 + specifier: ^5.0.0 + version: 5.0.0 '@livekit/krisp-noise-filter': specifier: ~0.2.1 version: 0.2.2(livekit-client@2.2.0) @@ -46,7 +46,7 @@ dependencies: version: 1.41.2 '@rush-temp/account': specifier: file:./projects/account.tgz - version: file:projects/account.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(ts-node@10.9.2)(utf-8-validate@6.0.3) + version: file:projects/account.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(ts-node@10.9.2)(utf-8-validate@6.0.4) '@rush-temp/account-service': specifier: file:./projects/account-service.tgz version: file:projects/account-service.tgz @@ -127,7 +127,7 @@ dependencies: version: file:projects/collaboration.tgz(esbuild@0.20.1)(ts-node@10.9.2) '@rush-temp/collaborator': specifier: file:./projects/collaborator.tgz - version: file:projects/collaborator.tgz(@tiptap/pm@2.2.4)(bufferutil@4.0.8)(prosemirror-model@1.19.4)(utf-8-validate@6.0.3)(y-protocols@1.0.6) + version: file:projects/collaborator.tgz(@tiptap/pm@2.2.4)(bufferutil@4.0.8)(prosemirror-model@1.19.4)(utf-8-validate@6.0.4)(y-protocols@1.0.6) '@rush-temp/collaborator-client': specifier: file:./projects/collaborator-client.tgz version: file:projects/collaborator-client.tgz(ts-node@10.9.2) @@ -532,7 +532,7 @@ dependencies: version: file:projects/print-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2) '@rush-temp/prod': specifier: file:./projects/prod.tgz - version: file:projects/prod.tgz(bufferutil@4.0.8)(sass@1.71.1)(ts-node@10.9.2)(utf-8-validate@6.0.3) + version: file:projects/prod.tgz(bufferutil@4.0.8)(sass@1.71.1)(ts-node@10.9.2)(utf-8-validate@6.0.4) '@rush-temp/products': specifier: file:./projects/products.tgz version: file:projects/products.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) @@ -617,6 +617,9 @@ dependencies: '@rush-temp/server-chunter-resources': specifier: file:./projects/server-chunter-resources.tgz version: file:projects/server-chunter-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) + '@rush-temp/server-client': + specifier: file:./projects/server-client.tgz + version: file:projects/server-client.tgz(bufferutil@4.0.8)(esbuild@0.20.1)(ts-node@10.9.2)(utf-8-validate@6.0.4) '@rush-temp/server-collaboration': specifier: file:./projects/server-collaboration.tgz version: file:projects/server-collaboration.tgz(esbuild@0.20.1)(ts-node@10.9.2) @@ -751,7 +754,7 @@ dependencies: version: file:projects/server-token.tgz(esbuild@0.20.1)(ts-node@10.9.2) '@rush-temp/server-tool': specifier: file:./projects/server-tool.tgz - version: file:projects/server-tool.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(ts-node@10.9.2)(utf-8-validate@6.0.3) + version: file:projects/server-tool.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(ts-node@10.9.2)(utf-8-validate@6.0.4) '@rush-temp/server-tracker': specifier: file:./projects/server-tracker.tgz version: file:projects/server-tracker.tgz(esbuild@0.20.1)(ts-node@10.9.2) @@ -790,7 +793,7 @@ dependencies: version: file:projects/storage.tgz(esbuild@0.20.1)(ts-node@10.9.2) '@rush-temp/storybook': specifier: file:./projects/storybook.tgz - version: file:projects/storybook.tgz(bufferutil@4.0.8)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(svelte-loader@3.2.0)(svelte@4.2.12)(typescript@5.3.3)(utf-8-validate@6.0.3)(webpack-cli@5.1.4)(webpack@5.90.3) + version: file:projects/storybook.tgz(bufferutil@4.0.8)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(svelte-loader@3.2.0)(svelte@4.2.12)(typescript@5.3.3)(utf-8-validate@6.0.4)(webpack-cli@5.1.4)(webpack@5.90.3) '@rush-temp/support': specifier: file:./projects/support.tgz version: file:projects/support.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) @@ -841,7 +844,7 @@ dependencies: version: file:projects/tests-sanity.tgz '@rush-temp/text': specifier: file:./projects/text.tgz - version: file:projects/text.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(ts-node@10.9.2)(utf-8-validate@6.0.3)(y-protocols@1.0.6) + version: file:projects/text.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(ts-node@10.9.2)(utf-8-validate@6.0.4)(y-protocols@1.0.6) '@rush-temp/text-editor': specifier: file:./projects/text-editor.tgz version: file:projects/text-editor.tgz(@types/node@20.11.19)(esbuild@0.20.1)(svelte@4.2.12)(ts-node@10.9.2) @@ -850,7 +853,7 @@ dependencies: version: file:projects/text-editor-assets.tgz(esbuild@0.20.1)(ts-node@10.9.2) '@rush-temp/text-editor-resources': specifier: file:./projects/text-editor-resources.tgz - version: file:projects/text-editor-resources.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(prosemirror-model@1.19.4)(ts-node@10.9.2)(utf-8-validate@6.0.3) + version: file:projects/text-editor-resources.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(prosemirror-model@1.19.4)(ts-node@10.9.2)(utf-8-validate@6.0.4) '@rush-temp/theme': specifier: file:./projects/theme.tgz version: file:projects/theme.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2) @@ -865,7 +868,7 @@ dependencies: version: file:projects/time-resources.tgz(@tiptap/core@2.2.4)(@tiptap/pm@2.2.4)(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2) '@rush-temp/tool': specifier: file:./projects/tool.tgz - version: file:projects/tool.tgz(bufferutil@4.0.8)(utf-8-validate@6.0.3) + version: file:projects/tool.tgz(bufferutil@4.0.8)(utf-8-validate@6.0.4) '@rush-temp/tracker': specifier: file:./projects/tracker.tgz version: file:projects/tracker.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) @@ -1056,23 +1059,23 @@ dependencies: specifier: ^4.0.9 version: 4.0.9 '@types/koa': - specifier: 2.14.0 - version: 2.14.0 + specifier: ^2.15.0 + version: 2.15.0 '@types/koa-bodyparser': - specifier: ^4.3.3 + specifier: ^4.3.12 version: 4.3.12 '@types/koa-passport': - specifier: ^6.0.0 + specifier: ^6.0.3 version: 6.0.3 '@types/koa-router': - specifier: ^7.4.4 + specifier: ^7.4.8 version: 7.4.8 '@types/koa-session': specifier: ^6.4.5 version: 6.4.5 '@types/koa__cors': - specifier: ^3.0.3 - version: 3.3.1 + specifier: ^5.0.0 + version: 5.0.0 '@types/markdown-it': specifier: ~13.0.0 version: 13.0.8 @@ -1122,8 +1125,8 @@ dependencies: specifier: ~3.6.3 version: 3.6.3 '@types/ws': - specifier: ^8.5.3 - version: 8.5.10 + specifier: ^8.5.11 + version: 8.5.11 '@typescript-eslint/eslint-plugin': specifier: ^6.11.0 version: 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) @@ -1179,7 +1182,7 @@ dependencies: specifier: ^6.8.0 version: 6.8.0 bufferutil: - specifier: ^4.0.7 + specifier: ^4.0.8 version: 4.0.8 commander: specifier: ^8.1.0 @@ -1275,11 +1278,11 @@ dependencies: specifier: ^2.35.1 version: 2.35.1(eslint@8.56.0)(svelte@4.2.12)(ts-node@10.9.2) express: - specifier: ^4.18.3 - version: 4.18.3 + specifier: ^4.19.2 + version: 4.19.2 express-fileupload: - specifier: ^1.4.3 - version: 1.5.0 + specifier: ^1.5.1 + version: 1.5.1 express-static-gzip: specifier: ^2.1.7 version: 2.1.7 @@ -1315,7 +1318,7 @@ dependencies: version: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) jest-environment-jsdom: specifier: 29.7.0 - version: 29.7.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + version: 29.7.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) js-yaml: specifier: ^4.1.0 version: 4.1.0 @@ -1323,13 +1326,13 @@ dependencies: specifier: ^0.5.6 version: 0.5.6 koa: - specifier: ^2.13.1 - version: 2.15.0 + specifier: ^2.15.3 + version: 2.15.3 koa-bodyparser: - specifier: ^4.3.0 + specifier: ^4.4.1 version: 4.4.1 koa-passport: - specifier: ~6.0.0 + specifier: ^6.0.0 version: 6.0.0 koa-router: specifier: ^12.0.1 @@ -1441,7 +1444,7 @@ dependencies: version: 2.0.5 storybook: specifier: ^7.0.6 - version: 7.6.17(bufferutil@4.0.8)(utf-8-validate@6.0.3) + version: 7.6.17(bufferutil@4.0.8)(utf-8-validate@6.0.4) storybook-addon-themes: specifier: ^6.1.0 version: 6.1.0(react-dom@18.2.0)(react@18.2.0)(svelte@4.2.12) @@ -1491,8 +1494,8 @@ dependencies: specifier: ~4.1.1 version: 4.1.1(file-loader@6.2.0)(webpack@5.90.3) utf-8-validate: - specifier: ^6.0.3 - version: 6.0.3 + specifier: ^6.0.4 + version: 6.0.4 uuid: specifier: ^8.3.2 version: 8.3.2 @@ -1504,16 +1507,16 @@ dependencies: version: 5.90.3(esbuild@0.20.1)(webpack-cli@5.1.4) webpack-bundle-analyzer: specifier: ^4.7.0 - version: 4.10.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) + version: 4.10.1(bufferutil@4.0.8)(utf-8-validate@6.0.4) webpack-cli: specifier: ^5.0.1 version: 5.1.4(webpack-bundle-analyzer@4.10.1)(webpack-dev-server@4.15.1)(webpack@5.90.3) webpack-dev-server: specifier: ^4.11.1 - version: 4.15.1(bufferutil@4.0.8)(utf-8-validate@6.0.3)(webpack-cli@5.1.4)(webpack@5.90.3) + version: 4.15.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)(webpack-cli@5.1.4)(webpack@5.90.3) ws: - specifier: ^8.16.0 - version: 8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + specifier: ^8.18.0 + version: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) y-prosemirror: specifier: ^1.2.1 version: 1.2.2(prosemirror-model@1.19.4)(y-protocols@1.0.6)(yjs@13.6.12) @@ -4316,7 +4319,7 @@ packages: lib0: 0.2.89 dev: false - /@hocuspocus/provider@2.11.2(bufferutil@4.0.8)(utf-8-validate@6.0.3)(y-protocols@1.0.6)(yjs@13.6.12): + /@hocuspocus/provider@2.11.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6)(yjs@13.6.12): resolution: {integrity: sha512-h6/2KZC8Z3Wz+rP17WQ5eqxdgtsq5JZ4iC72WSXdIOJn3RO4sv63ZvT+EWc4aA1YM7UVDBYVjdOjMtEYZF0Flg==} peerDependencies: y-protocols: ^1.0.6 @@ -4325,7 +4328,7 @@ packages: '@hocuspocus/common': 2.11.2 '@lifeomic/attempt': 3.0.3 lib0: 0.2.89 - ws: 8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) y-protocols: 1.0.6(yjs@13.6.12) yjs: 13.6.12 transitivePeerDependencies: @@ -4333,7 +4336,7 @@ packages: - utf-8-validate dev: false - /@hocuspocus/server@2.11.2(bufferutil@4.0.8)(utf-8-validate@6.0.3)(y-protocols@1.0.6)(yjs@13.6.12): + /@hocuspocus/server@2.11.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6)(yjs@13.6.12): resolution: {integrity: sha512-/djaNUSS9vYmz5H/bformB9BXKVhSXS10WIURT1OqFLnN0dZnOB0gxL/IyGCZgXCNyiD8U03n7FIgad9MckV8g==} peerDependencies: y-protocols: ^1.0.6 @@ -4344,7 +4347,7 @@ packages: kleur: 4.1.5 lib0: 0.2.89 uuid: 9.0.1 - ws: 8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) y-protocols: 1.0.6(yjs@13.6.12) yjs: 13.6.12 transitivePeerDependencies: @@ -4702,9 +4705,9 @@ packages: resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} dev: false - /@koa/cors@3.4.3: - resolution: {integrity: sha512-WPXQUaAeAMVaLTEFpoq3T2O1C+FstkjJnDQqy95Ck1UdILajsRhu6mhJ8H2f4NFPRBoCNN+qywTJfq/gGki5mw==} - engines: {node: '>= 8.0.0'} + /@koa/cors@5.0.0: + resolution: {integrity: sha512-x/iUDjcS90W69PryLDIMgFyV21YLTnG9zOpPXS7Bkt2b8AsY3zZsIpOLBkYr9fBcF3HbkKaER5hOBZLfpLgYNw==} + engines: {node: '>= 14.0.0'} dependencies: vary: 1.1.2 dev: false @@ -6218,7 +6221,7 @@ packages: ejs: 3.1.9 esbuild: 0.18.20 esbuild-plugin-alias: 0.2.1 - express: 4.18.3 + express: 4.19.2 find-cache-dir: 3.3.2 fs-extra: 11.2.0 process: 0.11.10 @@ -6255,7 +6258,7 @@ packages: constants-browserify: 1.0.0 css-loader: 6.10.0(webpack@5.90.3) es-module-lexer: 1.4.1 - express: 4.18.3 + express: 4.19.2 fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.3.3)(webpack@5.90.3) fs-extra: 11.2.0 html-webpack-plugin: 5.6.0(webpack@5.90.3) @@ -6304,7 +6307,7 @@ packages: tiny-invariant: 1.3.1 dev: false - /@storybook/cli@7.6.17(bufferutil@4.0.8)(utf-8-validate@6.0.3): + /@storybook/cli@7.6.17(bufferutil@4.0.8)(utf-8-validate@6.0.4): resolution: {integrity: sha512-1sCo+nCqyR+nKfTcEidVu8XzNoECC7Y1l+uW38/r7s2f/TdDorXaIGAVrpjbSaXSoQpx5DxYJVaKCcQuOgqwcA==} hasBin: true dependencies: @@ -6315,7 +6318,7 @@ packages: '@storybook/codemod': 7.6.17 '@storybook/core-common': 7.6.17 '@storybook/core-events': 7.6.17 - '@storybook/core-server': 7.6.17(bufferutil@4.0.8)(utf-8-validate@6.0.3) + '@storybook/core-server': 7.6.17(bufferutil@4.0.8)(utf-8-validate@6.0.4) '@storybook/csf-tools': 7.6.17 '@storybook/node-logger': 7.6.17 '@storybook/telemetry': 7.6.17 @@ -6329,7 +6332,7 @@ packages: detect-indent: 6.1.0 envinfo: 7.11.1 execa: 5.1.1 - express: 4.18.3 + express: 4.19.2 find-up: 5.0.0 fs-extra: 11.2.0 get-npm-tarball-url: 2.1.0 @@ -6480,7 +6483,7 @@ packages: ts-dedent: 2.2.0 dev: false - /@storybook/core-server@7.6.17(bufferutil@4.0.8)(utf-8-validate@6.0.3): + /@storybook/core-server@7.6.17(bufferutil@4.0.8)(utf-8-validate@6.0.4): resolution: {integrity: sha512-KWGhTTaL1Q14FolcoKKZgytlPJUbH6sbJ1Ptj/84EYWFewcnEgVs0Zlnh1VStRZg+Rd1WC1V4yVd/bbDzxrvQA==} dependencies: '@aw-web-design/x-default-browser': 1.4.126 @@ -6507,7 +6510,7 @@ packages: cli-table3: 0.6.3 compression: 1.7.4 detect-port: 1.5.1 - express: 4.18.3 + express: 4.19.2 fs-extra: 11.2.0 globby: 11.1.0 ip: 2.0.1 @@ -6523,7 +6526,7 @@ packages: util: 0.12.5 util-deprecate: 1.0.2 watchpack: 2.4.0 - ws: 8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) transitivePeerDependencies: - bufferutil - encoding @@ -7699,13 +7702,13 @@ packages: /@types/koa-bodyparser@4.3.12: resolution: {integrity: sha512-hKMmRMVP889gPIdLZmmtou/BijaU1tHPyMNmcK7FAHAdATnRcGQQy78EqTTxLH1D4FTsrxIzklAQCso9oGoebQ==} dependencies: - '@types/koa': 2.14.0 + '@types/koa': 2.15.0 dev: false /@types/koa-compose@3.2.8: resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==} dependencies: - '@types/koa': 2.14.0 + '@types/koa': 2.15.0 dev: false /@types/koa-morgan@1.0.8: @@ -7718,21 +7721,21 @@ packages: /@types/koa-passport@6.0.3: resolution: {integrity: sha512-9iV8aiSICfBZpqkkzUshqxN2wTuoj4HGRRH0Idv3lHhXV9rZRCOyDdJSg+vZ6RHSQg0ZeBeAdIL4r/l8bR4SPA==} dependencies: - '@types/koa': 2.14.0 + '@types/koa': 2.15.0 '@types/passport': 1.0.16 dev: false /@types/koa-router@7.4.8: resolution: {integrity: sha512-SkWlv4F9f+l3WqYNQHnWjYnyTxYthqt8W9az2RTdQW7Ay8bc00iRZcrb8MC75iEfPqnGcg2csEl8tTG1NQPD4A==} dependencies: - '@types/koa': 2.14.0 + '@types/koa': 2.15.0 dev: false /@types/koa-session@6.4.5: resolution: {integrity: sha512-Vc6+fslnPuMH2v9y80WYeo39UMo8mweuNNthKCwYU2ZE6l5vnRrzRU3BRvexKwsoI5sxsRl5CxDsBlLI8kY/XA==} dependencies: '@types/cookies': 0.9.0 - '@types/koa': 2.14.0 + '@types/koa': 2.15.0 dev: false /@types/koa@2.14.0: @@ -7748,10 +7751,23 @@ packages: '@types/node': 20.11.19 dev: false - /@types/koa__cors@3.3.1: - resolution: {integrity: sha512-aFGYhTFW7651KhmZZ05VG0QZJre7QxBxDj2LF1lf6GA/wSXEfKVAJxiQQWzRV4ZoMzQIO8vJBXKsUcRuvYK9qw==} + /@types/koa@2.15.0: + resolution: {integrity: sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==} dependencies: - '@types/koa': 2.14.0 + '@types/accepts': 1.3.7 + '@types/content-disposition': 0.5.8 + '@types/cookies': 0.9.0 + '@types/http-assert': 1.5.5 + '@types/http-errors': 2.0.4 + '@types/keygrip': 1.0.6 + '@types/koa-compose': 3.2.8 + '@types/node': 20.11.19 + dev: false + + /@types/koa__cors@5.0.0: + resolution: {integrity: sha512-LCk/n25Obq5qlernGOK/2LUwa/2YJb2lxHUkkvYFDOpLXlVI6tKcdfCHRBQnOY4LwH6el5WOLs6PD/a8Uzau6g==} + dependencies: + '@types/koa': 2.15.0 dev: false /@types/linkify-it@3.0.5: @@ -8035,8 +8051,8 @@ packages: '@types/webidl-conversions': 7.0.3 dev: false - /@types/ws@8.5.10: - resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} + /@types/ws@8.5.11: + resolution: {integrity: sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==} dependencies: '@types/node': 20.11.19 dev: false @@ -8506,7 +8522,7 @@ packages: dependencies: webpack: 5.90.3(esbuild@0.20.1)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.1)(webpack-dev-server@4.15.1)(webpack@5.90.3) - webpack-dev-server: 4.15.1(bufferutil@4.0.8)(utf-8-validate@6.0.3)(webpack-cli@5.1.4)(webpack@5.90.3) + webpack-dev-server: 4.15.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)(webpack-cli@5.1.4)(webpack@5.90.3) dev: false /@xtuc/ieee754@1.2.0: @@ -9833,8 +9849,8 @@ packages: engines: {node: '>= 0.6'} dev: false - /cookie@0.5.0: - resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + /cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} dev: false @@ -11429,8 +11445,8 @@ packages: jest-util: 29.7.0 dev: false - /express-fileupload@1.5.0: - resolution: {integrity: sha512-jSW3w9evqM37VWkEPkL2Ck5wUo2a8qa03MH+Ou/0ZSTpNlQFBvSLjU12k2nYcHhaMPv4JVvv6+Ac1OuLgUZb7w==} + /express-fileupload@1.5.1: + resolution: {integrity: sha512-LsYG1ALXEB7vlmjuSw8ABeOctMp8a31aUC5ZF55zuz7O2jLFnmJYrCv10py357ky48aEoBQ/9bVXgFynjvaPmA==} engines: {node: '>=12.0.0'} dependencies: busboy: 1.6.0 @@ -11442,8 +11458,8 @@ packages: serve-static: 1.15.0 dev: false - /express@4.18.3: - resolution: {integrity: sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==} + /express@4.19.2: + resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} engines: {node: '>= 0.10.0'} dependencies: accepts: 1.3.8 @@ -11451,7 +11467,7 @@ packages: body-parser: 1.20.2 content-disposition: 0.5.4 content-type: 1.0.5 - cookie: 0.5.0 + cookie: 0.6.0 cookie-signature: 1.0.6 debug: 2.6.9 depd: 2.0.0 @@ -13189,7 +13205,7 @@ packages: pretty-format: 29.7.0 dev: false - /jest-environment-jsdom@29.7.0(bufferutil@4.0.8)(utf-8-validate@6.0.3): + /jest-environment-jsdom@29.7.0(bufferutil@4.0.8)(utf-8-validate@6.0.4): resolution: {integrity: sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -13205,7 +13221,7 @@ packages: '@types/node': 20.11.19 jest-mock: 29.7.0 jest-util: 29.7.0 - jsdom: 20.0.3(bufferutil@4.0.8)(utf-8-validate@6.0.3) + jsdom: 20.0.3(bufferutil@4.0.8)(utf-8-validate@6.0.4) transitivePeerDependencies: - bufferutil - supports-color @@ -13563,7 +13579,7 @@ packages: - supports-color dev: false - /jsdom@20.0.3(bufferutil@4.0.8)(utf-8-validate@6.0.3): + /jsdom@20.0.3(bufferutil@4.0.8)(utf-8-validate@6.0.4): resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} engines: {node: '>=14'} peerDependencies: @@ -13596,7 +13612,7 @@ packages: whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 whatwg-url: 11.0.0 - ws: 8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) xml-name-validator: 4.0.0 transitivePeerDependencies: - bufferutil @@ -13771,8 +13787,8 @@ packages: - supports-color dev: false - /koa@2.15.0: - resolution: {integrity: sha512-KEL/vU1knsoUvfP4MC4/GthpQrY/p6dzwaaGI6Rt4NQuFqkw3qrvsdYF5pz3wOfi7IGTvMPHC9aZIcUKYFNxsw==} + /koa@2.15.3: + resolution: {integrity: sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg==} engines: {node: ^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4} dependencies: accepts: 1.3.8 @@ -16767,11 +16783,11 @@ packages: - react-dom dev: false - /storybook@7.6.17(bufferutil@4.0.8)(utf-8-validate@6.0.3): + /storybook@7.6.17(bufferutil@4.0.8)(utf-8-validate@6.0.4): resolution: {integrity: sha512-8+EIo91bwmeFWPg1eysrxXlhIYv3OsXrznTr4+4Eq0NikqAoq6oBhtlN5K2RGS2lBVF537eN+9jTCNbR+WrzDA==} hasBin: true dependencies: - '@storybook/cli': 7.6.17(bufferutil@4.0.8)(utf-8-validate@6.0.3) + '@storybook/cli': 7.6.17(bufferutil@4.0.8)(utf-8-validate@6.0.4) transitivePeerDependencies: - bufferutil - encoding @@ -17935,8 +17951,8 @@ packages: tslib: 2.6.2 dev: false - /utf-8-validate@6.0.3: - resolution: {integrity: sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==} + /utf-8-validate@6.0.4: + resolution: {integrity: sha512-xu9GQDeFp+eZ6LnCywXN/zBancWvOpUMzgjLPSjy4BRHSmTelvn2E0DG0o1sTiw5hkCKBHo8rwSKncfRfv2EEQ==} engines: {node: '>=6.14.2'} requiresBuild: true dependencies: @@ -18073,7 +18089,7 @@ packages: engines: {node: '>=12'} dev: false - /webpack-bundle-analyzer@4.10.1(bufferutil@4.0.8)(utf-8-validate@6.0.3): + /webpack-bundle-analyzer@4.10.1(bufferutil@4.0.8)(utf-8-validate@6.0.4): resolution: {integrity: sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ==} engines: {node: '>= 10.13.0'} hasBin: true @@ -18090,7 +18106,7 @@ packages: opener: 1.5.2 picocolors: 1.0.0 sirv: 2.0.4 - ws: 7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.4) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -18126,8 +18142,8 @@ packages: interpret: 3.1.1 rechoir: 0.8.0 webpack: 5.90.3(esbuild@0.20.1)(webpack-cli@5.1.4) - webpack-bundle-analyzer: 4.10.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) - webpack-dev-server: 4.15.1(bufferutil@4.0.8)(utf-8-validate@6.0.3)(webpack-cli@5.1.4)(webpack@5.90.3) + webpack-bundle-analyzer: 4.10.1(bufferutil@4.0.8)(utf-8-validate@6.0.4) + webpack-dev-server: 4.15.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)(webpack-cli@5.1.4)(webpack@5.90.3) webpack-merge: 5.10.0 dev: false @@ -18162,7 +18178,7 @@ packages: webpack: 5.90.3(@swc/core@1.4.2)(esbuild@0.20.1)(webpack-cli@5.1.4) dev: false - /webpack-dev-server@4.15.1(bufferutil@4.0.8)(utf-8-validate@6.0.3)(webpack-cli@5.1.4)(webpack@5.90.3): + /webpack-dev-server@4.15.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)(webpack-cli@5.1.4)(webpack@5.90.3): resolution: {integrity: sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==} engines: {node: '>= 12.13.0'} hasBin: true @@ -18181,7 +18197,7 @@ packages: '@types/serve-index': 1.9.4 '@types/serve-static': 1.15.5 '@types/sockjs': 0.3.36 - '@types/ws': 8.5.10 + '@types/ws': 8.5.11 ansi-html-community: 0.0.8 bonjour-service: 1.2.1 chokidar: 3.6.0 @@ -18189,7 +18205,7 @@ packages: compression: 1.7.4 connect-history-api-fallback: 2.0.0 default-gateway: 6.0.3 - express: 4.18.3 + express: 4.19.2 graceful-fs: 4.2.11 html-entities: 2.4.0 http-proxy-middleware: 2.0.6(@types/express@4.17.21) @@ -18206,7 +18222,7 @@ packages: webpack: 5.90.3(esbuild@0.20.1)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.1)(webpack-dev-server@4.15.1)(webpack@5.90.3) webpack-dev-middleware: 5.3.3(webpack@5.90.3) - ws: 8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) transitivePeerDependencies: - bufferutil - debug @@ -18483,7 +18499,7 @@ packages: async-limiter: 1.0.1 dev: false - /ws@7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.3): + /ws@7.5.9(bufferutil@4.0.8)(utf-8-validate@6.0.4): resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==} engines: {node: '>=8.3.0'} peerDependencies: @@ -18496,11 +18512,11 @@ packages: optional: true dependencies: bufferutil: 4.0.8 - utf-8-validate: 6.0.3 + utf-8-validate: 6.0.4 dev: false - /ws@8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3): - resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} + /ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4): + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -18512,7 +18528,7 @@ packages: optional: true dependencies: bufferutil: 4.0.8 - utf-8-validate: 6.0.3 + utf-8-validate: 6.0.4 dev: false /xml-name-validator@4.0.0: @@ -18655,17 +18671,17 @@ packages: dev: false file:projects/account-service.tgz: - resolution: {integrity: sha512-6mRpNCi4FRpm+ks+wuuSDxGFA9Zvjn++OdmqzbwuOMYj6veUSWc3xwDNV/sf3uoqr9+XvGmcJ97iSIkmtGu+TA==, tarball: file:projects/account-service.tgz} + resolution: {integrity: sha512-m2wnBUxpiATGM4uSuNprCebldYmLYweixWs/lkbmn2oxzV797WaTrigIQEEPowYOydBskozS7uc/PgBj9WhkCw==, tarball: file:projects/account-service.tgz} name: '@rush-temp/account-service' version: 0.0.0 dependencies: - '@koa/cors': 3.4.3 + '@koa/cors': 5.0.0 '@types/jest': 29.5.12 - '@types/koa': 2.14.0 + '@types/koa': 2.15.0 '@types/koa-bodyparser': 4.3.12 '@types/koa-morgan': 1.0.8 '@types/koa-router': 7.4.8 - '@types/koa__cors': 3.3.1 + '@types/koa__cors': 5.0.0 '@types/node': 20.11.19 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) @@ -18677,7 +18693,7 @@ packages: eslint-plugin-n: 15.7.0(eslint@8.56.0) eslint-plugin-promise: 6.1.1(eslint@8.56.0) jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) - koa: 2.15.0 + koa: 2.15.3 koa-bodyparser: 4.4.1 koa-morgan: 1.0.1 koa-router: 12.0.1 @@ -18704,15 +18720,15 @@ packages: - supports-color dev: false - file:projects/account.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(ts-node@10.9.2)(utf-8-validate@6.0.3): - resolution: {integrity: sha512-JTnXiO6DfjRlbofOhtUcTRVJbvTjlVGRk+n1uFvLhQrH0JtaGxlpeeXRAtc49yVlMGr6PuEsiw64yl/HvXuIGQ==, tarball: file:projects/account.tgz} + file:projects/account.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(ts-node@10.9.2)(utf-8-validate@6.0.4): + resolution: {integrity: sha512-kGVjnMyyrgr9iznwGzYUmdCLfT1xilVVLSKnYO5Lw/+rIIDboDsql1L2V54L0f+8nPAPGdR8D2KjUYNfHAdEug==, tarball: file:projects/account.tgz} id: file:projects/account.tgz name: '@rush-temp/account' version: 0.0.0 dependencies: '@types/jest': 29.5.12 '@types/node-fetch': 2.6.11 - '@types/ws': 8.5.10 + '@types/ws': 8.5.11 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) eslint: 8.56.0 @@ -18726,7 +18742,7 @@ packages: prettier: 3.2.5 ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) typescript: 5.3.3 - ws: 8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) transitivePeerDependencies: - '@aws-sdk/credential-providers' - '@babel/core' @@ -19030,20 +19046,20 @@ packages: dev: false file:projects/auth-providers.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2): - resolution: {integrity: sha512-PDpwpxlOBO4EtMwLUAdsxCl7tOsiiPSA5ob/54WvKYhaoADxg9VAZAEcuyPZgvjnRQFOiRicaSjtNl8gKvfhGw==, tarball: file:projects/auth-providers.tgz} + resolution: {integrity: sha512-1rnMBtJmT15O/aYKqKngvn9iIxcbSOBJOUNyivTY89+Rmp5NvP/p8J+2IMJZbCvqdA0xRgWtAcgVlFD2dwCGww==, tarball: file:projects/auth-providers.tgz} id: file:projects/auth-providers.tgz name: '@rush-temp/auth-providers' version: 0.0.0 dependencies: '@types/jest': 29.5.12 - '@types/koa': 2.14.0 + '@types/koa': 2.15.0 '@types/koa-passport': 6.0.3 '@types/koa-router': 7.4.8 '@types/koa-session': 6.4.5 '@types/node-fetch': 2.6.11 '@types/passport-github2': 1.2.9 '@types/passport-google-oauth20': 2.0.14 - '@types/ws': 8.5.10 + '@types/ws': 8.5.11 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) eslint: 8.56.0 @@ -19052,7 +19068,7 @@ packages: eslint-plugin-n: 15.7.0(eslint@8.56.0) eslint-plugin-promise: 6.1.1(eslint@8.56.0) jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) - koa: 2.15.0 + koa: 2.15.3 koa-passport: 6.0.0 koa-router: 12.0.1 koa-session: 6.4.0 @@ -19083,7 +19099,7 @@ packages: dev: false file:projects/backup-service.tgz(esbuild@0.20.1)(ts-node@10.9.2): - resolution: {integrity: sha512-ieEVBe6Ql9U5jEPlXPttIxFkkqUjjhAFwzWRXZCZVZ+l0b46LTW/HsrwMob+2bgZZ7N+xsd/uv3v1QCGr6jlCQ==, tarball: file:projects/backup-service.tgz} + resolution: {integrity: sha512-yVHrvyvHvtRFu894lGDl0o19qrJv3scuxI80Dg+rQDzjb4nWXAPGAj477N6TR7vrZSmYAmsR5qEaHkXxc35ipg==, tarball: file:projects/backup-service.tgz} id: file:projects/backup-service.tgz name: '@rush-temp/backup-service' version: 0.0.0 @@ -19683,13 +19699,13 @@ packages: - ts-node dev: false - file:projects/collaborator.tgz(@tiptap/pm@2.2.4)(bufferutil@4.0.8)(prosemirror-model@1.19.4)(utf-8-validate@6.0.3)(y-protocols@1.0.6): - resolution: {integrity: sha512-RHYlR+704EhdJDPooKU+D4BzSDC0Ov00TritI4GOFPnTBbbVkQHCWLqowr1om/rTE/lJ3oHTGPwMBkkfNvAkpw==, tarball: file:projects/collaborator.tgz} + file:projects/collaborator.tgz(@tiptap/pm@2.2.4)(bufferutil@4.0.8)(prosemirror-model@1.19.4)(utf-8-validate@6.0.4)(y-protocols@1.0.6): + resolution: {integrity: sha512-VOfczS2vceO0InYO/QbF0peWyX0/gct5EbWid4nKn1CJM166EN1NbgC4k/OnMSkmpjjmlOY23tfoUhyze5B6JA==, tarball: file:projects/collaborator.tgz} id: file:projects/collaborator.tgz name: '@rush-temp/collaborator' version: 0.0.0 dependencies: - '@hocuspocus/server': 2.11.2(bufferutil@4.0.8)(utf-8-validate@6.0.3)(y-protocols@1.0.6)(yjs@13.6.12) + '@hocuspocus/server': 2.11.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6)(yjs@13.6.12) '@hocuspocus/transformer': 2.11.2(@tiptap/pm@2.2.4)(y-prosemirror@1.2.2)(yjs@13.6.12) '@tiptap/core': 2.2.4(@tiptap/pm@2.2.4) '@tiptap/html': 2.2.4(@tiptap/core@2.2.4)(@tiptap/pm@2.2.4) @@ -19699,7 +19715,7 @@ packages: '@types/express': 4.17.21 '@types/jest': 29.5.12 '@types/node': 20.11.19 - '@types/ws': 8.5.10 + '@types/ws': 8.5.11 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) body-parser: 1.20.2 @@ -19712,14 +19728,14 @@ packages: eslint-plugin-import: 2.29.1(eslint@8.56.0) eslint-plugin-n: 15.7.0(eslint@8.56.0) eslint-plugin-promise: 6.1.1(eslint@8.56.0) - express: 4.18.3 + express: 4.19.2 jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) mongodb: 6.8.0 prettier: 3.2.5 ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) ts-node: 10.9.2(@types/node@20.11.19)(typescript@5.3.3) typescript: 5.3.3 - ws: 8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) y-prosemirror: 1.2.2(prosemirror-model@1.19.4)(y-protocols@1.0.6)(yjs@13.6.12) yjs: 13.6.12 transitivePeerDependencies: @@ -20333,7 +20349,7 @@ packages: dev: false file:projects/front.tgz(esbuild@0.20.1): - resolution: {integrity: sha512-QUH7Kqve2kcPTxKhIwk1RpnaMv/0Dw1gfBkNyS1MK25bfJNlKrYUKdigIp2JFVk/nIuJSZedwD03rHWadzxJHQ==, tarball: file:projects/front.tgz} + resolution: {integrity: sha512-kbnGRdB+2sGaVlIVQlK7RJo8OwjxEgTaNzWOg+3CSzpLuJFGE29mFog3D7BB2Q2g7hkEip1B6SiXHR/leCVvVQ==, tarball: file:projects/front.tgz} id: file:projects/front.tgz name: '@rush-temp/front' version: 0.0.0 @@ -20361,8 +20377,8 @@ packages: eslint-plugin-import: 2.29.1(eslint@8.56.0) eslint-plugin-n: 15.7.0(eslint@8.56.0) eslint-plugin-promise: 6.1.1(eslint@8.56.0) - express: 4.18.3 - express-fileupload: 1.5.0 + express: 4.19.2 + express-fileupload: 1.5.1 express-static-gzip: 2.1.7 jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) morgan: 1.10.0 @@ -23009,16 +23025,16 @@ packages: dev: false file:projects/pod-account.tgz: - resolution: {integrity: sha512-R8dx0uU9jPZ0AmS23UqoxKen0+94WDDHy8YfwhIVuZge+FMUwSBR/7nKBry0bNIyHA2C8yl8AxfJa/o2YedxzQ==, tarball: file:projects/pod-account.tgz} + resolution: {integrity: sha512-8TNm2djgcpy9V0WMdb8XmOwbMxGSSpvVs76DMoKhEcAjFHtmK/G32IOOvh721krtM5NibFqpEo4SERxjYWn7Qg==, tarball: file:projects/pod-account.tgz} name: '@rush-temp/pod-account' version: 0.0.0 dependencies: - '@koa/cors': 3.4.3 + '@koa/cors': 5.0.0 '@types/jest': 29.5.12 - '@types/koa': 2.14.0 + '@types/koa': 2.15.0 '@types/koa-bodyparser': 4.3.12 '@types/koa-router': 7.4.8 - '@types/koa__cors': 3.3.1 + '@types/koa__cors': 5.0.0 '@types/node': 20.11.19 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) @@ -23030,7 +23046,7 @@ packages: eslint-plugin-n: 15.7.0(eslint@8.56.0) eslint-plugin-promise: 6.1.1(eslint@8.56.0) jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) - koa: 2.15.0 + koa: 2.15.3 koa-bodyparser: 4.4.1 koa-router: 12.0.1 mongodb: 6.8.0 @@ -23058,7 +23074,7 @@ packages: dev: false file:projects/pod-backup.tgz: - resolution: {integrity: sha512-s9kcgWCYc8P3K8PwE+faNd1ZMgf5B8WYobO8YnPxUlfzOIOdAWoZwzSzWK7brEh8NGeYsfCGn8zxRj+EijOv2Q==, tarball: file:projects/pod-backup.tgz} + resolution: {integrity: sha512-cHCcTxI/Vi0V6XlrDbRHoYPHkyxXMt46iCY6sAlIuSU/6pgTQbNfZvrwCMVvlJS6AbZ/aCtmKsw6B54x9hnPQQ==, tarball: file:projects/pod-backup.tgz} name: '@rush-temp/pod-backup' version: 0.0.0 dependencies: @@ -23127,7 +23143,7 @@ packages: dev: false file:projects/pod-front.tgz: - resolution: {integrity: sha512-6NTN0wveHwOAxtkLMh9hzEgbHrs5kW6znj81iEMrUMkZDpv9+HKPRhCoE1jfNREMcBI/XifuivO1d3dTa0+VtA==, tarball: file:projects/pod-front.tgz} + resolution: {integrity: sha512-TjmoiIgWSRZLN6LJ4JAkk5dQ2kkF8WFEw36PAsNJPILc09xtKsxiJSsWwrn78epTB6jk2ugx4P8VuFnAERPvaA==, tarball: file:projects/pod-front.tgz} name: '@rush-temp/pod-front' version: 0.0.0 dependencies: @@ -23152,8 +23168,8 @@ packages: eslint-plugin-import: 2.29.1(eslint@8.56.0) eslint-plugin-n: 15.7.0(eslint@8.56.0) eslint-plugin-promise: 6.1.1(eslint@8.56.0) - express: 4.18.3 - express-fileupload: 1.5.0 + express: 4.19.2 + express-fileupload: 1.5.1 jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) prettier: 3.2.5 prettier-plugin-svelte: 3.2.1(prettier@3.2.5)(svelte@4.2.11) @@ -23174,13 +23190,13 @@ packages: dev: false file:projects/pod-server.tgz: - resolution: {integrity: sha512-e3S57PlJ3ME1jMupiLtYmdZFewiWjjAeY/uyOZ1yENsAf6WnET/uLgDpgRqHy/Kw/rLPvyAfWQR69mTFXpAOZA==, tarball: file:projects/pod-server.tgz} + resolution: {integrity: sha512-XoIVqsoRTp9S4Pn4XFHZJODjWt0EtAp/yhFw5QkINxMOaAN6Wr7uiFuXngkbe52JQbvx2pJ39WNnEXwMi5s40w==, tarball: file:projects/pod-server.tgz} name: '@rush-temp/pod-server' version: 0.0.0 dependencies: '@types/jest': 29.5.12 '@types/node': 20.11.19 - '@types/ws': 8.5.10 + '@types/ws': 8.5.11 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) cross-env: 7.0.3 @@ -23427,7 +23443,7 @@ packages: - ts-node dev: false - file:projects/prod.tgz(bufferutil@4.0.8)(sass@1.71.1)(ts-node@10.9.2)(utf-8-validate@6.0.3): + file:projects/prod.tgz(bufferutil@4.0.8)(sass@1.71.1)(ts-node@10.9.2)(utf-8-validate@6.0.4): resolution: {integrity: sha512-ALcGhikSF7a0YF6j81P1X3YX9B7x+Pu+wCS5mNa4YgcJEod+A7uGhAMcqBXHUsYsBvbJ1aymocOD88ACDDWEUQ==, tarball: file:projects/prod.tgz} id: file:projects/prod.tgz name: '@rush-temp/prod' @@ -23459,9 +23475,9 @@ packages: typescript: 5.3.3 update-browserslist-db: 1.0.13(browserslist@4.21.5) webpack: 5.90.3(esbuild@0.20.1)(webpack-cli@5.1.4) - webpack-bundle-analyzer: 4.10.1(bufferutil@4.0.8)(utf-8-validate@6.0.3) + webpack-bundle-analyzer: 4.10.1(bufferutil@4.0.8)(utf-8-validate@6.0.4) webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.1)(webpack-dev-server@4.15.1)(webpack@5.90.3) - webpack-dev-server: 4.15.1(bufferutil@4.0.8)(utf-8-validate@6.0.3)(webpack-cli@5.1.4)(webpack@5.90.3) + webpack-dev-server: 4.15.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)(webpack-cli@5.1.4)(webpack@5.90.3) transitivePeerDependencies: - '@babel/core' - '@rspack/core' @@ -24036,14 +24052,14 @@ packages: dev: false file:projects/rpc.tgz(esbuild@0.20.1)(ts-node@10.9.2): - resolution: {integrity: sha512-UYlMZK906EALJqTTz4fGg5l5W1MIBy5XObK1XYYWfvrE7XdPGePjRi6pdW5g/TVcoWkYCyK0ai9v4TVJlvlD5A==, tarball: file:projects/rpc.tgz} + resolution: {integrity: sha512-Uenitv91cmvTD9IwKWwLnPwaB7lH/b9B0iHQRJ6wh38kgKuvqIaZR45n9yZlsrTqz7qvq9S1GHZM5Da2zix/nQ==, tarball: file:projects/rpc.tgz} id: file:projects/rpc.tgz name: '@rush-temp/rpc' version: 0.0.0 dependencies: '@types/jest': 29.5.12 '@types/node': 20.11.19 - '@types/ws': 8.5.10 + '@types/ws': 8.5.11 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) eslint: 8.56.0 @@ -24227,7 +24243,7 @@ packages: dev: false file:projects/server-backup.tgz(esbuild@0.20.1)(ts-node@10.9.2): - resolution: {integrity: sha512-LuaW1naKF+DcYgzDYKohAfO1mMtqzpnglsAfNyAl0hNYUWA/F7M5+/qcy0LpHuwRTltol+RWBBLVeLMzMX9A/Q==, tarball: file:projects/server-backup.tgz} + resolution: {integrity: sha512-1G/CeRC6a8hTyfEkae5eIXGQ65v4y07RK1DO+W8WH7uhKgLRLPChyjLIoYa2/GpsxPTV1NyTZ63M3hd8HvSCrw==, tarball: file:projects/server-backup.tgz} id: file:projects/server-backup.tgz name: '@rush-temp/server-backup' version: 0.0.0 @@ -24383,6 +24399,41 @@ packages: - ts-node dev: false + file:projects/server-client.tgz(bufferutil@4.0.8)(esbuild@0.20.1)(ts-node@10.9.2)(utf-8-validate@6.0.4): + resolution: {integrity: sha512-L1a1mgOD2Qd1S0E6twotWEVeiFeZfvIK5v93gaPH+A5LkkQoOi4qXoEQv/Udul/cDP/MxPFA9HQ3WbdMIn8XYg==, tarball: file:projects/server-client.tgz} + id: file:projects/server-client.tgz + name: '@rush-temp/server-client' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@types/node': 20.11.19 + '@types/uuid': 8.3.4 + '@types/ws': 8.5.11 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - babel-jest + - babel-plugin-macros + - bufferutil + - esbuild + - node-notifier + - supports-color + - ts-node + - utf-8-validate + dev: false + file:projects/server-collaboration-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2): resolution: {integrity: sha512-CpLsMZJoj1GsG2/RgbvlCzlIIOe0q1bht2uXzJQ5B6vJQvsHJ3nEt1xxe4GNb6HEPwgvty8LmC1pf7FjLE05RQ==, tarball: file:projects/server-collaboration-resources.tgz} id: file:projects/server-collaboration-resources.tgz @@ -25169,13 +25220,13 @@ packages: dev: false file:projects/server-pipeline.tgz: - resolution: {integrity: sha512-DE5BaOMANpAiN/Gy/0wWfbSY97sPRjXjwIyQbUGRVhpQeu9AznyK9afkGowQjA7fexT7WHu3GgCVtMzLdh7xDg==, tarball: file:projects/server-pipeline.tgz} + resolution: {integrity: sha512-SmB9+QJy2nEfGizS/BADIxgNsUGf/2T5v08AaDp1gS8FecGNrXwnu8oUUVBmBWVV6n1dEfpGk/CjHYuXC07O3g==, tarball: file:projects/server-pipeline.tgz} name: '@rush-temp/server-pipeline' version: 0.0.0 dependencies: '@types/jest': 29.5.12 '@types/node': 20.11.19 - '@types/ws': 8.5.10 + '@types/ws': 8.5.11 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) cross-env: 7.0.3 @@ -25420,14 +25471,14 @@ packages: dev: false file:projects/server-storage.tgz(esbuild@0.20.1): - resolution: {integrity: sha512-SK29RSVcEogidJ5QNbOUrn5R8G0wvDVGa5SwwKZ1a3DbMMeAiujQmeiDdcTpanV5z8+IzJRjWqYchVla0AYehw==, tarball: file:projects/server-storage.tgz} + resolution: {integrity: sha512-nuggpJP7L/8s3sSJ393e8cISWODMQoCKtqyewY8lN7+n8gnL7RHlVDdinb4N9qvUACoSoExtK41dqAHoTnZxlQ==, tarball: file:projects/server-storage.tgz} id: file:projects/server-storage.tgz name: '@rush-temp/server-storage' version: 0.0.0 dependencies: '@types/jest': 29.5.12 '@types/node': 20.11.19 - '@types/ws': 8.5.10 + '@types/ws': 8.5.11 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) cross-env: 7.0.3 @@ -25766,8 +25817,8 @@ packages: - ts-node dev: false - file:projects/server-tool.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(ts-node@10.9.2)(utf-8-validate@6.0.3): - resolution: {integrity: sha512-3TUSDFQDCGfnf+u6ADBSVX7xBJY3hxkTMPCXdA8r7vmHw2jD+goCUOWjFwCKHyBm2/ObM3ABgrqoysGt2loLsA==, tarball: file:projects/server-tool.tgz} + file:projects/server-tool.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(ts-node@10.9.2)(utf-8-validate@6.0.4): + resolution: {integrity: sha512-GNqSQpgao0m+VBNx/GMS9FVTgAu38RYMHB6wHx75ZNzbCctyIiS2DLc+SfxTPZWkDprZb71Gs0y7A4e9nGc8qA==, tarball: file:projects/server-tool.tgz} id: file:projects/server-tool.tgz name: '@rush-temp/server-tool' version: 0.0.0 @@ -25775,7 +25826,7 @@ packages: '@types/jest': 29.5.12 '@types/js-yaml': 4.0.9 '@types/uuid': 8.3.4 - '@types/ws': 8.5.10 + '@types/ws': 8.5.11 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) eslint: 8.56.0 @@ -25791,7 +25842,7 @@ packages: ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) typescript: 5.3.3 uuid: 8.3.2 - ws: 8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) transitivePeerDependencies: - '@aws-sdk/credential-providers' - '@babel/core' @@ -26000,7 +26051,7 @@ packages: dev: false file:projects/server-ws.tgz(esbuild@0.20.1)(ts-node@10.9.2): - resolution: {integrity: sha512-DBdrFnKiROjLRor8JAqF2VBQaxhAn4HcHQdGSj/i7pUOzUFTHv/SkzOnhsTXOsGj1GpfJcm6YU+CDgoM79UoGw==, tarball: file:projects/server-ws.tgz} + resolution: {integrity: sha512-YrBVAtr3TNiYfwgohue5M/bOosO2VQnlmkRC2t8YWix+eviRY+RLECpxlGmBqGdrIjgG3fLF+ZE5Qv01Uf9qzw==, tarball: file:projects/server-ws.tgz} id: file:projects/server-ws.tgz name: '@rush-temp/server-ws' version: 0.0.0 @@ -26011,7 +26062,7 @@ packages: '@types/express': 4.17.21 '@types/jest': 29.5.12 '@types/node': 20.11.19 - '@types/ws': 8.5.10 + '@types/ws': 8.5.11 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) body-parser: 1.20.2 @@ -26023,15 +26074,15 @@ packages: eslint-plugin-import: 2.29.1(eslint@8.56.0) eslint-plugin-n: 15.7.0(eslint@8.56.0) eslint-plugin-promise: 6.1.1(eslint@8.56.0) - express: 4.18.3 + express: 4.19.2 fflate: 0.8.2 jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) prettier: 3.2.5 prettier-plugin-svelte: 3.2.1(prettier@3.2.5)(svelte@4.2.11) ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) typescript: 5.3.3 - utf-8-validate: 6.0.3 - ws: 8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + utf-8-validate: 6.0.4 + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) transitivePeerDependencies: - '@babel/core' - '@jest/types' @@ -26044,14 +26095,14 @@ packages: dev: false file:projects/server.tgz(esbuild@0.20.1): - resolution: {integrity: sha512-YKH6H2XDD+xXvxaXPjlpRlBnVJhCPfz+7paern0Umfl6ZvSUDyiSvgTV5LNCP4AUxWg47yDuaOuyvj1fadgMuA==, tarball: file:projects/server.tgz} + resolution: {integrity: sha512-fTgDuks26uZ0IhbPq9N/65R1I/hQG/gRRygfBsTmwAQqcJfo25EMz9gfLsuxu6YgZIWgI+UU6wbphtNeWLW/2A==, tarball: file:projects/server.tgz} id: file:projects/server.tgz name: '@rush-temp/server' version: 0.0.0 dependencies: '@types/jest': 29.5.12 '@types/node': 20.11.19 - '@types/ws': 8.5.10 + '@types/ws': 8.5.11 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) cross-env: 7.0.3 @@ -26251,7 +26302,7 @@ packages: - ts-node dev: false - file:projects/storybook.tgz(bufferutil@4.0.8)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(svelte-loader@3.2.0)(svelte@4.2.12)(typescript@5.3.3)(utf-8-validate@6.0.3)(webpack-cli@5.1.4)(webpack@5.90.3): + file:projects/storybook.tgz(bufferutil@4.0.8)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(svelte-loader@3.2.0)(svelte@4.2.12)(typescript@5.3.3)(utf-8-validate@6.0.4)(webpack-cli@5.1.4)(webpack@5.90.3): resolution: {integrity: sha512-oAsHcrV5MjaW6j7FTka343GWv6euRoMUQNqeYXGLNqXdDfkM8MTI0WDYI/EQCgaQ1hKv4F4qGpBRiIhYc2J5GQ==, tarball: file:projects/storybook.tgz} id: file:projects/storybook.tgz name: '@rush-temp/storybook' @@ -26270,7 +26321,7 @@ packages: resolve-url-loader: 5.0.0 sass: 1.71.1 sass-loader: 13.3.3(sass@1.71.1)(webpack@5.90.3) - storybook: 7.6.17(bufferutil@4.0.8)(utf-8-validate@6.0.3) + storybook: 7.6.17(bufferutil@4.0.8)(utf-8-validate@6.0.4) storybook-addon-themes: 6.1.0(react-dom@18.2.0)(react@18.2.0)(svelte@4.2.12) svelte-preprocess: 5.1.3(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.12)(typescript@5.3.3) transitivePeerDependencies: @@ -26895,13 +26946,13 @@ packages: - ts-node dev: false - file:projects/text-editor-resources.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(prosemirror-model@1.19.4)(ts-node@10.9.2)(utf-8-validate@6.0.3): + file:projects/text-editor-resources.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(prosemirror-model@1.19.4)(ts-node@10.9.2)(utf-8-validate@6.0.4): resolution: {integrity: sha512-piHhMfwGIkFR7VYjYWS1lGTwg5zIQaKJmKSSAY7gpRGM4BA88RagpPmx8eT/hXvFv83a+4CzkMUK/atGHWRZcQ==, tarball: file:projects/text-editor-resources.tgz} id: file:projects/text-editor-resources.tgz name: '@rush-temp/text-editor-resources' version: 0.0.0 dependencies: - '@hocuspocus/provider': 2.11.2(bufferutil@4.0.8)(utf-8-validate@6.0.3)(y-protocols@1.0.6)(yjs@13.6.12) + '@hocuspocus/provider': 2.11.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6)(yjs@13.6.12) '@tiptap/core': 2.2.4(@tiptap/pm@2.2.4) '@tiptap/extension-bubble-menu': 2.2.4(@tiptap/core@2.2.4)(@tiptap/pm@2.2.4) '@tiptap/extension-code': 2.2.4(@tiptap/core@2.2.4) @@ -26918,8 +26969,6 @@ packages: '@tiptap/extension-table-cell': 2.2.4(@tiptap/core@2.2.4) '@tiptap/extension-table-header': 2.2.4(@tiptap/core@2.2.4) '@tiptap/extension-table-row': 2.2.4(@tiptap/core@2.2.4) - '@tiptap/extension-task-item': 2.2.4(@tiptap/core@2.2.4)(@tiptap/pm@2.2.4) - '@tiptap/extension-task-list': 2.2.4(@tiptap/core@2.2.4) '@tiptap/extension-typography': 2.2.4(@tiptap/core@2.2.4) '@tiptap/extension-underline': 2.2.4(@tiptap/core@2.2.4) '@tiptap/pm': 2.2.4 @@ -27016,7 +27065,7 @@ packages: - ts-node dev: false - file:projects/text.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(ts-node@10.9.2)(utf-8-validate@6.0.3)(y-protocols@1.0.6): + file:projects/text.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.20.1)(ts-node@10.9.2)(utf-8-validate@6.0.4)(y-protocols@1.0.6): resolution: {integrity: sha512-ZQInjd9DHWVEXBeaR3wy+jQjDfHYnvJGKBiRWjr6i1AKIl/GNdDG/3G8QTQpu6O637ME04TzDGRbe3/9KNY+0w==, tarball: file:projects/text.tgz} id: file:projects/text.tgz name: '@rush-temp/text' @@ -27054,7 +27103,7 @@ packages: eslint-plugin-promise: 6.1.1(eslint@8.56.0) fast-equals: 5.0.1 jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) - jest-environment-jsdom: 29.7.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + jest-environment-jsdom: 29.7.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) markdown-it: 14.0.0 prettier: 3.2.5 prosemirror-codemark: 0.4.2(prosemirror-model@1.19.4) @@ -27239,8 +27288,8 @@ packages: - ts-node dev: false - file:projects/tool.tgz(bufferutil@4.0.8)(utf-8-validate@6.0.3): - resolution: {integrity: sha512-azLyJg979njyDjMIjjb/48UDVkrmgZnP8xOzH4Xov1JHAKw7ioNGNmq3wdpG4csgdghhrnmhMMXkae1ur2stlA==, tarball: file:projects/tool.tgz} + file:projects/tool.tgz(bufferutil@4.0.8)(utf-8-validate@6.0.4): + resolution: {integrity: sha512-lU7I2J+om0YHTY8FtcR2DtsEo4wSjZFP7FxAmpPgzFRSz7SwPtvdNDiXlM5zdMLiDszCXxUE/fNluCcxOeO8rg==, tarball: file:projects/tool.tgz} id: file:projects/tool.tgz name: '@rush-temp/tool' version: 0.0.0 @@ -27251,7 +27300,7 @@ packages: '@types/minio': 7.0.18 '@types/node': 20.11.19 '@types/request': 2.48.12 - '@types/ws': 8.5.10 + '@types/ws': 8.5.11 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) commander: 8.3.0 @@ -27274,7 +27323,7 @@ packages: ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) ts-node: 10.9.2(@types/node@20.11.19)(typescript@5.3.3) typescript: 5.3.3 - ws: 8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) transitivePeerDependencies: - '@aws-sdk/credential-providers' - '@babel/core' diff --git a/dev/docker-compose.yaml b/dev/docker-compose.yaml index 3528843d8f..5553e05a58 100644 --- a/dev/docker-compose.yaml +++ b/dev/docker-compose.yaml @@ -52,8 +52,7 @@ services: - SERVER_PORT=3000 - SERVER_SECRET=secret - MONGO_URL=mongodb://mongodb:27017 - - TRANSACTOR_URL=ws://transactor:3333 - - ENDPOINT_URL=ws://localhost:3333 + - TRANSACTOR_URL=ws://transactor:3333;ws://localhost:3333 - STORAGE_CONFIG=${STORAGE_CONFIG} - FRONT_URL=http://front:8080 - INIT_WORKSPACE=demo-tracker @@ -98,7 +97,6 @@ services: - COLLABORATOR_PORT=3078 - SECRET=secret - ACCOUNTS_URL=http://account:3000 - - TRANSACTOR_URL=ws://transactor:3333 - UPLOAD_URL=/files - MONGO_URL=mongodb://mongodb:27017 - STORAGE_CONFIG=${STORAGE_CONFIG} @@ -118,7 +116,6 @@ services: # - ACCOUNTS_URL=http://localhost:3000 # - FRONT_URL=http://localhost:8084 # - UPLOAD_URL=/files - # - TRANSACTOR_URL=ws://localhost:3333 # - ELASTIC_URL=http://elastic:9200 # - STORAGE_CONFIG=${STORAGE_CONFIG} transactor: diff --git a/dev/prod/.env b/dev/prod/.env index 64621b7ded..ef133cf03a 100644 --- a/dev/prod/.env +++ b/dev/prod/.env @@ -1,6 +1,4 @@ -LOGIN_ENDPOINT=ws://localhost:3333 - TELEGRAM_URL=http://localhost:8086 GMAIL_URL=http://localhost:8088 CALENDAR_URL=http://localhost:8095 diff --git a/dev/prod/src/platform-dev.ts b/dev/prod/src/platform-dev.ts index 2805e90d85..555c010e48 100644 --- a/dev/prod/src/platform-dev.ts +++ b/dev/prod/src/platform-dev.ts @@ -20,9 +20,6 @@ import { addLocation, setMetadata } from '@hcengineering/platform' import presentation from '@hcengineering/presentation' export function configurePlatformDevServer() { - console.log('Use Endpoint override:', process.env.LOGIN_ENDPOINT) - setMetadata(login.metadata.OverrideEndpoint, process.env.LOGIN_ENDPOINT) - // Set devmodel to hook client to be able to present all activity enableDevModel() } diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index f79913e4b9..f42cc5670b 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -258,8 +258,6 @@ export async function configurePlatform() { setMetadata(calendar.metadata.CalendarServiceURL, config.CALENDAR_URL ?? 'http://localhost:8095') setMetadata(notification.metadata.PushPublicKey, config.PUSH_PUBLIC_KEY) - setMetadata(login.metadata.OverrideEndpoint, process.env.LOGIN_ENDPOINT) - setMetadata(rekoni.metadata.RekoniUrl, config.REKONI_URL) setMetadata(love.metadata.ServiceEnpdoint, config.LOVE_ENDPOINT) setMetadata(love.metadata.WebSocketURL, config.LIVEKIT_WS) diff --git a/dev/tool/package.json b/dev/tool/package.json index e48fd7ea0a..314f3ab964 100644 --- a/dev/tool/package.json +++ b/dev/tool/package.json @@ -18,7 +18,7 @@ "docker:build": "../../common/scripts/docker_build.sh hardcoreeng/tool", "docker:staging": "../../common/scripts/docker_tag.sh hardcoreeng/tool staging", "docker:push": "../../common/scripts/docker_tag.sh hardcoreeng/tool", - "run-local": "rush bundle --to @hcengineering/tool >/dev/null && cross-env SERVER_SECRET=secret MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin MINIO_ENDPOINT=localhost MONGO_URL=mongodb://localhost:27017 TRANSACTOR_URL=ws://localhost:3333 TELEGRAM_DATABASE=telegram-service ELASTIC_URL=http://localhost:9200 REKONI_URL=http://localhost:4004 MODEL_VERSION=$(node ../../common/scripts/show_version.js) GIT_REVISION=$(git describe --all --long) node --max-old-space-size=18000 ./bundle/bundle.js", + "run-local": "rush bundle --to @hcengineering/tool >/dev/null && cross-env SERVER_SECRET=secret MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin MINIO_ENDPOINT=localhost MONGO_URL=mongodb://localhost:27017 TELEGRAM_DATABASE=telegram-service ELASTIC_URL=http://localhost:9200 REKONI_URL=http://localhost:4004 MODEL_VERSION=$(node ../../common/scripts/show_version.js) GIT_REVISION=$(git describe --all --long) node --max-old-space-size=18000 ./bundle/bundle.js", "run": "rush bundle --to @hcengineering/tool >/dev/null && cross-env node --max-old-space-size=8000 ./bundle/bundle.js", "upgrade": "rushx run-local upgrade", "format": "format src", @@ -44,7 +44,7 @@ "eslint-config-standard-with-typescript": "^40.0.0", "prettier": "^3.1.0", "typescript": "^5.3.3", - "@types/ws": "^8.5.3", + "@types/ws": "^8.5.11", "@types/mime-types": "~2.1.1", "@types/request": "~2.48.8", "jest": "^29.7.0", @@ -122,6 +122,7 @@ "@hcengineering/server-telegram-resources": "^0.6.0", "@hcengineering/server-token": "^0.6.11", "@hcengineering/server-tool": "^0.6.0", + "@hcengineering/server-client": "^0.6.0", "@hcengineering/server-tracker": "^0.6.0", "@hcengineering/server-tracker-resources": "^0.6.0", "@hcengineering/server-view": "^0.6.0", @@ -147,6 +148,6 @@ "libphonenumber-js": "^1.9.46", "mime-types": "~2.1.34", "mongodb": "^6.8.0", - "ws": "^8.16.0" + "ws": "^8.18.0" } } diff --git a/dev/tool/src/benchmark.ts b/dev/tool/src/benchmark.ts index 3f1e967fc3..b84e751cb7 100644 --- a/dev/tool/src/benchmark.ts +++ b/dev/tool/src/benchmark.ts @@ -33,6 +33,7 @@ import { connect } from '@hcengineering/server-tool' import client from '@hcengineering/client' import { setMetadata } from '@hcengineering/platform' +import serverClientPlugin, { getTransactorEndpoint } from '@hcengineering/server-client' import os from 'os' import { Worker, isMainThread, parentPort } from 'worker_threads' import { CSVWriter } from './csv' @@ -78,7 +79,7 @@ interface PendingMsg extends Msg { export async function benchmark ( workspaceId: WorkspaceId[], users: Map, - transactorUrl: string, + accountsUrl: string, cmd: { from: number steps: number @@ -168,11 +169,14 @@ export async function benchmark ( const token = generateToken(systemAccountEmail, workspaceId[0]) + setMetadata(serverClientPlugin.metadata.Endpoint, accountsUrl) + const endpoint = await getTransactorEndpoint(token, 'external') + console.log('monitor endpoint', endpoint, 'workspace', workspaceId[0].name) const monitorConnection = isMainThread ? ((await ctx.with( 'connect', {}, - async () => await connect(transactorUrl, workspaceId[0], undefined, { mode: 'backup' }) + async () => await connect(endpoint, workspaceId[0], undefined, { mode: 'backup' }) )) as BackupClient & Client) : undefined @@ -202,7 +206,7 @@ export async function benchmark ( const st = Date.now() try { - const fetchUrl = transactorUrl.replace('ws:/', 'http:/') + '/api/v1/statistics?token=' + token + const fetchUrl = endpoint.replace('ws:/', 'http:/') + '/api/v1/statistics?token=' + token void fetch(fetchUrl) .then((res) => { void res @@ -286,14 +290,18 @@ export async function benchmark ( await Promise.all( Array.from(Array(i)) .map((it, idx) => idx) - .map((it) => { + .map(async (it) => { const wsid = workspaceId[randNum(workspaceId.length)] const workId = 'w-' + i + '-' + it const wsUsers = users.get(wsid.name) ?? [] + + const token = generateToken(systemAccountEmail, wsid) + const endpoint = await getTransactorEndpoint(token, 'external') + console.log('endpoint', endpoint, 'workspace', wsid.name) const msg: StartMessage = { email: wsUsers[randNum(wsUsers.length)], workspaceId: wsid, - transactorUrl, + transactorUrl: endpoint, id: i, idd: it, workId, @@ -314,7 +322,7 @@ export async function benchmark ( } workers[i % workers.length].postMessage(msg) - return new Promise((resolve) => { + return await new Promise((resolve) => { works.set(workId, () => { resolve(null) }) diff --git a/dev/tool/src/clean.ts b/dev/tool/src/clean.ts index 001ae64196..c99353ae46 100644 --- a/dev/tool/src/clean.ts +++ b/dev/tool/src/clean.ts @@ -21,49 +21,52 @@ import { loadCollaborativeDoc, saveCollaborativeDoc, yDocToBuffer } from '@hceng import contact from '@hcengineering/contact' import core, { type ArrOf, + type BackupClient, + type Class, ClassifierKind, type CollaborativeDoc, + type Client as CoreClient, DOMAIN_BENCHMARK, DOMAIN_DOC_INDEX_STATE, + DOMAIN_MIGRATION, DOMAIN_MODEL, DOMAIN_STATUS, DOMAIN_TX, + type Doc, type DocumentUpdate, + type Domain, type Hierarchy, type Markup, - type RefTo, - SortingOrder, - TxOperations, - TxProcessor, - generateId, - getObjectValue, - getWorkspaceId, - toIdMap, - updateAttribute, - type BackupClient, - type Class, - type Client as CoreClient, - type Doc, - type Domain, type MeasureContext, + type MigrationState, type Ref, + type RefTo, + type RelatedDocument, + SortingOrder, type Status, type StatusCategory, type TxCUD, type TxCreateDoc, type TxMixin, + TxOperations, + TxProcessor, type TxUpdateDoc, type WorkspaceId, - DOMAIN_MIGRATION, - type MigrationState, - type RelatedDocument + generateId, + getObjectValue, + getWorkspaceId, + systemAccountEmail, + toIdMap, + updateAttribute } from '@hcengineering/core' import activity, { DOMAIN_ACTIVITY } from '@hcengineering/model-activity' import { DOMAIN_SPACE } from '@hcengineering/model-core' import recruitModel, { defaultApplicantStatuses } from '@hcengineering/model-recruit' import { getMongoClient, getWorkspaceDB } from '@hcengineering/mongo' import recruit, { type Applicant, type Vacancy } from '@hcengineering/recruit' +import { getTransactorEndpoint } from '@hcengineering/server-client' import { type StorageAdapter } from '@hcengineering/server-core' +import { generateToken } from '@hcengineering/server-token' import { connect } from '@hcengineering/server-tool' import tags, { type TagCategory, type TagElement, type TagReference } from '@hcengineering/tags' import task, { type ProjectType, type Task, type TaskType } from '@hcengineering/task' @@ -1054,7 +1057,7 @@ export async function removeDuplicateIds ( mongodbUri: string, storageAdapter: StorageAdapter, productId: string, - transactorUrl: string, + accountsUrl: string, initWorkspacesStr: string ): Promise { const state = 'REMOVE_DUPLICATE_IDS' @@ -1127,7 +1130,8 @@ export async function removeDuplicateIds ( const check = await db.collection(DOMAIN_MIGRATION).findOne({ state, plugin: workspace.workspace }) if (check != null) continue - const wsClient = (await connect(transactorUrl, workspaceId, undefined, { + const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, workspaceId)) + const wsClient = (await connect(endpoint, workspaceId, undefined, { model: 'upgrade' })) as CoreClient & BackupClient for (const set of ids) { diff --git a/dev/tool/src/index.ts b/dev/tool/src/index.ts index 8b29572d0e..554051c231 100644 --- a/dev/tool/src/index.ts +++ b/dev/tool/src/index.ts @@ -14,7 +14,7 @@ // limitations under the License. // -import { +import accountPlugin, { ACCOUNT_DB, assignWorkspace, confirmEmail, @@ -46,8 +46,9 @@ import { createStorageBackupStorage, restore } from '@hcengineering/server-backup' +import serverClientPlugin, { BlobClient, getTransactorEndpoint } from '@hcengineering/server-client' import serverToken, { decodeToken, generateToken } from '@hcengineering/server-token' -import toolPlugin, { BlobClient } from '@hcengineering/server-tool' +import toolPlugin from '@hcengineering/server-tool' import { buildStorageFromConfig, storageConfigFromEnv } from '@hcengineering/server-storage' import { program, type Command } from 'commander' @@ -60,12 +61,14 @@ import core, { getWorkspaceId, MeasureMetricsContext, metricsToString, + systemAccountEmail, versionToString, type Data, type Doc, type Ref, type Tx, - type Version + type Version, + type WorkspaceId } from '@hcengineering/core' import { consoleModelLogger, type MigrateOperation } from '@hcengineering/model' import contact from '@hcengineering/model-contact' @@ -126,10 +129,15 @@ export function devTool ( process.exit(1) } + const accountsUrl = process.env.ACCOUNTS_URL + if (accountsUrl === undefined) { + console.error('please provide accounts url.') + process.exit(1) + } + const transactorUrl = process.env.TRANSACTOR_URL if (transactorUrl === undefined) { console.error('please provide transactor url.') - process.exit(1) } function getElasticUrl (): string { @@ -150,8 +158,8 @@ export function devTool ( setMetadata(toolPlugin.metadata.InitScriptURL, initScriptUrl) } - setMetadata(toolPlugin.metadata.Endpoint, transactorUrl) - setMetadata(toolPlugin.metadata.Transactor, transactorUrl) + setMetadata(accountPlugin.metadata.Transactors, transactorUrl) + setMetadata(serverClientPlugin.metadata.Endpoint, accountsUrl) setMetadata(serverToken.metadata.Secret, serverSecret) async function withDatabase (uri: string, f: (db: Db, client: MongoClient) => Promise): Promise { @@ -214,7 +222,7 @@ export function devTool ( const { mongodbUri } = prepareTools() await withDatabase(mongodbUri, async (db) => { console.log(`update account ${email} to ${newEmail}`) - await renameAccount(toolCtx, db, productId, transactorUrl, email, newEmail) + await renameAccount(toolCtx, db, productId, accountsUrl, email, newEmail) }) }) @@ -225,7 +233,7 @@ export function devTool ( const { mongodbUri } = prepareTools() await withDatabase(mongodbUri, async (db) => { console.log(`update account ${email} to ${newEmail}`) - await fixAccountEmails(toolCtx, db, productId, transactorUrl, email, newEmail) + await fixAccountEmails(toolCtx, db, productId, accountsUrl, email, newEmail) }) }) @@ -263,7 +271,9 @@ export function devTool ( cmd: { token: string, host: string, enable: string, tokenLimit: string, embeddings: string } ) => { console.log(`enabling OpenAI for workspace ${workspace}...`) - await openAIConfig(transactorUrl, workspace, productId, { + const wsid = getWorkspaceId(workspace, productId) + const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') + await openAIConfig(endpoint, workspace, productId, { token: cmd.token, endpoint: cmd.host, enabled: cmd.enable === 'true', @@ -315,8 +325,11 @@ export function devTool ( .command('set-user-role ') .description('set user role') .action(async (email: string, workspace: string, role: AccountRole, cmd) => { + const { mongodbUri } = prepareTools() console.log(`set user ${email} role for ${workspace}...`) - await setRole(email, workspace, productId, role) + await withDatabase(mongodbUri, async (db) => { + await setRole(toolCtx, db, email, workspace, productId, role) + }) }) program @@ -650,7 +663,9 @@ export function devTool ( } ) => { const storage = await createFileBackupStorage(dirName) - await backup(toolCtx, transactorUrl, getWorkspaceId(workspace, productId), storage, { + const wsid = getWorkspaceId(workspace, productId) + const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') + await backup(toolCtx, endpoint, wsid, storage, { force: cmd.force, recheck: cmd.recheck, include: cmd.include === '*' ? undefined : new Set(cmd.include.split(';').map((it) => it.trim())), @@ -699,7 +714,9 @@ export function devTool ( cmd: { merge: boolean, parallel: string, recheck: boolean, include: string, skip: string } ) => { const storage = await createFileBackupStorage(dirName) - await restore(toolCtx, transactorUrl, getWorkspaceId(workspace, productId), storage, { + const wsid = getWorkspaceId(workspace, productId) + const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') + await restore(toolCtx, endpoint, wsid, storage, { date: parseInt(date ?? '-1'), merge: cmd.merge, parallel: parseInt(cmd.parallel ?? '1'), @@ -730,7 +747,9 @@ export function devTool ( getWorkspaceId(bucketName, productId), dirName ) - await backup(toolCtx, transactorUrl, getWorkspaceId(workspace, productId), storage) + const wsid = getWorkspaceId(workspace, productId) + const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') + await backup(toolCtx, endpoint, wsid, storage) }) }) @@ -782,7 +801,9 @@ export function devTool ( const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { const storage = await createStorageBackupStorage(toolCtx, adapter, getWorkspaceId(bucketName), dirName) - await restore(toolCtx, transactorUrl, getWorkspaceId(workspace, productId), storage, { + const wsid = getWorkspaceId(workspace, productId) + const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') + await restore(toolCtx, endpoint, wsid, storage, { date: parseInt(date ?? '-1') }) }) @@ -892,15 +913,9 @@ export function devTool ( const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { await withDatabase(mongodbUri, async (db) => { - await cleanWorkspace( - toolCtx, - mongodbUri, - getWorkspaceId(workspace, productId), - adapter, - getElasticUrl(), - transactorUrl, - cmd - ) + const wsid = getWorkspaceId(workspace, productId) + const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') + await cleanWorkspace(toolCtx, mongodbUri, wsid, adapter, getElasticUrl(), endpoint, cmd) }) }) }) @@ -926,10 +941,13 @@ export function devTool ( .action(async (workspace: string, local: string, remote: string, contentType: string, cmd: any) => { const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { - const blobClient = new BlobClient(transactorUrl, { + const wsId: WorkspaceId = { name: workspace, productId - }) + } + const token = generateToken(systemAccountEmail, wsId) + const endpoint = await getTransactorEndpoint(token) + const blobClient = new BlobClient(endpoint, token, wsId) const buffer = readFileSync(local) await blobClient.upload(toolCtx, remote, buffer.length, contentType, buffer) }) @@ -940,10 +958,13 @@ export function devTool ( .action(async (workspace: string, remote: string, local: string, cmd: any) => { const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { - const blobClient = new BlobClient(transactorUrl, { + const wsId: WorkspaceId = { name: workspace, productId - }) + } + const token = generateToken(systemAccountEmail, wsId) + const endpoint = await getTransactorEndpoint(token) + const blobClient = new BlobClient(endpoint, token, wsId) const wrstream = createWriteStream(local) await blobClient.writeTo(toolCtx, remote, -1, { write: (buffer, cb) => { @@ -967,21 +988,30 @@ export function devTool ( .command('clean-removed-transactions ') .description('clean removed transactions') .action(async (workspace: string, cmd: any) => { - await cleanRemovedTransactions(getWorkspaceId(workspace, productId), transactorUrl) + const wsid = getWorkspaceId(workspace, productId) + const token = generateToken(systemAccountEmail, wsid) + const endpoint = await getTransactorEndpoint(token) + await cleanRemovedTransactions(wsid, endpoint) }) program .command('clean-archived-spaces ') .description('clean archived spaces') .action(async (workspace: string, cmd: any) => { - await cleanArchivedSpaces(getWorkspaceId(workspace, productId), transactorUrl) + const wsid = getWorkspaceId(workspace, productId) + const token = generateToken(systemAccountEmail, wsid) + const endpoint = await getTransactorEndpoint(token) + await cleanArchivedSpaces(wsid, endpoint) }) program .command('chunter-fix-comments ') .description('chunter-fix-comments') .action(async (workspace: string, cmd: any) => { - await fixCommentDoubleIdCreate(getWorkspaceId(workspace, productId), transactorUrl) + const wsid = getWorkspaceId(workspace, productId) + const token = generateToken(systemAccountEmail, wsid) + const endpoint = await getTransactorEndpoint(token) + await fixCommentDoubleIdCreate(wsid, endpoint) }) program @@ -991,7 +1021,10 @@ export function devTool ( .option('--property ', 'Property name', '') .option('--detail ', 'Show details', false) .action(async (workspace: string, cmd: { detail: boolean, mixin: string, property: string }) => { - await showMixinForeignAttributes(getWorkspaceId(workspace, productId), transactorUrl, cmd) + const wsid = getWorkspaceId(workspace, productId) + const token = generateToken(systemAccountEmail, wsid) + const endpoint = await getTransactorEndpoint(token) + await showMixinForeignAttributes(wsid, endpoint, cmd) }) program @@ -1001,7 +1034,10 @@ export function devTool ( .option('--property ', 'Property name', '') .action(async (workspace: string, cmd: { mixin: string, property: string }) => { const { mongodbUri } = prepareTools() - await fixMixinForeignAttributes(mongodbUri, getWorkspaceId(workspace, productId), transactorUrl, cmd) + const wsid = getWorkspaceId(workspace, productId) + const token = generateToken(systemAccountEmail, wsid) + const endpoint = await getTransactorEndpoint(token) + await fixMixinForeignAttributes(mongodbUri, wsid, endpoint, cmd) }) program @@ -1012,7 +1048,9 @@ export function devTool ( .option('--list', 'List plugin states', false) .action(async (workspace: string, cmd: { enable: string, disable: string, list: boolean }) => { console.log(JSON.stringify(cmd)) - await changeConfiguration(getWorkspaceId(workspace, productId), transactorUrl, cmd) + const wsid = getWorkspaceId(workspace, productId) + const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') + await changeConfiguration(wsid, endpoint, cmd) }) program @@ -1029,7 +1067,10 @@ export function devTool ( const workspaces = await listWorkspacesRaw(db, productId) for (const ws of workspaces) { console.log('configure', ws.workspaceName ?? ws.workspace) - await changeConfiguration(getWorkspaceId(ws.workspace, productId), transactorUrl, cmd) + const wsid = getWorkspaceId(ws.workspace, productId) + const token = generateToken(systemAccountEmail, wsid) + const endpoint = await getTransactorEndpoint(token) + await changeConfiguration(wsid, endpoint, cmd) } }) }) @@ -1039,7 +1080,10 @@ export function devTool ( .description('optimize model') .action(async (workspace: string, cmd: { enable: string, disable: string, list: boolean }) => { console.log(JSON.stringify(cmd)) - await optimizeModel(getWorkspaceId(workspace, productId), transactorUrl) + const wsid = getWorkspaceId(workspace, productId) + const token = generateToken(systemAccountEmail, wsid) + const endpoint = await getTransactorEndpoint(token) + await optimizeModel(wsid, endpoint) }) program @@ -1096,7 +1140,7 @@ export function devTool ( ) } } - await benchmark(workspaces, accountWorkspaces, transactorUrl, { + await benchmark(workspaces, accountWorkspaces, accountsUrl, { steps: parseInt(cmd.steps), from: parseInt(cmd.from), sleep: parseInt(cmd.sleep), @@ -1121,7 +1165,10 @@ export function devTool ( .description('fix skills for workspace') .action(async (workspace: string, step: string) => { const { mongodbUri } = prepareTools() - await fixSkills(mongodbUri, getWorkspaceId(workspace, productId), transactorUrl, step) + const wsid = getWorkspaceId(workspace, productId) + const token = generateToken(systemAccountEmail, wsid) + const endpoint = await getTransactorEndpoint(token) + await fixSkills(mongodbUri, wsid, endpoint, step) }) program @@ -1130,7 +1177,9 @@ export function devTool ( .action(async (workspace: string) => { const { mongodbUri } = prepareTools() console.log('Restoring recruiting task types in workspace ', workspace, '...') - await restoreRecruitingTaskTypes(mongodbUri, getWorkspaceId(workspace, productId), transactorUrl) + const wsid = getWorkspaceId(workspace, productId) + const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') + await restoreRecruitingTaskTypes(mongodbUri, wsid, endpoint) }) program @@ -1139,7 +1188,9 @@ export function devTool ( .action(async (workspace: string) => { const { mongodbUri } = prepareTools() console.log('Restoring recruiting task types in workspace ', workspace, '...') - await restoreHrTaskTypesFromUpdates(mongodbUri, getWorkspaceId(workspace, productId), transactorUrl) + const wsid = getWorkspaceId(workspace, productId) + const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') + await restoreHrTaskTypesFromUpdates(mongodbUri, wsid, endpoint) }) program @@ -1157,7 +1208,9 @@ export function devTool ( cmd: { objectId: string, objectClass: string, type: string, attribute: string, value: string, domain: string } ) => { const { mongodbUri } = prepareTools() - await updateField(mongodbUri, getWorkspaceId(workspace, productId), transactorUrl, cmd) + const wsid = getWorkspaceId(workspace, productId) + const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') + await updateField(mongodbUri, wsid, endpoint, cmd) } ) @@ -1166,7 +1219,9 @@ export function devTool ( .description('reindex workspace to elastic') .action(async (workspace: string) => { const { mongodbUri } = prepareTools() - await recreateElastic(mongodbUri, getWorkspaceId(workspace, productId), transactorUrl) + const wsid = getWorkspaceId(workspace, productId) + const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') + await recreateElastic(mongodbUri, wsid, endpoint) }) program @@ -1175,7 +1230,9 @@ export function devTool ( .action(async (workspace: string) => { const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { - await fixJsonMarkup(toolCtx, mongodbUri, adapter, getWorkspaceId(workspace, productId), transactorUrl) + const wsid = getWorkspaceId(workspace, productId) + const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') + await fixJsonMarkup(toolCtx, mongodbUri, adapter, wsid, endpoint) }) }) @@ -1185,7 +1242,7 @@ export function devTool ( .action(async (workspaces: string) => { const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { - await removeDuplicateIds(toolCtx, mongodbUri, adapter, productId, transactorUrl, workspaces) + await removeDuplicateIds(toolCtx, mongodbUri, adapter, productId, accountsUrl, workspaces) }) }) diff --git a/dev/tool/src/renameAccount.ts b/dev/tool/src/renameAccount.ts index 24b5df535d..21f734b0b6 100644 --- a/dev/tool/src/renameAccount.ts +++ b/dev/tool/src/renameAccount.ts @@ -1,6 +1,8 @@ import { type Account, changeEmail, getAccount, listWorkspacesPure, type Workspace } from '@hcengineering/account' -import core, { type MeasureContext, TxOperations } from '@hcengineering/core' +import core, { getWorkspaceId, type MeasureContext, systemAccountEmail, TxOperations } from '@hcengineering/core' import contact from '@hcengineering/model-contact' +import { getTransactorEndpoint } from '@hcengineering/server-client' +import { generateToken } from '@hcengineering/server-token' import { connect } from '@hcengineering/server-tool' import { type Db } from 'mongodb' @@ -8,7 +10,7 @@ export async function renameAccount ( ctx: MeasureContext, db: Db, productId: string, - transactorUrl: string, + accountsUrl: string, oldEmail: string, newEmail: string ): Promise { @@ -24,7 +26,7 @@ export async function renameAccount ( await changeEmail(ctx, db, account, newEmail) - await fixWorkspaceEmails(account, db, productId, transactorUrl, oldEmail, newEmail) + await fixWorkspaceEmails(account, db, productId, accountsUrl, oldEmail, newEmail) } export async function fixAccountEmails ( @@ -46,7 +48,7 @@ async function fixWorkspaceEmails ( account: Account, db: Db, productId: string, - transactorUrl: string, + accountsUrl: string, oldEmail: string, newEmail: string ): Promise { @@ -59,8 +61,11 @@ async function fixWorkspaceEmails ( } console.log('checking workspace', ws.workspaceName, ws.workspace) + const wsid = getWorkspaceId(ws.workspace, productId) + const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid)) + // Let's connect and update account information. - await fixEmailInWorkspace(transactorUrl, ws, oldEmail, newEmail) + await fixEmailInWorkspace(endpoint, ws, oldEmail, newEmail) } } diff --git a/dev/upgrade.sh b/dev/upgrade.sh index 2410e44198..258523318b 100755 --- a/dev/upgrade.sh +++ b/dev/upgrade.sh @@ -1,6 +1,5 @@ docker run -ti -e SERVER_SECRET=secret \ -e MONGO_URL=mongodb://127.0.0.1:27017 \ - -e TRANSACTOR_URL=ws://127.0.0.1:3333 \ -e MINIO_ENDPOINT=minio \ -e MINIO_ACCESS_KEY=minioadmin \ -e MINIO_SECRET_KEY=minioadmin \ diff --git a/packages/core/src/classes.ts b/packages/core/src/classes.ts index 6db82fc8eb..5c979c590e 100644 --- a/packages/core/src/classes.ts +++ b/packages/core/src/classes.ts @@ -664,4 +664,6 @@ export interface BaseWorkspaceInfo { creating?: boolean createProgress?: number // Some progress + + endpoint: string } diff --git a/plugins/client-resources/src/connection.ts b/plugins/client-resources/src/connection.ts index b5a5a84ef1..249496e044 100644 --- a/plugins/client-resources/src/connection.ts +++ b/plugins/client-resources/src/connection.ts @@ -15,7 +15,7 @@ // import { Analytics } from '@hcengineering/analytics' -import client, { ClientSocket, ClientSocketReadyState } from '@hcengineering/client' +import client, { ClientSocket, ClientSocketReadyState, type ClientFactoryOptions } from '@hcengineering/client' import core, { Account, Class, @@ -42,8 +42,7 @@ import core, { } from '@hcengineering/core' import { PlatformError, UNAUTHORIZED, broadcastEvent, getMetadata, unknownError } from '@hcengineering/platform' -import { HelloRequest, HelloResponse, ReqId, type Response } from '@hcengineering/rpc' -import { RPCHandler } from '@hcengineering/rpc' +import { HelloRequest, HelloResponse, RPCHandler, ReqId, type Response } from '@hcengineering/rpc' const SECOND = 1000 const pingTimeout = 10 * SECOND @@ -99,9 +98,7 @@ class Connection implements ClientConnection { private readonly handler: TxHandler, readonly workspace: string, readonly email: string, - private readonly onUpgrade?: () => void, - private readonly onUnauthorized?: () => void, - readonly onConnect?: (event: ClientConnectEvent, data?: any) => Promise + readonly opt?: ClientFactoryOptions ) { if (typeof sessionStorage !== 'undefined') { // Find local session id in session storage only if user refresh a page. @@ -228,7 +225,7 @@ class Connection implements ClientConnection { Analytics.handleError(new PlatformError(resp.error)) this.closed = true this.websocket?.close() - this.onUnauthorized?.() + this.opt?.onUnauthorized?.() } console.error(resp.error) return @@ -237,7 +234,7 @@ class Connection implements ClientConnection { if (resp.id === -1) { this.delay = 0 if (resp.result?.state === 'upgrading') { - void this.onConnect?.(ClientConnectEvent.Maintenance, resp.result.stats) + void this.opt?.onConnect?.(ClientConnectEvent.Maintenance, resp.result.stats) this.upgrading = true this.delay = 3 return @@ -245,7 +242,7 @@ class Connection implements ClientConnection { if (resp.result === 'hello') { if (this.upgrading) { // We need to call upgrade since connection is upgraded - this.onUpgrade?.() + this.opt?.onUpgrade?.() } this.upgrading = false @@ -262,8 +259,9 @@ class Connection implements ClientConnection { v.reconnect?.() } - void this.onConnect?.( - (resp as HelloResponse).reconnect === true ? ClientConnectEvent.Reconnected : ClientConnectEvent.Connected + void this.opt?.onConnect?.( + (resp as HelloResponse).reconnect === true ? ClientConnectEvent.Reconnected : ClientConnectEvent.Connected, + null ) this.schedulePing(socketId) return @@ -358,7 +356,7 @@ class Connection implements ClientConnection { for (const tx of txArr) { if (tx?._class === core.class.TxModelUpgrade) { console.log('Processing upgrade', this.workspace, this.email) - this.onUpgrade?.() + this.opt?.onUpgrade?.() return } } @@ -398,6 +396,7 @@ class Connection implements ClientConnection { this.dialTimer = setTimeout(() => { if (!opened && !this.closed) { + void this.opt?.onDialTimeout?.() this.scheduleOpen(true) } }, dialTimeout) @@ -645,11 +644,7 @@ export function connect ( handler: TxHandler, workspace: string, user: string, - onUpgrade?: () => void, - onUnauthorized?: () => void, - onConnect?: (event: ClientConnectEvent, data?: any) => void + opt?: ClientFactoryOptions ): ClientConnection { - return new Connection(url, handler, workspace, user, onUpgrade, onUnauthorized, async (event, data) => { - onConnect?.(event, data) - }) + return new Connection(url, handler, workspace, user, opt) } diff --git a/plugins/client-resources/src/index.ts b/plugins/client-resources/src/index.ts index d98dc3f286..9d91fc40b7 100644 --- a/plugins/client-resources/src/index.ts +++ b/plugins/client-resources/src/index.ts @@ -14,11 +14,10 @@ // import clientPlugin from '@hcengineering/client' +import type { ClientFactoryOptions } from '@hcengineering/client/src' import core, { AccountClient, - ClientConnectEvent, LoadModelResponse, - MeasureContext, Tx, TxHandler, TxPersistenceStore, @@ -71,14 +70,7 @@ function decodeTokenPayload (token: string): any { export default async () => { return { function: { - GetClient: async ( - token: string, - endpoint: string, - onUpgrade?: () => void, - onUnauthorized?: () => void, - onConnect?: (event: ClientConnectEvent, data: any) => void, - ctx?: MeasureContext - ): Promise => { + GetClient: async (token: string, endpoint: string, opt?: ClientFactoryOptions): Promise => { const filterModel = getMetadata(clientPlugin.metadata.FilterModel) ?? false const client = createClient( @@ -88,7 +80,7 @@ export default async () => { const upgradeHandler: TxHandler = (...txes: Tx[]) => { for (const tx of txes) { if (tx?._class === core.class.TxModelUpgrade) { - onUpgrade?.() + opt?.onUpgrade?.() return } if (tx?._class === core.class.TxWorkspaceEvent) { @@ -105,15 +97,7 @@ export default async () => { handler(...txes) } const tokenPayload: { workspace: string, email: string } = decodeTokenPayload(token) - const clientConnection = connect( - url, - upgradeHandler, - tokenPayload.workspace, - tokenPayload.email, - onUpgrade, - onUnauthorized, - onConnect - ) + const clientConnection = connect(url, upgradeHandler, tokenPayload.workspace, tokenPayload.email, opt) const connectTimeout = getMetadata(clientPlugin.metadata.ConnectionTimeout) if ((connectTimeout ?? 0) > 0) { return new Promise((resolve, reject) => { @@ -121,6 +105,7 @@ export default async () => { if (!clientConnection.isConnected()) { clientConnection.onConnect = undefined void clientConnection?.close() + void opt?.onDialTimeout?.() reject(new Error(`Connection timeout, and no connection established to ${endpoint}`)) } }, connectTimeout) @@ -135,7 +120,7 @@ export default async () => { }, filterModel ? [...getPlugins(), ...(getMetadata(clientPlugin.metadata.ExtraPlugins) ?? [])] : undefined, createModelPersistence(getWSFromToken(token)), - ctx + opt?.ctx ) return await client } diff --git a/plugins/client/src/index.ts b/plugins/client/src/index.ts index 31969efa3a..ee37f855d1 100644 --- a/plugins/client/src/index.ts +++ b/plugins/client/src/index.ts @@ -55,17 +55,18 @@ export enum ClientSocketReadyState { CLOSED = 3 } +export interface ClientFactoryOptions { + onUpgrade?: () => void + onUnauthorized?: () => void + onConnect?: (event: ClientConnectEvent, data: any) => void + ctx?: MeasureContext + onDialTimeout?: () => void | Promise +} + /** * @public */ -export type ClientFactory = ( - token: string, - endpoint: string, - onUpgrade?: () => void, - onUnauthorized?: () => void, - onConnect?: (event: ClientConnectEvent, data: any) => void, - ctx?: MeasureContext -) => Promise +export type ClientFactory = (token: string, endpoint: string, opt?: ClientFactoryOptions) => Promise export default plugin(clientId, { metadata: { diff --git a/plugins/guest-resources/src/connect.ts b/plugins/guest-resources/src/connect.ts index ae36d1f404..0324f77d87 100644 --- a/plugins/guest-resources/src/connect.ts +++ b/plugins/guest-resources/src/connect.ts @@ -36,15 +36,17 @@ export async function connect (title: string): Promise { setPresentationCookie(token, getCurrentWorkspaceUrl()) - const getEndpoint = await getResource(login.function.GetEndpoint) - const endpoint = await getEndpoint() - if (endpoint == null) { + const selectWorkspace = await getResource(login.function.SelectWorkspace) + const workspaceLoginInfo = (await selectWorkspace(ws, token))[1] + if (workspaceLoginInfo == null) { navigate({ path: [loginId] }) return } + setMetadata(presentation.metadata.Token, token) + if (_token !== token && _client !== undefined) { await _client.close() _client = undefined @@ -56,13 +58,11 @@ export async function connect (title: string): Promise { let version: Version | undefined const clientFactory = await getResource(client.function.GetClient) - _client = await clientFactory( - token, - endpoint, - () => { + _client = await clientFactory(token, workspaceLoginInfo.endpoint, { + onUpgrade: () => { location.reload() }, - () => { + onUnauthorized: () => { clearMetadata(ws) navigate({ path: [loginId], @@ -70,7 +70,7 @@ export async function connect (title: string): Promise { }) }, // We need to refresh all active live queries and clear old queries. - (event: ClientConnectEvent) => { + onConnect: (event: ClientConnectEvent) => { console.log('WorkbenchClient: onConnect', event) try { if ((_clientSet && event === ClientConnectEvent.Connected) || event === ClientConnectEvent.Refresh) { @@ -110,7 +110,7 @@ export async function connect (title: string): Promise { console.error(err) } } - ) + }) console.log('logging in as guest') Analytics.handleEvent('GUEST LOGIN') Analytics.setWorkspace(ws) @@ -167,7 +167,6 @@ function clearMetadata (ws: string): void { setMetadata(presentation.metadata.Token, null) setMetadataLocalStorage(login.metadata.LastToken, null) setPresentationCookie('', getCurrentWorkspaceUrl()) - setMetadataLocalStorage(login.metadata.LoginEndpoint, null) setMetadataLocalStorage(login.metadata.LoginEmail, null) void closeClient() } diff --git a/plugins/login-resources/src/utils.ts b/plugins/login-resources/src/utils.ts index 7593f266c2..2d36b95335 100644 --- a/plugins/login-resources/src/utils.ts +++ b/plugins/login-resources/src/utils.ts @@ -14,7 +14,7 @@ // import { Analytics } from '@hcengineering/analytics' -import { AccountRole, type Doc, type Ref, concatLink } from '@hcengineering/core' +import { AccountRole, concatLink, type Doc, type Ref } from '@hcengineering/core' import login, { loginId, type LoginInfo, type Workspace, type WorkspaceLoginInfo } from '@hcengineering/login' import { OK, @@ -39,8 +39,6 @@ import { import { workbenchId } from '@hcengineering/workbench' import { type Pages } from './index' -const DEV_WORKSPACE = 'DEV WORKSPACE' - /** * Perform a login operation to required workspace with user credentials. */ @@ -51,14 +49,6 @@ export async function doLogin (email: string, password: string): Promise<[Status throw new Error('accounts url not specified') } - const token = getMetadata(login.metadata.OverrideLoginToken) - if (token !== undefined) { - const endpoint = getMetadata(login.metadata.OverrideEndpoint) - if (endpoint !== undefined) { - return [OK, { token, endpoint, email, confirmed: true }] - } - } - const request = { method: 'login', params: [email, password] @@ -100,14 +90,6 @@ export async function signUp ( throw new Error('accounts url not specified') } - const token = getMetadata(login.metadata.OverrideLoginToken) - if (token !== undefined) { - const endpoint = getMetadata(login.metadata.OverrideEndpoint) - if (endpoint !== undefined) { - return [OK, { token, endpoint, email, confirmed: true }] - } - } - const request = { method: 'createAccount', params: [email, password, first, last] @@ -144,15 +126,6 @@ export async function createWorkspace ( throw new Error('accounts url not specified') } - const overrideToken = getMetadata(login.metadata.OverrideLoginToken) - const email = fetchMetadataLocalStorage(login.metadata.LoginEmail) ?? '' - if (overrideToken !== undefined) { - const endpoint = getMetadata(login.metadata.OverrideEndpoint) - if (endpoint !== undefined) { - return [OK, { token: overrideToken, endpoint, email, confirmed: true, workspace: workspaceName }] - } - } - const token = getMetadata(presentation.metadata.Token) if (token === undefined) { const loc = getCurrentLocation() @@ -197,20 +170,6 @@ export async function getWorkspaces (): Promise { throw new Error('accounts url not specified') } - const overrideToken = getMetadata(login.metadata.OverrideLoginToken) - if (overrideToken !== undefined) { - const endpoint = getMetadata(login.metadata.OverrideEndpoint) - if (endpoint !== undefined) { - return [ - { - workspace: DEV_WORKSPACE, - workspaceId: DEV_WORKSPACE, - lastVisit: 0 - } - ] - } - } - const token = getMetadata(presentation.metadata.Token) if (token === undefined) { const loc = getCurrentLocation() @@ -251,15 +210,6 @@ export async function getAccount (doNavigate: boolean = true): Promise { +export async function selectWorkspace ( + workspace: string, + token: string | null | undefined +): Promise<[Status, WorkspaceLoginInfo | undefined]> { const accountsUrl = getMetadata(login.metadata.AccountsUrl) if (accountsUrl === undefined) { throw new Error('accounts url not specified') } - const overrideToken = getMetadata(login.metadata.OverrideLoginToken) - const email = fetchMetadataLocalStorage(login.metadata.LoginEmail) ?? '' - if (overrideToken !== undefined) { - const endpoint = getMetadata(login.metadata.OverrideEndpoint) - if (endpoint !== undefined) { - return [OK, { token: overrideToken, endpoint, email, workspace, confirmed: true }] - } - } - - const token = getMetadata(presentation.metadata.Token) ?? fetchMetadataLocalStorage(login.metadata.LastToken) + token = token ?? getMetadata(presentation.metadata.Token) ?? fetchMetadataLocalStorage(login.metadata.LastToken) if (token === undefined) { const loc = getCurrentLocation() loc.path[0] = 'login' @@ -323,7 +267,7 @@ export async function selectWorkspace (workspace: string): Promise<[Status, Work const request = { method: 'selectWorkspace', - params: [workspace] + params: [workspace, 'external'] } try { @@ -356,15 +300,6 @@ export async function fetchWorkspace (workspace: string): Promise<[Status, Works throw new Error('accounts url not specified') } - const overrideToken = getMetadata(login.metadata.OverrideLoginToken) - const email = fetchMetadataLocalStorage(login.metadata.LoginEmail) ?? '' - if (overrideToken !== undefined) { - const endpoint = getMetadata(login.metadata.OverrideEndpoint) - if (endpoint !== undefined) { - return [OK, { token: overrideToken, endpoint, email, workspace, confirmed: true }] - } - } - const token = getMetadata(presentation.metadata.Token) if (token === undefined) { return [unknownStatus('Please login'), undefined] @@ -404,14 +339,6 @@ export async function createMissingEmployee (workspace: string): Promise<[Status throw new Error('accounts url not specified') } - const overrideToken = getMetadata(login.metadata.OverrideLoginToken) - if (overrideToken !== undefined) { - const endpoint = getMetadata(login.metadata.OverrideEndpoint) - if (endpoint !== undefined) { - return [OK] - } - } - const token = getMetadata(presentation.metadata.Token) if (token === undefined) { return [unknownStatus('Please login')] @@ -487,15 +414,6 @@ export async function checkJoined (inviteId: string): Promise<[Status, Workspace throw new Error('accounts url not specified') } - const overrideToken = getMetadata(login.metadata.OverrideLoginToken) - const email = fetchMetadataLocalStorage(login.metadata.LoginEmail) ?? '' - if (overrideToken !== undefined) { - const endpoint = getMetadata(login.metadata.OverrideEndpoint) - if (endpoint !== undefined) { - return [OK, { token: overrideToken, endpoint, email, workspace: DEV_WORKSPACE, confirmed: true }] - } - } - let token = getMetadata(presentation.metadata.Token) if (token === undefined) { const tokens: Record = fetchMetadataLocalStorage(login.metadata.LoginTokens) ?? {} @@ -608,14 +526,6 @@ export async function join ( throw new Error('accounts url not specified') } - const token = getMetadata(login.metadata.OverrideLoginToken) - if (token !== undefined) { - const endpoint = getMetadata(login.metadata.OverrideEndpoint) - if (endpoint !== undefined) { - return [OK, { token, endpoint, email, workspace: DEV_WORKSPACE, confirmed: true }] - } - } - const request = { method: 'join', params: [email, password, inviteId] @@ -656,14 +566,6 @@ export async function signUpJoin ( throw new Error('accounts url not specified') } - const token = getMetadata(login.metadata.OverrideLoginToken) - if (token !== undefined) { - const endpoint = getMetadata(login.metadata.OverrideEndpoint) - if (endpoint !== undefined) { - return [OK, { token, endpoint, email, workspace: DEV_WORKSPACE, confirmed: true }] - } - } - const request = { method: 'signUpJoin', params: [email, password, first, last, inviteId] @@ -698,13 +600,6 @@ export async function changePassword (oldPassword: string, password: string): Pr throw new Error('accounts url not specified') } - const overrideToken = getMetadata(login.metadata.OverrideLoginToken) - if (overrideToken !== undefined) { - const endpoint = getMetadata(login.metadata.OverrideEndpoint) - if (endpoint !== undefined) { - return - } - } const token = getMetadata(presentation.metadata.Token) as string const request = { @@ -735,13 +630,6 @@ export async function leaveWorkspace (email: string): Promise { throw new Error('accounts url not specified') } - const overrideToken = getMetadata(login.metadata.OverrideLoginToken) - if (overrideToken !== undefined) { - const endpoint = getMetadata(login.metadata.OverrideEndpoint) - if (endpoint !== undefined) { - return - } - } const token = getMetadata(presentation.metadata.Token) as string const request = { @@ -766,13 +654,6 @@ export async function sendInvite (email: string, personId?: Ref, role?: Acc throw new Error('accounts url not specified') } - const overrideToken = getMetadata(login.metadata.OverrideLoginToken) - if (overrideToken !== undefined) { - const endpoint = getMetadata(login.metadata.OverrideEndpoint) - if (endpoint !== undefined) { - return - } - } const token = getMetadata(presentation.metadata.Token) as string const params = [email, personId, role] @@ -799,13 +680,6 @@ export async function requestPassword (email: string): Promise { throw new Error('accounts url not specified') } - const overrideToken = getMetadata(login.metadata.OverrideLoginToken) - if (overrideToken !== undefined) { - const endpoint = getMetadata(login.metadata.OverrideEndpoint) - if (endpoint !== undefined) { - return OK - } - } const request = { method: 'requestPassword', params: [email] @@ -837,13 +711,6 @@ export async function confirm (email: string): Promise<[Status, LoginInfo | unde throw new Error('accounts url not specified') } - const overrideToken = getMetadata(login.metadata.OverrideLoginToken) - if (overrideToken !== undefined) { - const endpoint = getMetadata(login.metadata.OverrideEndpoint) - if (endpoint !== undefined) { - return [OK, { token: overrideToken, endpoint, email, confirmed: true }] - } - } const request = { method: 'confirm', params: [email] @@ -936,7 +803,7 @@ export async function afterConfirm (): Promise { if (joinedWS.length === 0) { goTo('createWorkspace') } else if (joinedWS.length === 1) { - const result = (await selectWorkspace(joinedWS[0].workspace))[1] + const result = (await selectWorkspace(joinedWS[0].workspace, null))[1] if (result !== undefined) { setMetadata(presentation.metadata.Token, result.token) setMetadataLocalStorage(login.metadata.LastToken, result.token) @@ -960,12 +827,6 @@ export async function getEnpoint (): Promise { throw new Error('accounts url not specified') } - const endpoint = getMetadata(login.metadata.OverrideEndpoint) - - if (endpoint !== undefined) { - return endpoint - } - const params: [] = [] const request = { @@ -1016,12 +877,18 @@ export async function getProviders (): Promise { throw new Error('accounts url not specified') } - try { - const response = await fetch(concatLink(accountsUrl, '/providers')) - const result = await response.json() - return result - } catch (err: any) { - Analytics.handleError(err) - return [] + for (let i = 0; i < 5; i++) { + try { + const response = await fetch(concatLink(accountsUrl, '/providers')) + const result = await response.json() + return result + } catch (err: any) { + if (i === 4) { + Analytics.handleError(err) + return [] + } + await new Promise((resolve) => setTimeout(resolve, 100)) + } } + return [] } diff --git a/plugins/login/src/index.ts b/plugins/login/src/index.ts index 4fff2061c1..e3df8a0414 100644 --- a/plugins/login/src/index.ts +++ b/plugins/login/src/index.ts @@ -62,9 +62,7 @@ export default plugin(loginId, { LoginTokens: '' as Metadata>, LastToken: '' as Metadata, LoginEndpoint: '' as Metadata, - LoginEmail: '' as Metadata, - OverrideLoginToken: '' as Metadata, // debug purposes - OverrideEndpoint: '' as Metadata + LoginEmail: '' as Metadata }, component: { LoginApp: '' as AnyComponent, @@ -86,7 +84,9 @@ export default plugin(loginId, { >, LeaveWorkspace: '' as Resource<(email: string) => Promise>, ChangePassword: '' as Resource<(oldPassword: string, password: string) => Promise>, - SelectWorkspace: '' as Resource<(workspace: string) => Promise<[Status, WorkspaceLoginInfo | undefined]>>, + SelectWorkspace: '' as Resource< + (workspace: string, token: string | null | undefined) => Promise<[Status, WorkspaceLoginInfo | undefined]> + >, FetchWorkspace: '' as Resource<(workspace: string) => Promise<[Status, WorkspaceLoginInfo | undefined]>>, CreateEmployee: '' as Resource<(workspace: string) => Promise<[Status]>>, GetWorkspaces: '' as Resource<() => Promise>, diff --git a/plugins/workbench-resources/src/connect.ts b/plugins/workbench-resources/src/connect.ts index b1da296bac..6d4d953d59 100644 --- a/plugins/workbench-resources/src/connect.ts +++ b/plugins/workbench-resources/src/connect.ts @@ -70,42 +70,41 @@ export async function connect (title: string): Promise { const tokens: Record = fetchMetadataLocalStorage(login.metadata.LoginTokens) ?? {} let token = tokens[ws] - if ( - token === undefined && - (getMetadata(presentation.metadata.Token) !== undefined || - fetchMetadataLocalStorage(login.metadata.LastToken) != null) - ) { - const selectWorkspace = await getResource(login.function.SelectWorkspace) - const loginInfo = await ctx.with('select-workspace', {}, async () => (await selectWorkspace(ws))[1]) - if (loginInfo !== undefined) { - tokens[ws] = loginInfo.token - token = loginInfo.token - setMetadataLocalStorage(login.metadata.LoginTokens, tokens) - } + const selectWorkspace = await getResource(login.function.SelectWorkspace) + const workspaceLoginInfo = await ctx.with('select-workspace', {}, async () => (await selectWorkspace(ws, token))[1]) + if (workspaceLoginInfo !== undefined) { + tokens[ws] = workspaceLoginInfo.token + token = workspaceLoginInfo.token + setMetadataLocalStorage(login.metadata.LoginTokens, tokens) } + setMetadata(presentation.metadata.Token, token) - const fetchWorkspace = await getResource(login.function.FetchWorkspace) - let loginInfo = await ctx.with('fetch-workspace', {}, async () => (await fetchWorkspace(ws))[1]) - if (loginInfo?.creating === true) { - while (true) { - if (ws !== getCurrentLocation().path[1]) return - workspaceCreating.set(loginInfo?.createProgress ?? 0) - loginInfo = await ctx.with('fetch-workspace', {}, async () => (await fetchWorkspace(ws))[1]) - workspaceCreating.set(loginInfo?.createProgress) - if (loginInfo?.creating === false) { - workspaceCreating.set(-1) - break + if (workspaceLoginInfo?.creating === true) { + const fetchWorkspace = await getResource(login.function.FetchWorkspace) + let loginInfo = await ctx.with('fetch-workspace', {}, async () => (await fetchWorkspace(ws))[1]) + if (loginInfo?.creating === true) { + while (true) { + if (ws !== getCurrentLocation().path[1]) return + workspaceCreating.set(loginInfo?.createProgress ?? 0) + loginInfo = await ctx.with('fetch-workspace', {}, async () => (await fetchWorkspace(ws))[1]) + workspaceCreating.set(loginInfo?.createProgress) + if (loginInfo?.creating === false) { + workspaceCreating.set(-1) + break + } + await new Promise((resolve) => setTimeout(resolve, 1000)) } - await new Promise((resolve) => setTimeout(resolve, 1000)) } } setPresentationCookie(token, getCurrentWorkspaceUrl()) - const endpoint = fetchMetadataLocalStorage(login.metadata.LoginEndpoint) - const email = fetchMetadataLocalStorage(login.metadata.LoginEmail) - if (token === undefined || endpoint === null || email === null) { + setMetadataLocalStorage(login.metadata.LoginEndpoint, workspaceLoginInfo?.endpoint) + + const endpoint = workspaceLoginInfo?.endpoint // fetchMetadataLocalStorage(login.metadata.LoginEndpoint) + const email = workspaceLoginInfo?.email // fetchMetadataLocalStorage(login.metadata.LoginEmail) + if (token == null || endpoint == null || email == null) { const navigateUrl = encodeURIComponent(JSON.stringify(loc)) navigate({ path: [loginId], @@ -138,13 +137,11 @@ export async function connect (title: string): Promise { 'create-client', {}, async (ctx) => - await clientFactory( - token, - endpoint, - () => { + await clientFactory(token, endpoint, { + onUpgrade: () => { location.reload() }, - () => { + onUnauthorized: () => { clearMetadata(ws) navigate({ path: [loginId], @@ -152,7 +149,7 @@ export async function connect (title: string): Promise { }) }, // We need to refresh all active live queries and clear old queries. - (event: ClientConnectEvent, data: any) => { + onConnect: (event: ClientConnectEvent, data: any) => { console.log('WorkbenchClient: onConnect', event) if (event === ClientConnectEvent.Maintenance) { if (data != null && data.total !== 0) { @@ -213,8 +210,15 @@ export async function connect (title: string): Promise { console.error(err) } }, - ctx - ) + ctx, + onDialTimeout: async () => { + const newLoginInfo = await ctx.with('select-workspace', {}, async () => (await selectWorkspace(ws, token))[1]) + if (newLoginInfo?.endpoint !== endpoint) { + console.log('endpoint changed, reloading') + location.reload() + } + } + }) ) _client = newClient diff --git a/pods/account/package.json b/pods/account/package.json index 79be7dc399..3d6cd452c6 100644 --- a/pods/account/package.json +++ b/pods/account/package.json @@ -36,10 +36,10 @@ "eslint-plugin-n": "^15.4.0", "eslint": "^8.54.0", "esbuild": "^0.20.0", - "@types/koa-bodyparser": "^4.3.3", - "@types/koa-router": "^7.4.4", - "@types/koa": "2.14.0", - "@types/koa__cors": "^3.0.3", + "@types/koa-bodyparser": "^4.3.12", + "@types/koa-router": "^7.4.8", + "@types/koa": "^2.15.0", + "@types/koa__cors": "^5.0.0", "@typescript-eslint/parser": "^6.11.0", "eslint-config-standard-with-typescript": "^40.0.0", "prettier": "^3.1.0", @@ -56,11 +56,10 @@ "@hcengineering/auth-providers": "^0.6.0", "@hcengineering/core": "^0.6.32", "mongodb": "^6.8.0", - "koa": "^2.13.1", + "koa": "^2.15.3", "koa-router": "^12.0.1", - "koa-bodyparser": "^4.3.0", - "@koa/cors": "^3.1.0", - "@hcengineering/server-tool": "^0.6.0", + "koa-bodyparser": "^4.4.1", + "@koa/cors": "^5.0.0", "@hcengineering/server-token": "^0.6.11", "@hcengineering/server-core": "^0.6.1", "@hcengineering/model-all": "^0.6.0" diff --git a/pods/authProviders/package.json b/pods/authProviders/package.json index 1a69c5936a..ea9f406311 100644 --- a/pods/authProviders/package.json +++ b/pods/authProviders/package.json @@ -32,15 +32,15 @@ "eslint-config-standard-with-typescript": "^40.0.0", "prettier": "^3.1.0", "typescript": "^5.3.3", - "@types/ws": "^8.5.3", + "@types/ws": "^8.5.11", "@types/node-fetch": "~2.6.2", "jest": "^29.7.0", "ts-jest": "^29.1.1", "@types/jest": "^29.5.5", - "@types/koa-passport": "^6.0.0", + "@types/koa-passport": "^6.0.3", "@types/passport-google-oauth20": "^2.0.0", - "@types/koa-router": "^7.4.4", - "@types/koa": "2.14.0", + "@types/koa-router": "^7.4.8", + "@types/koa": "^2.15.0", "@types/koa-session": "^6.4.5", "@types/passport-github2": "^1.2.9" }, @@ -50,8 +50,8 @@ "@hcengineering/account": "^0.6.0", "passport-google-oauth20": "~2.0.0", "passport-github2": "~0.1.12", - "koa-passport": "~6.0.0", - "koa": "^2.13.1", + "koa-passport": "^6.0.0", + "koa": "^2.15.3", "koa-router": "^12.0.1", "koa-session": "^6.4.0" } diff --git a/pods/backup/package.json b/pods/backup/package.json index bfd166bf5f..cc73be451d 100644 --- a/pods/backup/package.json +++ b/pods/backup/package.json @@ -18,7 +18,7 @@ "docker:build": "../../common/scripts/docker_build.sh hardcoreeng/backup", "docker:staging": "../../common/scripts/docker_tag.sh hardcoreeng/backup staging", "docker:push": "../../common/scripts/docker_tag.sh hardcoreeng/backup", - "run-local": "cross-env ACCOUNTS_URL=http://localhost:3000/ SECRET=secret MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin MINIO_ENDPOINT=localhost TRANSACTOR_URL=ws://localhost:3333 BUCKET_NAME=backups INTERVAL=30 ts-node src/index.ts", + "run-local": "cross-env ACCOUNTS_URL=http://localhost:3000/ SECRET=secret MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin MINIO_ENDPOINT=localhost BUCKET_NAME=backups INTERVAL=30 ts-node src/index.ts", "format": "format src", "test": "jest --passWithNoTests --silent --forceExit", "_phase:build": "compile transpile src", @@ -47,7 +47,6 @@ }, "dependencies": { "@hcengineering/platform": "^0.6.11", - "@hcengineering/server-tool": "^0.6.0", "@hcengineering/server-token": "^0.6.11", "@hcengineering/server-core": "^0.6.1", "@hcengineering/server-pipeline": "^0.6.0", diff --git a/pods/collaborator/package.json b/pods/collaborator/package.json index a06dbb4325..5b1ab2a6e6 100644 --- a/pods/collaborator/package.json +++ b/pods/collaborator/package.json @@ -17,7 +17,7 @@ "docker:build": "../../common/scripts/docker_build.sh hardcoreeng/collaborator", "docker:staging": "../../common/scripts/docker_tag.sh hardcoreeng/collaborator staging", "docker:push": "../../common/scripts/docker_tag.sh hardcoreeng/collaborator", - "run-local": "cross-env MONGO_URL=mongodb://localhost:27017 TRANSACTOR_URL=ws://localhost:3333 SECRET=secret MINIO_ENDPOINT=localhost MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin ts-node src/__start.ts", + "run-local": "cross-env MONGO_URL=mongodb://localhost:27017 SECRET=secret MINIO_ENDPOINT=localhost MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin ts-node src/__start.ts", "format": "format src", "test": "jest --passWithNoTests --silent --forceExit", "_phase:build": "compile transpile src", diff --git a/pods/front/package.json b/pods/front/package.json index d328ce724d..62d77f3c45 100644 --- a/pods/front/package.json +++ b/pods/front/package.json @@ -59,8 +59,8 @@ "@hcengineering/front": "^0.6.0", "@hcengineering/core": "^0.6.32", "@hcengineering/platform": "^0.6.11", - "express": "^4.18.3", - "express-fileupload": "^1.4.3", + "express": "^4.19.2", + "express-fileupload": "^1.5.1", "uuid": "^8.3.2", "cors": "^2.8.5", "@hcengineering/elastic": "^0.6.0", @@ -68,7 +68,6 @@ "@hcengineering/server-token": "^0.6.11", "@hcengineering/attachment": "^0.6.14", "body-parser": "^1.20.2", - "compression": "~1.7.4", "sharp": "~0.32.0" } } diff --git a/pods/server/package.json b/pods/server/package.json index 1707a8c2ea..d73bacbaae 100644 --- a/pods/server/package.json +++ b/pods/server/package.json @@ -35,7 +35,7 @@ "eslint-plugin-promise": "^6.1.1", "eslint-plugin-n": "^15.4.0", "eslint": "^8.54.0", - "@types/ws": "^8.5.3", + "@types/ws": "^8.5.11", "ts-node": "^10.8.0", "esbuild": "^0.20.0", "@typescript-eslint/parser": "^6.11.0", diff --git a/products/tracker/.env b/products/tracker/.env index c2b6aadc76..968e3c9034 100644 --- a/products/tracker/.env +++ b/products/tracker/.env @@ -1,7 +1,3 @@ - -LOGIN_ENDPOINT=ws://localhost:3333 - - GMAIL_URL=http://localhost:8087 TELEGRAM_URL=http://localhost:8086 FRONT_URL=http://localhost:8080 diff --git a/products/tracker/src/platform-dev.ts b/products/tracker/src/platform-dev.ts index 3c89fbbea3..6fdfd3202a 100644 --- a/products/tracker/src/platform-dev.ts +++ b/products/tracker/src/platform-dev.ts @@ -20,9 +20,6 @@ import devmodel, { devModelId } from '@hcengineering/devmodel' import client from '@hcengineering/client' export function configurePlatformDevServer() { - console.log('Use Endpoint override:', process.env.LOGIN_ENDPOINT) - setMetadata(login.metadata.OverrideEndpoint, process.env.LOGIN_ENDPOINT) - // Set devmodel to hook client to be able to present all activity enableDevModel() } diff --git a/products/tracker/src/platform.ts b/products/tracker/src/platform.ts index df8793296b..f628ee5d0b 100644 --- a/products/tracker/src/platform.ts +++ b/products/tracker/src/platform.ts @@ -67,7 +67,6 @@ export async function configurePlatform() { } setMetadata(telegram.metadata.TelegramURL, process.env.TELEGRAM_URL ?? 'http://localhost:8086') setMetadata(gmail.metadata.GmailURL, process.env.GMAIL_URL ?? 'http://localhost:8087') - setMetadata(login.metadata.OverrideEndpoint, process.env.LOGIN_ENDPOINT) setMetadata(uiPlugin.metadata.DefaultApplication, workbench.component.WorkbenchApp) setMetadata(workbench.metadata.ExcludedApplications, [contact.app.Contacts]) diff --git a/qms-tests/docker-compose.yaml b/qms-tests/docker-compose.yaml index 7e9e1d7182..2527c23ed1 100644 --- a/qms-tests/docker-compose.yaml +++ b/qms-tests/docker-compose.yaml @@ -50,8 +50,7 @@ services: - ACCOUNT_PORT=3003 - SERVER_SECRET=secret - MONGO_URL=mongodb://mongodb:27018 - - TRANSACTOR_URL=ws://transactor:3334 - - ENDPOINT_URL=ws://localhost:3334 + - TRANSACTOR_URL=ws://transactor:3334;ws://localhost:3334 - STORAGE_CONFIG=${STORAGE_CONFIG} - MODEL_ENABLED=* - BRANDING_PATH=/var/cfg/branding-test.json @@ -125,7 +124,6 @@ services: - COLLABORATOR_PORT=3079 - SECRET=secret - ACCOUNTS_URL=http://account:3003 - - TRANSACTOR_URL=ws://transactor:3334 - UPLOAD_URL=/files - MONGO_URL=mongodb://mongodb:27018 - STORAGE_CONFIG=${STORAGE_CONFIG} @@ -147,7 +145,6 @@ services: # - account # environment: # - SECRET=secret - # - TRANSACTOR_URL=ws://transactor:3334 # - ACCOUNTS_URL=http://account:3003 volumes: files: diff --git a/qms-tests/tool.sh b/qms-tests/tool.sh index 05ca1b1889..539ef8ebde 100755 --- a/qms-tests/tool.sh +++ b/qms-tests/tool.sh @@ -3,8 +3,9 @@ export MINIO_ACCESS_KEY=minioadmin export MINIO_SECRET_KEY=minioadmin export MINIO_ENDPOINT=localhost:9002 -export MONGO_URL=mongodb://localhost:27018 +export ACCOUNTS_URL=http://localhost:3003 export TRANSACTOR_URL=ws://localhost:3334 +export MONGO_URL=mongodb://localhost:27018 export ELASTIC_URL=http://localhost:9201 export SERVER_SECRET=secret export STORAGE_CONFIG="minio|localhost:9002?accessKey=minioadmin&secretKey=minioadmin" diff --git a/rush.json b/rush.json index 1bb96d7222..be489dcfb6 100644 --- a/rush.json +++ b/rush.json @@ -1071,6 +1071,11 @@ "projectFolder": "server/tool", "shouldPublish": false }, + { + "packageName": "@hcengineering/server-client", + "projectFolder": "server/client", + "shouldPublish": false + }, { "packageName": "@hcengineering/tests-sanity", "projectFolder": "tests/sanity", diff --git a/server/account-service/package.json b/server/account-service/package.json index b95503530e..e0ced8ad59 100644 --- a/server/account-service/package.json +++ b/server/account-service/package.json @@ -28,10 +28,10 @@ "eslint-plugin-n": "^15.4.0", "eslint": "^8.54.0", "esbuild": "^0.20.0", - "@types/koa-bodyparser": "^4.3.3", - "@types/koa-router": "^7.4.4", - "@types/koa": "2.14.0", - "@types/koa__cors": "^3.0.3", + "@types/koa-bodyparser": "^4.3.12", + "@types/koa-router": "^7.4.8", + "@types/koa": "^2.15.0", + "@types/koa__cors": "^5.0.0", "@typescript-eslint/parser": "^6.11.0", "eslint-config-standard-with-typescript": "^40.0.0", "prettier": "^3.1.0", @@ -48,11 +48,12 @@ "@hcengineering/auth-providers": "^0.6.0", "@hcengineering/core": "^0.6.32", "mongodb": "^6.8.0", - "koa": "^2.13.1", + "koa": "^2.15.3", "koa-router": "^12.0.1", - "koa-bodyparser": "^4.3.0", - "@koa/cors": "^3.1.0", + "koa-bodyparser": "^4.4.1", + "@koa/cors": "^5.0.0", "@hcengineering/server-tool": "^0.6.0", + "@hcengineering/server-client": "^0.6.0", "@hcengineering/server-token": "^0.6.11", "@hcengineering/analytics": "^0.6.0" } diff --git a/server/account-service/src/index.ts b/server/account-service/src/index.ts index 84aa0d623c..40220d4ab8 100644 --- a/server/account-service/src/index.ts +++ b/server/account-service/src/index.ts @@ -23,6 +23,7 @@ import { } from '@hcengineering/core' import { type MigrateOperation } from '@hcengineering/model' import platform, { Severity, Status, addStringsLoader, setMetadata } from '@hcengineering/platform' +import serverClientPlugin from '@hcengineering/server-client' import serverToken, { decodeToken } from '@hcengineering/server-token' import toolPlugin from '@hcengineering/server-tool' import cors from '@koa/cors' @@ -60,8 +61,6 @@ export function serveAccount ( process.exit(1) } - const endpointUri = process.env.ENDPOINT_URL ?? transactorUri - const serverSecret = process.env.SERVER_SECRET if (serverSecret === undefined) { console.log('Please provide server secret') @@ -84,6 +83,7 @@ export function serveAccount ( const productName = process.env.PRODUCT_NAME const lang = process.env.LANGUAGE ?? 'en' + setMetadata(account.metadata.Transactors, transactorUri) setMetadata(platform.metadata.locale, lang) setMetadata(account.metadata.ProductName, productName) setMetadata(account.metadata.SES_URL, ses) @@ -99,9 +99,7 @@ export function serveAccount ( if (initScriptUrl !== undefined) { setMetadata(toolPlugin.metadata.InitScriptURL, initScriptUrl) } - setMetadata(toolPlugin.metadata.Endpoint, endpointUri) - setMetadata(toolPlugin.metadata.Transactor, transactorUri) - setMetadata(toolPlugin.metadata.UserAgent, 'AccountService') + setMetadata(serverClientPlugin.metadata.UserAgent, 'AccountService') let client: MongoClient | Promise = MongoClient.connect(dbUri) diff --git a/server/account/package.json b/server/account/package.json index e1b4b94d70..4d7dd3d117 100644 --- a/server/account/package.json +++ b/server/account/package.json @@ -32,7 +32,7 @@ "eslint-config-standard-with-typescript": "^40.0.0", "prettier": "^3.1.0", "typescript": "^5.3.3", - "@types/ws": "^8.5.3", + "@types/ws": "^8.5.11", "@types/node-fetch": "~2.6.2", "jest": "^29.7.0", "ts-jest": "^29.1.1", @@ -45,7 +45,7 @@ "@hcengineering/contact": "^0.6.24", "@hcengineering/client-resources": "^0.6.27", "@hcengineering/client": "^0.6.18", - "ws": "^8.16.0", + "ws": "^8.18.0", "@hcengineering/model": "^0.6.11", "@hcengineering/server-backup": "^0.6.0", "@hcengineering/server-tool": "^0.6.0", diff --git a/server/account/src/operations.ts b/server/account/src/operations.ts index 657bb11642..24cf146bc8 100644 --- a/server/account/src/operations.ts +++ b/server/account/src/operations.ts @@ -33,6 +33,7 @@ import core, { Data, generateId, getWorkspaceId, + groupByArray, MeasureContext, MeasureMetricsContext, RateLimiter, @@ -80,26 +81,71 @@ import { accountPlugin } from './plugin' const WORKSPACE_COLLECTION = 'workspace' const ACCOUNT_COLLECTION = 'account' const INVITE_COLLECTION = 'invite' - /** * @public */ export const ACCOUNT_DB = 'account' -const getEndpoint = (): string => { - const endpoint = getMetadata(toolPlugin.metadata.Endpoint) - if (endpoint === undefined) { - throw new Error('Please provide transactor endpoint url') - } - return endpoint +/** + * Returns a hash code for a string. + * (Compatible to Java's String.hashCode()) + * + * The hash code for a string object is computed as + * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] + * using number arithmetic, where s[i] is the i th character + * of the given string, n is the length of the string, + * and ^ indicates exponentiation. + * (The hash value of the empty string is zero.) + * + */ +function hashWorkspace (workspace: Workspace): number { + return [...workspace.workspace].reduce((hash, c) => (Math.imul(31, hash) + c.charCodeAt(0)) | 0, 0) } -const getTransactor = (): string => { - const transactor = getMetadata(toolPlugin.metadata.Transactor) - if (transactor === undefined) { - throw new Error('Please provide transactor url') +export enum EndpointKind { + Internal, + External +} + +const getEndpoint = (ctx: MeasureContext, workspaceInfo: Workspace, kind: EndpointKind): string => { + const transactorsUrl = getMetadata(accountPlugin.metadata.Transactors) + if (transactorsUrl === undefined) { + throw new Error('Please provide transactor endpoint url') } - return transactor + const endpoints = transactorsUrl + .split(',') + .map((it) => it.trim()) + .filter((it) => it.length > 0) + + if (endpoints.length === 0) { + throw new Error('Please provide transactor endpoint url') + } + + const toTransactor = (line: string): { internalUrl: string, group: string, externalUrl: string } => { + const [internalUrl, externalUrl, group] = line.split(';') + return { internalUrl, group: group ?? '', externalUrl: externalUrl ?? internalUrl } + } + + const groups = groupByArray(endpoints.map(toTransactor), (it) => it.group) + let transactors = (groups.get(workspaceInfo.transactor ?? '') ?? []) + .map((it) => (kind === EndpointKind.Internal ? it.internalUrl : it.externalUrl)) + .flat() + + // This is really bad + if (transactors.length === 0) { + ctx.error('No transactors for group, will use default group', { group: workspaceInfo.transactor }) + } + transactors = (groups.get('') ?? []) + .map((it) => (kind === EndpointKind.Internal ? it.internalUrl : it.externalUrl)) + .flat() + + if (transactors.length === 0) { + ctx.error('No transactors for default group', { group: workspaceInfo.transactor }) + throw new Error('Please provide transactor endpoint url') + } + + const hash = hashWorkspace(workspaceInfo) + return transactors[Math.abs(hash % transactors.length)] } /** @@ -128,6 +174,8 @@ export interface Account { export interface Workspace extends BaseWorkspaceInfo { _id: ObjectId accounts: ObjectId[] + + transactor?: string // Transactor group name } /** @@ -281,8 +329,9 @@ async function getAccountInfoByToken ( token: string ): Promise { let email: string = '' + let workspace: WorkspaceId try { - email = decodeToken(token)?.email + ;({ email, workspace } = decodeToken(token)) } catch (err: any) { Analytics.handleError(err) ctx.error('Invalid token', { token }) @@ -293,8 +342,10 @@ async function getAccountInfoByToken ( throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account: email })) } const info = toAccountInfo(account) + + const workspaceInfo = await getWorkspaceById(db, productId, workspace.name) const result = { - endpoint: getEndpoint(), + endpoint: workspaceInfo != null ? getEndpoint(ctx, workspaceInfo, EndpointKind.External) : '', email, confirmed: info.confirmed ?? true, token: generateToken(email, getWorkspaceId('', productId), getExtra(info)) @@ -322,7 +373,7 @@ export async function login ( try { const info = await getAccountInfo(ctx, db, productId, branding, email, password) const result = { - endpoint: getEndpoint(), + endpoint: '', email, confirmed: info.confirmed ?? true, token: generateToken(email, getWorkspaceId('', productId), getExtra(info)) @@ -348,6 +399,8 @@ function getExtra (info: Account | AccountInfo | null, rec?: Record return res } +export const guestAccountEmail = '#guest@hc.engineering' + /** * @public */ @@ -358,22 +411,56 @@ export async function selectWorkspace ( branding: Branding | null, token: string, workspaceUrl: string, + kind: 'external' | 'internal', allowAdmin: boolean = true ): Promise { - let { email } = decodeToken(token) - email = cleanEmail(email) - const accountInfo = await getAccount(db, email) - if (accountInfo === null) { - throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account: email })) + const decodedToken = decodeToken(token) + const email = cleanEmail(decodedToken.email) + + const endpointKind = kind === 'external' ? EndpointKind.External : EndpointKind.Internal + + if (email === guestAccountEmail && decodedToken.extra?.guest === 'true') { + const workspaceInfo = await getWorkspaceByUrl(db, productId, workspaceUrl) + if (workspaceInfo == null) { + throw new PlatformError( + new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspace: workspaceUrl }) + ) + } + // Guest mode select workspace + return { + endpoint: getEndpoint(ctx, workspaceInfo, kind === 'external' ? EndpointKind.External : EndpointKind.Internal), + email, + token, + workspace: workspaceUrl, + productId, + creating: workspaceInfo.creating, + createProgress: workspaceInfo.createProgress + } } - const workspaceInfo = await getWorkspaceByUrl(db, productId, workspaceUrl) + let accountInfo: Account | null = null + if (email !== systemAccountEmail) { + accountInfo = await getAccount(db, email) + + if (accountInfo === null) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account: email })) + } + } + + let workspaceInfo: Workspace | null + if (workspaceUrl === '') { + // Find from token + workspaceInfo = await getWorkspaceById(db, decodedToken.workspace.productId, decodedToken.workspace.name) + } else { + workspaceInfo = await getWorkspaceByUrl(db, productId, workspaceUrl) + } if (workspaceInfo == null) { throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspace: workspaceUrl })) } - if (accountInfo.admin === true && allowAdmin) { + + if ((accountInfo?.admin === true || email === systemAccountEmail) && allowAdmin) { return { - endpoint: getEndpoint(), + endpoint: getEndpoint(ctx, workspaceInfo, endpointKind), email, token: generateToken(email, getWorkspaceId(workspaceInfo.workspace, productId), getExtra(accountInfo)), workspace: workspaceUrl, @@ -390,12 +477,12 @@ export async function selectWorkspace ( new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspace: workspaceUrl }) ) } - const workspaces = accountInfo.workspaces + const workspaces = accountInfo?.workspaces ?? [] for (const w of workspaces) { if (w.equals(workspaceInfo._id)) { const result = { - endpoint: getEndpoint(), + endpoint: getEndpoint(ctx, workspaceInfo, endpointKind), email, token: generateToken(email, getWorkspaceId(workspaceInfo.workspace, productId), getExtra(accountInfo)), workspace: workspaceUrl, @@ -476,7 +563,7 @@ export async function join ( ) const token = (await login(ctx, db, productId, branding, email, password)).token - const result = await selectWorkspace(ctx, db, productId, branding, token, ws.workspaceUrl ?? ws.workspace) + const result = await selectWorkspace(ctx, db, productId, branding, token, ws.workspaceUrl ?? ws.workspace, 'external') await useInvite(db, inviteId) return result } @@ -519,9 +606,9 @@ export async function confirm ( } const email = cleanEmail(_email) const account = await confirmEmail(db, email) - + const workspaceInfo = await getWorkspaceById(db, productId, decode.workspace.name) const result = { - endpoint: getEndpoint(), + endpoint: workspaceInfo != null ? getEndpoint(ctx, workspaceInfo, EndpointKind.External) : '', email, token: generateToken(email, getWorkspaceId('', productId), getExtra(account)) } @@ -615,7 +702,7 @@ export async function signUpJoin ( ) const token = (await login(ctx, db, productId, branding, email, password)).token - const result = await selectWorkspace(ctx, db, productId, branding, token, ws.workspaceUrl ?? ws.workspace) + const result = await selectWorkspace(ctx, db, productId, branding, token, ws.workspaceUrl ?? ws.workspace, 'external') await useInvite(db, inviteId) return result } @@ -708,7 +795,7 @@ export async function createAccount ( ) const result = { - endpoint: getEndpoint(), + endpoint: '', email, token: generateToken(email, getWorkspaceId('', productId), getExtra(account)) } @@ -832,7 +919,7 @@ async function generateWorkspaceRecord ( workspaceName: string, fixedWorkspace?: string ): Promise { - const coll = db.collection>(WORKSPACE_COLLECTION) + const coll = db.collection>(WORKSPACE_COLLECTION) const brandingKey = branding?.key ?? 'huly' if (fixedWorkspace !== undefined) { const ws = await coll.find({ workspaceUrl: fixedWorkspace }).toArray() @@ -858,7 +945,7 @@ async function generateWorkspaceRecord ( } // Add fixed workspace const id = await coll.insertOne(data) - return { _id: id.insertedId, ...data } + return { _id: id.insertedId, ...data, endpoint: '' } } const workspaceUrlPrefix = stripId(workspaceName) const workspaceIdPrefix = stripId(getEmailName(email)).slice(0, 12) + '-' + workspaceUrlPrefix.slice(0, 12) @@ -891,7 +978,7 @@ async function generateWorkspaceRecord ( } // Nice we do not have a workspace or workspaceUrl duplicated. const id = await coll.insertOne(data) - return { _id: id.insertedId, ...data } + return { _id: id.insertedId, ...data, endpoint: '' } } for (const w of ws) { if (w.workspace === workspaceUrl) { @@ -1125,7 +1212,7 @@ export async function upgradeWorkspace ( }) await upgradeModel( ctx, - getTransactor(), + getEndpoint(ctx, ws, EndpointKind.Internal), getWorkspaceId(ws.workspace, productId), txes, migrationOperation, @@ -1194,7 +1281,7 @@ export const createUserWorkspace = const initWS = branding?.initWorkspace ?? getMetadata(toolPlugin.metadata.InitWorkspace) const shouldUpdateAccount = initWS !== undefined const client = await connect( - getTransactor(), + getEndpoint(ctx, workspace, EndpointKind.Internal), getWorkspaceId(workspace.workspace, productId), undefined, { @@ -1251,7 +1338,7 @@ export const createUserWorkspace = await assignWorkspaceRaw(db, { account: info, workspace: workspaceInfo }) const result = { - endpoint: getEndpoint(), + endpoint: getEndpoint(ctx, workspaceInfo, EndpointKind.External), email, token: generateToken(email, getWorkspaceId(workspaceInfo.workspace, productId), getExtra(info)), productId, @@ -1385,7 +1472,7 @@ export async function getWorkspaceInfo ( query._id = { $in: account.workspaces } } - const [ws] = await ctx.with('get-account', {}, async () => + const [ws] = await ctx.with('get-workspace', {}, async () => (await db.collection(WORKSPACE_COLLECTION).find(withProductId(productId, query)).toArray()).filter( (it) => it.disabled !== true || account?.admin === true || it.creating === true ) @@ -1437,6 +1524,8 @@ async function getWorkspaceAndAccount ( * @public */ export async function setRole ( + ctx: MeasureContext, + db: Db, _email: string, workspace: string, productId: string, @@ -1445,7 +1534,13 @@ export async function setRole ( ): Promise { if (!Object.values(AccountRole).includes(role)) return const email = cleanEmail(_email) - const connection = client ?? (await connect(getTransactor(), getWorkspaceId(workspace, productId))) + const workspaceInfo = await getWorkspaceById(db, productId, workspace) + if (workspaceInfo == null) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspace })) + } + const connection = + client ?? + (await connect(getEndpoint(ctx, workspaceInfo, EndpointKind.Internal), getWorkspaceId(workspace, productId))) try { const ops = new TxOperations(connection, core.account.System) @@ -1481,7 +1576,7 @@ export async function createMissingEmployee ( throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account: email })) } - await createPersonAccount(account, productId, wsInfo.workspaceId, AccountRole.Guest) + await createPersonAccount(ctx, db, account, productId, wsInfo.workspaceId, AccountRole.Guest) } /** @@ -1511,6 +1606,8 @@ export async function assignWorkspace ( if (workspaceInfo.account !== null) { await createPersonAccount( + ctx, + db, workspaceInfo.account, productId, workspaceId, @@ -1631,6 +1728,8 @@ async function replaceCurrentAccount ( } async function createPersonAccount ( + ctx: MeasureContext, + db: Db, account: Account, productId: string, workspace: string, @@ -1640,7 +1739,13 @@ async function createPersonAccount ( client?: Client, personAccountId?: Ref ): Promise { - const connection = client ?? (await connect(getTransactor(), getWorkspaceId(workspace, productId))) + const workspaceInfo = await getWorkspaceById(db, productId, workspace) + if (workspaceInfo == null) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspace })) + } + const connection = + client ?? + (await connect(getEndpoint(ctx, workspaceInfo, EndpointKind.Internal), getWorkspaceId(workspace, productId))) try { const ops = new TxOperations(connection, core.account.System) @@ -1881,7 +1986,7 @@ export async function checkJoin ( new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspace: workspace.name }) ) } - return await selectWorkspace(ctx, db, productId, branding, token, ws?.workspaceUrl ?? ws.workspace, false) + return await selectWorkspace(ctx, db, productId, branding, token, ws?.workspaceUrl ?? ws.workspace, 'external', false) } /** @@ -1952,7 +2057,7 @@ export async function dropAccount ( await Promise.all( workspaces.map(async (ws) => { - await deactivatePersonAccount(ctx, account.email, ws.workspace, productId) + await deactivatePersonAccount(ctx, db, account.email, ws.workspace, productId) }) ) @@ -1988,7 +2093,7 @@ export async function leaveWorkspace ( ) } - await deactivatePersonAccount(ctx, email, workspace.workspace, workspace.productId) + await deactivatePersonAccount(ctx, db, email, workspace.workspace, workspace.productId) const account = tokenData.email !== email ? await getAccount(db, email) : currentAccount if (account !== null) { @@ -2071,11 +2176,19 @@ export async function sendInvite ( async function deactivatePersonAccount ( ctx: MeasureContext, + db: Db, email: string, workspace: string, productId: string ): Promise { - const connection = await connect(getTransactor(), getWorkspaceId(workspace, productId)) + const workspaceInfo = await getWorkspaceById(db, productId, workspace) + if (workspaceInfo == null) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspace })) + } + const connection = await connect( + getEndpoint(ctx, workspaceInfo, EndpointKind.Internal), + getWorkspaceId(workspace, productId) + ) try { const ops = new TxOperations(connection, core.account.System) @@ -2176,9 +2289,9 @@ export async function joinWithProvider ( const token = generateToken(email, getWorkspaceId('', productId), getExtra(account)) const ws = await getWorkspaceById(db, productId, workspace.name) - if (ws?.accounts.includes(account._id) ?? false) { + if (ws != null && ws.accounts.includes(account._id)) { const result = { - endpoint: getEndpoint(), + endpoint: getEndpoint(ctx, ws, EndpointKind.External), email, token } @@ -2202,6 +2315,7 @@ export async function joinWithProvider ( branding, token, wsRes.workspaceUrl ?? wsRes.workspace, + 'external', false ) @@ -2220,7 +2334,16 @@ export async function joinWithProvider ( invite?.role ?? AccountRole.User, invite?.personId ) - const result = await selectWorkspace(ctx, db, productId, branding, token, ws.workspaceUrl ?? ws.workspace, false) + const result = await selectWorkspace( + ctx, + db, + productId, + branding, + token, + ws.workspaceUrl ?? ws.workspace, + 'external', + false + ) await useInvite(db, inviteId) @@ -2257,7 +2380,7 @@ export async function loginWithProvider ( await updatePassword(db, account, null) } const result = { - endpoint: getEndpoint(), + endpoint: '', email, token: generateToken(email, getWorkspaceId('', productId), getExtra(account)) } @@ -2266,7 +2389,7 @@ export async function loginWithProvider ( const newAccount = await createAcc(ctx, db, productId, branding, email, null, first, last, true, extra) const result = { - endpoint: getEndpoint(), + endpoint: '', email, token: generateToken(email, getWorkspaceId('', productId), getExtra(newAccount)) } @@ -2287,7 +2410,6 @@ export function getMethods ( migrateOperations: [string, MigrateOperation][] ): Record { return { - getEndpoint: wrap(async () => getEndpoint()), login: wrap(login), join: wrap(join), checkJoin: wrap(checkJoin), diff --git a/server/account/src/plugin.ts b/server/account/src/plugin.ts index 0f5df6979e..56c110535f 100644 --- a/server/account/src/plugin.ts +++ b/server/account/src/plugin.ts @@ -12,7 +12,8 @@ export const accountPlugin = plugin(accountId, { metadata: { FrontURL: '' as Metadata, SES_URL: '' as Metadata, - ProductName: '' as Metadata + ProductName: '' as Metadata, + Transactors: '' as Metadata }, string: { ConfirmationText: '' as IntlString, diff --git a/server/account/src/service.ts b/server/account/src/service.ts index bf1f07eecc..3658054c37 100644 --- a/server/account/src/service.ts +++ b/server/account/src/service.ts @@ -112,7 +112,7 @@ export class UpgradeWorker { logger.log('error', err) if (!opt.console) { - ctx.error('error', err) + ctx.error('error', { err }) } ctx.info('---failed---------', { diff --git a/server/backup-service/package.json b/server/backup-service/package.json index 4315d2b4a6..d048040661 100644 --- a/server/backup-service/package.json +++ b/server/backup-service/package.json @@ -52,6 +52,7 @@ "@hcengineering/server-storage": "^0.6.0", "@hcengineering/server-backup": "^0.6.0", "@hcengineering/minio": "^0.6.0", - "@hcengineering/server-token": "^0.6.11" + "@hcengineering/server-token": "^0.6.11", + "@hcengineering/server-client": "^0.6.0" } } diff --git a/server/backup-service/src/config.ts b/server/backup-service/src/config.ts index 192bbbde4c..c8c75a628a 100644 --- a/server/backup-service/src/config.ts +++ b/server/backup-service/src/config.ts @@ -16,7 +16,6 @@ import { type BackupConfig } from '@hcengineering/server-backup' interface Config extends Omit { - TransactorURL: string AccountsURL: string ServiceID: string Secret: string @@ -33,7 +32,6 @@ interface Config extends Omit { } const envMap: { [key in keyof Config]: string } = { - TransactorURL: 'TRANSACTOR_URL', AccountsURL: 'ACCOUNTS_URL', ServiceID: 'SERVICE_ID', Secret: 'SECRET', @@ -47,7 +45,6 @@ const envMap: { [key in keyof Config]: string } = { } const required: Array = [ - 'TransactorURL', 'AccountsURL', 'Secret', 'ServiceID', @@ -59,7 +56,6 @@ const required: Array = [ const config: Config = (() => { const params: Partial = { - TransactorURL: process.env[envMap.TransactorURL], AccountsURL: process.env[envMap.AccountsURL], Secret: process.env[envMap.Secret], BucketName: process.env[envMap.BucketName] ?? 'backups', diff --git a/server/backup-service/src/index.ts b/server/backup-service/src/index.ts index 8ea3d7ecdb..a7a66fa15b 100644 --- a/server/backup-service/src/index.ts +++ b/server/backup-service/src/index.ts @@ -16,10 +16,10 @@ import { MeasureContext, systemAccountEmail } from '@hcengineering/core' import { setMetadata } from '@hcengineering/platform' import { backupService } from '@hcengineering/server-backup' +import serverClientPlugin from '@hcengineering/server-client' import { type PipelineFactory, type StorageAdapter } from '@hcengineering/server-core' -import { buildStorageFromConfig, storageConfigFromEnv, createStorageFromConfig } from '@hcengineering/server-storage' +import { buildStorageFromConfig, createStorageFromConfig, storageConfigFromEnv } from '@hcengineering/server-storage' import serverToken, { generateToken } from '@hcengineering/server-token' -import toolPlugin from '@hcengineering/server-tool' import config from './config' export function startBackup ( @@ -27,6 +27,8 @@ export function startBackup ( pipelineFactoryFactory: (mongoUrl: string, storage: StorageAdapter) => PipelineFactory ): void { setMetadata(serverToken.metadata.Secret, config.Secret) + setMetadata(serverClientPlugin.metadata.Endpoint, config.AccountsURL) + setMetadata(serverClientPlugin.metadata.UserAgent, config.ServiceID) const backupStorageConfig = storageConfigFromEnv(config.Storage) const workspaceStorageConfig = storageConfigFromEnv(config.WorkspaceStorage) @@ -34,8 +36,6 @@ export function startBackup ( const storageAdapter = createStorageFromConfig(backupStorageConfig.storages[0]) const workspaceStorageAdapter = buildStorageFromConfig(workspaceStorageConfig, config.MongoURL) - setMetadata(toolPlugin.metadata.UserAgent, config.ServiceID) - const pipelineFactory = pipelineFactoryFactory(config.MongoURL, workspaceStorageAdapter) // A token to access account service diff --git a/server/backup/package.json b/server/backup/package.json index ddb895f7c2..16a0bc55bc 100644 --- a/server/backup/package.json +++ b/server/backup/package.json @@ -49,6 +49,8 @@ "@hcengineering/analytics": "^0.6.0", "tar-stream": "^2.2.0", "@hcengineering/server-tool": "^0.6.0", + "@hcengineering/server-client": "^0.6.0", + "@hcengineering/server-token": "^0.6.11", "@hcengineering/server-core": "^0.6.1" } } diff --git a/server/backup/src/backup.ts b/server/backup/src/backup.ts index c451fe9b24..bf9bf3d7ea 100644 --- a/server/backup/src/backup.ts +++ b/server/backup/src/backup.ts @@ -31,13 +31,16 @@ import core, { RateLimiter, Ref, SortingOrder, + systemAccountEmail, TxCollectionCUD, WorkspaceId, type Blob, type DocIndexState } from '@hcengineering/core' +import { BlobClient, createClient } from '@hcengineering/server-client' import { fullTextPushStagePrefix, type StorageAdapter } from '@hcengineering/server-core' -import { BlobClient, connect } from '@hcengineering/server-tool' +import { generateToken } from '@hcengineering/server-token' +import { connect } from '@hcengineering/server-tool' import { mkdtemp, writeFile } from 'node:fs/promises' import { PassThrough } from 'node:stream' import { createGzip } from 'node:zlib' @@ -531,20 +534,16 @@ export async function backup ( canceled = true }, options.timeout) } + + const token = generateToken(systemAccountEmail, workspaceId, { + mode: 'backup' + }) + const connection = options.connection ?? - ((await connect( - transactorUrl, - workspaceId, - undefined, - { - mode: 'backup' - }, - undefined, - options.connectTimeout - )) as unknown as CoreClient & BackupClient) + ((await createClient(transactorUrl, token, undefined, options.connectTimeout)) as CoreClient & BackupClient) - const blobClient = new BlobClient(transactorUrl, workspaceId, { storageAdapter: options.storageAdapter }) + const blobClient = new BlobClient(transactorUrl, token, workspaceId, { storageAdapter: options.storageAdapter }) ctx.info('starting backup', { workspace: workspaceId.name }) let tmpDir: string | undefined @@ -1121,12 +1120,15 @@ export async function restore ( } ctx.info('connecting:', { transactorUrl, workspace: workspaceId.name }) - const connection = (await connect(transactorUrl, workspaceId, undefined, { + + const token = generateToken(systemAccountEmail, workspaceId, { mode: 'backup', model: 'upgrade' - })) as unknown as CoreClient & BackupClient + }) - const blobClient = new BlobClient(transactorUrl, workspaceId) + const connection = (await createClient(transactorUrl, token)) as CoreClient & BackupClient + + const blobClient = new BlobClient(transactorUrl, token, workspaceId) console.log('connected') // We need to find empty domains and clean them. diff --git a/server/backup/src/service.ts b/server/backup/src/service.ts index d0113e19c3..ebde81abc6 100644 --- a/server/backup/src/service.ts +++ b/server/backup/src/service.ts @@ -22,6 +22,7 @@ import { type MeasureContext, type WorkspaceIdWithUrl } from '@hcengineering/core' +import { listAccountWorkspaces } from '@hcengineering/server-client' import { BackupClientOps, SessionContextImpl, @@ -31,26 +32,7 @@ import { } from '@hcengineering/server-core' import { backup } from '.' import { createStorageBackupStorage } from './storage' - -async function getWorkspaces (accounts: string, token: string): Promise { - const workspaces = await ( - await fetch(accounts, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - method: 'listWorkspaces', - params: [token] - }) - }) - ).json() - - return (workspaces.result as BaseWorkspaceInfo[]) ?? [] -} - export interface BackupConfig { - TransactorURL: string AccountsURL: string Token: string @@ -99,7 +81,7 @@ class BackupWorker { async backup (ctx: MeasureContext): Promise { const workspacesIgnore = new Set(this.config.SkipWorkspaces.split(';')) - const workspaces = (await getWorkspaces(this.config.AccountsURL, this.config.Token)).filter((it) => { + const workspaces = (await listAccountWorkspaces(this.config.Token)).filter((it) => { return !workspacesIgnore.has(it.workspace) }) workspaces.sort((a, b) => b.lastVisit - a.lastVisit) @@ -140,8 +122,9 @@ class BackupWorker { workspaceUrl: ws.workspaceUrl ?? '' } const pipeline = await this.pipelineFactory(ctx, wsUrl, true, () => {}, null) + await ctx.with('backup', { workspace: ws.workspace }, async (ctx) => { - await backup(ctx, this.config.TransactorURL, getWorkspaceId(ws.workspace, ws.productId), storage, { + await backup(ctx, ws.endpoint, getWorkspaceId(ws.workspace, ws.productId), storage, { skipDomains: [], force: false, recheck: false, diff --git a/server/client/.eslintrc.js b/server/client/.eslintrc.js new file mode 100644 index 0000000000..72235dc283 --- /dev/null +++ b/server/client/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/server/client/.npmignore b/server/client/.npmignore new file mode 100644 index 0000000000..e3ec093c38 --- /dev/null +++ b/server/client/.npmignore @@ -0,0 +1,4 @@ +* +!/lib/** +!CHANGELOG.md +/lib/**/__tests__/ diff --git a/server/client/config/rig.json b/server/client/config/rig.json new file mode 100644 index 0000000000..0110930f55 --- /dev/null +++ b/server/client/config/rig.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig" +} diff --git a/server/client/jest.config.js b/server/client/jest.config.js new file mode 100644 index 0000000000..2cfd408b67 --- /dev/null +++ b/server/client/jest.config.js @@ -0,0 +1,7 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + roots: ["./src"], + coverageReporters: ["text-summary", "html"] +} diff --git a/server/client/package.json b/server/client/package.json new file mode 100644 index 0000000000..4c90504459 --- /dev/null +++ b/server/client/package.json @@ -0,0 +1,50 @@ +{ + "name": "@hcengineering/server-client", + "version": "0.6.0", + "main": "lib/index.js", + "svelte": "src/index.ts", + "types": "types/index.d.ts", + "files": [ + "lib/**/*", + "types/**/*", + "tsconfig.json" + ], + "author": "Anticrm Platform Contributors", + "license": "EPL-2.0", + "scripts": { + "build": "compile", + "build:watch": "compile", + "format": "format src", + "test": "jest --passWithNoTests --silent", + "_phase:build": "compile transpile src", + "_phase:test": "jest --passWithNoTests --silent", + "_phase:format": "format src", + "_phase:validate": "compile validate" + }, + "devDependencies": { + "@hcengineering/platform-rig": "^0.6.0", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-n": "^15.4.0", + "eslint": "^8.54.0", + "@typescript-eslint/parser": "^6.11.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "prettier": "^3.1.0", + "typescript": "^5.3.3", + "@types/uuid": "^8.3.1", + "@types/ws": "^8.5.11", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "@types/jest": "^29.5.5", + "@types/node": "~20.11.16" + }, + "dependencies": { + "@hcengineering/platform": "^0.6.11", + "@hcengineering/core": "^0.6.32", + "@hcengineering/client-resources": "^0.6.27", + "@hcengineering/client": "^0.6.18", + "@hcengineering/server-core": "^0.6.1", + "ws": "^8.18.0" + } +} diff --git a/server/client/src/index.ts b/server/client/src/index.ts new file mode 100644 index 0000000000..b209aa772a --- /dev/null +++ b/server/client/src/index.ts @@ -0,0 +1,360 @@ +// +// Copyright © 2020, 2021 Anticrm Platform Contributors. +// Copyright © 2021, 2022 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import client, { clientId } from '@hcengineering/client' +import { + Client, + LoadModelResponse, + Tx, + WorkspaceId, + type BaseWorkspaceInfo, + type MeasureContext +} from '@hcengineering/core' +import { + addLocation, + getMetadata, + getResource, + PlatformError, + setMetadata, + unknownError +} from '@hcengineering/platform' +import type { StorageAdapter } from '@hcengineering/server-core' +import { Buffer } from 'node:buffer' +import crypto from 'node:crypto' +import plugin from './plugin' + +export default plugin + +export async function listAccountWorkspaces (token: string): Promise { + const accountsUrl = getMetadata(plugin.metadata.Endpoint) + if (accountsUrl == null) { + throw new PlatformError(unknownError('No endpoint specified')) + } + const workspaces = await ( + await fetch(accountsUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + method: 'listWorkspaces', + params: [token] + }) + }) + ).json() + + return (workspaces.result as BaseWorkspaceInfo[]) ?? [] +} + +export async function getTransactorEndpoint ( + token: string, + kind: 'internal' | 'external' = 'internal', + timeout: number = -1 +): Promise { + const accountsUrl = getMetadata(plugin.metadata.Endpoint) + if (accountsUrl == null) { + throw new PlatformError(unknownError('No endpoint specified')) + } + + const st = Date.now() + while (true) { + try { + const workspaceInfo: { result: BaseWorkspaceInfo } = await ( + await fetch(accountsUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer ' + token + }, + body: JSON.stringify({ + method: 'selectWorkspace', + params: ['', kind] + }) + }) + ).json() + return workspaceInfo.result.endpoint + } catch (err: any) { + if (timeout > 0 && st + timeout < Date.now()) { + // Timeout happened + throw err + } + if (err?.cause?.code === 'ECONNRESET' || err?.cause?.code === 'ECONNREFUSED') { + await new Promise((resolve) => setTimeout(resolve, 1000)) + } else { + throw err + } + } + } +} + +/** + * @public + * + * If connectTimeout is set, connect will try to connect only specified amount of time, and will return failure if failed. + */ +export async function createClient ( + transactorUrl: string, + token: string, + model?: Tx[], + connectTimeout: number = 0 +): Promise { + // We need to override default factory with 'ws' one. + // eslint-disable-next-line + const WebSocket = require('ws') + + setMetadata(client.metadata.UseBinaryProtocol, true) + setMetadata(client.metadata.UseProtocolCompression, true) + setMetadata(client.metadata.ConnectionTimeout, connectTimeout) + + setMetadata(client.metadata.ClientSocketFactory, (url) => { + const socket = new WebSocket(url, { + headers: { + 'User-Agent': getMetadata(plugin.metadata.UserAgent) ?? 'Anticrm Client' + } + }) + return socket + }) + addLocation(clientId, () => import('@hcengineering/client-resources')) + + if (model !== undefined) { + let prev = '' + const hashes = model.map((it) => { + const h = crypto.createHash('sha1') + h.update(prev) + h.update(JSON.stringify(it)) + prev = h.digest('hex') + return prev + }) + setMetadata(client.metadata.OverridePersistenceStore, { + load: async () => ({ + hash: hashes[hashes.length - 1], + transactions: model, + full: true + }), + store: async (model: LoadModelResponse) => {} + }) + } + + const clientFactory = await getResource(client.function.GetClient) + return await clientFactory(token, transactorUrl) +} + +// Will use temporary file to store huge content into +export class BlobClient { + transactorAPIUrl: string + index: number + constructor ( + readonly transactorUrl: string, + readonly token: string, + readonly workspace: WorkspaceId, + readonly opt?: { + storageAdapter?: StorageAdapter + } + ) { + this.index = 0 + let url = transactorUrl + if (url.endsWith('/')) { + url = url.slice(0, url.length - 1) + } + + this.transactorAPIUrl = url.replaceAll('wss://', 'https://').replace('ws://', 'http://') + '/api/v1/blob' + } + + async checkFile (ctx: MeasureContext, name: string): Promise { + if (this.opt?.storageAdapter !== undefined) { + const obj = await this.opt?.storageAdapter.stat(ctx, this.workspace, name) + if (obj !== undefined) { + return true + } + } + for (let i = 0; i < 5; i++) { + try { + const response = await fetch(this.transactorAPIUrl + `?name=${encodeURIComponent(name)}`, { + headers: { + Authorization: 'Bearer ' + this.token, + Range: 'bytes=0-1' + } + }) + if (response.status === 404) { + return false + } + const buff = await response.arrayBuffer() + return buff.byteLength > 0 + } catch (err: any) { + if (i === 4) { + ctx.error('Failed to check file', { name, error: err }) + } + await new Promise((resolve) => setTimeout(resolve, 500)) + } + } + return false + } + + async writeTo ( + ctx: MeasureContext, + name: string, + size: number, + writable: { + write: (buffer: Buffer, cb: (err?: any) => void) => void + end: (cb: () => void) => void + } + ): Promise { + let written = 0 + const chunkSize = 50 * 1024 * 1024 + let writtenMb = 0 + + // Use ranges to iterave through file with retry if required. + while (written < size || size === -1) { + let i = 0 + let response: Response | undefined + for (; i < 5; i++) { + try { + const st = Date.now() + let chunk: Buffer + + if (this.opt?.storageAdapter !== undefined) { + const chunks: Buffer[] = [] + const readable = await this.opt.storageAdapter.partial(ctx, this.workspace, name, written, chunkSize) + await new Promise((resolve) => { + readable.on('data', (chunk) => { + chunks.push(chunk) + }) + readable.on('end', () => { + resolve() + }) + }) + chunk = Buffer.concat(chunks) + } else { + const header: Record = { + Authorization: 'Bearer ' + this.token + } + + if (!(size !== -1 && written === 0 && size < chunkSize)) { + header.Range = `bytes=${written}-${size === -1 ? written + chunkSize : Math.min(size - 1, written + chunkSize)}` + } + + response = await fetch(this.transactorAPIUrl + `?name=${encodeURIComponent(name)}`, { headers: header }) + if (header.Range != null) { + ctx.info('fetch part', { time: Date.now() - st, blobId: name, written, size }) + } + if (response.status === 403) { + i = 5 + // No file, so make it empty + throw new Error(`Unauthorized ${this.transactorAPIUrl}/${this.workspace.name}/${name}`) + } + if (response.status === 404) { + i = 5 + // No file, so make it empty + throw new Error(`No file for ${this.transactorAPIUrl}/${this.workspace.name}/${name}`) + } + if (response.status === 416) { + if (size === -1) { + size = parseInt((response.headers.get('content-range') ?? '').split('*/')[1]) + continue + } + + // No file, so make it empty + throw new Error(`No file for ${this.transactorAPIUrl}/${this.workspace.name}/${name}`) + } + chunk = Buffer.from(await response.arrayBuffer()) + + if (header.Range == null) { + size = chunk.length + } + // We need to parse + // 'Content-Range': `bytes ${start}-${end}/${size}` + // To determine if something is left + const range = response.headers.get('Content-Range') + if (range !== null) { + const [, total] = range.split(' ')[1].split('/') + if (total !== undefined) { + size = parseInt(total) + } + } + } + + await new Promise((resolve, reject) => { + writable.write(chunk, (err) => { + if (err != null) { + reject(err) + } + resolve() + }) + }) + + written += chunk.length + const newWrittenMb = Math.round(written / (1024 * 1024)) + const newWrittenId = Math.round(newWrittenMb / 5) + if (writtenMb !== newWrittenId) { + writtenMb = newWrittenId + ctx.info(' >>>>Chunk', { + name, + written: newWrittenMb, + of: Math.round(size / (1024 * 1024)) + }) + } + break + } catch (err: any) { + if (err?.code === 'NoSuchKey') { + ctx.info('No such key', { name }) + return + } + if (i > 4) { + await new Promise((resolve) => { + writable.end(resolve) + }) + throw err + } + await new Promise((resolve) => setTimeout(resolve, 1000)) + // retry + } + } + } + await new Promise((resolve) => { + writable.end(resolve) + }) + } + + async upload (ctx: MeasureContext, name: string, size: number, contentType: string, buffer: Buffer): Promise { + if (this.opt?.storageAdapter !== undefined) { + await this.opt.storageAdapter.put(ctx, this.workspace, name, buffer, contentType, size) + } else { + // TODO: We need to improve this logig, to allow restore of huge blobs + for (let i = 0; i < 5; i++) { + try { + await fetch( + this.transactorAPIUrl + + `?name=${encodeURIComponent(name)}&contentType=${encodeURIComponent(contentType)}&size=${size}`, + { + method: 'PUT', + headers: { + Authorization: 'Bearer ' + this.token, + 'Content-Type': contentType + }, + body: buffer + } + ) + break + } catch (err: any) { + if (i === 4) { + ctx.error('failed to upload file', { name }) + throw err + } + } + } + } + } +} diff --git a/server/client/src/plugin.ts b/server/client/src/plugin.ts new file mode 100644 index 0000000000..6d692230bd --- /dev/null +++ b/server/client/src/plugin.ts @@ -0,0 +1,18 @@ +import { Metadata, plugin, Plugin } from '@hcengineering/platform' + +/** + * @public + */ +export const toolId = 'server-client' as Plugin + +/** + * @public + */ +const serverClientPlugin = plugin(toolId, { + metadata: { + Endpoint: '' as Metadata, // Account URL endpoint + UserAgent: '' as Metadata + } +}) + +export default serverClientPlugin diff --git a/server/client/tsconfig.json b/server/client/tsconfig.json new file mode 100644 index 0000000000..59e4fd4297 --- /dev/null +++ b/server/client/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types", + "tsBuildInfoFile": ".build/build.tsbuildinfo" + } +} \ No newline at end of file diff --git a/server/collaborator/Dockerfile b/server/collaborator/Dockerfile index 6bc7f5c41b..20807f1330 100644 --- a/server/collaborator/Dockerfile +++ b/server/collaborator/Dockerfile @@ -6,4 +6,4 @@ WORKDIR /usr/src/app COPY bundle/bundle.js ./ EXPOSE 3078 -CMD [ "node", "bundle.js" ] +CMD [ "node", "bundle.js" ] \ No newline at end of file diff --git a/server/collaborator/build.sh b/server/collaborator/build.sh deleted file mode 100755 index 9155353607..0000000000 --- a/server/collaborator/build.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright © 2020, 2021 Anticrm Platform Contributors. -# Copyright © 2021 Hardcore Engineering Inc. -# -# Licensed under the Eclipse Public License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. You may -# obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# -# See the License for the specific language governing permissions and -# limitations under the License. -# - -rushx bundle -rushx docker:build -rushx docker:push diff --git a/server/collaborator/package.json b/server/collaborator/package.json index 4b80c4b0b8..95ca915a80 100644 --- a/server/collaborator/package.json +++ b/server/collaborator/package.json @@ -21,7 +21,7 @@ "docker:build": "../../common/scripts/docker_build.sh hardcoreeng/collaborator", "docker:staging": "../../common/scripts/docker_tag.sh hardcoreeng/collaborator staging", "docker:push": "../../common/scripts/docker_tag.sh hardcoreeng/collaborator", - "run-local": "cross-env MONGO_URL=mongodb://localhost:27017 TRANSACTOR_URL=ws://localhost:3333 SECRET=secret MINIO_ENDPOINT=localhost MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin ts-node src/__start.ts", + "run-local": "cross-env MONGO_URL=mongodb://localhost:27017 SECRET=secret MINIO_ENDPOINT=localhost MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin ts-node src/__start.ts", "format": "format src", "test": "jest --passWithNoTests --silent", "_phase:build": "compile transpile src", @@ -48,7 +48,7 @@ "@types/express": "^4.17.13", "@types/cors": "^2.8.12", "@types/compression": "~1.7.2", - "@types/ws": "^8.5.3", + "@types/ws": "^8.5.11", "jest": "^29.7.0", "ts-jest": "^29.1.1", "@types/jest": "^29.5.5" @@ -58,7 +58,7 @@ "@hcengineering/core": "^0.6.32", "@hcengineering/account": "^0.6.0", "@hcengineering/platform": "^0.6.11", - "@hcengineering/server-tool": "^0.6.0", + "@hcengineering/server-client": "^0.6.0", "@hcengineering/server-storage": "^0.6.0", "@hcengineering/server-token": "^0.6.11", "@hcengineering/server-core": "^0.6.1", @@ -76,10 +76,9 @@ "mongodb": "^6.8.0", "yjs": "^13.5.52", "y-prosemirror": "^1.2.1", - "express": "^4.18.3", + "express": "^4.19.2", "body-parser": "^1.20.2", "cors": "^2.8.5", - "compression": "~1.7.4", - "ws": "^8.16.0" + "ws": "^8.18.0" } } diff --git a/server/collaborator/src/config.ts b/server/collaborator/src/config.ts index e09feef367..7e075286c5 100644 --- a/server/collaborator/src/config.ts +++ b/server/collaborator/src/config.ts @@ -25,7 +25,6 @@ export interface Config { Port: number AccountsUrl: string - TransactorUrl: string MongoUrl: string UploadUrl: string } @@ -36,12 +35,11 @@ const envMap: { [key in keyof Config]: string } = { Interval: 'INTERVAL', Port: 'COLLABORATOR_PORT', AccountsUrl: 'ACCOUNTS_URL', - TransactorUrl: 'TRANSACTOR_URL', MongoUrl: 'MONGO_URL', UploadUrl: 'UPLOAD_URL' } -const required: Array = ['Secret', 'ServiceID', 'Port', 'AccountsUrl', 'TransactorUrl', 'MongoUrl'] +const required: Array = ['Secret', 'ServiceID', 'Port', 'AccountsUrl', 'MongoUrl'] const config: Config = (() => { const params: Partial = { @@ -50,7 +48,6 @@ const config: Config = (() => { Interval: parseInt(process.env[envMap.Interval] ?? '30000'), Port: parseInt(process.env[envMap.Port] ?? '3078'), AccountsUrl: process.env[envMap.AccountsUrl], - TransactorUrl: process.env[envMap.TransactorUrl], MongoUrl: process.env[envMap.MongoUrl], UploadUrl: process.env[envMap.UploadUrl] ?? '/files' } diff --git a/server/collaborator/src/platform.ts b/server/collaborator/src/platform.ts index 0a9ff48480..d075ba5b8f 100644 --- a/server/collaborator/src/platform.ts +++ b/server/collaborator/src/platform.ts @@ -13,26 +13,13 @@ // limitations under the License. // -import client from '@hcengineering/client' -import clientResources from '@hcengineering/client-resources' import core, { Client, TxOperations, WorkspaceId, systemAccountEmail, toWorkspaceString } from '@hcengineering/core' -import { setMetadata } from '@hcengineering/platform' +import { createClient, getTransactorEndpoint } from '@hcengineering/server-client' import { Token, generateToken } from '@hcengineering/server-token' -import config from './config' - -// eslint-disable-next-line -const WebSocket = require('ws') async function connect (token: string): Promise { - // We need to override default factory with 'ws' one. - setMetadata(client.metadata.ClientSocketFactory, (url) => { - return new WebSocket(url, { - headers: { - 'User-Agent': config.ServiceID - } - }) - }) - return await (await clientResources()).function.GetClient(token, config.TransactorUrl) + const endpoint = await getTransactorEndpoint(token) + return await createClient(endpoint, token) } async function getTxOperations (client: Client, token: Token, isDerived: boolean = false): Promise { diff --git a/server/collaborator/src/starter.ts b/server/collaborator/src/starter.ts index 0eea12f8f4..2fd66c00c7 100644 --- a/server/collaborator/src/starter.ts +++ b/server/collaborator/src/starter.ts @@ -23,9 +23,12 @@ import type { StorageConfiguration } from '@hcengineering/server-core' import { buildStorageFromConfig, storageConfigFromEnv } from '@hcengineering/server-storage' import config from './config' import { start } from './server' +import serverClient from '@hcengineering/server-client' export async function startCollaborator (ctx: MeasureContext, onClose?: () => void): Promise { setMetadata(serverToken.metadata.Secret, config.Secret) + setMetadata(serverClient.metadata.UserAgent, config.ServiceID) + setMetadata(serverClient.metadata.Endpoint, config.AccountsUrl) const storageConfig: StorageConfiguration = storageConfigFromEnv() const storageAdapter = buildStorageFromConfig(storageConfig, config.MongoUrl) diff --git a/server/collaborator/src/storage/platform.ts b/server/collaborator/src/storage/platform.ts index e5ba7bae1f..e373040c78 100644 --- a/server/collaborator/src/storage/platform.ts +++ b/server/collaborator/src/storage/platform.ts @@ -306,7 +306,7 @@ async function withRetry ( return await op() } catch (err: any) { error = err - ctx.error('error', err) + ctx.error('error', { err }) if (retries !== 0) { await new Promise((resolve) => setTimeout(resolve, delay)) } diff --git a/server/front/package.json b/server/front/package.json index 050ce5dca4..5f1963175d 100644 --- a/server/front/package.json +++ b/server/front/package.json @@ -49,8 +49,8 @@ "dependencies": { "@hcengineering/core": "^0.6.32", "@hcengineering/platform": "^0.6.11", - "express": "^4.18.3", - "express-fileupload": "^1.4.3", + "express": "^4.19.2", + "express-fileupload": "^1.5.1", "express-static-gzip": "^2.1.7", "uuid": "^8.3.2", "cors": "^2.8.5", diff --git a/server/front/src/index.ts b/server/front/src/index.ts index e58fc61dab..5d66f7c552 100644 --- a/server/front/src/index.ts +++ b/server/front/src/index.ts @@ -293,8 +293,9 @@ export function start ( PUSH_PUBLIC_KEY: config.pushPublicKey, ...(extraConfig ?? {}) } - res.set('Cache-Control', cacheControlNoCache) res.status(200) + res.set('Cache-Control', cacheControlNoCache) + res.set('Connection', 'keep-alive') res.json(data) }) @@ -305,6 +306,7 @@ export function start ( const admin = payload.extra?.admin === 'true' res.status(200) res.setHeader('Content-Type', 'application/json') + res.set('Connection', 'keep-alive') res.setHeader('Cache-Control', cacheControlNoCache) const json = JSON.stringify({ @@ -351,6 +353,7 @@ export function start ( ) { res.setHeader('Cache-Control', cacheControlNoCache) } + res.setHeader('Connection', 'keep-alive') } } }) @@ -713,7 +716,7 @@ export function start ( '.avif' ] - app.get('*', function (request, response) { + app.get('*', (request, response) => { if (filesPatterns.some((it) => request.path.endsWith(it))) { response.sendStatus(404) return diff --git a/server/rpc/package.json b/server/rpc/package.json index a9d87325a0..149f4e1a0b 100644 --- a/server/rpc/package.json +++ b/server/rpc/package.json @@ -29,7 +29,7 @@ "eslint-plugin-promise": "^6.1.1", "eslint-plugin-n": "^15.4.0", "eslint": "^8.54.0", - "@types/ws": "^8.5.3", + "@types/ws": "^8.5.11", "@typescript-eslint/parser": "^6.11.0", "eslint-config-standard-with-typescript": "^40.0.0", "prettier": "^3.1.0", diff --git a/server/server-pipeline/package.json b/server/server-pipeline/package.json index d4f7d72985..13c5251cae 100644 --- a/server/server-pipeline/package.json +++ b/server/server-pipeline/package.json @@ -26,7 +26,7 @@ "eslint-plugin-promise": "^6.1.1", "eslint-plugin-n": "^15.4.0", "eslint": "^8.54.0", - "@types/ws": "^8.5.3", + "@types/ws": "^8.5.11", "ts-node": "^10.8.0", "esbuild": "^0.20.0", "@typescript-eslint/parser": "^6.11.0", diff --git a/server/server-storage/package.json b/server/server-storage/package.json index 5a32c3e9d3..e455c9a568 100644 --- a/server/server-storage/package.json +++ b/server/server-storage/package.json @@ -30,7 +30,7 @@ "eslint-plugin-promise": "^6.1.1", "eslint-plugin-n": "^15.4.0", "eslint": "^8.54.0", - "@types/ws": "^8.5.3", + "@types/ws": "^8.5.11", "ts-node": "^10.8.0", "@typescript-eslint/parser": "^6.11.0", "eslint-config-standard-with-typescript": "^40.0.0", diff --git a/server/server/package.json b/server/server/package.json index add40dfd77..fee316ab1e 100644 --- a/server/server/package.json +++ b/server/server/package.json @@ -30,7 +30,7 @@ "eslint-plugin-promise": "^6.1.1", "eslint-plugin-n": "^15.4.0", "eslint": "^8.54.0", - "@types/ws": "^8.5.3", + "@types/ws": "^8.5.11", "ts-node": "^10.8.0", "@typescript-eslint/parser": "^6.11.0", "eslint-config-standard-with-typescript": "^40.0.0", diff --git a/server/tool/package.json b/server/tool/package.json index b65912bab2..1ac615f1df 100644 --- a/server/tool/package.json +++ b/server/tool/package.json @@ -33,7 +33,7 @@ "prettier": "^3.1.0", "typescript": "^5.3.3", "@types/uuid": "^8.3.1", - "@types/ws": "^8.5.3", + "@types/ws": "^8.5.11", "jest": "^29.7.0", "ts-jest": "^29.1.1", "@types/jest": "^29.5.5", @@ -46,12 +46,13 @@ "@hcengineering/contact": "^0.6.24", "@hcengineering/client-resources": "^0.6.27", "@hcengineering/client": "^0.6.18", - "ws": "^8.16.0", + "ws": "^8.18.0", "@hcengineering/model": "^0.6.11", "@hcengineering/rank": "^0.6.4", "uuid": "^8.3.2", "@hcengineering/server-token": "^0.6.11", "@hcengineering/server-core": "^0.6.1", + "@hcengineering/server-client": "^0.6.0", "@hcengineering/server": "^0.6.4", "@hcengineering/server-storage": "^0.6.0", "@hcengineering/mongo": "^0.6.1", diff --git a/server/tool/src/connect.ts b/server/tool/src/connect.ts index 21bff3d20a..dcfd98c58d 100644 --- a/server/tool/src/connect.ts +++ b/server/tool/src/connect.ts @@ -14,20 +14,9 @@ // limitations under the License. // -import client, { clientId } from '@hcengineering/client' -import { - Client, - LoadModelResponse, - systemAccountEmail, - Tx, - WorkspaceId, - type MeasureContext -} from '@hcengineering/core' -import { addLocation, getMetadata, getResource, setMetadata } from '@hcengineering/platform' -import type { StorageAdapter } from '@hcengineering/server-core' +import { Client, systemAccountEmail, Tx, WorkspaceId } from '@hcengineering/core' +import { createClient } from '@hcengineering/server-client' import { generateToken } from '@hcengineering/server-token' -import crypto from 'node:crypto' -import plugin from './plugin' /** * @public @@ -43,254 +32,5 @@ export async function connect ( connectTimeout: number = 0 ): Promise { const token = generateToken(email ?? systemAccountEmail, workspace, extra) - - // We need to override default factory with 'ws' one. - // eslint-disable-next-line - const WebSocket = require('ws') - - setMetadata(client.metadata.UseBinaryProtocol, true) - setMetadata(client.metadata.UseProtocolCompression, true) - setMetadata(client.metadata.ConnectionTimeout, connectTimeout) - - setMetadata(client.metadata.ClientSocketFactory, (url) => { - const socket = new WebSocket(url, { - headers: { - 'User-Agent': getMetadata(plugin.metadata.UserAgent) ?? 'Anticrm Tool Client' - } - }) - return socket - }) - addLocation(clientId, () => import('@hcengineering/client-resources')) - - if (model !== undefined) { - let prev = '' - const hashes = model.map((it) => { - const h = crypto.createHash('sha1') - h.update(prev) - h.update(JSON.stringify(it)) - prev = h.digest('hex') - return prev - }) - setMetadata(client.metadata.OverridePersistenceStore, { - load: async () => ({ - hash: hashes[hashes.length - 1], - transactions: model, - full: true - }), - store: async (model: LoadModelResponse) => {} - }) - } - - const clientFactory = await getResource(client.function.GetClient) - return await clientFactory(token, transactorUrl) -} - -// Will use temporary file to store huge content into -export class BlobClient { - transactorAPIUrl: string - token: string - index: number - constructor ( - readonly transactorUrl: string, - readonly workspace: WorkspaceId, - readonly opt?: { - email?: string - extra?: Record - storageAdapter?: StorageAdapter - } - ) { - this.index = 0 - this.token = generateToken(opt?.email ?? systemAccountEmail, workspace, opt?.extra) - let url = transactorUrl - if (url.endsWith('/')) { - url = url.slice(0, url.length - 1) - } - - this.transactorAPIUrl = url.replaceAll('wss://', 'https://').replace('ws://', 'http://') + '/api/v1/blob' - } - - async checkFile (ctx: MeasureContext, name: string): Promise { - if (this.opt?.storageAdapter !== undefined) { - const obj = await this.opt?.storageAdapter.stat(ctx, this.workspace, name) - if (obj !== undefined) { - return true - } - } - for (let i = 0; i < 5; i++) { - try { - const response = await fetch(this.transactorAPIUrl + `?name=${encodeURIComponent(name)}`, { - headers: { - Authorization: 'Bearer ' + this.token, - Range: 'bytes=0-1' - } - }) - if (response.status === 404) { - return false - } - const buff = await response.arrayBuffer() - return buff.byteLength > 0 - } catch (err: any) { - if (i === 4) { - ctx.error('Failed to check file', { name, error: err }) - } - await new Promise((resolve) => setTimeout(resolve, 500)) - } - } - return false - } - - async writeTo ( - ctx: MeasureContext, - name: string, - size: number, - writable: { - write: (buffer: Buffer, cb: (err?: any) => void) => void - end: (cb: () => void) => void - } - ): Promise { - let written = 0 - const chunkSize = 50 * 1024 * 1024 - let writtenMb = 0 - - // Use ranges to iterave through file with retry if required. - while (written < size || size === -1) { - let i = 0 - let response: Response | undefined - for (; i < 5; i++) { - try { - const st = Date.now() - let chunk: Buffer - - if (this.opt?.storageAdapter !== undefined) { - const chunks: Buffer[] = [] - const readable = await this.opt.storageAdapter.partial(ctx, this.workspace, name, written, chunkSize) - await new Promise((resolve) => { - readable.on('data', (chunk) => { - chunks.push(chunk) - }) - readable.on('end', () => { - resolve() - }) - }) - chunk = Buffer.concat(chunks) - } else { - const header: Record = { - Authorization: 'Bearer ' + this.token - } - - if (!(size !== -1 && written === 0 && size < chunkSize)) { - header.Range = `bytes=${written}-${size === -1 ? written + chunkSize : Math.min(size - 1, written + chunkSize)}` - } - - response = await fetch(this.transactorAPIUrl + `?name=${encodeURIComponent(name)}`, { headers: header }) - if (header.Range != null) { - ctx.info('fetch part', { time: Date.now() - st, blobId: name, written, size }) - } - if (response.status === 403) { - i = 5 - // No file, so make it empty - throw new Error(`Unauthorized ${this.transactorAPIUrl}/${this.workspace.name}/${name}`) - } - if (response.status === 404) { - i = 5 - // No file, so make it empty - throw new Error(`No file for ${this.transactorAPIUrl}/${this.workspace.name}/${name}`) - } - if (response.status === 416) { - if (size === -1) { - size = parseInt((response.headers.get('content-range') ?? '').split('*/')[1]) - continue - } - - // No file, so make it empty - throw new Error(`No file for ${this.transactorAPIUrl}/${this.workspace.name}/${name}`) - } - chunk = Buffer.from(await response.arrayBuffer()) - - if (header.Range == null) { - size = chunk.length - } - // We need to parse - // 'Content-Range': `bytes ${start}-${end}/${size}` - // To determine if something is left - const range = response.headers.get('Content-Range') - if (range !== null) { - const [, total] = range.split(' ')[1].split('/') - if (total !== undefined) { - size = parseInt(total) - } - } - } - - await new Promise((resolve, reject) => { - writable.write(chunk, (err) => { - if (err != null) { - reject(err) - } - resolve() - }) - }) - - written += chunk.length - const newWrittenMb = Math.round(written / (1024 * 1024)) - const newWrittenId = Math.round(newWrittenMb / 5) - if (writtenMb !== newWrittenId) { - writtenMb = newWrittenId - ctx.info(' >>>>Chunk', { - name, - written: newWrittenMb, - of: Math.round(size / (1024 * 1024)) - }) - } - break - } catch (err: any) { - if (err?.code === 'NoSuchKey') { - ctx.info('No such key', { name }) - return - } - if (i > 4) { - await new Promise((resolve) => { - writable.end(resolve) - }) - throw err - } - await new Promise((resolve) => setTimeout(resolve, 1000)) - // retry - } - } - } - await new Promise((resolve) => { - writable.end(resolve) - }) - } - - async upload (ctx: MeasureContext, name: string, size: number, contentType: string, buffer: Buffer): Promise { - if (this.opt?.storageAdapter !== undefined) { - await this.opt.storageAdapter.put(ctx, this.workspace, name, buffer, contentType, size) - } else { - // TODO: We need to improve this logig, to allow restore of huge blobs - for (let i = 0; i < 5; i++) { - try { - await fetch( - this.transactorAPIUrl + - `?name=${encodeURIComponent(name)}&contentType=${encodeURIComponent(contentType)}&size=${size}`, - { - method: 'PUT', - headers: { - Authorization: 'Bearer ' + this.token, - 'Content-Type': contentType - }, - body: buffer - } - ) - break - } catch (err: any) { - if (i === 4) { - ctx.error('failed to upload file', { name }) - throw err - } - } - } - } - } + return await createClient(transactorUrl, token, model, connectTimeout) } diff --git a/server/tool/src/index.ts b/server/tool/src/index.ts index b5b14129c6..bf42df070a 100644 --- a/server/tool/src/index.ts +++ b/server/tool/src/index.ts @@ -407,7 +407,7 @@ export async function upgradeModel ( if (connection === undefined) { // We need to send reboot for workspace - console.info('send force close') + ctx.info('send force close', { workspace: workspaceId.name, transactorUrl }) const serverEndpoint = transactorUrl.replaceAll('wss://', 'https://').replace('ws://', 'http://') const token = generateToken(systemAccountEmail, workspaceId, { admin: 'true' }) await fetch( diff --git a/server/tool/src/plugin.ts b/server/tool/src/plugin.ts index bb22eab72c..880560884d 100644 --- a/server/tool/src/plugin.ts +++ b/server/tool/src/plugin.ts @@ -10,11 +10,8 @@ export const toolId = 'tool' as Plugin */ const toolPlugin = plugin(toolId, { metadata: { - Endpoint: '' as Metadata, - Transactor: '' as Metadata, InitWorkspace: '' as Metadata, - InitScriptURL: '' as Metadata, - UserAgent: '' as Metadata + InitScriptURL: '' as Metadata } }) diff --git a/server/ws/package.json b/server/ws/package.json index 271a2c319b..ea57c00ac9 100644 --- a/server/ws/package.json +++ b/server/ws/package.json @@ -24,7 +24,7 @@ "@types/express": "^4.17.13", "@types/jest": "^29.5.5", "@types/node": "~20.11.16", - "@types/ws": "^8.5.3", + "@types/ws": "^8.5.11", "@typescript-eslint/eslint-plugin": "^6.11.0", "@typescript-eslint/parser": "^6.11.0", "eslint": "^8.54.0", @@ -46,12 +46,11 @@ "@hcengineering/rpc": "^0.6.5", "@hcengineering/server-core": "^0.6.1", "@hcengineering/server-token": "^0.6.11", - "bufferutil": "^4.0.7", - "compression": "~1.7.4", + "bufferutil": "^4.0.8", "cors": "^2.8.5", - "express": "^4.18.3", - "utf-8-validate": "^6.0.3", - "ws": "^8.16.0", + "express": "^4.19.2", + "utf-8-validate": "^6.0.4", + "ws": "^8.18.0", "body-parser": "^1.20.2" } } diff --git a/server/ws/src/server.ts b/server/ws/src/server.ts index 247aaf07f7..b7fb1dc133 100644 --- a/server/ws/src/server.ts +++ b/server/ws/src/server.ts @@ -408,7 +408,8 @@ class TSessionManager implements SessionManager { productId: '', createProgress: 100, creating: false, - disabled: false + disabled: false, + endpoint: '' } } diff --git a/server/ws/src/server_http.ts b/server/ws/src/server_http.ts index 2d637cf338..a3e150b78e 100644 --- a/server/ws/src/server_http.ts +++ b/server/ws/src/server_http.ts @@ -18,7 +18,6 @@ import { generateId, toWorkspaceString, type MeasureContext } from '@hcengineeri import { UNAUTHORIZED } from '@hcengineering/platform' import { RPCHandler, type Response } from '@hcengineering/rpc' import { decodeToken, type Token } from '@hcengineering/server-token' -import compression from 'compression' import cors from 'cors' import express, { type Response as ExpressResponse } from 'express' import http, { type IncomingMessage } from 'http' @@ -27,12 +26,14 @@ import { WebSocketServer, type RawData, type WebSocket } from 'ws' import { getStatistics, wipeStatistics } from './stats' import { LOGGING_ENABLED, type ConnectionSocket, type HandleRequestFunction, type SessionManager } from './types' -import { type StorageAdapter, type PipelineFactory } from '@hcengineering/server-core' +import { type PipelineFactory, type StorageAdapter } from '@hcengineering/server-core' import 'bufferutil' import 'utf-8-validate' import { getFile, getFileRange, type BlobResponse } from './blobs' import { doSessionOp, processRequest, type WebsocketData } from './utils' +const rpcHandler = new RPCHandler() + /** * @public * @param sessionFactory - @@ -60,25 +61,8 @@ export function startHttpServer ( }) } - const rpcHandler = new RPCHandler() - const app = express() app.use(cors()) - app.use( - compression({ - filter: (req, res) => { - if (req.headers['x-no-compression'] != null) { - // don't compress responses with this request header - return false - } - - // fallback to standard filter function - return compression.filter(req, res) - }, - level: 1, - memLevel: 9 - }) - ) const getUsers = (): any => Array.from(sessions.sessions.entries()).map(([k, v]) => v.session.getUser()) @@ -433,7 +417,6 @@ function createWebsocketClientSocket ( email: string mode: any model: any - rpcHandler: RPCHandler } ): ConnectionSocket { const cs: ConnectionSocket = { @@ -444,12 +427,12 @@ function createWebsocketClientSocket ( ws.close() }, readRequest: (buffer: Buffer, binary: boolean) => { - return data.rpcHandler.readRequest(buffer, binary) + return rpcHandler.readRequest(buffer, binary) }, data: () => data, send: async (ctx: MeasureContext, msg, binary, compression) => { const sst = Date.now() - const smsg = data.rpcHandler.serialize(msg, binary) + const smsg = rpcHandler.serialize(msg, binary) ctx.measure('serialize', Date.now() - sst) ctx.measure('send-data', smsg.length) diff --git a/server/ws/src/server_u.ts b/server/ws/src/server_u.ts index 06ec17c703..14c1f90f56 100644 --- a/server/ws/src/server_u.ts +++ b/server/ws/src/server_u.ts @@ -24,12 +24,14 @@ import { RPCHandler } from '@hcengineering/rpc' import { getStatistics, wipeStatistics } from './stats' import { LOGGING_ENABLED, type ConnectionSocket, type HandleRequestFunction, type SessionManager } from './types' -import { type StorageAdapter, type PipelineFactory } from '@hcengineering/server-core' +import { type PipelineFactory, type StorageAdapter } from '@hcengineering/server-core' import uWebSockets, { DISABLED, SHARED_COMPRESSOR, type HttpResponse, type WebSocket } from '@hcengineering/uws' import { Readable } from 'stream' import { getFile, getFileRange, type BlobResponse } from './blobs' import { doSessionOp, processRequest, type WebsocketData } from './utils' +const rpcHandler = new RPCHandler() + interface WebsocketUserData extends WebsocketData { backPressure?: Promise backPressureResolve?: () => void @@ -55,8 +57,6 @@ export function startUWebsocketServer ( const uAPP = uWebSockets.App() - const rpcHandler = new RPCHandler() - const writeStatus = (response: HttpResponse, status: string): HttpResponse => { return response .writeStatus(status) @@ -374,7 +374,6 @@ function createWebSocketClientSocket ( email: string mode: any model: any - rpcHandler: RPCHandler }, ws: uWebSockets.WebSocket, data: WebsocketUserData @@ -392,13 +391,13 @@ function createWebSocketClientSocket ( } }, readRequest: (buffer: Buffer, binary: boolean) => { - return wrData.rpcHandler.readRequest(buffer, binary) + return rpcHandler.readRequest(buffer, binary) }, send: async (ctx, msg, binary, compression): Promise => { if (data.backPressure !== undefined) { await data.backPressure } - const serialized = wrData.rpcHandler.serialize(msg, binary) + const serialized = rpcHandler.serialize(msg, binary) try { const sendR = ws.send(serialized, binary, compression) if (sendR === 2) { diff --git a/tests/docker-compose.yaml b/tests/docker-compose.yaml index bd4bad6002..538a70bf61 100644 --- a/tests/docker-compose.yaml +++ b/tests/docker-compose.yaml @@ -45,8 +45,7 @@ services: - ACCOUNT_PORT=3003 - SERVER_SECRET=secret - MONGO_URL=mongodb://mongodb:27018 - - TRANSACTOR_URL=ws://transactor:3334 - - ENDPOINT_URL=ws://localhost:3334 + - TRANSACTOR_URL=ws://transactor:3334;ws://localhost:3334 - STORAGE_CONFIG=${STORAGE_CONFIG} - MODEL_ENABLED=* - BRANDING_PATH=/var/cfg/branding.json @@ -120,7 +119,6 @@ services: - COLLABORATOR_PORT=3078 - SECRET=secret - ACCOUNTS_URL=http://account:3003 - - TRANSACTOR_URL=ws://transactor:3334 - UPLOAD_URL=/files - MONGO_URL=mongodb://mongodb:27018 - STORAGE_CONFIG=${STORAGE_CONFIG} diff --git a/tests/restore-local.sh b/tests/restore-local.sh index f61707192a..10c3993701 100755 --- a/tests/restore-local.sh +++ b/tests/restore-local.sh @@ -3,7 +3,6 @@ export MINIO_ACCESS_KEY=minioadmin export MINIO_SECRET_KEY=minioadmin export MINIO_ENDPOINT=localhost:9000 export MONGO_URL=mongodb://localhost:27017 -export TRANSACTOR_URL=ws://localhost:3333 export ELASTIC_URL=http://localhost:9200 export SERVER_SECRET=secret diff --git a/tests/sanity/tests/model/tracker/common-tracker-page.ts b/tests/sanity/tests/model/tracker/common-tracker-page.ts index 7a36c75cfd..983ca45e34 100644 --- a/tests/sanity/tests/model/tracker/common-tracker-page.ts +++ b/tests/sanity/tests/model/tracker/common-tracker-page.ts @@ -113,7 +113,13 @@ export class CommonTrackerPage extends CalendarPage { await this.viewButton().click() await this.firstOptionButton().click() await this.assigneeMenuItem().click() - await this.page.keyboard.press('Escape') + await this.closePopup() + } + + async closePopup (): Promise { + while (await this.page.locator('.popup').isVisible()) { + await this.page.keyboard.press('Escape') + } } async verifyViewOption (panel: string, viewletSelector: string): Promise { @@ -122,7 +128,7 @@ export class CommonTrackerPage extends CalendarPage { await expect(viewlet).toHaveClass(/selected/) await this.viewButton().click() await expect(this.firstOptionButton()).toContainText('Assignee') - await this.page.keyboard.press('Escape') + await this.closePopup() } async navigateToComponents (PlatformURI: string): Promise { diff --git a/tests/tool-local.sh b/tests/tool-local.sh index e8e4aa525a..c4f89ce13e 100755 --- a/tests/tool-local.sh +++ b/tests/tool-local.sh @@ -3,6 +3,7 @@ export MINIO_ACCESS_KEY=minioadmin export MINIO_SECRET_KEY=minioadmin export MINIO_ENDPOINT=localhost:9000 export MONGO_URL=mongodb://localhost:27017 +export ACCOUNTS_URL=http://localhost:3000 export TRANSACTOR_URL=ws://localhost:3333 export ELASTIC_URL=http://localhost:9200 export SERVER_SECRET=secret diff --git a/tests/tool.sh b/tests/tool.sh index 98f3447dfe..6eddc1e151 100755 --- a/tests/tool.sh +++ b/tests/tool.sh @@ -3,8 +3,9 @@ export MINIO_ACCESS_KEY=minioadmin export MINIO_SECRET_KEY=minioadmin export MINIO_ENDPOINT=localhost:9002 -export MONGO_URL=mongodb://localhost:27018 +export ACCOUNTS_URL=http://localhost:3003 export TRANSACTOR_URL=ws://localhost:3334 +export MONGO_URL=mongodb://localhost:27018 export ELASTIC_URL=http://localhost:9201 export SERVER_SECRET=secret diff --git a/tests/update-snapshot.sh b/tests/update-snapshot.sh index ec6e6c369b..46d710498d 100755 --- a/tests/update-snapshot.sh +++ b/tests/update-snapshot.sh @@ -3,7 +3,6 @@ export MINIO_ACCESS_KEY=minioadmin export MINIO_SECRET_KEY=minioadmin export MINIO_ENDPOINT=localhost:9000 export MONGO_URL=mongodb://localhost:27017 -export TRANSACTOR_URL=ws://localhost:3333 export ELASTIC_URL=http://localhost:9200 export SERVER_SECRET=secret