diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 71a2fe5d7c..d4f3a98cd7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -243,6 +243,7 @@ jobs: docker logs $(docker ps | grep transactor | cut -f 1 -d ' ') > logs/transactor.log docker logs $(docker ps | grep account | cut -f 1 -d ' ') > logs/account.log docker logs $(docker ps | grep front | cut -f 1 -d ' ') > logs/front.log + docker logs $(docker ps | grep collaborator | cut -f 1 -d ' ') > logs/collaborator.log - name: Upload test results if: always() uses: actions/upload-artifact@v4 @@ -438,6 +439,7 @@ jobs: docker logs $(docker ps | grep transactor | cut -f 1 -d ' ') > logs/uweb-transactor.log docker logs $(docker ps | grep account | cut -f 1 -d ' ') > logs/uweb-account.log docker logs $(docker ps | grep front | cut -f 1 -d ' ') > logs/uweb-front.log + docker logs $(docker ps | grep collaborator | cut -f 1 -d ' ') > logs/uweb-collaborator.log - name: Upload test results if: always() uses: actions/upload-artifact@v4 diff --git a/.vscode/launch.json b/.vscode/launch.json index 6a14f43c43..5d8c9333ed 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -43,7 +43,6 @@ "SERVER_SECRET": "secret", "ENABLE_CONSOLE": "true", "COLLABORATOR_URL": "ws://localhost:3078", - "COLLABORATOR_API_URL": "http://localhost:3078", "REKONI_URL": "http://localhost:4004", "FRONT_URL": "http://localhost:8080", "ACCOUNTS_URL": "http://localhost:3000", @@ -104,7 +103,6 @@ "UPLOAD_URL": "/files", "SERVER_PORT": "8087", "COLLABORATOR_URL": "ws://localhost:3078", - "COLLABORATOR_API_URL": "http://localhost:3078", "CALENDAR_URL": "http://localhost:8095", "GMAIL_URL": "http://localhost:8088", "TELEGRAM_URL": "http://localhost:8086", @@ -239,7 +237,7 @@ "CLIENT_ID": "${env:POD_GITHUB_CLIENTID}", "CLIENT_SECRET": "${env:POD_GITHUB_CLIENT_SECRET}", "PRIVATE_KEY": "${env:POD_GITHUB_PRIVATE_KEY}", - "COLLABORATOR_API_URL": "http://localhost:3078", + "COLLABORATOR_URL": "ws://localhost:3078", "SYSTEM_EMAIL": "anticrm@hc.engineering", "MINIO_ENDPOINT": "localhost", "MINIO_ACCESS_KEY": "minioadmin", @@ -280,7 +278,7 @@ "env": { "SERVER_SECRET": "secret", "ACCOUNTS_URL": "http://localhost:3000", - "COLLABORATOR_API_URL": "http://localhost:3078", + "COLLABORATOR_URL": "ws://localhost:3078", "STORAGE_CONFIG": "minio|localhost?accessKey=minioadmin&secretKey=minioadmin", "MONGO_URL": "mongodb://localhost:27017" }, @@ -353,7 +351,7 @@ "request": "launch", "args": ["src/index.ts"], "env": { - "PORT": "4077", + "PORT": "4007", "SECRET": "secret", "MONGO_URL": "mongodb://localhost:27017", "MINIO_ENDPOINT": "localhost", diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index d9bfcfe71a..345e8450ed 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -83,6 +83,9 @@ dependencies: '@rush-temp/analytics-collector-assets': specifier: file:./projects/analytics-collector-assets.tgz version: file:projects/analytics-collector-assets.tgz(esbuild@0.20.1)(svelte@4.2.12)(ts-node@10.9.2) + '@rush-temp/analytics-collector-resources': + specifier: file:./projects/analytics-collector-resources.tgz + version: file:projects/analytics-collector-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/analytics-service': specifier: file:./projects/analytics-service.tgz version: file:projects/analytics-service.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2) @@ -464,9 +467,6 @@ dependencies: '@rush-temp/model-server-notification': specifier: file:./projects/model-server-notification.tgz version: file:projects/model-server-notification.tgz - '@rush-temp/model-server-openai': - specifier: file:./projects/model-server-openai.tgz - version: file:projects/model-server-openai.tgz '@rush-temp/model-server-products': specifier: file:./projects/model-server-products.tgz version: file:projects/model-server-products.tgz @@ -566,9 +566,6 @@ dependencies: '@rush-temp/onboard-resources': specifier: file:./projects/onboard-resources.tgz version: file:projects/onboard-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(file-loader@6.2.0)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2)(webpack@5.90.3) - '@rush-temp/openai': - specifier: file:./projects/openai.tgz - version: file:projects/openai.tgz(esbuild@0.20.1)(ts-node@10.9.2) '@rush-temp/panel': specifier: file:./projects/panel.tgz version: file:projects/panel.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) @@ -23686,6 +23683,16 @@ packages: engines: {node: '>=0.4'} dev: false + /y-indexeddb@9.0.12(yjs@13.6.12): + resolution: {integrity: sha512-9oCFRSPPzBK7/w5vOkJBaVCQZKHXB/v6SIT+WYhnJxlEC61juqG0hBrAf+y3gmSMLFLwICNH9nQ53uscuse6Hg==} + engines: {node: '>=16.0.0', npm: '>=8.0.0'} + peerDependencies: + yjs: ^13.0.0 + dependencies: + lib0: 0.2.89 + yjs: 13.6.12 + dev: false + /y-prosemirror@1.2.2(prosemirror-model@1.19.4)(y-protocols@1.0.6)(yjs@13.6.12): resolution: {integrity: sha512-hHdnIAhfa8mIoLWtTkMDb6RBzN3lye1QVkaZwVm58sledAA1zTl+yyEtgkrY/sdH6SaQL0rsLj61zHjgr5D0HQ==} engines: {node: '>=16.0.0', npm: '>=8.0.0'} @@ -24118,6 +24125,51 @@ packages: - ts-node dev: false + file:projects/analytics-collector-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): + resolution: {integrity: sha512-UzpZTJE37q+ijqBeKDNHJ3JCUMXIakaHRSuBcBNGLXvW/wDtOW+zJNpou3I69TVwVZNcT/6/uRVYfFfy9thpow==, tarball: file:projects/analytics-collector-resources.tgz} + id: file:projects/analytics-collector-resources.tgz + name: '@rush-temp/analytics-collector-resources' + version: 0.0.0 + dependencies: + '@types/jest': 29.5.12 + '@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) + eslint-plugin-svelte: 2.35.1(eslint@8.56.0)(svelte@4.2.12)(ts-node@10.9.2) + jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) + prettier: 3.2.5 + prettier-plugin-svelte: 3.2.2(prettier@3.2.5)(svelte@4.2.12) + sass: 1.71.1 + svelte: 4.2.12 + svelte-check: 3.6.9(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.12) + svelte-eslint-parser: 0.33.1(svelte@4.2.12) + svelte-loader: 3.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) + ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/types' + - '@types/node' + - babel-jest + - babel-plugin-macros + - coffeescript + - esbuild + - less + - node-notifier + - postcss + - postcss-load-config + - pug + - stylus + - sugarss + - supports-color + - ts-node + dev: false + file:projects/analytics-collector.tgz(@types/node@20.11.19)(esbuild@0.20.1)(svelte@4.2.12)(ts-node@10.9.2): resolution: {integrity: sha512-m298Tce6R1UfZPYO43qimcjCqQNDKFQ08UiLhIcwIfk5Tg+Gm67q9Da79dE2fKDxKqIoAIPytneAgtOOXIMVaw==, tarball: file:projects/analytics-collector.tgz} id: file:projects/analytics-collector.tgz @@ -25018,7 +25070,7 @@ packages: 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.4)(y-protocols@1.0.6): - resolution: {integrity: sha512-2kBYCHIIHeDD99Oj4ViM3+h4CPlelHbpP+HOtuYXSxNeM+hsIhmqdQJrSBoLA3u5LGTsHHapjg94S4sgaMz60Q==, tarball: file:projects/collaborator.tgz} + resolution: {integrity: sha512-svaotn6ykOZH+tModYWz/Y3hct1yHDm/enwC9NCpH17VVNALvRIqUx8F2DWvQV5mUwJYxLcKgdsBqNPB4MjMag==, tarball: file:projects/collaborator.tgz} id: file:projects/collaborator.tgz name: '@rush-temp/collaborator' version: 0.0.0 @@ -25113,7 +25165,7 @@ packages: dev: false file:projects/contact-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): - resolution: {integrity: sha512-Vlw7yfM/X0ViZ1mzmcJFVPAZk/2hp4l768lxErLF/sA2HKsRUCAvkjbzfHmImsK5e2WycFWOWwnUJu9p1irQWQ==, tarball: file:projects/contact-resources.tgz} + resolution: {integrity: sha512-MzZXj700B09rHWIeXhV8qKlv9OTeZvERBG/Clv+H6OyU9CR03EZpiYEYM725EjYKMTvso1XWsYbjEBfwBi0LYg==, tarball: file:projects/contact-resources.tgz} id: file:projects/contact-resources.tgz name: '@rush-temp/contact-resources' version: 0.0.0 @@ -25501,7 +25553,7 @@ packages: dev: false file:projects/desktop.tgz(bufferutil@4.0.8)(sass@1.71.1)(utf-8-validate@6.0.4): - resolution: {integrity: sha512-+dbbvdgHJD76pLjOUSctj1tb9kC3hWa4CisFJxvSmlObAUII4NI+4EmE4u1x8OWaGEDqRCPCmrGFk1aSiPF5fQ==, tarball: file:projects/desktop.tgz} + resolution: {integrity: sha512-/SB+ER8kNF2XYUvZ61U+U9GSaTya1qsHJWc3FDUdz8d12APYoMYf6c2Ax1+Kqk1GUWMu9+rYRfypLOVyqirjyw==, tarball: file:projects/desktop.tgz} id: file:projects/desktop.tgz name: '@rush-temp/desktop' version: 0.0.0 @@ -27145,7 +27197,7 @@ packages: dev: false file:projects/model-all.tgz: - resolution: {integrity: sha512-GYOekXK7++TNstTuTVtmUp1crs2chkWBmgdMBGzWaKVnuYbvHCKSfaNhaZfnAKV3ZnXc4nrbFbq6MUZgu97hnQ==, tarball: file:projects/model-all.tgz} + resolution: {integrity: sha512-CGsFwuvoNA494VSF007Kq0J3C+9iQrspmkFbYCRkrrrpBgY+nWtveFOtCFabCqkmfKLjQ+e3JPl5IBBZ8u/ypQ==, tarball: file:projects/model-all.tgz} name: '@rush-temp/model-all' version: 0.0.0 dependencies: @@ -27168,7 +27220,7 @@ packages: dev: false file:projects/model-analytics-collector.tgz: - resolution: {integrity: sha512-ht/R4aa2RvdT8odpbznVfFTed3dJGToS1q5tKHqJIF8BQhjNcVh9ZUmAJNnNZNTeD8J6yuKIPq7n/Io2CuTYdg==, tarball: file:projects/model-analytics-collector.tgz} + resolution: {integrity: sha512-bYgqacNTGcL0kAYSi3aFT9mseXoIamKxvjP/7gWyh2Hm61bf9FSeQsgVXUxYBLACWeTWFfZsTie8Go1Hjs0JlQ==, tarball: file:projects/model-analytics-collector.tgz} name: '@rush-temp/model-analytics-collector' version: 0.0.0 dependencies: @@ -27302,7 +27354,7 @@ packages: dev: false file:projects/model-controlled-documents.tgz: - resolution: {integrity: sha512-Fg2GDNEA/E5UucxJtuVYq9G9aPq2BTBbo5HYwk64AzpWOfYEI0mzv+FAeAeTMPRKwZR8TDKxKgNCmmMXsmo/PQ==, tarball: file:projects/model-controlled-documents.tgz} + resolution: {integrity: sha512-mBRvO5kEuPrWKHjTZdQFnzV5CyVAysglTX/XFVp7Hlx8lEbRcWI91ZnU/BhOn2vTw7B3ZFCiVRDd8lgh8fAJJA==, tarball: file:projects/model-controlled-documents.tgz} name: '@rush-temp/model-controlled-documents' version: 0.0.0 dependencies: @@ -27321,7 +27373,7 @@ packages: dev: false file:projects/model-core.tgz: - resolution: {integrity: sha512-CbejItmOqGGWqUDIH8lag6srILkEB4ydxks6y/nKZdcQ5z+I3RWW7td/P1nPbsccIyYg+9AIgd1R7N8LSPh8DA==, tarball: file:projects/model-core.tgz} + resolution: {integrity: sha512-zEK+S8loxsZcxXH31h/0nAbjHb8XwgZ4t7wCgVb6SdHMFZ4Ysqt/ttLE3mT1vJXlYYpb4wWNGAPjq1HVK2Da+Q==, tarball: file:projects/model-core.tgz} name: '@rush-temp/model-core' version: 0.0.0 dependencies: @@ -28032,25 +28084,6 @@ packages: - supports-color dev: false - file:projects/model-server-openai.tgz: - resolution: {integrity: sha512-L6OzKdWORrO+k3k1VpKKCED60uWC4pQvqDDPjhG0EzcL0A+RwgPq8EuxrRZfXVVgp6QpDKUA5xrscXKx1N+ZmA==, tarball: file:projects/model-server-openai.tgz} - name: '@rush-temp/model-server-openai' - version: 0.0.0 - dependencies: - '@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) - prettier: 3.2.5 - prettier-plugin-svelte: 3.2.1(prettier@3.2.5)(svelte@4.2.11) - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - dev: false - file:projects/model-server-products.tgz: resolution: {integrity: sha512-UYmxTYnt3/cbQCwyce5nCZcGBCvWgxwTLk7AxUIhMLAlA2pvR1y3BCtR0RHF5r6zHlO8cV2D3i50m+cm8AA/qg==, tarball: file:projects/model-server-products.tgz} name: '@rush-temp/model-server-products' @@ -28844,41 +28877,6 @@ packages: - ts-node dev: false - file:projects/openai.tgz(esbuild@0.20.1)(ts-node@10.9.2): - resolution: {integrity: sha512-FTxDecAefUxIdNRC3gHwqZrNJpHI6Mi/fDduPFKm4YCxy3jpzFgWTKeRRpIQ2AZTQ6Cw4cWANh0VAI2DyTo8Ig==, tarball: file:projects/openai.tgz} - id: file:projects/openai.tgz - name: '@rush-temp/openai' - version: 0.0.0 - dependencies: - '@types/html-to-text': 8.1.1 - '@types/jest': 29.5.12 - '@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) - 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) - fast-equals: 5.0.1 - got: 11.8.6 - html-to-text: 9.0.5 - 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 - transitivePeerDependencies: - - '@babel/core' - - '@jest/types' - - babel-jest - - babel-plugin-macros - - esbuild - - node-notifier - - supports-color - - ts-node - dev: false - file:projects/panel.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): resolution: {integrity: sha512-1v1IuSTwDSjPgaZsKWG4tEUINvY3KtFNYXDHvUVkefqJm280nZg5G4QnphrKdOseOxHN7zaSq5CoVxURn1hV2g==, tarball: file:projects/panel.tgz} id: file:projects/panel.tgz @@ -29085,7 +29083,7 @@ packages: dev: false file:projects/pod-analytics-collector.tgz(bufferutil@4.0.8)(utf-8-validate@6.0.4): - resolution: {integrity: sha512-15hQZSiqnsgeZDg3n3AE2D2mx6xBToMB+2z+nDKLAKbaa+XAjMyj7J/Z9nLwfjs2EX6Z8x10JoPvs4au/SXxlA==, tarball: file:projects/pod-analytics-collector.tgz} + resolution: {integrity: sha512-PbfH6v1MeYjOCQjFEQ2Lb63CwlQrqeeHkEzZYbPjgMC1x9XoUOzv+2krfWR8RXHCm7pW1TnWn8UHRbaZIUVJzg==, tarball: file:projects/pod-analytics-collector.tgz} id: file:projects/pod-analytics-collector.tgz name: '@rush-temp/pod-analytics-collector' version: 0.0.0 @@ -29109,6 +29107,7 @@ packages: eslint-plugin-promise: 6.1.1(eslint@8.56.0) 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 puppeteer: 22.14.0(bufferutil@4.0.8)(typescript@5.3.3)(utf-8-validate@6.0.4) ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) @@ -29116,14 +29115,21 @@ packages: typescript: 5.3.3 ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) transitivePeerDependencies: + - '@aws-sdk/credential-providers' - '@babel/core' - '@jest/types' + - '@mongodb-js/zstd' - '@swc/core' - '@swc/wasm' - babel-jest - babel-plugin-macros - bufferutil + - gcp-metadata + - kerberos + - mongodb-client-encryption - node-notifier + - snappy + - socks - supports-color - utf-8-validate dev: false @@ -29550,7 +29556,7 @@ packages: dev: false file:projects/pod-server.tgz: - resolution: {integrity: sha512-cbeFqSKcEIP6H/55Ux47qcZ1os7nwgBryGQGIF3DILmB/1J7wc6dlbJizE6S7/f3PON5c3H5s7VY2NmpVzhD5g==, tarball: file:projects/pod-server.tgz} + resolution: {integrity: sha512-ZxtZx6xD8K6aE/+YNCmfUaRxZCU8LQdOEhhPY32sdCdD/JbTI0TiooPxFTqJ7ywmFt2P/QIUgGi+KbYqzjxOlQ==, tarball: file:projects/pod-server.tgz} name: '@rush-temp/pod-server' version: 0.0.0 dependencies: @@ -32124,7 +32130,7 @@ packages: dev: false file:projects/server-pipeline.tgz: - resolution: {integrity: sha512-qGocP2RKEaAspOP4lJZzIYkjIk/hbsYMGGyC4oO62GgWyfBw3O6o2u2R9sU/Jtkkeu2/GBIxkkAc9XEzQ4LbDQ==, tarball: file:projects/server-pipeline.tgz} + resolution: {integrity: sha512-LU5dxarK3XG4FXu2W/Wmu1IZh93faWFIdjKmurdgNZ9AV2Mv7B6fTALyVZoA0O7gysXFNDlxvH/qq7PyfAHivQ==, tarball: file:projects/server-pipeline.tgz} name: '@rush-temp/server-pipeline' version: 0.0.0 dependencies: @@ -33852,7 +33858,7 @@ packages: 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.4): - resolution: {integrity: sha512-piHhMfwGIkFR7VYjYWS1lGTwg5zIQaKJmKSSAY7gpRGM4BA88RagpPmx8eT/hXvFv83a+4CzkMUK/atGHWRZcQ==, tarball: file:projects/text-editor-resources.tgz} + resolution: {integrity: sha512-PNeHw/vqYgLq6arghBnAisN4GlvA0DWANKiXTvjahoXM0EhEz6SVX2bh6Fd9xrDqYKWVdfVc2X5o5Zk1dI4qEw==, tarball: file:projects/text-editor-resources.tgz} id: file:projects/text-editor-resources.tgz name: '@rush-temp/text-editor-resources' version: 0.0.0 @@ -33905,6 +33911,7 @@ packages: 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) ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3) typescript: 5.3.3 + y-indexeddb: 9.0.12(yjs@13.6.12) y-prosemirror: 1.2.2(prosemirror-model@1.19.4)(y-protocols@1.0.6)(yjs@13.6.12) y-protocols: 1.0.6(yjs@13.6.12) yjs: 13.6.12 @@ -34194,7 +34201,7 @@ packages: dev: false file:projects/tool.tgz(bufferutil@4.0.8)(utf-8-validate@6.0.4): - resolution: {integrity: sha512-eZQ8XE+deR+AVtEVrG2p1xfsPLpLfDF4HkcWFNK9SXarUz05pP5xp2KPLXLiVYBbG81Qx56t5jjWi6bMaboTwQ==, tarball: file:projects/tool.tgz} + resolution: {integrity: sha512-s4tgmvq13d9Vu2xJD59Y4BtMgYqxVoNIlyghB5y5WEqLiTHUr7FZ4nSnaTWkg96K0KOAH6LsUvPOrD4W1eW8NA==, tarball: file:projects/tool.tgz} id: file:projects/tool.tgz name: '@rush-temp/tool' version: 0.0.0 diff --git a/desktop/package.json b/desktop/package.json index a3f4ecfa41..79eab846c5 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -200,6 +200,7 @@ "@hcengineering/products-resources": "^0.1.0", "@hcengineering/analytics-collector": "^0.6.0", "@hcengineering/analytics-collector-assets": "^0.6.0", + "@hcengineering/analytics-collector-resources": "^0.6.0", "electron-squirrel-startup": "~1.0.0", "dotenv": "~16.0.0", "electron-context-menu": "^4.0.1", diff --git a/desktop/src/ui/platform.ts b/desktop/src/ui/platform.ts index 82b14aa378..b9fc55bd53 100644 --- a/desktop/src/ui/platform.ts +++ b/desktop/src/ui/platform.ts @@ -203,7 +203,6 @@ export async function configurePlatform (): Promise { setMetadata(presentation.metadata.UploadURL, config.UPLOAD_URL) setMetadata(presentation.metadata.FilesURL, config.FILES_URL) setMetadata(presentation.metadata.CollaboratorUrl, config.COLLABORATOR_URL) - setMetadata(presentation.metadata.CollaboratorApiUrl, config.COLLABORATOR_API_URL) setMetadata(presentation.metadata.PreviewConfig, parsePreviewConfig(config.PREVIEW_CONFIG)) setMetadata(presentation.metadata.FrontUrl, config.FRONT_URL) @@ -275,6 +274,7 @@ export async function configurePlatform (): Promise { addLocation(notificationId, async () => await import('@hcengineering/notification-resources')) addLocation(tagsId, async () => await import('@hcengineering/tags-resources')) addLocation(calendarId, async () => await import('@hcengineering/calendar-resources')) + addLocation(analyticsCollectorId, async () => await import('@hcengineering/analytics-collector-resources')) addLocation(trackerId, async () => await import('@hcengineering/tracker-resources')) addLocation(boardId, async () => await import('@hcengineering/board-resources')) diff --git a/desktop/src/ui/types.ts b/desktop/src/ui/types.ts index 786fd14acd..e6e896ffc4 100644 --- a/desktop/src/ui/types.ts +++ b/desktop/src/ui/types.ts @@ -6,7 +6,6 @@ import { ScreenSource } from '@hcengineering/love' export interface Config { ACCOUNTS_URL: string COLLABORATOR_URL: string - COLLABORATOR_API_URL: string FRONT_URL: string FILES_URL: string UPLOAD_URL: string diff --git a/dev/doc-import-tool/package.json b/dev/doc-import-tool/package.json index 393c7d4b1e..87f0604b97 100644 --- a/dev/doc-import-tool/package.json +++ b/dev/doc-import-tool/package.json @@ -15,7 +15,7 @@ "build:watch": "compile", "_phase:bundle": "rushx bundle", "bundle": "mkdir -p bundle && node esbuild.js", - "run-local": "cross-env SERVER_SECRET=secret MONGO_URL=mongodb://localhost:27017 COLLABORATOR_URL=ws://localhost:3078 COLLABORATOR_API_URL=http://localhost:3078 STORAGE_CONFIG=minio|minio?accessKey=minioadmin&secretKey=minioadmin PRODUCT_ID=ezqms node --nolazy -r ts-node/register ./src/__start.ts", + "run-local": "cross-env SERVER_SECRET=secret MONGO_URL=mongodb://localhost:27017 COLLABORATOR_URL=ws://localhost:3078 STORAGE_CONFIG=minio|minio?accessKey=minioadmin&secretKey=minioadmin node --nolazy -r ts-node/register ./src/__start.ts", "run": "cross-env node -r ts-node/register --max-old-space-size=8000 ./src/__start.ts", "format": "format src", "test": "jest --passWithNoTests --silent", diff --git a/dev/doc-import-tool/src/config.ts b/dev/doc-import-tool/src/config.ts index 3e826ff1fc..1503c73eb5 100644 --- a/dev/doc-import-tool/src/config.ts +++ b/dev/doc-import-tool/src/config.ts @@ -8,7 +8,7 @@ import { HtmlConversionBackend } from './convert/convert' export interface Config { doc: string token: string - collaboratorApiURL: string + collaboratorURL: string uploadURL: string workspaceId: WorkspaceId owner: Ref diff --git a/dev/doc-import-tool/src/import.ts b/dev/doc-import-tool/src/import.ts index 0d2569cd7f..1db7697f53 100644 --- a/dev/doc-import-tool/src/import.ts +++ b/dev/doc-import-tool/src/import.ts @@ -21,7 +21,7 @@ import core, { Ref, TxOperations, generateId, - getCollaborativeDoc, + makeCollaborativeDoc, systemAccountEmail, type Blob } from '@hcengineering/core' @@ -100,7 +100,7 @@ async function createDocument ( abstract: '', effectiveDate: 0, reviewInterval: DEFAULT_PERIODIC_REVIEW_INTERVAL, - content: getCollaborativeDoc(generateId()), + content: makeCollaborativeDoc(generateId()), snapshots: 0, plannedEffectiveDate: 0 } @@ -168,7 +168,7 @@ async function createTemplateIfNotExist ( approvers: [], coAuthors: [], changeControl: ccRecordId, - content: getCollaborativeDoc(generateId()), + content: makeCollaborativeDoc(generateId()), snapshots: 0, plannedEffectiveDate: 0 } @@ -208,8 +208,8 @@ async function createSections ( throw new Error(`Invalid document: ${JSON.stringify(doc)}`) } - const { collaboratorApiURL, token, workspaceId } = config - const collaborator = getCollaboratorClient(txops.getHierarchy(), workspaceId, token, collaboratorApiURL) + const { collaboratorURL, token, workspaceId } = config + const collaborator = getCollaboratorClient(workspaceId, token, collaboratorURL) console.log('Creating document content') @@ -229,7 +229,7 @@ async function createSections ( content += `

${section.title}

${section.content}` } - await collaborator.updateContent(collabId, 'content', content) + await collaborator.updateContent(collabId, { content }) } finally { // do nothing } diff --git a/dev/doc-import-tool/src/index.ts b/dev/doc-import-tool/src/index.ts index 27526f93f6..e90c941049 100644 --- a/dev/doc-import-tool/src/index.ts +++ b/dev/doc-import-tool/src/index.ts @@ -44,8 +44,8 @@ export function docImportTool (): void { process.exit(1) } - const collaboratorApiUrl = process.env.COLLABORATOR_API_URL - if (collaboratorApiUrl === undefined) { + const collaboratorUrl = process.env.COLLABORATOR_URL + if (collaboratorUrl === undefined) { console.error('please provide collaborator url') process.exit(1) } @@ -104,7 +104,7 @@ export function docImportTool (): void { space: cmd.space, uploadURL: uploadUrl, storageAdapter, - collaboratorApiURL: collaboratorApiUrl, + collaboratorURL: collaboratorUrl, token: generateToken(systemAccountEmail, workspaceId) } diff --git a/dev/docker-compose.yaml b/dev/docker-compose.yaml index 4e1b6a1f9c..e7e9d000ac 100644 --- a/dev/docker-compose.yaml +++ b/dev/docker-compose.yaml @@ -106,12 +106,11 @@ services: - TELEGRAM_URL=http://localhost:8086 - REKONI_URL=http://localhost:4004 - COLLABORATOR_URL=ws://localhost:3078 - - COLLABORATOR_API_URL=http://localhost:3078 - STORAGE_CONFIG=${STORAGE_CONFIG} - GITHUB_URL=http://localhost:3500 - PRINT_URL=http://localhost:4005 - SIGN_URL=http://localhost:4006 - - ANALYTICS_COLLECTOR_URL=http://localhost:4077 + - ANALYTICS_COLLECTOR_URL=http://localhost:4007 - DESKTOP_UPDATES_URL=https://dist.huly.io - DESKTOP_UPDATES_CHANNEL=dev - BRANDING_URL=http://localhost:8087/branding.json @@ -201,7 +200,7 @@ services: image: hardcoreeng/analytics-collector restart: unless-stopped ports: - - 4077:4007 + - 4007:4007 environment: - SECRET=secret - PORT=4007 diff --git a/dev/local-mongo/docker-compose.yaml b/dev/local-mongo/docker-compose.yaml index 67a475b083..228b33aec0 100644 --- a/dev/local-mongo/docker-compose.yaml +++ b/dev/local-mongo/docker-compose.yaml @@ -93,12 +93,11 @@ services: - TELEGRAM_URL=http://localhost:8086 - REKONI_URL=http://localhost:4004 - COLLABORATOR_URL=ws://localhost:3078 - - COLLABORATOR_API_URL=http://localhost:3078 - STORAGE_CONFIG=${STORAGE_CONFIG} - GITHUB_URL=http://localhost:3500 - PRINT_URL=http://localhost:4005 - SIGN_URL=http://localhost:4006 - - ANALYTICS_COLLECTOR_URL=http://localhost:4077 + - ANALYTICS_COLLECTOR_URL=http://localhost:4007 - DESKTOP_UPDATES_URL=https://dist.huly.io - DESKTOP_UPDATES_CHANNEL=dev - BRANDING_URL=http://localhost:8087/branding.json @@ -188,7 +187,7 @@ services: image: hardcoreeng/analytics-collector restart: unless-stopped ports: - - 4077:4007 + - 4007:4007 environment: - SECRET=secret - PORT=4007 diff --git a/dev/prod/.env b/dev/prod/.env index ce9bafd5d0..cbfc478199 100644 --- a/dev/prod/.env +++ b/dev/prod/.env @@ -5,9 +5,8 @@ FRONT_URL=http://localhost:8080 REKONI_URL=http://localhost:4004 COLLABORATOR_URL=ws://locahost:3078 -COLLABORATOR_API_URL=http://locahost:3078 PRINT_URL=http://localhost:4005 SIGN_URL=http://localhost:4006 -ANALYTICS_COLLECTOR_URL=http://localhost:4007 \ No newline at end of file +ANALYTICS_COLLECTOR_URL=http://localhost:4007 diff --git a/dev/prod/config.json b/dev/prod/config.json index f292e076a8..a29f38cbea 100644 --- a/dev/prod/config.json +++ b/dev/prod/config.json @@ -1,7 +1,6 @@ { "ACCOUNTS_URL":"http://localhost:3000", "COLLABORATOR_URL": "ws://localhost:3078", - "COLLABORATOR_API_URL": "http://localhost:3078", "UPLOAD_URL":"/files", "REKONI_URL": "http://localhost:4004", "PRINT_URL": "http://localhost:4005", diff --git a/dev/prod/package.json b/dev/prod/package.json index ee3cddfae8..6e10a6a868 100644 --- a/dev/prod/package.json +++ b/dev/prod/package.json @@ -216,6 +216,7 @@ "@hcengineering/uploader-resources": "^0.6.0", "@hcengineering/analytics-collector": "^0.6.0", "@hcengineering/analytics-collector-assets": "^0.6.0", + "@hcengineering/analytics-collector-resources": "^0.6.0", "@hcengineering/controlled-documents": "^0.1.0", "@hcengineering/controlled-documents-assets": "^0.1.0", "@hcengineering/controlled-documents-resources": "^0.1.0", diff --git a/dev/prod/public/config-dev.json b/dev/prod/public/config-dev.json index a1af7ca5fb..842ed6d3b8 100644 --- a/dev/prod/public/config-dev.json +++ b/dev/prod/public/config-dev.json @@ -6,6 +6,5 @@ "GMAIL_URL": "https://gmail.hc.engineering", "CALENDAR_URL": "https://calendar.hc.engineering", "REKONI_URL": "https://rekoni.hc.engineering", - "COLLABORATOR_URL": "wss://collaborator.hc.engineering", - "COLLABORATOR_API_URL": "https://collaborator.hc.engineering" + "COLLABORATOR_URL": "wss://collaborator.hc.engineering" } \ No newline at end of file diff --git a/dev/prod/public/config.json b/dev/prod/public/config.json index 46d9f904a3..9b3e3ce052 100644 --- a/dev/prod/public/config.json +++ b/dev/prod/public/config.json @@ -1,7 +1,6 @@ { "ACCOUNTS_URL":"/account", "COLLABORATOR_URL": "ws://localhost:3078", - "COLLABORATOR_API_URL": "http://localhost:3078", "UPLOAD_URL":"/files", "TELEGRAM_URL": "http://localhost:8086", "GMAIL_URL": "http://localhost:8088", @@ -13,7 +12,7 @@ "LAST_NAME_FIRST": "true", "PRINT_URL": "http://localhost:4005", "SIGN_URL": "http://localhost:4006", - "ANALYTICS_COLLECTOR_URL": "http://localhost:4077", + "ANALYTICS_COLLECTOR_URL": "http://localhost:4007", "BRANDING_URL": "/branding.json", "VERSION": null, "MODEL_VERSION": null diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index 83a37b9967..c8724610ae 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -124,7 +124,6 @@ export interface Config { MODEL_VERSION: string VERSION: string COLLABORATOR_URL: string - COLLABORATOR_API_URL: string REKONI_URL: string TELEGRAM_URL: string GMAIL_URL: string @@ -288,7 +287,6 @@ export async function configurePlatform() { setMetadata(presentation.metadata.FilesURL, config.FILES_URL) setMetadata(presentation.metadata.UploadURL, config.UPLOAD_URL) setMetadata(presentation.metadata.CollaboratorUrl, config.COLLABORATOR_URL) - setMetadata(presentation.metadata.CollaboratorApiUrl, config.COLLABORATOR_API_URL) setMetadata(presentation.metadata.FrontUrl, config.FRONT_URL) setMetadata(presentation.metadata.PreviewConfig, parsePreviewConfig(config.PREVIEW_CONFIG)) @@ -366,6 +364,7 @@ export async function configurePlatform() { addLocation(diffviewId, () => import(/* webpackChunkName: "diffview" */ '@hcengineering/diffview-resources')) addLocation(timeId, () => import(/* webpackChunkName: "time" */ '@hcengineering/time-resources')) addLocation(desktopPreferencesId, () => import(/* webpackChunkName: "desktop-preferences" */ '@hcengineering/desktop-preferences-resources')) + addLocation(analyticsCollectorId, async () => await import('@hcengineering/analytics-collector-resources')) addLocation(trackerId, () => import(/* webpackChunkName: "tracker" */ '@hcengineering/tracker-resources')) addLocation(boardId, () => import(/* webpackChunkName: "board" */ '@hcengineering/board-resources')) diff --git a/dev/tool/package.json b/dev/tool/package.json index 4b701d3138..6d0e67c600 100644 --- a/dev/tool/package.json +++ b/dev/tool/package.json @@ -77,7 +77,6 @@ "@hcengineering/model-activity": "^0.6.0", "@hcengineering/model-lead": "^0.6.0", "@hcengineering/mongo": "^0.6.1", - "@hcengineering/openai": "^0.6.0", "@hcengineering/platform": "^0.6.11", "@hcengineering/recruit": "^0.6.29", "@hcengineering/rekoni": "^0.6.0", diff --git a/dev/tool/src/__start.ts b/dev/tool/src/__start.ts index 5ba341d8ed..c3a43ea79c 100644 --- a/dev/tool/src/__start.ts +++ b/dev/tool/src/__start.ts @@ -84,4 +84,4 @@ function prepareTools (): { console.log(`tools git_version: ${process.env.GIT_REVISION ?? ''} model_version: ${process.env.MODEL_VERSION ?? ''}`) -devTool(prepareTools, process.env.PRODUCT_ID ?? '') +devTool(prepareTools) diff --git a/dev/tool/src/benchmark.ts b/dev/tool/src/benchmark.ts index 90e443c972..dfa440535f 100644 --- a/dev/tool/src/benchmark.ts +++ b/dev/tool/src/benchmark.ts @@ -481,7 +481,7 @@ export async function stressBenchmark (transactor: string, mode: StressBenchmark try { counter++ console.log('Attempt', counter) - const token = generateToken(generateId(), { name: generateId(), productId: '' }) + const token = generateToken(generateId(), { name: generateId() }) await rate.add(async () => { try { const ws = new WebSocket(concatLink(transactor, token)) diff --git a/dev/tool/src/clean.ts b/dev/tool/src/clean.ts index 20cca7c2b5..9e8e6a3a93 100644 --- a/dev/tool/src/clean.ts +++ b/dev/tool/src/clean.ts @@ -1056,7 +1056,6 @@ export async function removeDuplicateIds ( ctx: MeasureContext, mongodbUri: string, storageAdapter: StorageAdapter, - productId: string, accountsUrl: string, initWorkspacesStr: string ): Promise { @@ -1066,13 +1065,13 @@ export async function removeDuplicateIds ( // disable spaces while change hardocded ids const skippedDomains: string[] = [DOMAIN_DOC_INDEX_STATE, DOMAIN_BENCHMARK, DOMAIN_TX, DOMAIN_SPACE] try { - const workspaces = await listWorkspacesRaw(_client.db(ACCOUNT_DB), productId) + const workspaces = await listWorkspacesRaw(_client.db(ACCOUNT_DB)) workspaces.sort((a, b) => b.lastVisit - a.lastVisit) const initWorkspaces = initWorkspacesStr.split(';') const initWS = workspaces.filter((p) => initWorkspaces.includes(p.workspace)) const ids = new Map() for (const workspace of initWS) { - const workspaceId = getWorkspaceId(workspace.workspace, productId) + const workspaceId = getWorkspaceId(workspace.workspace) const db = getWorkspaceDB(_client, workspaceId) const txex = await db.collection(DOMAIN_TX).find>({}).toArray() @@ -1125,7 +1124,7 @@ export async function removeDuplicateIds ( if (initWorkspaces.includes(workspace.workspace)) continue ctx.info(`Processing workspace ${workspace.workspaceName ?? workspace.workspace}`) - const workspaceId = getWorkspaceId(workspace.workspace, productId) + const workspaceId = getWorkspaceId(workspace.workspace) const db = getWorkspaceDB(_client, workspaceId) const check = await db.collection(DOMAIN_MIGRATION).findOne({ state, plugin: workspace.workspace }) if (check != null) continue @@ -1214,10 +1213,6 @@ async function updateId ( const markup = (contentDoc as any)[attrName] as Markup const newMarkup = markup.replaceAll(doc._id, newId) await update(h, db, contentDoc, { [attrName]: newMarkup }) - } else if (attr.type._class === core.class.TypeCollaborativeMarkup) { - const markup = (contentDoc as any)[attrName] - const newMarkup = markup.replaceAll(doc._id, newId) - await update(h, db, contentDoc, { [attrName]: newMarkup }) } else if (attr.type._class === core.class.TypeCollaborativeDoc) { const collaborativeDoc = (contentDoc as any)[attr.name] as CollaborativeDoc await updateYDoc(ctx, collaborativeDoc, storage, workspaceId, contentDoc, newId, doc) diff --git a/dev/tool/src/index.ts b/dev/tool/src/index.ts index f0bb2ba9dd..0f87adf1c3 100644 --- a/dev/tool/src/index.ts +++ b/dev/tool/src/index.ts @@ -73,7 +73,6 @@ import core, { import { consoleModelLogger, type MigrateOperation } from '@hcengineering/model' import contact from '@hcengineering/model-contact' import { getMongoClient, getWorkspaceDB } from '@hcengineering/mongo' -import { openAIConfigDefaults } from '@hcengineering/openai' import type { StorageAdapter, StorageAdapterEx } from '@hcengineering/server-core' import { deepEqual } from 'fast-equals' import { createWriteStream, readFileSync } from 'fs' @@ -93,7 +92,6 @@ import { import { changeConfiguration } from './configuration' import { fixJsonMarkup } from './markup' import { fixMixinForeignAttributes, showMixinForeignAttributes } from './mixin' -import { openAIConfig } from './openai' import { fixAccountEmails, renameAccount } from './renameAccount' import { moveFiles } from './storage' @@ -119,7 +117,6 @@ export function devTool ( version: Data migrateOperations: [string, MigrateOperation][] }, - productId: string, extendProgram?: (prog: Command) => void ): void { const toolCtx = new MeasureMetricsContext('tool', {}) @@ -200,7 +197,7 @@ export function devTool ( const { mongodbUri } = prepareTools() await withDatabase(mongodbUri, async (db) => { console.log(`creating account ${cmd.first as string} ${cmd.last as string} (${email})...`) - await createAcc(toolCtx, db, productId, null, email, cmd.password, cmd.first, cmd.last, true) + await createAcc(toolCtx, db, null, email, cmd.password, cmd.first, cmd.last, true) }) }) @@ -212,7 +209,7 @@ export function devTool ( const { mongodbUri } = prepareTools() await withDatabase(mongodbUri, async (db) => { console.log(`update account ${email} ${cmd.first as string} ${cmd.last as string}...`) - await replacePassword(db, productId, email, cmd.password) + await replacePassword(db, email, cmd.password) }) }) @@ -223,7 +220,7 @@ export function devTool ( const { mongodbUri } = prepareTools() await withDatabase(mongodbUri, async (db) => { console.log(`update account ${email} to ${newEmail}`) - await renameAccount(toolCtx, db, productId, accountsUrl, email, newEmail) + await renameAccount(toolCtx, db, accountsUrl, email, newEmail) }) }) @@ -234,7 +231,7 @@ export function devTool ( const { mongodbUri } = prepareTools() await withDatabase(mongodbUri, async (db) => { console.log(`update account ${email} to ${newEmail}`) - await fixAccountEmails(toolCtx, db, productId, accountsUrl, email, newEmail) + await fixAccountEmails(toolCtx, db, accountsUrl, email, newEmail) }) }) @@ -248,13 +245,13 @@ export function devTool ( console.log('compacting db ...') let gtotal: number = 0 try { - const workspaces = await listWorkspacesPure(db, productId) + const workspaces = await listWorkspacesPure(db) for (const workspace of workspaces) { if (cmd.workspace !== '' && workspace.workspace !== cmd.workspace) { continue } let total: number = 0 - const wsDb = getWorkspaceDB(client, { name: workspace.workspace, productId }) + const wsDb = getWorkspaceDB(client, { name: workspace.workspace }) const collections = wsDb.listCollections() while (true) { const collInfo = await collections.next() @@ -282,11 +279,11 @@ export function devTool ( await withDatabase(mongodbUri, async (db, client) => { console.log(`assigning user ${email} to ${workspace}...`) try { - const workspaceInfo = await getWorkspaceById(db, productId, workspace) + const workspaceInfo = await getWorkspaceById(db, workspace) if (workspaceInfo === null) { throw new Error(`workspace ${workspace} not found`) } - const token = generateToken(systemAccountEmail, { name: workspaceInfo.workspace, productId }) + const token = generateToken(systemAccountEmail, { name: workspaceInfo.workspace }) const endpoint = await getTransactorEndpoint(token, 'external') console.log('assigning to workspace', workspaceInfo, endpoint) const client = await createClient(endpoint, token) @@ -294,7 +291,6 @@ export function devTool ( await assignWorkspace( toolCtx, db, - productId, null, email, workspaceInfo.workspace, @@ -310,32 +306,6 @@ export function devTool ( }) }) - program - .command('openai ') - .description('assign workspace') - .requiredOption('-t, --token ', 'OpenAI token') - .option('-h, --host ', 'OpenAI API Host', openAIConfigDefaults.endpoint) - .option('--enable ', 'Enable or disable', true) - .option('--embeddings ', 'Enable or disable embeddings generation', true) - .option('--tokenLimit ', 'Acceptable token limit', `${openAIConfigDefaults.tokenLimit}`) - .action( - async ( - workspace: string, - cmd: { token: string, host: string, enable: string, tokenLimit: string, embeddings: string } - ) => { - console.log(`enabling OpenAI for workspace ${workspace}...`) - 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', - tokenLimit: parseInt(cmd.tokenLimit), - embeddings: cmd.embeddings === 'true' - }) - } - ) - program .command('show-user ') .description('show user') @@ -363,7 +333,6 @@ export function devTool ( txes, migrateOperations, db, - productId, cmd.init !== undefined || cmd.branding !== undefined ? { initWorkspace: cmd.init, key: cmd.branding ?? 'huly' } : null, @@ -381,15 +350,15 @@ export function devTool ( const { mongodbUri } = prepareTools() console.log(`set user ${email} role for ${workspace}...`) await withDatabase(mongodbUri, async (db) => { - const workspaceInfo = await getWorkspaceById(db, productId, workspace) + const workspaceInfo = await getWorkspaceById(db, workspace) if (workspaceInfo === null) { throw new Error(`workspace ${workspace} not found`) } console.log('assigning to workspace', workspaceInfo) - const token = generateToken(systemAccountEmail, { name: workspaceInfo.workspace, productId }) + const token = generateToken(systemAccountEmail, { name: workspaceInfo.workspace }) const endpoint = await getTransactorEndpoint(token, 'external') const client = await createClient(endpoint, token) - await setRole(toolCtx, db, email, workspace, productId, role, client) + await setRole(toolCtx, db, email, workspace, role, client) await client.close() }) }) @@ -413,7 +382,7 @@ export function devTool ( .action(async (workspace, cmd: { force: boolean, indexes: boolean }) => { const { mongodbUri, version, txes, migrateOperations } = prepareTools() await withDatabase(mongodbUri, async (db) => { - const info = await getWorkspaceById(db, productId, workspace) + const info = await getWorkspaceById(db, workspace) if (info === null) { throw new Error(`workspace ${workspace} not found`) } @@ -425,7 +394,6 @@ export function devTool ( version, txes, migrateOperations, - productId, db, info.workspaceUrl ?? info.workspace, consoleModelLogger, @@ -461,7 +429,7 @@ export function devTool ( }) => { const { mongodbUri, version, txes, migrateOperations } = prepareTools() await withDatabase(mongodbUri, async (db, client) => { - const worker = new UpgradeWorker(db, client, version, txes, migrateOperations, productId) + const worker = new UpgradeWorker(db, client, version, txes, migrateOperations) await worker.upgradeAll(toolCtx, { errorHandler: async (ws, err) => {}, force: cmd.force, @@ -484,7 +452,7 @@ export function devTool ( .action(async (cmd: { remove: boolean, disable: boolean, exclude: string, timeout: string }) => { const { mongodbUri } = prepareTools() await withDatabase(mongodbUri, async (db, client) => { - const workspaces = new Map((await listWorkspacesPure(db, productId)).map((p) => [p._id.toString(), p])) + const workspaces = new Map((await listWorkspacesPure(db)).map((p) => [p._id.toString(), p])) const accounts = await listAccounts(db) @@ -516,7 +484,7 @@ export function devTool ( lastVisitDays }) if (cmd.remove) { - await dropWorkspaceFull(toolCtx, db, client, productId, null, ws.workspace, adapter) + await dropWorkspaceFull(toolCtx, db, client, null, ws.workspace, adapter) } } else { toolCtx.warn(' +++ used', { @@ -542,15 +510,15 @@ export function devTool ( await withStorage(mongodbUri, async (storageAdapter) => { await withDatabase(mongodbUri, async (db, client) => { - const ws = await getWorkspaceById(db, productId, workspace) + const ws = await getWorkspaceById(db, workspace) if (ws === null) { console.log('no workspace exists') return } if (cmd.full) { - await dropWorkspaceFull(toolCtx, db, client, productId, null, workspace, storageAdapter) + await dropWorkspaceFull(toolCtx, db, client, null, workspace, storageAdapter) } else { - await dropWorkspace(toolCtx, db, productId, null, workspace) + await dropWorkspace(toolCtx, db, null, workspace) } }) }) @@ -564,11 +532,11 @@ export function devTool ( const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (storageAdapter) => { await withDatabase(mongodbUri, async (db, client) => { - for (const workspace of await listWorkspacesByAccount(db, productId, email)) { + for (const workspace of await listWorkspacesByAccount(db, email)) { if (cmd.full) { - await dropWorkspaceFull(toolCtx, db, client, productId, null, workspace.workspace, storageAdapter) + await dropWorkspaceFull(toolCtx, db, client, null, workspace.workspace, storageAdapter) } else { - await dropWorkspace(toolCtx, db, productId, null, workspace.workspace) + await dropWorkspace(toolCtx, db, null, workspace.workspace) } } }) @@ -581,7 +549,7 @@ export function devTool ( .action(async (email, cmd: { full: boolean }) => { const { mongodbUri } = prepareTools() await withDatabase(mongodbUri, async (db, client) => { - for (const workspace of await listWorkspacesByAccount(db, productId, email)) { + for (const workspace of await listWorkspacesByAccount(db, email)) { console.log(workspace.workspace, workspace.workspaceUrl, workspace.workspaceName) } }) @@ -595,11 +563,11 @@ export function devTool ( await withStorage(mongodbUri, async (storageAdapter) => { await withDatabase(mongodbUri, async (db, client) => { - const workspacesJSON = await listWorkspacesPure(db, productId) + const workspacesJSON = await listWorkspacesPure(db) for (const ws of workspacesJSON) { const lastVisit = Math.floor((Date.now() - ws.lastVisit) / 1000 / 3600 / 24) if (lastVisit > 30) { - await dropWorkspaceFull(toolCtx, db, client, productId, null, ws.workspace, storageAdapter) + await dropWorkspaceFull(toolCtx, db, client, null, ws.workspace, storageAdapter) } } }) @@ -613,7 +581,7 @@ export function devTool ( .action(async (cmd: { expired: boolean }) => { const { mongodbUri, version } = prepareTools() await withDatabase(mongodbUri, async (db) => { - const workspacesJSON = await listWorkspacesPure(db, productId) + const workspacesJSON = await listWorkspacesPure(db) for (const ws of workspacesJSON) { let lastVisit = Math.floor((Date.now() - ws.lastVisit) / 1000 / 3600 / 24) if (cmd.expired && lastVisit <= 7) { @@ -653,9 +621,9 @@ export function devTool ( program.command('fix-person-accounts').action(async () => { const { mongodbUri, version } = prepareTools() await withDatabase(mongodbUri, async (db, client) => { - const ws = await listWorkspacesPure(db, productId) + const ws = await listWorkspacesPure(db) for (const w of ws) { - const wsDb = getWorkspaceDB(client, { name: w.workspace, productId }) + const wsDb = getWorkspaceDB(client, { name: w.workspace }) await wsDb.collection('tx').updateMany( { objectClass: contact.class.PersonAccount, @@ -675,7 +643,7 @@ export function devTool ( .action(async () => { const { mongodbUri } = prepareTools() await withDatabase(mongodbUri, async (db) => { - const workspaces = await listWorkspacesPure(db, productId) + const workspaces = await listWorkspacesPure(db) const accounts = await listAccounts(db) for (const a of accounts) { const wss = a.workspaces.map((it) => it.toString()) @@ -694,7 +662,7 @@ export function devTool ( .action(async (email: string, cmd) => { const { mongodbUri } = prepareTools() await withDatabase(mongodbUri, async (db) => { - await dropAccount(toolCtx, db, productId, null, email) + await dropAccount(toolCtx, db, null, email) }) }) @@ -727,7 +695,7 @@ export function devTool ( } ) => { const storage = await createFileBackupStorage(dirName) - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') await backup(toolCtx, endpoint, wsid, storage, { force: cmd.force, @@ -778,7 +746,7 @@ export function devTool ( cmd: { merge: boolean, parallel: string, recheck: boolean, include: string, skip: string } ) => { const storage = await createFileBackupStorage(dirName) - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') await restore(toolCtx, endpoint, wsid, storage, { date: parseInt(date ?? '-1'), @@ -805,13 +773,8 @@ export function devTool ( .action(async (bucketName: string, dirName: string, workspace: string, cmd) => { const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { - const storage = await createStorageBackupStorage( - toolCtx, - adapter, - getWorkspaceId(bucketName, productId), - dirName - ) - const wsid = getWorkspaceId(workspace, productId) + const storage = await createStorageBackupStorage(toolCtx, adapter, getWorkspaceId(bucketName), dirName) + const wsid = getWorkspaceId(workspace) const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') await backup(toolCtx, endpoint, wsid, storage) }) @@ -824,12 +787,7 @@ export function devTool ( .action(async (bucketName: string, dirName: string, cmd: { force: boolean }) => { const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { - const storage = await createStorageBackupStorage( - toolCtx, - adapter, - getWorkspaceId(bucketName, productId), - dirName - ) + const storage = await createStorageBackupStorage(toolCtx, adapter, getWorkspaceId(bucketName), dirName) await compactBackup(toolCtx, storage, cmd.force) }) }) @@ -843,13 +801,8 @@ export function devTool ( await withDatabase(mongodbUri, async (db) => { const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { - const storage = await createStorageBackupStorage( - toolCtx, - adapter, - getWorkspaceId(bucketName, productId), - dirName - ) - const workspaces = await listWorkspacesPure(db, productId) + const storage = await createStorageBackupStorage(toolCtx, adapter, getWorkspaceId(bucketName), dirName) + const workspaces = await listWorkspacesPure(db) for (const w of workspaces) { console.log(`clearing ${w.workspace} history:`) @@ -865,7 +818,7 @@ export function devTool ( const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { const storage = await createStorageBackupStorage(toolCtx, adapter, getWorkspaceId(bucketName), dirName) - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') await restore(toolCtx, endpoint, wsid, storage, { date: parseInt(date ?? '-1') @@ -878,12 +831,7 @@ export function devTool ( .action(async (bucketName: string, dirName: string, cmd) => { const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { - const storage = await createStorageBackupStorage( - toolCtx, - adapter, - getWorkspaceId(bucketName, productId), - dirName - ) + const storage = await createStorageBackupStorage(toolCtx, adapter, getWorkspaceId(bucketName), dirName) await backupList(storage) }) }) @@ -908,7 +856,7 @@ export function devTool ( .description('restore workspace transactions and minio resources from previous dump.') .action(async (workspace: string, cmd) => { const { mongodbUri, txes } = prepareTools() - await diffWorkspace(mongodbUri, getWorkspaceId(workspace, productId), txes) + await diffWorkspace(mongodbUri, getWorkspaceId(workspace), txes) }) program @@ -926,7 +874,7 @@ export function devTool ( } console.log(`clearing ${workspace} history:`) - await clearTelegramHistory(toolCtx, mongodbUri, getWorkspaceId(workspace, productId), telegramDB, adapter) + await clearTelegramHistory(toolCtx, mongodbUri, getWorkspaceId(workspace), telegramDB, adapter) }) }) }) @@ -944,21 +892,21 @@ export function devTool ( process.exit(1) } - const workspaces = await listWorkspacesPure(db, productId) + const workspaces = await listWorkspacesPure(db) for (const w of workspaces) { console.log(`clearing ${w.workspace} history:`) - await clearTelegramHistory(toolCtx, mongodbUri, getWorkspaceId(w.workspace, productId), telegramDB, adapter) + await clearTelegramHistory(toolCtx, mongodbUri, getWorkspaceId(w.workspace), telegramDB, adapter) } }) }) }) program - .command('generate-token ') + .command('generate-token ') .description('generate token') - .action(async (name: string, workspace: string, productId) => { - console.log(generateToken(name, getWorkspaceId(workspace, productId))) + .action(async (name: string, workspace: string) => { + console.log(generateToken(name, getWorkspaceId(workspace))) }) program .command('decode-token ') @@ -977,7 +925,7 @@ export function devTool ( const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { await withDatabase(mongodbUri, async (db) => { - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') await cleanWorkspace(toolCtx, mongodbUri, wsid, adapter, getElasticUrl(), endpoint, cmd) }) @@ -986,7 +934,7 @@ export function devTool ( program.command('clean-empty-buckets').action(async (cmd: any) => { const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { - const buckets = await adapter.listBuckets(toolCtx, productId) + const buckets = await adapter.listBuckets(toolCtx) for (const ws of buckets) { const l = await ws.list() if ((await l.next()) === undefined) { @@ -1006,8 +954,7 @@ export function devTool ( const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { const wsId: WorkspaceId = { - name: workspace, - productId + name: workspace } const token = generateToken(systemAccountEmail, wsId) const endpoint = await getTransactorEndpoint(token) @@ -1023,8 +970,7 @@ export function devTool ( const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { const wsId: WorkspaceId = { - name: workspace, - productId + name: workspace } const token = generateToken(systemAccountEmail, wsId) const endpoint = await getTransactorEndpoint(token) @@ -1044,7 +990,8 @@ export function devTool ( program .command('move-files') .option('-w, --workspace ', 'Selected workspace only', '') - .action(async (cmd: { workspace: string }) => { + .option('-bl, --blobLimit ', 'A blob size limit in megabytes (default 50mb)', '50') + .action(async (cmd: { workspace: string, blobLimit: string }) => { const { mongodbUri } = prepareTools() await withDatabase(mongodbUri, async (db, client) => { await withStorage(mongodbUri, async (adapter) => { @@ -1056,14 +1003,14 @@ export function devTool ( console.log('moving files to storage provider', exAdapter.defaultAdapter) - const workspaces = await listWorkspacesPure(db, productId) + const workspaces = await listWorkspacesPure(db) for (const workspace of workspaces) { if (cmd.workspace !== '' && workspace.workspace !== cmd.workspace) { continue } - const wsId = getWorkspaceId(workspace.workspace, productId) - await moveFiles(toolCtx, wsId, exAdapter) + const wsId = getWorkspaceId(workspace.workspace) + await moveFiles(toolCtx, wsId, exAdapter, parseInt(cmd.blobLimit)) } } catch (err: any) { console.error(err) @@ -1075,7 +1022,7 @@ export function devTool ( program.command('fix-bw-workspace ').action(async (workspace: string) => { const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { - await fixMinioBW(toolCtx, getWorkspaceId(workspace, productId), adapter) + await fixMinioBW(toolCtx, getWorkspaceId(workspace), adapter) }) }) @@ -1083,7 +1030,7 @@ export function devTool ( .command('clean-removed-transactions ') .description('clean removed transactions') .action(async (workspace: string, cmd: any) => { - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const token = generateToken(systemAccountEmail, wsid) const endpoint = await getTransactorEndpoint(token) await cleanRemovedTransactions(wsid, endpoint) @@ -1093,7 +1040,7 @@ export function devTool ( .command('clean-archived-spaces ') .description('clean archived spaces') .action(async (workspace: string, cmd: any) => { - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const token = generateToken(systemAccountEmail, wsid) const endpoint = await getTransactorEndpoint(token) await cleanArchivedSpaces(wsid, endpoint) @@ -1103,7 +1050,7 @@ export function devTool ( .command('chunter-fix-comments ') .description('chunter-fix-comments') .action(async (workspace: string, cmd: any) => { - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const token = generateToken(systemAccountEmail, wsid) const endpoint = await getTransactorEndpoint(token) await fixCommentDoubleIdCreate(wsid, endpoint) @@ -1116,7 +1063,7 @@ export function devTool ( .option('--property ', 'Property name', '') .option('--detail ', 'Show details', false) .action(async (workspace: string, cmd: { detail: boolean, mixin: string, property: string }) => { - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const token = generateToken(systemAccountEmail, wsid) const endpoint = await getTransactorEndpoint(token) await showMixinForeignAttributes(wsid, endpoint, cmd) @@ -1129,7 +1076,7 @@ export function devTool ( .option('--property ', 'Property name', '') .action(async (workspace: string, cmd: { mixin: string, property: string }) => { const { mongodbUri } = prepareTools() - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const token = generateToken(systemAccountEmail, wsid) const endpoint = await getTransactorEndpoint(token) await fixMixinForeignAttributes(mongodbUri, wsid, endpoint, cmd) @@ -1143,7 +1090,7 @@ 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)) - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') await changeConfiguration(wsid, endpoint, cmd) }) @@ -1159,10 +1106,10 @@ export function devTool ( await withDatabase(mongodbUri, async (db) => { console.log('configure all workspaces') console.log(JSON.stringify(cmd)) - const workspaces = await listWorkspacesRaw(db, productId) + const workspaces = await listWorkspacesRaw(db) for (const ws of workspaces) { console.log('configure', ws.workspaceName ?? ws.workspace) - const wsid = getWorkspaceId(ws.workspace, productId) + const wsid = getWorkspaceId(ws.workspace) const token = generateToken(systemAccountEmail, wsid) const endpoint = await getTransactorEndpoint(token) await changeConfiguration(wsid, endpoint, cmd) @@ -1175,7 +1122,7 @@ export function devTool ( .description('optimize model') .action(async (workspace: string, cmd: { enable: string, disable: string, list: boolean }) => { console.log(JSON.stringify(cmd)) - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const token = generateToken(systemAccountEmail, wsid) const endpoint = await getTransactorEndpoint(token) await optimizeModel(wsid, endpoint) @@ -1211,17 +1158,17 @@ export function devTool ( return } - const allWorkspacesPure = Array.from(await listWorkspacesPure(db, productId)) + const allWorkspacesPure = Array.from(await listWorkspacesPure(db)) const allWorkspaces = new Map(allWorkspacesPure.map((it) => [it.workspace, it])) let workspaces = cmd.workspaces .split(',') .map((it) => it.trim()) .filter((it) => it.length > 0) - .map((it) => getWorkspaceId(it, productId)) + .map((it) => getWorkspaceId(it)) if (cmd.workspaces.length === 0) { - workspaces = allWorkspacesPure.map((it) => getWorkspaceId(it.workspace, productId)) + workspaces = allWorkspacesPure.map((it) => getWorkspaceId(it.workspace)) } const accounts = new Map(Array.from(await listAccounts(db)).map((it) => [it._id.toString(), it.email])) @@ -1268,7 +1215,7 @@ export function devTool ( .description('fix skills for workspace') .action(async (workspace: string, step: string) => { const { mongodbUri } = prepareTools() - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const token = generateToken(systemAccountEmail, wsid) const endpoint = await getTransactorEndpoint(token) await fixSkills(mongodbUri, wsid, endpoint, step) @@ -1280,7 +1227,7 @@ export function devTool ( .action(async (workspace: string) => { const { mongodbUri } = prepareTools() console.log('Restoring recruiting task types in workspace ', workspace, '...') - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') await restoreRecruitingTaskTypes(mongodbUri, wsid, endpoint) }) @@ -1291,7 +1238,7 @@ export function devTool ( .action(async (workspace: string) => { const { mongodbUri } = prepareTools() console.log('Restoring recruiting task types in workspace ', workspace, '...') - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') await restoreHrTaskTypesFromUpdates(mongodbUri, wsid, endpoint) }) @@ -1311,7 +1258,7 @@ export function devTool ( cmd: { objectId: string, objectClass: string, type: string, attribute: string, value: string, domain: string } ) => { const { mongodbUri } = prepareTools() - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') await updateField(mongodbUri, wsid, endpoint, cmd) } @@ -1322,7 +1269,7 @@ export function devTool ( .description('reindex workspace to elastic') .action(async (workspace: string) => { const { mongodbUri } = prepareTools() - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') await recreateElastic(mongodbUri, wsid, endpoint) }) @@ -1333,7 +1280,7 @@ export function devTool ( .action(async (workspace: string) => { const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { - const wsid = getWorkspaceId(workspace, productId) + const wsid = getWorkspaceId(workspace) const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external') await fixJsonMarkup(toolCtx, mongodbUri, adapter, wsid, endpoint) }) @@ -1345,7 +1292,7 @@ export function devTool ( .action(async (workspaces: string) => { const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { - await removeDuplicateIds(toolCtx, mongodbUri, adapter, productId, accountsUrl, workspaces) + await removeDuplicateIds(toolCtx, mongodbUri, adapter, accountsUrl, workspaces) }) }) diff --git a/dev/tool/src/markup.ts b/dev/tool/src/markup.ts index f5b494cc85..da3cdb9d75 100644 --- a/dev/tool/src/markup.ts +++ b/dev/tool/src/markup.ts @@ -7,7 +7,7 @@ import core, { type MeasureContext, type Ref, type WorkspaceId, - getCollaborativeDocId + makeCollaborativeDoc } from '@hcengineering/core' import { getMongoClient, getWorkspaceDB } from '@hcengineering/mongo' import { type StorageAdapter } from '@hcengineering/server-core' @@ -39,10 +39,7 @@ export async function fixJsonMarkup ( const attributes = hierarchy.getAllAttributes(_class) const filtered = Array.from(attributes.values()).filter((attribute) => { - return ( - hierarchy.isDerived(attribute.type._class, core.class.TypeMarkup) || - hierarchy.isDerived(attribute.type._class, core.class.TypeCollaborativeMarkup) - ) + return hierarchy.isDerived(attribute.type._class, core.class.TypeMarkup) }) if (filtered.length === 0) continue @@ -88,7 +85,7 @@ async function processFixJsonMarkupFor ( } if (res !== value) { update[attribute.name] = res - remove.push(getCollaborativeDocId(doc._id, attribute.name)) + remove.push(makeCollaborativeDoc(doc._id, attribute.name)) } } } catch {} diff --git a/dev/tool/src/openai.ts b/dev/tool/src/openai.ts deleted file mode 100644 index 81d627a5f7..0000000000 --- a/dev/tool/src/openai.ts +++ /dev/null @@ -1,34 +0,0 @@ -import core, { getWorkspaceId, TxOperations } from '@hcengineering/core' -import openai, { openAIConfigDefaults } from '@hcengineering/openai' -import { connect } from '@hcengineering/server-tool' - -export async function openAIConfig ( - transactorUrl: string, - workspace: string, - productId: string, - opt: { endpoint: string, token: string, enabled: boolean, tokenLimit: number, embeddings: boolean } -): Promise { - const connection = await connect(transactorUrl, getWorkspaceId(workspace, productId), '#configurator@hc.engineering') - try { - const ops = new TxOperations(connection, core.account.System) - - // Check if EmployeeAccoun is not exists - const config = await ops.findOne(openai.class.OpenAIConfiguration, {}) - - if (config === undefined) { - await ops.createDoc(openai.class.OpenAIConfiguration, core.space.Configuration, { - ...openAIConfigDefaults, - ...opt - }) - } else { - await ops.update(config, { - ...openAIConfigDefaults, - ...opt - }) - } - } catch (err: any) { - console.trace(err) - } finally { - await connection.close() - } -} diff --git a/dev/tool/src/renameAccount.ts b/dev/tool/src/renameAccount.ts index 21f734b0b6..421400305f 100644 --- a/dev/tool/src/renameAccount.ts +++ b/dev/tool/src/renameAccount.ts @@ -9,7 +9,6 @@ import { type Db } from 'mongodb' export async function renameAccount ( ctx: MeasureContext, db: Db, - productId: string, accountsUrl: string, oldEmail: string, newEmail: string @@ -26,13 +25,12 @@ export async function renameAccount ( await changeEmail(ctx, db, account, newEmail) - await fixWorkspaceEmails(account, db, productId, accountsUrl, oldEmail, newEmail) + await fixWorkspaceEmails(account, db, accountsUrl, oldEmail, newEmail) } export async function fixAccountEmails ( ctx: MeasureContext, db: Db, - productId: string, transactorUrl: string, oldEmail: string, newEmail: string @@ -42,26 +40,25 @@ export async function fixAccountEmails ( throw new Error("Account does'n exists") } - await fixWorkspaceEmails(account, db, productId, transactorUrl, oldEmail, newEmail) + await fixWorkspaceEmails(account, db, transactorUrl, oldEmail, newEmail) } async function fixWorkspaceEmails ( account: Account, db: Db, - productId: string, accountsUrl: string, oldEmail: string, newEmail: string ): Promise { const accountWorkspaces = account.workspaces.map((it) => it.toString()) // We need to update all workspaces - const workspaces = await listWorkspacesPure(db, productId) + const workspaces = await listWorkspacesPure(db) for (const ws of workspaces) { if (!accountWorkspaces.includes(ws._id.toString())) { continue } console.log('checking workspace', ws.workspaceName, ws.workspace) - const wsid = getWorkspaceId(ws.workspace, productId) + const wsid = getWorkspaceId(ws.workspace) const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid)) // Let's connect and update account information. @@ -75,7 +72,7 @@ async function fixEmailInWorkspace ( oldEmail: string, newEmail: string ): Promise { - const connection = await connect(transactorUrl, { name: ws.workspace, productId: ws.productId }, undefined, { + const connection = await connect(transactorUrl, { name: ws.workspace }, undefined, { mode: 'backup', model: 'upgrade', // Required for force all clients reload after operation will be complete. admin: 'true' diff --git a/dev/tool/src/storage.ts b/dev/tool/src/storage.ts index 6231c8ca36..f091549a37 100644 --- a/dev/tool/src/storage.ts +++ b/dev/tool/src/storage.ts @@ -13,14 +13,15 @@ // limitations under the License. // -import { type MeasureContext, type WorkspaceId } from '@hcengineering/core' +import { type Blob, type MeasureContext, type WorkspaceId } from '@hcengineering/core' import { type StorageAdapterEx } from '@hcengineering/server-core' import { PassThrough } from 'stream' export async function moveFiles ( ctx: MeasureContext, workspaceId: WorkspaceId, - exAdapter: StorageAdapterEx + exAdapter: StorageAdapterEx, + blobSizeLimitMb: number ): Promise { if (exAdapter.adapters === undefined) return @@ -45,13 +46,20 @@ export async function moveFiles ( if (blob === undefined) continue if (blob.provider === target) continue + if (blob.size > blobSizeLimitMb * 1024 * 1024) { + console.log('skipping large blob', name, data._id, Math.round(blob.size / 1024 / 1024)) + continue + } + try { - const readable = await exAdapter.get(ctx, workspaceId, data._id) - readable.on('end', () => { - readable.destroy() - }) - const stream = readable.pipe(new PassThrough()) - await exAdapter.put(ctx, workspaceId, data._id, stream, blob.contentType, blob.size) + await retryOnFailure( + ctx, + 5, + async () => { + await moveFile(ctx, exAdapter, workspaceId, blob) + }, + 50 + ) } catch (err) { console.error('failed to process blob', name, data._id, err) } @@ -66,3 +74,44 @@ export async function moveFiles ( console.log('...done', workspaceId.name, count) } + +async function moveFile ( + ctx: MeasureContext, + exAdapter: StorageAdapterEx, + workspaceId: WorkspaceId, + blob: Blob +): Promise { + const readable = await exAdapter.get(ctx, workspaceId, blob._id) + try { + readable.on('end', () => { + readable.destroy() + }) + const stream = readable.pipe(new PassThrough()) + await exAdapter.put(ctx, workspaceId, blob._id, stream, blob.contentType, blob.size) + } catch (err) { + readable.destroy() + throw err + } +} + +async function retryOnFailure ( + ctx: MeasureContext, + retries: number, + op: () => Promise, + delay: number = 0 +): Promise { + let error: any + while (retries > 0) { + retries-- + try { + return await op() + } catch (err: any) { + error = err + ctx.error('error', { err, retries }) + if (retries !== 0 && delay > 0) { + await new Promise((resolve) => setTimeout(resolve, delay)) + } + } + } + throw error +} diff --git a/models/activity/src/index.ts b/models/activity/src/index.ts index 83b979d3bb..14d804a652 100644 --- a/models/activity/src/index.ts +++ b/models/activity/src/index.ts @@ -124,7 +124,7 @@ export class TDocUpdateMessage extends TActivityMessage implements DocUpdateMess @Prop(TypeRef(core.class.TxCUD), core.string.Object) // @Index(IndexKind.Indexed) - txId!: Ref> + txId?: Ref> @Prop(TypeString(), core.string.Object) // @Index(IndexKind.Indexed) diff --git a/models/activity/src/migration.ts b/models/activity/src/migration.ts index 7d4b040834..374ea40215 100644 --- a/models/activity/src/migration.ts +++ b/models/activity/src/migration.ts @@ -46,9 +46,7 @@ async function migrateMarkup (client: MigrationClient): Promise { DOMAIN_ACTIVITY, { _class: activity.class.DocUpdateMessage, - 'attributeUpdates.attrClass': { - $in: [core.class.TypeMarkup, core.class.TypeCollaborativeMarkup] - } + 'attributeUpdates.attrClass': core.class.TypeMarkup }, { projection: { diff --git a/models/all/package.json b/models/all/package.json index 42350fa276..7c09014eb9 100644 --- a/models/all/package.json +++ b/models/all/package.json @@ -85,7 +85,6 @@ "@hcengineering/model-support": "^0.6.0", "@hcengineering/model-server-request": "^0.6.0", "@hcengineering/model-server-view": "^0.6.0", - "@hcengineering/model-server-openai": "^0.6.0", "@hcengineering/model-server-translate": "^0.6.0", "@hcengineering/model-server-activity": "^0.6.0", "@hcengineering/model-drive": "^0.6.0", diff --git a/models/all/src/index.ts b/models/all/src/index.ts index 37322b9d51..78f4f623f5 100644 --- a/models/all/src/index.ts +++ b/models/all/src/index.ts @@ -76,7 +76,6 @@ import view, { viewId, createModel as viewModel } from '@hcengineering/model-vie import workbench, { workbenchId, createModel as workbenchModel } from '@hcengineering/model-workbench' import { desktopPreferencesId, createModel as desktopPreferencesModel } from '@hcengineering/model-desktop-preferences' -import { openAIId, createModel as serverOpenAI } from '@hcengineering/model-server-openai' import { createModel as serverTranslate, translateId } from '@hcengineering/model-server-translate' import document, { documentId, createModel as documentModel } from '@hcengineering/model-document' import { serverDocumentId, createModel as serverDocumentModel } from '@hcengineering/model-server-document' @@ -250,6 +249,17 @@ export default function buildModel (enabled: string[] = ['*'], disabled: string[ [uploaderModel, uploaderId], [notificationModel, notificationId], [preferenceModel, preferenceId], + [ + analyticsCollectorModel, + analyticsCollectorId, + { + label: inventory.string.ConfigLabel, + description: inventory.string.ConfigDescription, + enabled: false, + beta: false, + classFilter: defaultFilter + } + ], [ hrModel, hrId, @@ -349,7 +359,6 @@ export default function buildModel (enabled: string[] = ['*'], disabled: string[ } ], [printModel, printId], - [analyticsCollectorModel, analyticsCollectorId], [driveModel, driveId], [ documentsModel, @@ -416,7 +425,6 @@ export default function buildModel (enabled: string[] = ['*'], disabled: string[ [serverViewModel, serverViewId], [serverActivityModel, serverActivityId], [serverTranslate, translateId], - [serverOpenAI, openAIId], [serverDocumentModel, serverDocumentId], [serverGithubModel, serverGithubId], [serverLoveModel, serverLoveId], diff --git a/models/analytics-collector/package.json b/models/analytics-collector/package.json index 68c30d5268..aa1337b10b 100644 --- a/models/analytics-collector/package.json +++ b/models/analytics-collector/package.json @@ -28,18 +28,19 @@ "typescript": "^5.3.3" }, "dependencies": { + "@hcengineering/activity": "^0.6.0", + "@hcengineering/analytics-collector": "^0.6.0", "@hcengineering/attachment": "^0.6.14", "@hcengineering/chunter": "^0.6.20", "@hcengineering/core": "^0.6.32", "@hcengineering/model": "^0.6.11", "@hcengineering/model-activity": "^0.6.0", - "@hcengineering/model-notification": "^0.6.0", "@hcengineering/model-chunter": "^0.6.0", "@hcengineering/model-core": "^0.6.0", + "@hcengineering/model-notification": "^0.6.0", "@hcengineering/model-view": "^0.6.0", "@hcengineering/platform": "^0.6.11", "@hcengineering/ui": "^0.6.15", - "@hcengineering/view": "^0.6.13", - "@hcengineering/analytics-collector": "^0.6.0" + "@hcengineering/view": "^0.6.13" } } diff --git a/models/analytics-collector/src/index.ts b/models/analytics-collector/src/index.ts index 999ef785d4..aaa58ef93f 100644 --- a/models/analytics-collector/src/index.ts +++ b/models/analytics-collector/src/index.ts @@ -13,10 +13,12 @@ // limitations under the License. // -import { type Builder, Mixin } from '@hcengineering/model' -import { type AnalyticsChannel } from '@hcengineering/analytics-collector' +import { type Builder, Model, Prop, ReadOnly, TypeString, UX } from '@hcengineering/model' +import { type OnboardingChannel } from '@hcengineering/analytics-collector' import chunter from '@hcengineering/chunter' import { TChannel } from '@hcengineering/model-chunter' +import activity, { type ActivityMessageControl } from '@hcengineering/activity' +import core from '@hcengineering/core' import analyticsCollector from './plugin' @@ -24,12 +26,66 @@ export { analyticsCollectorId } from '@hcengineering/analytics-collector' export { analyticsCollectorOperation } from './migration' export default analyticsCollector -@Mixin(analyticsCollector.mixin.AnalyticsChannel, chunter.class.Channel) -export class TAnalyticsChannel extends TChannel implements AnalyticsChannel { - workspace!: string - email!: string +@Model(analyticsCollector.class.OnboardingChannel, chunter.class.Channel) +@UX( + analyticsCollector.string.OnboardingChannel, + chunter.icon.Hashtag, + undefined, + undefined, + undefined, + analyticsCollector.string.OnboardingChannels +) +export class TOnboardingChannel extends TChannel implements OnboardingChannel { + @Prop(TypeString(), analyticsCollector.string.UserName) + @ReadOnly() + userName!: string + + @Prop(TypeString(), analyticsCollector.string.Email) + @ReadOnly() + email!: string + + @Prop(TypeString(), analyticsCollector.string.WorkspaceName) + @ReadOnly() + workspaceName!: string + + @Prop(TypeString(), analyticsCollector.string.WorkspaceUrl) + @ReadOnly() + workspaceUrl!: string + + @Prop(TypeString(), analyticsCollector.string.WorkspaceId) + @ReadOnly() + workspaceId!: string } export function createModel (builder: Builder): void { - builder.createModel(TAnalyticsChannel) + builder.createModel(TOnboardingChannel) + + builder.createDoc(activity.class.ActivityExtension, core.space.Model, { + ofClass: analyticsCollector.class.OnboardingChannel, + components: { input: chunter.component.ChatMessageInput } + }) + + builder.createDoc>( + activity.class.ActivityMessageControl, + core.space.Model, + { + objectClass: analyticsCollector.class.OnboardingChannel, + skip: [ + { _class: core.class.TxMixin }, + { _class: core.class.TxCreateDoc, objectClass: { $ne: analyticsCollector.class.OnboardingChannel } }, + { _class: core.class.TxRemoveDoc } + ], + allowedFields: ['members'] + } + ) + + builder.createDoc(activity.class.DocUpdateMessageViewlet, core.space.Model, { + objectClass: analyticsCollector.class.OnboardingChannel, + action: 'update', + config: { + members: { + presenter: chunter.activity.MembersChangedMessage + } + } + }) } diff --git a/models/analytics-collector/src/migration.ts b/models/analytics-collector/src/migration.ts index ee80e23af2..aad168359a 100644 --- a/models/analytics-collector/src/migration.ts +++ b/models/analytics-collector/src/migration.ts @@ -19,15 +19,13 @@ import { type MigrationUpgradeClient, tryMigrate } from '@hcengineering/model' -import analyticsCollector, { analyticsCollectorId } from '@hcengineering/analytics-collector' +import { analyticsCollectorId } from '@hcengineering/analytics-collector' import { DOMAIN_SPACE } from '@hcengineering/model-core' import { DOMAIN_DOC_NOTIFY, DOMAIN_NOTIFICATION } from '@hcengineering/model-notification' import { DOMAIN_ACTIVITY } from '@hcengineering/model-activity' -async function removeAnalyticsChannels (client: MigrationClient): Promise { - const channels = await client.find(DOMAIN_SPACE, { - [`${analyticsCollector.mixin.AnalyticsChannel}`]: { $exists: true } - }) +async function removeOnboardingChannels (client: MigrationClient): Promise { + const channels = await client.find(DOMAIN_SPACE, { 'analytics:mixin:AnalyticsChannel': { $exists: true } }) if (channels.length === 0) { return @@ -40,15 +38,15 @@ async function removeAnalyticsChannels (client: MigrationClient): Promise await client.deleteMany(DOMAIN_ACTIVITY, { attachedTo: { $in: channelsIds } }) await client.deleteMany(DOMAIN_NOTIFICATION, { docNotifyContext: { $in: contextsIds } }) await client.deleteMany(DOMAIN_DOC_NOTIFY, { _id: { $in: contextsIds } }) - await client.deleteMany(DOMAIN_SPACE, { [`${analyticsCollector.mixin.AnalyticsChannel}`]: { $exists: true } }) + await client.deleteMany(DOMAIN_SPACE, { _id: { $in: channelsIds } }) } export const analyticsCollectorOperation: MigrateOperation = { async migrate (client: MigrationClient): Promise { await tryMigrate(client, analyticsCollectorId, [ { - state: 'remove-analytics-channels-v1', - func: removeAnalyticsChannels + state: 'remove-analytics-channels-v3', + func: removeOnboardingChannels } ]) }, diff --git a/models/chunter/src/index.ts b/models/chunter/src/index.ts index 67ac89d509..f8405261a8 100644 --- a/models/chunter/src/index.ts +++ b/models/chunter/src/index.ts @@ -24,7 +24,10 @@ import { type ObjectChatPanel, type ThreadMessage, type ChatInfo, - type ChannelInfo + type ChannelInfo, + type InlineButton, + type TypingInfo, + type InlineButtonAction } from '@hcengineering/chunter' import presentation from '@hcengineering/model-presentation' import contact, { type ChannelProvider as SocialChannelProvider, type Person } from '@hcengineering/contact' @@ -35,7 +38,8 @@ import { DOMAIN_MODEL, type Ref, type Timestamp, - IndexKind + IndexKind, + DOMAIN_TRANSIENT } from '@hcengineering/core' import { type Builder, @@ -52,11 +56,11 @@ import { Hidden } from '@hcengineering/model' import attachment from '@hcengineering/model-attachment' -import core, { TClass, TDoc, TSpace } from '@hcengineering/model-core' +import core, { TAttachedDoc, TClass, TDoc, TSpace } from '@hcengineering/model-core' import notification, { TDocNotifyContext } from '@hcengineering/model-notification' import view from '@hcengineering/model-view' import workbench from '@hcengineering/model-workbench' -import type { IntlString } from '@hcengineering/platform' +import { type IntlString, type Resource } from '@hcengineering/platform' import { TActivityMessage } from '@hcengineering/model-activity' import { type DocNotifyContext } from '@hcengineering/notification' @@ -101,6 +105,9 @@ export class TChatMessage extends TActivityMessage implements ChatMessage { @Prop(TypeRef(contact.class.ChannelProvider), core.string.Object) provider?: Ref + + @Prop(PropCollection(chunter.class.InlineButton), core.string.Object) + inlineButtons?: number } @Model(chunter.class.ThreadMessage, chunter.class.ChatMessage) @@ -155,6 +162,22 @@ export class TChatInfo extends TDoc implements ChatInfo { timestamp!: Timestamp } +@Model(chunter.class.InlineButton, core.class.Doc, DOMAIN_CHUNTER) +export class TInlineButton extends TAttachedDoc implements InlineButton { + name!: string + titleIntl?: IntlString + title?: string + action!: Resource +} + +@Model(chunter.class.TypingInfo, core.class.Doc, DOMAIN_TRANSIENT) +export class TTypingInfo extends TDoc implements TypingInfo { + objectId!: Ref + objectClass!: Ref> + person!: Ref + lastTyping!: Timestamp +} + export function createModel (builder: Builder): void { builder.createModel( TChunterSpace, @@ -165,7 +188,9 @@ export function createModel (builder: Builder): void { TChatMessageViewlet, TObjectChatPanel, TChatInfo, - TChannelInfo + TChannelInfo, + TInlineButton, + TTypingInfo ) const spaceClasses = [chunter.class.Channel, chunter.class.DirectMessage] diff --git a/models/chunter/src/plugin.ts b/models/chunter/src/plugin.ts index 0e039bbd7c..4a5efef78e 100644 --- a/models/chunter/src/plugin.ts +++ b/models/chunter/src/plugin.ts @@ -53,8 +53,7 @@ export default mergeIds(chunterId, chunter, { Chunter: '' as Ref }, activity: { - ChannelCreatedMessage: '' as AnyComponent, - MembersChangedMessage: '' as AnyComponent + ChannelCreatedMessage: '' as AnyComponent }, string: { ApplicationLabelChunter: '' as IntlString, diff --git a/models/contact/src/index.ts b/models/contact/src/index.ts index d3dcacdc10..b85a8cd7b0 100644 --- a/models/contact/src/index.ts +++ b/models/contact/src/index.ts @@ -39,8 +39,8 @@ import { IndexKind, type Blob, type Class, + type CollaborativeDoc, type Domain, - type Markup, type Ref, type Timestamp } from '@hcengineering/core' @@ -54,7 +54,7 @@ import { ReadOnly, TypeBlob, TypeBoolean, - TypeCollaborativeMarkup, + TypeCollaborativeDoc, TypeDate, TypeRecord, TypeRef, @@ -173,9 +173,9 @@ export class TMember extends TAttachedDoc implements Member { @Model(contact.class.Organization, contact.class.Contact) @UX(contact.string.Organization, contact.icon.Company, 'ORG', 'name', undefined, contact.string.Organizations) export class TOrganization extends TContact implements Organization { - @Prop(TypeCollaborativeMarkup(), core.string.Description) + @Prop(TypeCollaborativeDoc(), core.string.Description) @Index(IndexKind.FullText) - description?: Markup + description!: CollaborativeDoc @Prop(Collection(contact.class.Member), contact.string.Members) members!: number diff --git a/models/controlled-documents/src/migration.ts b/models/controlled-documents/src/migration.ts index 861ad75aaf..0672f170cb 100644 --- a/models/controlled-documents/src/migration.ts +++ b/models/controlled-documents/src/migration.ts @@ -8,7 +8,7 @@ import { TxOperations, generateId, DOMAIN_TX, - getCollaborativeDoc, + makeCollaborativeDoc, MeasureMetricsContext, type Class, type Doc, @@ -143,7 +143,7 @@ async function createProductChangeControlTemplate (tx: TxOperations): Promise { diff --git a/models/core/src/index.ts b/models/core/src/index.ts index adf3af9eaf..cad0f0b54b 100644 --- a/models/core/src/index.ts +++ b/models/core/src/index.ts @@ -61,7 +61,6 @@ import { TTypeBoolean, TTypeCollaborativeDoc, TTypeCollaborativeDocVersion, - TTypeCollaborativeMarkup, TTypeDate, TTypeFileSize, TTypeHyperlink, @@ -141,7 +140,6 @@ export function createModel (builder: Builder): void { TTypeMarkup, TTypeCollaborativeDoc, TTypeCollaborativeDocVersion, - TTypeCollaborativeMarkup, TArrOf, TRefTo, TTypeDate, diff --git a/models/core/src/migration.ts b/models/core/src/migration.ts index 0844c55c33..769dab9a69 100644 --- a/models/core/src/migration.ts +++ b/models/core/src/migration.ts @@ -13,16 +13,23 @@ // limitations under the License. // +import { saveCollaborativeDoc, takeCollaborativeDocSnapshot } from '@hcengineering/collaboration' import core, { DOMAIN_BLOB, DOMAIN_DOC_INDEX_STATE, DOMAIN_STATUS, DOMAIN_TX, MeasureMetricsContext, + collaborativeDocParse, coreId, generateId, isClassIndexable, + makeCollaborativeDoc, + type AnyAttribute, type Blob, + type Doc, + type Domain, + type MeasureContext, type Ref, type Space, type Status, @@ -33,10 +40,14 @@ import { tryMigrate, tryUpgrade, type MigrateOperation, + type MigrateUpdate, type MigrationClient, + type MigrationDocumentQuery, + type MigrationIterator, type MigrationUpgradeClient } from '@hcengineering/model' -import { type StorageAdapterEx } from '@hcengineering/storage' +import { type StorageAdapter, type StorageAdapterEx } from '@hcengineering/storage' +import { markupToYDoc } from '@hcengineering/text' import { DOMAIN_SPACE } from './security' async function migrateStatusesToModel (client: MigrationClient): Promise { @@ -143,6 +154,101 @@ async function migrateStatusTransactions (client: MigrationClient): Promise { + const ctx = new MeasureMetricsContext('migrate_content', {}) + const storageAdapter = client.storageAdapter + + const hierarchy = client.hierarchy + const classes = hierarchy.getDescendants(core.class.Doc) + for (const _class of classes) { + const domain = hierarchy.findDomain(_class) + if (domain === undefined) continue + + const attributes = hierarchy.getAllAttributes(_class) + const filtered = Array.from(attributes.values()).filter((attribute) => { + return hierarchy.isDerived(attribute.type._class, core.class.TypeCollaborativeDoc) + }) + if (filtered.length === 0) continue + + const iterator = await client.traverse(domain, { _class }) + try { + console.log('processing', _class) + await processMigrateContentFor(ctx, domain, filtered, client, storageAdapter, iterator) + } finally { + await iterator.close() + } + } +} + +async function processMigrateContentFor ( + ctx: MeasureContext, + domain: Domain, + attributes: AnyAttribute[], + client: MigrationClient, + storageAdapter: StorageAdapter, + iterator: MigrationIterator +): Promise { + let processed = 0 + while (true) { + const docs = await iterator.next(1000) + if (docs === null || docs.length === 0) { + break + } + + const timestamp = Date.now() + const revisionId = `${timestamp}` + + const operations: { filter: MigrationDocumentQuery, update: MigrateUpdate }[] = [] + + for (const doc of docs) { + const update: MigrateUpdate = {} + + for (const attribute of attributes) { + const collaborativeDoc = makeCollaborativeDoc(doc._id, attribute.name, revisionId) + + const value = (doc as any)[attribute.name] as string + if (value != null && value.startsWith('{')) { + const { documentId } = collaborativeDocParse(collaborativeDoc) + const blob = await storageAdapter.stat(ctx, client.workspaceId, documentId) + // only for documents not in storage + if (blob === undefined) { + const ydoc = markupToYDoc(value, attribute.name) + await saveCollaborativeDoc(storageAdapter, client.workspaceId, collaborativeDoc, ydoc, ctx) + await takeCollaborativeDocSnapshot( + storageAdapter, + client.workspaceId, + collaborativeDoc, + ydoc, + { + versionId: revisionId, + name: 'Migration to storage', + createdBy: core.account.System, + createdOn: Date.now() + }, + ctx + ) + } + + update[attribute.name] = collaborativeDoc + } else if (value == null) { + update[attribute.name] = makeCollaborativeDoc(doc._id, attribute.name, revisionId) + } + } + + if (Object.keys(update).length > 0) { + operations.push({ filter: { _id: doc._id }, update }) + } + } + + if (operations.length > 0) { + await client.bulk(domain, operations) + } + + processed += docs.length + console.log('...processed', processed) + } +} + export const coreOperation: MigrateOperation = { async migrate (client: MigrationClient): Promise { // We need to delete all documents in doc index state for missing classes @@ -189,6 +295,10 @@ export const coreOperation: MigrateOperation = { func: async (client: MigrationClient) => { await client.update(DOMAIN_DOC_INDEX_STATE, {}, { $set: { needIndex: true } }) } + }, + { + state: 'collaborative-content-to-storage', + func: migrateCollaborativeContentToStorage } ]) }, diff --git a/models/document/src/migration.ts b/models/document/src/migration.ts index b0d9a91980..89eeb311b8 100644 --- a/models/document/src/migration.ts +++ b/models/document/src/migration.ts @@ -13,15 +13,7 @@ // limitations under the License. // -import { type Attachment } from '@hcengineering/attachment' -import { - DOMAIN_TX, - getCollaborativeDoc, - MeasureMetricsContext, - type Class, - type Doc, - type Ref -} from '@hcengineering/core' +import { DOMAIN_TX, MeasureMetricsContext } from '@hcengineering/core' import { type Document, type Teamspace } from '@hcengineering/document' import { tryMigrate, @@ -29,126 +21,12 @@ import { type MigrationClient, type MigrationUpgradeClient } from '@hcengineering/model' -import { DOMAIN_ATTACHMENT } from '@hcengineering/model-attachment' import core, { DOMAIN_SPACE } from '@hcengineering/model-core' import { type Asset } from '@hcengineering/platform' import document, { documentId, DOMAIN_DOCUMENT } from './index' import { loadCollaborativeDoc, saveCollaborativeDoc, yDocCopyXmlField } from '@hcengineering/collaboration' -async function migrateCollaborativeContent (client: MigrationClient): Promise { - const attachedFiles = await client.find(DOMAIN_ATTACHMENT, { - _class: 'document:class:CollaboratorDocument' as Ref>, - attachedToClass: document.class.Document - }) - - for (const attachment of attachedFiles) { - const collaborativeDoc = getCollaborativeDoc(attachment._id) - - await client.update( - DOMAIN_DOCUMENT, - { - _id: attachment.attachedTo, - _class: attachment.attachedToClass, - content: { - $exists: false - } - }, - { - $set: { - content: collaborativeDoc - } - } - ) - } - - // delete snapshots in old format - await client.deleteMany(DOMAIN_DOCUMENT, { - _class: 'document:class:DocumentSnapshot' as Ref>, - contentId: { $exists: true } - }) - - await client.update( - DOMAIN_DOCUMENT, - { - _class: document.class.Document, - snapshots: { $gt: 0 } - }, - { - $set: { - snapshots: 0 - } - } - ) - - // delete old snapshot transactions - await client.deleteMany(DOMAIN_TX, { - _class: core.class.TxCollectionCUD, - objectClass: document.class.Document, - collection: 'snapshots' - }) -} - -async function fixCollaborativeContentId (client: MigrationClient): Promise { - const documents = await client.find(DOMAIN_DOCUMENT, { - content: { $exists: true } - }) - - // there was a wrong migration that assigned incorrect collaborative doc id - for (const document of documents) { - if (!document.content.includes(':')) { - await client.update(DOMAIN_DOCUMENT, { _id: document._id }, { content: getCollaborativeDoc(document.content) }) - } - } -} - -async function migrateWrongDomainContent (client: MigrationClient): Promise { - // migrate content saved into wrong domain - const attachedFiles = await client.find(DOMAIN_DOCUMENT, { - _class: 'document:class:CollaboratorDocument' as Ref>, - attachedToClass: document.class.Document - }) - - for (const attachment of attachedFiles) { - const collaborativeDoc = getCollaborativeDoc(attachment._id) - - await client.update( - DOMAIN_DOCUMENT, - { - _id: attachment.attachedTo, - _class: attachment.attachedToClass, - content: { - $exists: false - } - }, - { - $set: { - content: collaborativeDoc - } - } - ) - } - - await client.move( - DOMAIN_DOCUMENT, - { - _class: 'document:class:CollaboratorDocument' as Ref>, - attachedToClass: document.class.Document - }, - DOMAIN_ATTACHMENT - ) -} - -async function migrateDeleteCollaboratorDocument (client: MigrationClient): Promise { - await client.deleteMany(DOMAIN_ATTACHMENT, { _class: 'document:class:CollaboratorDocument' as Ref> }) - await client.deleteMany(DOMAIN_DOCUMENT, { _class: 'document:class:CollaboratorDocument' as Ref> }) - await client.deleteMany(DOMAIN_TX, { - _class: core.class.TxCollectionCUD, - collection: 'attachments', - 'tx.objectClass': 'document:class:CollaboratorDocument' as Ref> - }) -} - async function migrateDocumentIcons (client: MigrationClient): Promise { await client.update( DOMAIN_SPACE, @@ -173,60 +51,6 @@ async function migrateDocumentIcons (client: MigrationClient): Promise { ) } -async function setNoParent (client: MigrationClient): Promise { - await client.update( - DOMAIN_DOCUMENT, - { - _class: document.class.Document, - attachedTo: { $exists: false } - }, - { - $set: { - attachedTo: document.ids.NoParent, - attachedToClass: document.class.Document - } - } - ) - const docsWithParent = (await client.find(DOMAIN_DOCUMENT, { - _class: document.class.Document, - attachedTo: { $exists: true, $ne: document.ids.NoParent } - })) as Document[] - for (const doc of docsWithParent) { - const parent = await client.find(DOMAIN_DOCUMENT, { - _class: document.class.Document, - _id: doc.attachedTo - }) - if (parent.length === 0) continue - if (parent[0].space !== doc.space) { - await client.update( - DOMAIN_DOCUMENT, - { - _class: document.class.Document, - _id: doc._id - }, - { - $set: { - attachedTo: document.ids.NoParent - } - } - ) - } - } - - await client.update( - DOMAIN_DOCUMENT, - { - _class: document.class.Document, - attachedTo: '' - }, - { - $set: { - attachedTo: document.ids.NoParent - } - } - ) -} - async function migrateTeamspaces (client: MigrationClient): Promise { await client.update( DOMAIN_SPACE, @@ -306,28 +130,6 @@ async function migrateContentField (client: MigrationClient): Promise { export const documentOperation: MigrateOperation = { async migrate (client: MigrationClient): Promise { await tryMigrate(client, documentId, [ - { - state: 'migrate-no-parent', - func: async (client) => { - await setNoParent(client) - } - }, - { - state: 'collaborativeContent', - func: migrateCollaborativeContent - }, - { - state: 'fixCollaborativeContentId', - func: fixCollaborativeContentId - }, - { - state: 'wrongDomainContent', - func: migrateWrongDomainContent - }, - { - state: 'deleteCollaboratorDocument', - func: migrateDeleteCollaboratorDocument - }, { state: 'updateDocumentIcons', func: migrateDocumentIcons diff --git a/models/lead/src/types.ts b/models/lead/src/types.ts index 53fce71c18..9869970326 100644 --- a/models/lead/src/types.ts +++ b/models/lead/src/types.ts @@ -17,6 +17,7 @@ import type { Employee } from '@hcengineering/contact' import { Account, IndexKind, + type CollaborativeDoc, type Role, type RolesAssignment, type Ref, @@ -31,7 +32,7 @@ import { Model, Prop, ReadOnly, - TypeCollaborativeMarkup, + TypeCollaborativeDoc, TypeDate, TypeMarkup, TypeRef, @@ -94,9 +95,9 @@ export class TCustomer extends TContact implements Customer { @Prop(Collection(lead.class.Lead), lead.string.Leads) leads?: number - @Prop(TypeCollaborativeMarkup(), core.string.Description) + @Prop(TypeCollaborativeDoc(), core.string.Description) @Index(IndexKind.FullText) - description!: string + description!: CollaborativeDoc } @Mixin(lead.mixin.DefaultFunnelTypeData, lead.class.Funnel) diff --git a/models/recruit/src/types.ts b/models/recruit/src/types.ts index 3ed58cb304..cf94bbb76b 100644 --- a/models/recruit/src/types.ts +++ b/models/recruit/src/types.ts @@ -17,6 +17,7 @@ import type { Employee, Organization } from '@hcengineering/contact' import { Account, IndexKind, + type CollaborativeDoc, type Domain, type Markup, type Ref, @@ -34,7 +35,7 @@ import { Prop, ReadOnly, TypeBoolean, - TypeCollaborativeMarkup, + TypeCollaborativeDoc, TypeDate, TypeMarkup, TypeRef, @@ -63,9 +64,9 @@ import recruit from './plugin' @Model(recruit.class.Vacancy, task.class.Project) @UX(recruit.string.Vacancy, recruit.icon.Vacancy, 'VCN', 'name', undefined, recruit.string.Vacancies) export class TVacancy extends TProject implements Vacancy { - @Prop(TypeCollaborativeMarkup(), recruit.string.FullDescription) + @Prop(TypeCollaborativeDoc(), recruit.string.FullDescription) @Index(IndexKind.FullText) - fullDescription?: string + fullDescription!: CollaborativeDoc @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files }) attachments?: number diff --git a/models/server-ai-bot/src/index.ts b/models/server-ai-bot/src/index.ts index 077c9f9d16..d81b0c21d3 100644 --- a/models/server-ai-bot/src/index.ts +++ b/models/server-ai-bot/src/index.ts @@ -65,6 +65,7 @@ export class TAIBotTransferEvent extends TAIBotEvent implements AIBotTransferEve toEmail!: string toWorkspace!: string fromWorkspace!: string + fromWorkspaceName!: string fromWorkspaceUrl!: string messageId!: Ref parentMessageId?: Ref diff --git a/models/server-openai/.eslintrc.js b/models/server-openai/.eslintrc.js deleted file mode 100644 index c1cf82cba0..0000000000 --- a/models/server-openai/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'], - parserOptions: { - tsconfigRootDir: __dirname, - project: './tsconfig.json' - } -} diff --git a/models/server-openai/.npmignore b/models/server-openai/.npmignore deleted file mode 100644 index e3ec093c38..0000000000 --- a/models/server-openai/.npmignore +++ /dev/null @@ -1,4 +0,0 @@ -* -!/lib/** -!CHANGELOG.md -/lib/**/__tests__/ diff --git a/models/server-openai/CHANGELOG.json b/models/server-openai/CHANGELOG.json deleted file mode 100644 index 200ec36b8d..0000000000 --- a/models/server-openai/CHANGELOG.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "@hcengineering/model-server-core", - "entries": [ - { - "version": "0.6.0", - "tag": "@hcengineering/model-server-core_v0.6.0", - "date": "Sun, 08 Aug 2021 10:14:57 GMT", - "comments": { - "dependency": [ - { - "comment": "Updating dependency \"@hcengineering/platform\" from `~0.6.3` to `~0.6.4`" - } - ] - } - } - ] -} diff --git a/models/server-openai/CHANGELOG.md b/models/server-openai/CHANGELOG.md deleted file mode 100644 index 500a56bf3d..0000000000 --- a/models/server-openai/CHANGELOG.md +++ /dev/null @@ -1,9 +0,0 @@ -# Change Log - @hcengineering/model-server-core - -This log was last generated on Sun, 08 Aug 2021 10:14:57 GMT and should not be manually modified. - -## 0.6.0 -Sun, 08 Aug 2021 10:14:57 GMT - -_Initial release_ - diff --git a/models/server-openai/package.json b/models/server-openai/package.json deleted file mode 100644 index 708926a95c..0000000000 --- a/models/server-openai/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "@hcengineering/model-server-openai", - "version": "0.6.0", - "main": "lib/index.js", - "svelte": "src/index.ts", - "types": "types/index.d.ts", - "author": "Anticrm Platform Contributors", - "template": "@hcengineering/model-package", - "license": "EPL-2.0", - "scripts": { - "build": "compile", - "build:watch": "compile", - "format": "format src", - "_phase:build": "compile transpile src", - "_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" - }, - "dependencies": { - "@hcengineering/core": "^0.6.32", - "@hcengineering/model": "^0.6.11", - "@hcengineering/model-chunter": "^0.6.0", - "@hcengineering/model-core": "^0.6.0", - "@hcengineering/model-recruit": "^0.6.0", - "@hcengineering/openai": "^0.6.0", - "@hcengineering/platform": "^0.6.11", - "@hcengineering/server-core": "^0.6.1" - } -} diff --git a/models/server-openai/src/index.ts b/models/server-openai/src/index.ts deleted file mode 100644 index 7a08959f13..0000000000 --- a/models/server-openai/src/index.ts +++ /dev/null @@ -1,56 +0,0 @@ -// -// 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. -// - -import { type Builder, Model, Prop, TypeBoolean, TypeNumber, TypeString, UX } from '@hcengineering/model' -import { TConfiguration } from '@hcengineering/model-core' -import { getEmbeddedLabel } from '@hcengineering/platform' - -import core, { DOMAIN_CONFIGURATION } from '@hcengineering/core' -import openai, { type OpenAIConfiguration } from '@hcengineering/openai/src/plugin' -import serverCore from '@hcengineering/server-core' - -import recruit from '@hcengineering/model-recruit' -import chunter from '@hcengineering/model-chunter' - -export { openAIId } from '@hcengineering/openai/src/plugin' - -@Model(openai.class.OpenAIConfiguration, core.class.Configuration, DOMAIN_CONFIGURATION) -@UX(getEmbeddedLabel('OpenAI')) -export class TOpenAIConfiguration extends TConfiguration implements OpenAIConfiguration { - @Prop(TypeString(), getEmbeddedLabel('Token')) - token!: string - - @Prop(TypeString(), getEmbeddedLabel('Endpoint')) - endpoint!: string - - @Prop(TypeNumber(), getEmbeddedLabel('Token Limit')) - tokenLimit!: number - - @Prop(TypeBoolean(), getEmbeddedLabel('Use embeddings')) - embeddings!: boolean -} - -export function createModel (builder: Builder): void { - builder.createModel(TOpenAIConfiguration) - - builder.createDoc(serverCore.class.Trigger, core.space.Model, { - trigger: openai.trigger.AsyncOnGPTRequest, - txMatch: { - objectClass: { $in: [chunter.class.ChatMessage, recruit.class.ApplicantMatch] }, - _class: core.class.TxCreateDoc - } - }) -} diff --git a/models/text-editor/src/migration.ts b/models/text-editor/src/migration.ts index f999d0910a..f4505ac6c4 100644 --- a/models/text-editor/src/migration.ts +++ b/models/text-editor/src/migration.ts @@ -34,10 +34,7 @@ async function migrateMarkup (client: MigrationClient): Promise { const attributes = hierarchy.getAllAttributes(_class) const filtered = Array.from(attributes.values()).filter((attribute) => { - return ( - hierarchy.isDerived(attribute.type._class, core.class.TypeMarkup) || - hierarchy.isDerived(attribute.type._class, core.class.TypeCollaborativeMarkup) - ) + return hierarchy.isDerived(attribute.type._class, core.class.TypeMarkup) }) if (filtered.length === 0) continue @@ -106,10 +103,7 @@ async function fixMigrateMarkup (client: MigrationClient): Promise { const attributes = hierarchy.getAllAttributes(_class) const filtered = Array.from(attributes.values()).filter((attribute) => { - return ( - hierarchy.isDerived(attribute.type._class, core.class.TypeMarkup) || - hierarchy.isDerived(attribute.type._class, core.class.TypeCollaborativeMarkup) - ) + return hierarchy.isDerived(attribute.type._class, core.class.TypeMarkup) }) if (filtered.length === 0) continue diff --git a/models/tracker/src/types.ts b/models/tracker/src/types.ts index 4cbeb7dbdd..70cadb279e 100644 --- a/models/tracker/src/types.ts +++ b/models/tracker/src/types.ts @@ -18,8 +18,9 @@ import contact, { type Employee, type Person } from '@hcengineering/contact' import { DOMAIN_MODEL, DateRangeMode, - type Domain, IndexKind, + type CollaborativeDoc, + type Domain, type Markup, type Ref, type RelatedDocument, @@ -39,7 +40,7 @@ import { Model, Prop, ReadOnly, - TypeCollaborativeMarkup, + TypeCollaborativeDoc, TypeDate, TypeMarkup, TypeNumber, @@ -182,9 +183,9 @@ export class TIssue extends TTask implements Issue { @Index(IndexKind.FullText) title!: string - @Prop(TypeCollaborativeMarkup(), tracker.string.Description) + @Prop(TypeCollaborativeDoc(), tracker.string.Description) @Index(IndexKind.FullText) - description!: Markup + description!: CollaborativeDoc @Prop(TypeRef(tracker.class.IssueStatus), tracker.string.Status, { _id: tracker.attribute.IssueStatus, @@ -275,7 +276,7 @@ export class TIssueTemplate extends TDoc implements IssueTemplate { @Index(IndexKind.FullText) title!: string - @Prop(TypeCollaborativeMarkup(), tracker.string.Description) + @Prop(TypeMarkup(), tracker.string.Description) @Index(IndexKind.FullText) description!: Markup diff --git a/models/view/src/index.ts b/models/view/src/index.ts index f9aee0018b..e033ee20bd 100644 --- a/models/view/src/index.ts +++ b/models/view/src/index.ts @@ -495,17 +495,8 @@ export function createModel (builder: Builder): void { editor: view.component.HTMLEditor }) - classPresenter( - builder, - core.class.TypeCollaborativeMarkup, - view.component.MarkupPresenter, - undefined, - undefined, - view.component.MarkupDiffPresenter - ) - - builder.mixin(core.class.TypeCollaborativeMarkup, core.class.Class, view.mixin.InlineAttributEditor, { - editor: view.component.CollaborativeHTMLEditor + builder.mixin(core.class.TypeCollaborativeDoc, core.class.Class, view.mixin.ActivityAttributePresenter, { + presenter: view.component.MarkupDiffPresenter }) builder.mixin(core.class.TypeCollaborativeDoc, core.class.Class, view.mixin.InlineAttributEditor, { diff --git a/packages/collaborator-client/src/client.ts b/packages/collaborator-client/src/client.ts index 76c845ceac..2ae2f30307 100644 --- a/packages/collaborator-client/src/client.ts +++ b/packages/collaborator-client/src/client.ts @@ -16,33 +16,39 @@ import { Account, CollaborativeDoc, - Hierarchy, Markup, Ref, Timestamp, WorkspaceId, + collaborativeDocWithLastVersion, collaborativeDocWithVersion, concatLink } from '@hcengineering/core' import { DocumentId } from './types' import { formatMinioDocumentId } from './utils' +/** @public */ +export interface DocumentSnapshotParams { + createdBy: Ref + versionId: string + versionName?: string +} + /** @public */ export interface GetContentRequest { documentId: DocumentId - field: string } /** @public */ export interface GetContentResponse { - html: string + content: Record } /** @public */ export interface UpdateContentRequest { documentId: DocumentId - field: string - html: string + content: Record + snapshot?: DocumentSnapshotParams } /** @public */ @@ -54,6 +60,7 @@ export interface CopyContentRequest { documentId: DocumentId sourceField: string targetField: string + snapshot?: DocumentSnapshotParams } /** @public */ @@ -82,8 +89,7 @@ export interface RemoveDocumentResponse {} /** @public */ export interface TakeSnapshotRequest { documentId: DocumentId - createdBy: Ref - snapshotName: string + snapshot: DocumentSnapshotParams } /** @public */ @@ -95,38 +101,36 @@ export interface TakeSnapshotResponse { createdOn: Timestamp } -/** @public */ -export interface CollaborativeDocSnapshotParams { - snapshotName: string - createdBy: Ref -} - /** @public */ export interface CollaboratorClient { // field operations - getContent: (collaborativeDoc: CollaborativeDoc, field: string) => Promise - updateContent: (collaborativeDoc: CollaborativeDoc, field: string, value: Markup) => Promise - copyContent: (collaborativeDoc: CollaborativeDoc, sourceField: string, targetField: string) => Promise + getContent: (collaborativeDoc: CollaborativeDoc) => Promise> + updateContent: ( + document: CollaborativeDoc, + content: Record, + snapshot?: DocumentSnapshotParams + ) => Promise + copyContent: ( + document: CollaborativeDoc, + sourceField: string, + targetField: string, + snapshot?: DocumentSnapshotParams + ) => Promise // document operations branch: (source: CollaborativeDoc, target: CollaborativeDoc) => Promise remove: (collaborativeDoc: CollaborativeDoc) => Promise - snapshot: (collaborativeDoc: CollaborativeDoc, params: CollaborativeDocSnapshotParams) => Promise + snapshot: (collaborativeDoc: CollaborativeDoc, params: DocumentSnapshotParams) => Promise } /** @public */ -export function getClient ( - hierarchy: Hierarchy, - workspaceId: WorkspaceId, - token: string, - collaboratorUrl: string -): CollaboratorClient { - return new CollaboratorClientImpl(hierarchy, workspaceId, token, collaboratorUrl) +export function getClient (workspaceId: WorkspaceId, token: string, collaboratorUrl: string): CollaboratorClient { + const url = collaboratorUrl.replaceAll('wss://', 'https://').replace('ws://', 'http://') + return new CollaboratorClientImpl(workspaceId, token, url) } class CollaboratorClientImpl implements CollaboratorClient { constructor ( - private readonly hierarchy: Hierarchy, private readonly workspace: WorkspaceId, private readonly token: string, private readonly collaboratorUrl: string @@ -153,30 +157,43 @@ class CollaboratorClientImpl implements CollaboratorClient { return result } - async getContent (document: CollaborativeDoc, field: string): Promise { + async getContent (document: CollaborativeDoc): Promise> { const workspace = this.workspace.name const documentId = formatMinioDocumentId(workspace, document) - const payload: GetContentRequest = { documentId, field } + const payload: GetContentRequest = { documentId } const res = (await this.rpc('getContent', payload)) as GetContentResponse - return res.html ?? '' + return res.content ?? {} } - async updateContent (document: CollaborativeDoc, field: string, value: Markup): Promise { + async updateContent ( + document: CollaborativeDoc, + content: Record, + snapshot?: DocumentSnapshotParams + ): Promise { const workspace = this.workspace.name const documentId = formatMinioDocumentId(workspace, document) - const payload: UpdateContentRequest = { documentId, field, html: value } + const payload: UpdateContentRequest = { documentId, content, snapshot } await this.rpc('updateContent', payload) + + return snapshot !== undefined ? collaborativeDocWithLastVersion(document, snapshot.versionId) : document } - async copyContent (document: CollaborativeDoc, sourceField: string, targetField: string): Promise { + async copyContent ( + document: CollaborativeDoc, + sourceField: string, + targetField: string, + snapshot?: DocumentSnapshotParams + ): Promise { const workspace = this.workspace.name const documentId = formatMinioDocumentId(workspace, document) - const payload: CopyContentRequest = { documentId, sourceField, targetField } + const payload: CopyContentRequest = { documentId, sourceField, targetField, snapshot } await this.rpc('copyContent', payload) + + return snapshot !== undefined ? collaborativeDocWithLastVersion(document, snapshot.versionId) : document } async branch (source: CollaborativeDoc, target: CollaborativeDoc): Promise { @@ -198,11 +215,11 @@ class CollaboratorClientImpl implements CollaboratorClient { await this.rpc('removeDocument', payload) } - async snapshot (document: CollaborativeDoc, params: CollaborativeDocSnapshotParams): Promise { + async snapshot (document: CollaborativeDoc, snapshot: DocumentSnapshotParams): Promise { const workspace = this.workspace.name const documentId = formatMinioDocumentId(workspace, document) - const payload: TakeSnapshotRequest = { documentId, ...params } + const payload: TakeSnapshotRequest = { documentId, snapshot } const res = (await this.rpc('takeSnapshot', payload)) as TakeSnapshotResponse return collaborativeDocWithVersion(document, res.versionId) diff --git a/packages/collaborator-client/src/utils.ts b/packages/collaborator-client/src/utils.ts index 18886de3ea..a8b4b27649 100644 --- a/packages/collaborator-client/src/utils.ts +++ b/packages/collaborator-client/src/utils.ts @@ -17,7 +17,6 @@ import { Class, CollaborativeDoc, Doc, - Domain, Ref, collaborativeDocChain, collaborativeDocFormat, @@ -80,24 +79,21 @@ export function parseDocumentId (documentId: DocumentId): { /** @public */ export function formatPlatformDocumentId ( - objectDomain: Domain, objectClass: Ref>, objectId: Ref, objectAttr: string ): PlatformDocumentId { - return `${objectDomain}/${objectClass}/${objectId}/${objectAttr}` as PlatformDocumentId + return `${objectClass}/${objectId}/${objectAttr}` as PlatformDocumentId } /** @public */ export function parsePlatformDocumentId (platformDocumentId: PlatformDocumentId): { - objectDomain: Domain objectClass: Ref> objectId: Ref objectAttr: string } { - const [objectDomain, objectClass, objectId, objectAttr] = platformDocumentId.split('/') + const [objectClass, objectId, objectAttr] = platformDocumentId.split('/') return { - objectDomain: objectDomain as Domain, objectClass: objectClass as Ref>, objectId: objectId as Ref, objectAttr diff --git a/packages/core/src/classes.ts b/packages/core/src/classes.ts index b8e42732c0..e847294dfe 100644 --- a/packages/core/src/classes.ts +++ b/packages/core/src/classes.ts @@ -654,7 +654,6 @@ export interface DomainIndexConfiguration extends Doc { export interface BaseWorkspaceInfo { workspace: string // An uniq workspace name, Database names - productId: string disabled?: boolean version?: Data branding?: string diff --git a/packages/core/src/collaboration.ts b/packages/core/src/collaboration.ts index de6d1c6b61..cda6bd2751 100644 --- a/packages/core/src/collaboration.ts +++ b/packages/core/src/collaboration.ts @@ -44,16 +44,16 @@ export type CollaborativeDocVersion = string | typeof CollaborativeDocVersionHea export const CollaborativeDocVersionHead = 'HEAD' /** @public */ -export function getCollaborativeDocId (objectId: Ref, objectAttr?: string | undefined): string { - return objectAttr !== undefined && objectAttr !== '' ? `${objectId}%${objectAttr}` : `${objectId}` -} - -/** @public */ -export function getCollaborativeDoc (documentId: string): CollaborativeDoc { +export function makeCollaborativeDoc ( + objectId: Ref, + objectAttr?: string | undefined, + versionId?: string | undefined +): CollaborativeDoc { + const storageDocumentId = objectAttr !== undefined && objectAttr !== '' ? `${objectId}%${objectAttr}` : `${objectId}` return collaborativeDocFormat({ - documentId, + documentId: storageDocumentId, versionId: CollaborativeDocVersionHead, - lastVersionId: CollaborativeDocVersionHead + lastVersionId: versionId ?? '0' }) } diff --git a/packages/core/src/component.ts b/packages/core/src/component.ts index 436d77f8c6..2b26d1e5a6 100644 --- a/packages/core/src/component.ts +++ b/packages/core/src/component.ts @@ -34,7 +34,6 @@ import type { Hyperlink, IndexingConfiguration, Interface, - Markup, MigrationState, Obj, Permission, @@ -122,7 +121,6 @@ export default plugin(coreId, { TypeDate: '' as Ref>>, TypeCollaborativeDoc: '' as Ref>>, TypeCollaborativeDocVersion: '' as Ref>>, - TypeCollaborativeMarkup: '' as Ref>>, RefTo: '' as Ref>>, ArrOf: '' as Ref>>, Enum: '' as Ref>, @@ -164,8 +162,7 @@ export default plugin(coreId, { }, status: { ObjectNotFound: '' as StatusCode<{ _id: Ref }>, - ItemNotFound: '' as StatusCode<{ _id: Ref, _localId: string }>, - InvalidProduct: '' as StatusCode<{ productId: string }> + ItemNotFound: '' as StatusCode<{ _id: Ref, _localId: string }> }, version: { Model: '' as Ref diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index 895d1b0e1a..c8fc702086 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -122,7 +122,6 @@ export function toFindResult (docs: T[], total?: number, lookupMa */ export interface WorkspaceId { name: string - productId: string } /** @@ -136,20 +135,20 @@ export interface WorkspaceIdWithUrl extends WorkspaceId { /** * @public * - * Combine workspace with productId, if not equal '' + * Previously was combining workspace with productId, if not equal '' + * Now just returning workspace as is. Keeping it to simplify further refactoring of ws id. */ -export function getWorkspaceId (workspace: string, productId: string = ''): WorkspaceId { +export function getWorkspaceId (workspace: string): WorkspaceId { return { - name: workspace, - productId + name: workspace } } /** * @public */ -export function toWorkspaceString (id: WorkspaceId, sep = '@'): string { - return id.name + (id.productId === '' ? '' : sep + id.productId) +export function toWorkspaceString (id: WorkspaceId): string { + return id.name } const attributesPrefix = 'attributes.' diff --git a/packages/model/src/dsl.ts b/packages/model/src/dsl.ts index cec2247938..430aeaea72 100644 --- a/packages/model/src/dsl.ts +++ b/packages/model/src/dsl.ts @@ -265,21 +265,25 @@ function _generateTx (tx: ClassTxes): Tx[] { [ClassifierKind.INTERFACE]: core.class.Interface, [ClassifierKind.MIXIN]: core.class.Mixin } - const createTx = txFactory.createTxCreateDoc( + const createTx = txFactory.createTxCreateDoc( _cl[tx.kind], core.space.Model, { ...(tx.domain !== undefined ? { domain: tx.domain } : {}), kind: tx.kind, + label: tx.label, + icon: tx.icon, ...(tx.kind === ClassifierKind.INTERFACE ? { extends: tx.implements } : { extends: tx.extends, implements: tx.implements }), - label: tx.label, - icon: tx.icon, - shortLabel: tx.shortLabel, - sortingKey: tx.sortingKey, - filteringKey: tx.filteringKey, - pluralLabel: tx.pluralLabel + ...(tx.kind === ClassifierKind.INTERFACE + ? { extends: tx.implements } + : { + shortLabel: tx.shortLabel, + sortingKey: tx.sortingKey, + filteringKey: tx.filteringKey, + pluralLabel: tx.pluralLabel + }) }, objectId ) @@ -412,13 +416,6 @@ export function TypeMarkup (): Type { return { _class: core.class.TypeMarkup, label: core.string.Markup } } -/** - * @public - */ -export function TypeCollaborativeMarkup (): Type { - return { _class: core.class.TypeCollaborativeMarkup, label: core.string.Collaborative } -} - /** * @public */ diff --git a/packages/platform/lang/en.json b/packages/platform/lang/en.json index 5e01432a6e..b5ddbaf8f3 100644 --- a/packages/platform/lang/en.json +++ b/packages/platform/lang/en.json @@ -18,7 +18,6 @@ "WorkspaceRateLimit": "Server is busy, Please wait a bit and try again", "AccountAlreadyConfirmed": "Account already confirmed", "WorkspaceAlreadyExists": "Workspace already exists", - "ProductIdMismatch": "Product Mismatch", "InvalidOtp": "Invalid code" } } diff --git a/packages/platform/lang/es.json b/packages/platform/lang/es.json index 04c6ec0b1d..e008c8eb47 100644 --- a/packages/platform/lang/es.json +++ b/packages/platform/lang/es.json @@ -18,7 +18,6 @@ "WorkspaceRateLimit": "El servidor está ocupado. Espere un momento e inténtelo de nuevo", "AccountAlreadyConfirmed": "La cuenta ya está confirmada", "WorkspaceAlreadyExists": "El espacio de trabajo ya existe", - "ProductIdMismatch": "Desajuste de producto", "InvalidOtp": "Código no válido" } } \ No newline at end of file diff --git a/packages/platform/lang/fr.json b/packages/platform/lang/fr.json index 09ae879b64..60e421c310 100644 --- a/packages/platform/lang/fr.json +++ b/packages/platform/lang/fr.json @@ -18,7 +18,6 @@ "WorkspaceRateLimit": "Le serveur est occupé, veuillez patienter un moment et réessayer", "AccountAlreadyConfirmed": "Compte déjà confirmé", "WorkspaceAlreadyExists": "L'espace de travail existe déjà", - "ProductIdMismatch": "Incompatibilité de produit", "InvalidOtp": "Code invalide" } } \ No newline at end of file diff --git a/packages/platform/lang/pt.json b/packages/platform/lang/pt.json index ece7718437..603a90a8ad 100644 --- a/packages/platform/lang/pt.json +++ b/packages/platform/lang/pt.json @@ -18,7 +18,6 @@ "WorkspaceRateLimit": "O servidor está ocupado. Por favor, espere um pouco e tente novamente", "AccountAlreadyConfirmed": "Conta já confirmada", "WorkspaceAlreadyExists": "Espaço de trabalho já existe", - "ProductIdMismatch": "Discrepância de produto", "InvalidOtp": "Código inválido" } } \ No newline at end of file diff --git a/packages/platform/lang/ru.json b/packages/platform/lang/ru.json index 7b5759a159..7772a8efb1 100644 --- a/packages/platform/lang/ru.json +++ b/packages/platform/lang/ru.json @@ -18,7 +18,6 @@ "WorkspaceRateLimit": "Сервер перегружен, пожалуйста, подождите", "AccountAlreadyConfirmed": "Аккаунт уже подтвержден", "WorkspaceAlreadyExists": "Рабочее пространство уже существует", - "ProductIdMismatch": "Продукт не соответствует", "InvalidOtp": "Неверный код" } } diff --git a/packages/platform/lang/zh.json b/packages/platform/lang/zh.json index b153e21385..062c41d319 100644 --- a/packages/platform/lang/zh.json +++ b/packages/platform/lang/zh.json @@ -18,7 +18,6 @@ "WorkspaceRateLimit": "服务器繁忙,请稍后再试", "AccountAlreadyConfirmed": "账户已确认", "WorkspaceAlreadyExists": "工作区已存在", - "ProductIdMismatch": "产品不匹配", "InvalidOtp": "无效的代码" } } diff --git a/packages/platform/src/platform.ts b/packages/platform/src/platform.ts index eb9c784306..b4b7b2503c 100644 --- a/packages/platform/src/platform.ts +++ b/packages/platform/src/platform.ts @@ -153,7 +153,6 @@ export default plugin(platformId, { AccountAlreadyConfirmed: '' as StatusCode<{ account: string }>, WorkspaceAlreadyExists: '' as StatusCode<{ workspace: string }>, WorkspaceRateLimit: '' as StatusCode<{ workspace: string }>, - ProductIdMismatch: '' as StatusCode<{ productId: string }>, InvalidOtp: '' as StatusCode }, metadata: { diff --git a/packages/presentation/src/collaborator.ts b/packages/presentation/src/collaborator.ts index 8da19a5176..bde927c5a0 100644 --- a/packages/presentation/src/collaborator.ts +++ b/packages/presentation/src/collaborator.ts @@ -13,34 +13,36 @@ // limitations under the License. // -import { type CollaboratorClient, getClient as getCollaborator } from '@hcengineering/collaborator-client' +import { + type CollaboratorClient, + getClient as getCollaborator, + type DocumentSnapshotParams +} from '@hcengineering/collaborator-client' import { type CollaborativeDoc, type Markup, getCurrentAccount, getWorkspaceId } from '@hcengineering/core' import { getMetadata } from '@hcengineering/platform' import { getCurrentLocation } from '@hcengineering/ui' -import { getClient } from '.' import presentation from './plugin' /** @public */ export function getCollaboratorClient (): CollaboratorClient { const workspaceId = getWorkspaceId(getCurrentLocation().path[1] ?? '') - const hierarchy = getClient().getHierarchy() const token = getMetadata(presentation.metadata.Token) ?? '' - const collaboratorURL = getMetadata(presentation.metadata.CollaboratorApiUrl) ?? '' + const collaboratorURL = getMetadata(presentation.metadata.CollaboratorUrl) ?? '' - return getCollaborator(hierarchy, workspaceId, token, collaboratorURL) + return getCollaborator(workspaceId, token, collaboratorURL) } /** @public */ -export async function getMarkup (collaborativeDoc: CollaborativeDoc, field: string): Promise { +export async function getMarkup (collaborativeDoc: CollaborativeDoc): Promise> { const client = getCollaboratorClient() - return await client.getContent(collaborativeDoc, field) + return await client.getContent(collaborativeDoc) } /** @public */ -export async function updateMarkup (collaborativeDoc: CollaborativeDoc, field: string, value: Markup): Promise { +export async function updateMarkup (collaborativeDoc: CollaborativeDoc, content: Record): Promise { const client = getCollaboratorClient() - await client.updateContent(collaborativeDoc, field, value) + await client.updateContent(collaborativeDoc, content) } /** @public */ @@ -60,12 +62,10 @@ export async function copyDocument (source: CollaborativeDoc, target: Collaborat } /** @public */ -export async function takeSnapshot ( - collaborativeDoc: CollaborativeDoc, - snapshotName: string -): Promise { +export async function takeSnapshot (collaborativeDoc: CollaborativeDoc, versionName: string): Promise { const client = getCollaboratorClient() const createdBy = getCurrentAccount()._id - return await client.snapshot(collaborativeDoc, { createdBy, snapshotName }) + const snapshot: DocumentSnapshotParams = { createdBy, versionId: `${Date.now()}`, versionName } + return await client.snapshot(collaborativeDoc, snapshot) } diff --git a/packages/presentation/src/plugin.ts b/packages/presentation/src/plugin.ts index adb7a026d3..1f35e9f890 100644 --- a/packages/presentation/src/plugin.ts +++ b/packages/presentation/src/plugin.ts @@ -132,7 +132,6 @@ export default plugin(presentationId, { UploadURL: '' as Metadata, FilesURL: '' as Metadata, CollaboratorUrl: '' as Metadata, - CollaboratorApiUrl: '' as Metadata, Token: '' as Metadata, Endpoint: '' as Metadata, Workspace: '' as Metadata, diff --git a/packages/presentation/src/preview.ts b/packages/presentation/src/preview.ts index ed22aada99..dd9f06a7c1 100644 --- a/packages/presentation/src/preview.ts +++ b/packages/presentation/src/preview.ts @@ -54,6 +54,10 @@ export function getSrcSet (_blob: Ref, width?: number): string { } function blobToSrcSet (cfg: PreviewConfig, blob: Ref, width: number | undefined): string { + if (blob.includes('://')) { + return '' + } + let url = cfg.previewUrl.replaceAll(':workspace', encodeURIComponent(getCurrentWorkspace())) const downloadUrl = getFileUrl(blob) diff --git a/packages/presentation/src/utils.ts b/packages/presentation/src/utils.ts index 75c0e03b2a..5224918cb9 100644 --- a/packages/presentation/src/utils.ts +++ b/packages/presentation/src/utils.ts @@ -533,9 +533,6 @@ export function getAttributePresenterClass ( if (hierarchy.isDerived(attrClass, core.class.TypeMarkup)) { category = 'inplace' } - if (hierarchy.isDerived(attrClass, core.class.TypeCollaborativeMarkup)) { - category = 'inplace' - } if (hierarchy.isDerived(attrClass, core.class.TypeCollaborativeDoc)) { category = 'inplace' } diff --git a/packages/storage/src/index.ts b/packages/storage/src/index.ts index f4183dff34..5d3744fdc5 100644 --- a/packages/storage/src/index.ts +++ b/packages/storage/src/index.ts @@ -43,7 +43,7 @@ export interface StorageAdapter { make: (ctx: MeasureContext, workspaceId: WorkspaceId) => Promise delete: (ctx: MeasureContext, workspaceId: WorkspaceId) => Promise - listBuckets: (ctx: MeasureContext, productId: string) => Promise + listBuckets: (ctx: MeasureContext) => Promise remove: (ctx: MeasureContext, workspaceId: WorkspaceId, objectNames: string[]) => Promise listStream: (ctx: MeasureContext, workspaceId: WorkspaceId, prefix?: string) => Promise stat: (ctx: MeasureContext, workspaceId: WorkspaceId, objectName: string) => Promise @@ -104,7 +104,7 @@ export class DummyStorageAdapter implements StorageAdapter, StorageAdapterEx { } } - async listBuckets (ctx: MeasureContext, productId: string): Promise { + async listBuckets (ctx: MeasureContext): Promise { return [] } diff --git a/packages/text/src/ydoc.ts b/packages/text/src/ydoc.ts index 12e80f2773..f00e6c0b53 100644 --- a/packages/text/src/ydoc.ts +++ b/packages/text/src/ydoc.ts @@ -64,7 +64,7 @@ const defaultSchema = getSchema(defaultExtensions) * @public */ export function yDocToNode (ydoc: YDoc, field?: string, schema?: Schema, extensions?: Extensions): Node { - schema ??= extensions === undefined ? defaultSchema : getSchema(extensions ?? defaultExtensions) + schema ??= getSchema(extensions ?? defaultExtensions) try { const body = yDocToProsemirrorJSON(ydoc, field) diff --git a/plugins/activity-resources/src/activityMessagesUtils.ts b/plugins/activity-resources/src/activityMessagesUtils.ts index 4d503fd869..a13c1caec6 100644 --- a/plugins/activity-resources/src/activityMessagesUtils.ts +++ b/plugins/activity-resources/src/activityMessagesUtils.ts @@ -59,7 +59,6 @@ const valueTypes: ReadonlyArray>> = [ core.class.TypeDate, core.class.TypeFileSize, core.class.TypeMarkup, - core.class.TypeCollaborativeMarkup, core.class.TypeHyperlink ] diff --git a/plugins/activity-resources/src/components/Activity.svelte b/plugins/activity-resources/src/components/Activity.svelte index b2f47f38a2..9fea4555e9 100644 --- a/plugins/activity-resources/src/components/Activity.svelte +++ b/plugins/activity-resources/src/components/Activity.svelte @@ -250,7 +250,11 @@ {#if isNewestFirst && showCommenInput}
- +
{/if}
{#if showCommenInput && !isNewestFirst}
- +
{/if} diff --git a/plugins/activity-resources/src/utils.ts b/plugins/activity-resources/src/utils.ts index cde846e3d5..7a91bb20bb 100644 --- a/plugins/activity-resources/src/utils.ts +++ b/plugins/activity-resources/src/utils.ts @@ -144,7 +144,6 @@ export function getIsTextType (attributeModel?: AttributeModel): boolean { return ( attributeModel.attribute?.type?._class === core.class.TypeMarkup || - attributeModel.attribute?.type?._class === core.class.TypeCollaborativeMarkup || attributeModel.attribute?.type?._class === core.class.TypeCollaborativeDoc ) } diff --git a/plugins/activity/src/index.ts b/plugins/activity/src/index.ts index 256356380f..a55c4047cb 100644 --- a/plugins/activity/src/index.ts +++ b/plugins/activity/src/index.ts @@ -93,7 +93,7 @@ export interface DocUpdateMessage extends ActivityMessage { objectId: Ref objectClass: Ref> - txId: Ref> + txId?: Ref> action: DocUpdateAction updateCollection?: string diff --git a/plugins/ai-bot/src/index.ts b/plugins/ai-bot/src/index.ts index 376004992d..c612e4dd6f 100644 --- a/plugins/ai-bot/src/index.ts +++ b/plugins/ai-bot/src/index.ts @@ -39,6 +39,7 @@ export interface AIBotTransferEvent extends AIBotEvent { toEmail: string toWorkspace: string fromWorkspace: string + fromWorkspaceName: string fromWorkspaceUrl: string messageId: Ref parentMessageId?: Ref diff --git a/plugins/analytics-collector-assets/lang/en.json b/plugins/analytics-collector-assets/lang/en.json index e1a049dade..ffc075b674 100644 --- a/plugins/analytics-collector-assets/lang/en.json +++ b/plugins/analytics-collector-assets/lang/en.json @@ -1,6 +1,6 @@ { "string": { - "AnalyticsChannelDescription": "User: {user}; Workspace id: {workspace}", + "OnboardingChannelDescription": "User: {user}; Workspace: {workspace}", "Error": "Error: ", "InProject": " in project ", "NavigateToSpecial": "{app}: open {special}", @@ -8,6 +8,13 @@ "Set": "Set ", "To": " to ", "Open": "open ", - "Workbench": "Workbench: " + "Workbench": "Workbench: ", + "OnboardingChannel": "Onboarding Channel", + "OnboardingChannels": "Onboarding Channels", + "WorkspaceId": "Workspace id", + "WorkspaceName": "Workspace name", + "WorkspaceUrl": "Workspace url", + "Email": "Email", + "UserName": "User name" } } diff --git a/plugins/analytics-collector-assets/lang/es.json b/plugins/analytics-collector-assets/lang/es.json index 324bdcf600..13f5f142e3 100644 --- a/plugins/analytics-collector-assets/lang/es.json +++ b/plugins/analytics-collector-assets/lang/es.json @@ -1,6 +1,6 @@ { "string": { - "AnalyticsChannelDescription": "User: {user}; Workspace id: {workspace}", + "OnboardingChannelDescription": "User: {user}; Workspace: {workspace}", "Error": "Error: ", "InProject": " in project ", "NavigateToSpecial": "{app}: open {special}", @@ -8,6 +8,13 @@ "Set": "Set ", "To": " to ", "Open": "open ", - "Workbench": "Workbench: " + "Workbench": "Workbench: ", + "OnboardingChannel": "Canal de incorporación", + "OnboardingChannels": "Canales de incorporación", + "WorkspaceId": "Workspace id", + "WorkspaceName": "Workspace name", + "WorkspaceUrl": "Workspace url", + "Email": "Email", + "UserName": "User name" } } diff --git a/plugins/analytics-collector-assets/lang/pt.json b/plugins/analytics-collector-assets/lang/pt.json index 324bdcf600..13f5f142e3 100644 --- a/plugins/analytics-collector-assets/lang/pt.json +++ b/plugins/analytics-collector-assets/lang/pt.json @@ -1,6 +1,6 @@ { "string": { - "AnalyticsChannelDescription": "User: {user}; Workspace id: {workspace}", + "OnboardingChannelDescription": "User: {user}; Workspace: {workspace}", "Error": "Error: ", "InProject": " in project ", "NavigateToSpecial": "{app}: open {special}", @@ -8,6 +8,13 @@ "Set": "Set ", "To": " to ", "Open": "open ", - "Workbench": "Workbench: " + "Workbench": "Workbench: ", + "OnboardingChannel": "Canal de incorporación", + "OnboardingChannels": "Canales de incorporación", + "WorkspaceId": "Workspace id", + "WorkspaceName": "Workspace name", + "WorkspaceUrl": "Workspace url", + "Email": "Email", + "UserName": "User name" } } diff --git a/plugins/analytics-collector-assets/lang/ru.json b/plugins/analytics-collector-assets/lang/ru.json index 324bdcf600..f94f5e1ccb 100644 --- a/plugins/analytics-collector-assets/lang/ru.json +++ b/plugins/analytics-collector-assets/lang/ru.json @@ -1,6 +1,6 @@ { "string": { - "AnalyticsChannelDescription": "User: {user}; Workspace id: {workspace}", + "OnboardingChannelDescription": "User: {user}; Workspace: {workspace}", "Error": "Error: ", "InProject": " in project ", "NavigateToSpecial": "{app}: open {special}", @@ -8,6 +8,13 @@ "Set": "Set ", "To": " to ", "Open": "open ", - "Workbench": "Workbench: " + "Workbench": "Workbench: ", + "OnboardingChannel": "Канал обучения", + "OnboardingChannels": "Каналы обучения", + "WorkspaceId": "Id пространства", + "WorkspaceName": "Имя пространства", + "WorkspaceUrl": "URL пространства", + "Email": "Email", + "UserName": "Имя пользователя" } } diff --git a/plugins/analytics-collector-resources/.eslintrc.js b/plugins/analytics-collector-resources/.eslintrc.js new file mode 100644 index 0000000000..bb8fd7450d --- /dev/null +++ b/plugins/analytics-collector-resources/.eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/ui/eslint.config.json'], + parserOptions: { tsconfigRootDir: __dirname } +} diff --git a/plugins/analytics-collector-resources/.prettierrc b/plugins/analytics-collector-resources/.prettierrc new file mode 100644 index 0000000000..792942803a --- /dev/null +++ b/plugins/analytics-collector-resources/.prettierrc @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "trailingComma": "none", + "tabWidth": 2, + "semi": false, + "singleQuote": true, + "printWidth": 120, + "useTabs": false, + "bracketSpacing": true, + "proseWrap": "preserve", + "plugins": [ + "prettier-plugin-svelte" + ], + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte" + } + } + ] +} \ No newline at end of file diff --git a/models/server-openai/config/rig.json b/plugins/analytics-collector-resources/config/rig.json similarity index 85% rename from models/server-openai/config/rig.json rename to plugins/analytics-collector-resources/config/rig.json index 2f6be36605..bcad6f7c33 100644 --- a/models/server-openai/config/rig.json +++ b/plugins/analytics-collector-resources/config/rig.json @@ -1,5 +1,5 @@ { "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", "rigPackageName": "@hcengineering/platform-rig", - "rigProfile": "model" + "rigProfile": "ui" } diff --git a/plugins/analytics-collector-resources/jest.config.js b/plugins/analytics-collector-resources/jest.config.js new file mode 100644 index 0000000000..3656e284d3 --- /dev/null +++ b/plugins/analytics-collector-resources/jest.config.js @@ -0,0 +1,5 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'] +} diff --git a/plugins/analytics-collector-resources/package.json b/plugins/analytics-collector-resources/package.json new file mode 100644 index 0000000000..525ad90342 --- /dev/null +++ b/plugins/analytics-collector-resources/package.json @@ -0,0 +1,51 @@ +{ + "name": "@hcengineering/analytics-collector-resources", + "version": "0.6.0", + "main": "src/index.ts", + "author": "Anticrm Platform Contributors", + "license": "EPL-2.0", + "scripts": { + "build": "compile ui", + "build:docs": "api-extractor run --local", + "format": "format src", + "svelte-check": "do-svelte-check", + "_phase:svelte-check": "do-svelte-check", + "build:watch": "compile ui", + "_phase:build": "compile ui", + "_phase:format": "format src", + "_phase:validate": "compile validate" + }, + "devDependencies": { + "@hcengineering/platform-rig": "^0.6.0", + "@types/jest": "^29.5.5", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "@typescript-eslint/parser": "^6.11.0", + "eslint": "^8.54.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-n": "^15.4.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-svelte": "^2.35.1", + "jest": "^29.7.0", + "prettier": "^3.1.0", + "prettier-plugin-svelte": "^3.2.2", + "sass": "^1.53.0", + "svelte-check": "^3.6.9", + "svelte-eslint-parser": "^0.33.1", + "svelte-loader": "^3.2.0", + "svelte-preprocess": "^5.1.3", + "ts-jest": "^29.1.1", + "typescript": "^5.3.3" + }, + "dependencies": { + "@hcengineering/analytics-collector": "^0.6.0", + "@hcengineering/chunter": "^0.6.20", + "@hcengineering/chunter-resources": "^0.6.0", + "@hcengineering/contact": "^0.6.24", + "@hcengineering/contact-resources": "^0.6.0", + "@hcengineering/core": "^0.6.32", + "@hcengineering/platform": "^0.6.11", + "@hcengineering/presentation": "^0.6.3", + "svelte": "^4.2.12" + } +} diff --git a/plugins/analytics-collector-resources/postcss.config.js b/plugins/analytics-collector-resources/postcss.config.js new file mode 100644 index 0000000000..88752c6cb0 --- /dev/null +++ b/plugins/analytics-collector-resources/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: [ + require('autoprefixer') + ] +} diff --git a/plugins/analytics-collector-resources/src/index.ts b/plugins/analytics-collector-resources/src/index.ts new file mode 100644 index 0000000000..f21bd9da1b --- /dev/null +++ b/plugins/analytics-collector-resources/src/index.ts @@ -0,0 +1,23 @@ +// +// Copyright © 2024 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 { type Resources } from '@hcengineering/platform' +import { AnalyticsCollectorInlineAction } from './utils' + +export default async (): Promise => ({ + function: { + AnalyticsCollectorInlineAction + } +}) diff --git a/plugins/analytics-collector-resources/src/utils.ts b/plugins/analytics-collector-resources/src/utils.ts new file mode 100644 index 0000000000..4d90715c6d --- /dev/null +++ b/plugins/analytics-collector-resources/src/utils.ts @@ -0,0 +1,46 @@ +// +// Copyright © 2024 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 { type InlineButtonAction } from '@hcengineering/chunter' +import analyticsCollector from '@hcengineering/analytics-collector' +import { getMetadata } from '@hcengineering/platform' +import presentation from '@hcengineering/presentation' +import { concatLink } from '@hcengineering/core' + +export const AnalyticsCollectorInlineAction: InlineButtonAction = async ( + button, + messageId, + channelId +): Promise => { + const url = getMetadata(analyticsCollector.metadata.EndpointURL) ?? '' + const token = getMetadata(presentation.metadata.Token) ?? '' + + if (url === '' || token === '') { + return + } + + try { + await fetch(concatLink(url, 'action'), { + method: 'POST', + headers: { + Authorization: 'Bearer ' + token, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ _id: button._id, name: button.name, messageId, channelId }) + }) + } catch (e) { + console.error(e) + } +} diff --git a/plugins/analytics-collector-resources/svelte.config.js b/plugins/analytics-collector-resources/svelte.config.js new file mode 100644 index 0000000000..944a06f73e --- /dev/null +++ b/plugins/analytics-collector-resources/svelte.config.js @@ -0,0 +1,5 @@ +const sveltePreprocess = require('svelte-preprocess') + +module.exports = { + preprocess: sveltePreprocess() +}; \ No newline at end of file diff --git a/models/server-openai/tsconfig.json b/plugins/analytics-collector-resources/tsconfig.json similarity index 56% rename from models/server-openai/tsconfig.json rename to plugins/analytics-collector-resources/tsconfig.json index 9765b6086d..fb6ccb2d18 100644 --- a/models/server-openai/tsconfig.json +++ b/plugins/analytics-collector-resources/tsconfig.json @@ -1,10 +1,9 @@ { - "extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json", + "extends": "./node_modules/@hcengineering/platform-rig/profiles/ui/tsconfig.json", "compilerOptions": { "rootDir": "./src", "outDir": "./lib", - "declarationDir": "./types", - "tsBuildInfoFile": ".build/build.tsbuildinfo" + "declarationDir": "./types" } } \ No newline at end of file diff --git a/plugins/analytics-collector/src/index.ts b/plugins/analytics-collector/src/index.ts index d96f62dccf..d1009feae5 100644 --- a/plugins/analytics-collector/src/index.ts +++ b/plugins/analytics-collector/src/index.ts @@ -13,11 +13,12 @@ // limitations under the License. // -import type { IntlString, Metadata, Plugin } from '@hcengineering/platform' +import type { IntlString, Metadata, Plugin, Resource } from '@hcengineering/platform' import { plugin } from '@hcengineering/platform' -import type { Mixin, Ref } from '@hcengineering/core' +import type { Class, Ref } from '@hcengineering/core' +import { Channel, InlineButtonAction } from '@hcengineering/chunter' -import { AnalyticsChannel } from './types' +import { OnboardingChannel } from './types' export const analyticsCollectorId = 'analytics' as Plugin @@ -28,18 +29,31 @@ const analyticsCollector = plugin(analyticsCollectorId, { metadata: { EndpointURL: '' as Metadata }, - mixin: { - AnalyticsChannel: '' as Ref> + class: { + OnboardingChannel: '' as Ref> + }, + space: { + GeneralOnboardingChannel: '' as Ref + }, + function: { + AnalyticsCollectorInlineAction: '' as Resource }, string: { - AnalyticsChannelDescription: '' as IntlString, + OnboardingChannelDescription: '' as IntlString, Error: '' as IntlString, InProject: '' as IntlString, Open: '' as IntlString, OpenSpecial: '' as IntlString, Set: '' as IntlString, To: '' as IntlString, - Workbench: '' as IntlString + Workbench: '' as IntlString, + OnboardingChannel: '' as IntlString, + OnboardingChannels: '' as IntlString, + WorkspaceId: '' as IntlString, + WorkspaceName: '' as IntlString, + WorkspaceUrl: '' as IntlString, + Email: '' as IntlString, + UserName: '' as IntlString } }) diff --git a/plugins/analytics-collector/src/types.ts b/plugins/analytics-collector/src/types.ts index 8759474e23..75a86d154c 100644 --- a/plugins/analytics-collector/src/types.ts +++ b/plugins/analytics-collector/src/types.ts @@ -29,7 +29,10 @@ export interface AnalyticEvent { timestamp: number } -export interface AnalyticsChannel extends Channel { - workspace: string +export interface OnboardingChannel extends Channel { + workspaceId: string + workspaceName: string + workspaceUrl: string email: string + userName: string } diff --git a/plugins/analytics-collector/src/utils.ts b/plugins/analytics-collector/src/utils.ts index 2ab19eeb46..9b2c888ea3 100644 --- a/plugins/analytics-collector/src/utils.ts +++ b/plugins/analytics-collector/src/utils.ts @@ -13,6 +13,6 @@ // limitations under the License. // -export function getAnalyticsChannelName (worksapce: string, email: string): string { +export function getOnboardingChannelName (worksapce: string, email: string): string { return `${email}; ${worksapce}` } diff --git a/plugins/bitrix/src/hr.ts b/plugins/bitrix/src/hr.ts index e1753ffcc2..1ed9d9faa7 100644 --- a/plugins/bitrix/src/hr.ts +++ b/plugins/bitrix/src/hr.ts @@ -1,5 +1,16 @@ import { Organization } from '@hcengineering/contact' -import core, { Account, Client, Data, Doc, Ref, SortingOrder, Status, TxOperations } from '@hcengineering/core' +import core, { + Account, + Client, + Data, + Doc, + Ref, + SortingOrder, + Status, + TxOperations, + generateId, + makeCollaborativeDoc +} from '@hcengineering/core' import recruit, { Applicant, Vacancy } from '@hcengineering/recruit' import task, { ProjectType, makeRank } from '@hcengineering/task' @@ -23,17 +34,25 @@ export async function createVacancy ( const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true) - const id = await client.createDoc(recruit.class.Vacancy, core.space.Space, { - name, - description: type.shortDescription ?? '', - fullDescription: type.description, - private: false, - archived: false, - company, - number: (incResult as any).object.sequence, - members: [], - type: typeId - }) + const id: Ref = generateId() + await client.createDoc( + recruit.class.Vacancy, + core.space.Space, + { + name, + description: type.shortDescription ?? '', + fullDescription: makeCollaborativeDoc(id, 'fullDescription'), + private: false, + archived: false, + company, + number: (incResult as any).object.sequence, + members: [], + type: typeId + }, + id + ) + + // TODO type.description return id } diff --git a/plugins/chunter-assets/lang/en.json b/plugins/chunter-assets/lang/en.json index 72121e5360..369949aaff 100644 --- a/plugins/chunter-assets/lang/en.json +++ b/plugins/chunter-assets/lang/en.json @@ -119,6 +119,8 @@ "CreatedChannelOn": "Created this channel on {date}", "ChannelMessages": "Channel messages", "JoinChannel": "Join channel", - "YouJoinedChannel": "You have been joined to channel" + "YouJoinedChannel": "You have been joined to channel", + "AndMore": "and {count} more", + "IsTyping": "{count, plural, =1 {is} other {are}} typing..." } } \ No newline at end of file diff --git a/plugins/chunter-assets/lang/es.json b/plugins/chunter-assets/lang/es.json index 9cbd0877e7..22dc858795 100644 --- a/plugins/chunter-assets/lang/es.json +++ b/plugins/chunter-assets/lang/es.json @@ -119,6 +119,8 @@ "CreatedChannelOn": "Creó este canal el {date}", "ChannelMessages": "Mensajes del canal", "JoinChannel": "Unirse", - "YouJoinedChannel": "Te has unido al canal" + "YouJoinedChannel": "Te has unido al canal", + "AndMore": "y {count} más", + "IsTyping": "está escribiendo..." } } \ No newline at end of file diff --git a/plugins/chunter-assets/lang/fr.json b/plugins/chunter-assets/lang/fr.json index fb42dd0389..17f41e3cd7 100644 --- a/plugins/chunter-assets/lang/fr.json +++ b/plugins/chunter-assets/lang/fr.json @@ -119,6 +119,8 @@ "CreatedChannelOn": "A créé ce canal le {date}", "ChannelMessages": "Messages du canal", "JoinChannel": "Rejoindre", - "YouJoinedChannel": "Vous avez rejoint le canal" + "YouJoinedChannel": "Vous avez rejoint le canal", + "AndMore": "et {count} de plus", + "IsTyping": "est en train d'écrire..." } } \ No newline at end of file diff --git a/plugins/chunter-assets/lang/pt.json b/plugins/chunter-assets/lang/pt.json index 3758587fdf..0d658d13a2 100644 --- a/plugins/chunter-assets/lang/pt.json +++ b/plugins/chunter-assets/lang/pt.json @@ -119,6 +119,8 @@ "CreatedChannelOn": "Criou este canal em {date}", "ChannelMessages": "Mensagens do canal", "JoinChannel": "Participar no canal", - "YouJoinedChannel": "Entrou no canal" + "YouJoinedChannel": "Entrou no canal", + "AndMore": "e mais {count}", + "IsTyping": "está a escrever..." } } \ No newline at end of file diff --git a/plugins/chunter-assets/lang/ru.json b/plugins/chunter-assets/lang/ru.json index f18301ff30..ff275c5510 100644 --- a/plugins/chunter-assets/lang/ru.json +++ b/plugins/chunter-assets/lang/ru.json @@ -119,6 +119,8 @@ "CreatedChannelOn": "Создал этот канал {date}", "ChannelMessages": "Сообщения каналов", "JoinChannel": "Приссоединение к каналу", - "YouJoinedChannel": "Вы присоединились к каналу" + "YouJoinedChannel": "Вы присоединились к каналу", + "AndMore": "и еще {count}", + "IsTyping": "{count, plural, =1 {печатает} other {печатают}}..." } } \ No newline at end of file diff --git a/plugins/chunter-assets/lang/zh.json b/plugins/chunter-assets/lang/zh.json index 0da4296619..5365aaac63 100644 --- a/plugins/chunter-assets/lang/zh.json +++ b/plugins/chunter-assets/lang/zh.json @@ -119,6 +119,8 @@ "CreatedChannelOn": "于 {date} 创建此频道", "ChannelMessages": "频道消息", "JoinChannel": "加入频道", - "YouJoinedChannel": "你已加入频道" + "YouJoinedChannel": "你已加入频道", + "AndMore": "和 {count} 人", + "IsTyping": "正在输入..." } } diff --git a/plugins/chunter-resources/src/channelDataProvider.ts b/plugins/chunter-resources/src/channelDataProvider.ts index 722d2ce49e..37dd22a669 100644 --- a/plugins/chunter-resources/src/channelDataProvider.ts +++ b/plugins/chunter-resources/src/channelDataProvider.ts @@ -30,6 +30,7 @@ import activity, { type ActivityMessage, type ActivityReference } from '@hcengin import attachment from '@hcengineering/attachment' import { combineActivityMessages, sortActivityMessages } from '@hcengineering/activity-resources' import notification, { type DocNotifyContext } from '@hcengineering/notification' +import chunter from '@hcengineering/chunter' export type LoadMode = 'forward' | 'backward' @@ -285,7 +286,7 @@ export class ChannelDataProvider implements IChannelDataProvider { { sort: { createdOn: SortingOrder.Descending }, lookup: { - _id: { attachments: attachment.class.Attachment } + _id: { attachments: attachment.class.Attachment, inlineButtons: chunter.class.InlineButton } } } ) @@ -331,7 +332,7 @@ export class ChannelDataProvider implements IChannelDataProvider { limit: limit ?? this.limit, sort: { createdOn: isBackward ? SortingOrder.Descending : SortingOrder.Ascending }, lookup: { - _id: { attachments: attachment.class.Attachment } + _id: { attachments: attachment.class.Attachment, inlineButtons: chunter.class.InlineButton } } } ) diff --git a/plugins/chunter-resources/src/components/Channel.svelte b/plugins/chunter-resources/src/components/Channel.svelte index 966c148705..1a3e973538 100644 --- a/plugins/chunter-resources/src/components/Channel.svelte +++ b/plugins/chunter-resources/src/components/Channel.svelte @@ -98,8 +98,6 @@ {#if dataProvider} > - export let objectId: Ref + export let object: Doc export let selectedMessageId: Ref | undefined = undefined export let scrollElement: HTMLDivElement | undefined | null = undefined export let startFromBottom = false @@ -115,9 +114,9 @@ $: messages = $messagesStore $: isLoading = $isLoadingStore - $: extensions = client.getModel().findAllSync(activity.class.ActivityExtension, { ofClass: objectClass }) + $: extensions = client.getModel().findAllSync(activity.class.ActivityExtension, { ofClass: doc._class }) - $: notifyContext = $contextByDocStore.get(objectId) + $: notifyContext = $contextByDocStore.get(doc._id) void client .getModel() @@ -129,7 +128,7 @@ } }) - $: displayMessages = filterChatMessages(messages, filters, filterResources, objectClass, selectedFilters) + $: displayMessages = filterChatMessages(messages, filters, filterResources, doc._class, selectedFilters) const unsubscribe = inboxClient.inboxNotificationsByContext.subscribe(() => { if (notifyContext !== undefined) { @@ -360,7 +359,7 @@ clearTimeout(messagesToReadAccumulatorTimer) messagesToReadAccumulatorTimer = setTimeout(() => { const messagesToRead = [...messagesToReadAccumulator] - void readChannelMessages(messagesToRead, notifyContext) + void readChannelMessages(sortActivityMessages(messagesToRead), notifyContext) }, 500) } @@ -674,7 +673,7 @@ } const op = client.apply(generateId(), 'chunter.scrollDown') - await inboxClient.readDoc(op, objectId) + await inboxClient.readDoc(op, doc._id) await op.commit() } @@ -693,7 +692,7 @@ if (unViewed.length === 0) { forceRead = true const op = client.apply(generateId(), 'chunter.forceReadContext') - await inboxClient.readDoc(op, objectId) + await inboxClient.readDoc(op, object._id) await op.commit() } } @@ -790,7 +789,7 @@
{/if} @@ -805,6 +804,7 @@ .ref-input { flex-shrink: 0; margin: 1.25rem 1rem 1rem; + margin-bottom: 0; max-height: 18.75rem; } diff --git a/plugins/chunter-resources/src/components/ChannelTypingInfo.svelte b/plugins/chunter-resources/src/components/ChannelTypingInfo.svelte new file mode 100644 index 0000000000..29e4f3aedd --- /dev/null +++ b/plugins/chunter-resources/src/components/ChannelTypingInfo.svelte @@ -0,0 +1,83 @@ + + + + + {#if typingPersonsLabel !== ''} + + {typingPersonsLabel} + + {#if moreCount > 0} + + {/if} + + {/if} + + + diff --git a/plugins/chunter-resources/src/components/InlineButtons.svelte b/plugins/chunter-resources/src/components/InlineButtons.svelte new file mode 100644 index 0000000000..0af2bd8eb2 --- /dev/null +++ b/plugins/chunter-resources/src/components/InlineButtons.svelte @@ -0,0 +1,49 @@ + + + +{#each inlineButtons as button} + { + void handleInlineButtonClick(button) + }} + /> +{/each} diff --git a/plugins/chunter-resources/src/components/chat-message/ChatMessageInput.svelte b/plugins/chunter-resources/src/components/chat-message/ChatMessageInput.svelte index cbcbb100ca..76ea0a5b70 100644 --- a/plugins/chunter-resources/src/components/chat-message/ChatMessageInput.svelte +++ b/plugins/chunter-resources/src/components/chat-message/ChatMessageInput.svelte @@ -16,14 +16,18 @@ import activity, { ActivityMessage } from '@hcengineering/activity' import { Analytics } from '@hcengineering/analytics' import { AttachmentRefInput } from '@hcengineering/attachment-resources' - import chunter, { ChatMessage, ChunterEvents, ThreadMessage } from '@hcengineering/chunter' - import { Class, Doc, generateId, Ref, type CommitResult } from '@hcengineering/core' - import { createQuery, DraftController, draftsStore, getClient, isSpace } from '@hcengineering/presentation' - import { EmptyMarkup } from '@hcengineering/text' + import chunter, { ChatMessage, ChunterEvents, ThreadMessage, TypingInfo } from '@hcengineering/chunter' + import { Class, Doc, generateId, Ref, type CommitResult, getCurrentAccount } from '@hcengineering/core' + import { createQuery, DraftController, draftsStore, getClient } from '@hcengineering/presentation' + import { EmptyMarkup, isEmptyMarkup } from '@hcengineering/text' import { createEventDispatcher } from 'svelte' import { getObjectId } from '@hcengineering/view-resources' + import { ThrottledCaller } from '@hcengineering/ui' + import { getSpace } from '@hcengineering/activity-resources' + import { PersonAccount } from '@hcengineering/contact' import { getChannelSpace } from '../../utils' + import ChannelTypingInfo from '../ChannelTypingInfo.svelte' export let object: Doc export let chatMessage: ChatMessage | undefined = undefined @@ -33,6 +37,7 @@ export let loading = false export let collection: string = 'comments' export let autofocus = false + export let withTypingInfo = false type MessageDraft = Pick @@ -60,20 +65,26 @@ let inputContent = currentMessage.message $: if (currentDraft != null) { - createdMessageQuery.query( - _class, - { _id, space: getChannelSpace(object._class, object._id, object.space) }, - (result: ChatMessage[]) => { - if (result.length > 0 && _id !== chatMessage?._id) { - // Ouch we have got comment with same id created already. - clear() - } + createdMessageQuery.query(_class, { _id, space: getSpace(object) }, (result: ChatMessage[]) => { + if (result.length > 0 && _id !== chatMessage?._id) { + // Ouch we have got comment with same id created already. + clear() } - ) + }) } else { createdMessageQuery.unsubscribe() } + const typingInfoQuery = createQuery() + let typingInfo: TypingInfo[] = [] + $: if (withTypingInfo) { + typingInfoQuery.query(chunter.class.TypingInfo, { objectId: object._id, space: getSpace(object) }, (res) => { + typingInfo = res + }) + } else { + typingInfoQuery.unsubscribe() + } + function clear (): void { currentMessage = getDefault() _id = currentMessage._id @@ -95,7 +106,40 @@ } } + const me = getCurrentAccount() as PersonAccount + const throttle = new ThrottledCaller(500) + + async function deleteTypingInfo (): Promise { + if (!withTypingInfo) return + const myTypingInfo = typingInfo.find((info) => info.person === me.person) + if (myTypingInfo === undefined) return + await client.remove(myTypingInfo) + } + + async function updateTypingInfo (): Promise { + if (!withTypingInfo) return + const myTypingInfo = typingInfo.find((info) => info.person === me.person) + + if (myTypingInfo === undefined) { + await client.createDoc(chunter.class.TypingInfo, getSpace(object), { + objectId: object._id, + objectClass: object._class, + person: me.person, + lastTyping: Date.now() + }) + } else { + throttle.call(() => { + void client.update(myTypingInfo, { + lastTyping: Date.now() + }) + }) + } + } + function onUpdate (event: CustomEvent): void { + if (!isEmptyMarkup(event.detail.message)) { + void updateTypingInfo() + } if (!shouldSaveDraft) { return } @@ -141,6 +185,7 @@ await handleEdit(event) } else { void handleCreate(event, _id) + void deleteTypingInfo() } // Remove draft from Local Storage @@ -173,7 +218,7 @@ } else { await operations.addCollection( _class, - isSpace(object) ? object._id : object.space, + getSpace(object), object._id, object._class, collection, @@ -213,3 +258,7 @@ on:blur bind:loading /> + +{#if withTypingInfo} + +{/if} diff --git a/plugins/chunter-resources/src/components/chat-message/ChatMessagePresenter.svelte b/plugins/chunter-resources/src/components/chat-message/ChatMessagePresenter.svelte index 565c8ac628..6e37fe54a3 100644 --- a/plugins/chunter-resources/src/components/chat-message/ChatMessagePresenter.svelte +++ b/plugins/chunter-resources/src/components/chat-message/ChatMessagePresenter.svelte @@ -16,18 +16,20 @@ import contact, { Person, PersonAccount } from '@hcengineering/contact' import { personAccountByIdStore, personByIdStore } from '@hcengineering/contact-resources' import { Class, Doc, getCurrentAccount, Ref, Space, WithLookup } from '@hcengineering/core' - import { getClient, MessageViewer } from '@hcengineering/presentation' + import presentation, { createQuery, getClient, MessageViewer } from '@hcengineering/presentation' import { AttachmentDocList, AttachmentImageSize } from '@hcengineering/attachment-resources' import { getDocLinkTitle } from '@hcengineering/view-resources' import { Action, Button, IconEdit, ShowMore } from '@hcengineering/ui' import view from '@hcengineering/view' import activity, { ActivityMessage, ActivityMessageViewType, DisplayActivityMessage } from '@hcengineering/activity' import { ActivityDocLink, ActivityMessageTemplate } from '@hcengineering/activity-resources' - import chunter, { ChatMessage, ChatMessageViewlet } from '@hcengineering/chunter' + import chunter, { ChatMessage, ChatMessageViewlet, InlineButton } from '@hcengineering/chunter' import { Attachment } from '@hcengineering/attachment' + import { getMetadata } from '@hcengineering/platform' import ChatMessageHeader from './ChatMessageHeader.svelte' import ChatMessageInput from './ChatMessageInput.svelte' + import InlineButtons from '../InlineButtons.svelte' export let value: WithLookup | undefined export let doc: Doc | undefined = undefined @@ -142,6 +144,8 @@ let attachments: Attachment[] | undefined = undefined $: attachments = value?.$lookup?.attachments as Attachment[] | undefined + let inlineButtons: InlineButton[] = [] + $: inlineButtons = (value?.$lookup?.inlineButtons ?? []) as InlineButton[] $: socialProvider = value?.provider ? client.getModel().findAllSync(contact.class.ChannelProvider, { _id: value.provider })[0] @@ -192,12 +196,14 @@
+
{:else}
+
{/if} {:else if object} diff --git a/plugins/chunter-resources/src/components/threads/ThreadView.svelte b/plugins/chunter-resources/src/components/threads/ThreadView.svelte index a5afb44a60..cf9b4713e1 100644 --- a/plugins/chunter-resources/src/components/threads/ThreadView.svelte +++ b/plugins/chunter-resources/src/components/threads/ThreadView.svelte @@ -118,15 +118,7 @@
{#if message && dataProvider !== undefined} - +
diff --git a/plugins/chunter/src/index.ts b/plugins/chunter/src/index.ts index 93137eec92..1c87455e66 100644 --- a/plugins/chunter/src/index.ts +++ b/plugins/chunter/src/index.ts @@ -14,9 +14,9 @@ // import { ActivityMessage, ActivityMessageViewlet } from '@hcengineering/activity' -import type { Class, Doc, Markup, Mixin, Ref, Space, Timestamp } from '@hcengineering/core' +import type { AttachedDoc, Class, Doc, Markup, Mixin, Ref, Space, Timestamp } from '@hcengineering/core' import { DocNotifyContext, NotificationType } from '@hcengineering/notification' -import type { Asset, Plugin } from '@hcengineering/platform' +import type { Asset, Plugin, Resource } from '@hcengineering/platform' import { IntlString, plugin } from '@hcengineering/platform' import { AnyComponent } from '@hcengineering/ui' import { Action } from '@hcengineering/view' @@ -54,6 +54,7 @@ export interface ChatMessage extends ActivityMessage { attachments?: number editedOn?: Timestamp provider?: Ref + inlineButtons?: number } /** @@ -80,10 +81,26 @@ export interface ChatInfo extends Doc { timestamp: Timestamp } +export interface TypingInfo extends Doc { + objectId: Ref + objectClass: Ref> + person: Ref + lastTyping: Timestamp +} + export interface ChannelInfo extends DocNotifyContext { hidden: boolean } +export type InlineButtonAction = (button: InlineButton, message: Ref, channel: Ref) => Promise + +export interface InlineButton extends AttachedDoc { + name: string + titleIntl?: IntlString + title?: string + action: Resource +} + /** * @public */ @@ -117,6 +134,9 @@ export default plugin(chunterId, { ChatMessagePreview: '' as AnyComponent, ThreadMessagePreview: '' as AnyComponent }, + activity: { + MembersChangedMessage: '' as AnyComponent + }, class: { ThreadMessage: '' as Ref>, ChunterSpace: '' as Ref>, @@ -124,7 +144,9 @@ export default plugin(chunterId, { DirectMessage: '' as Ref>, ChatMessage: '' as Ref>, ChatMessageViewlet: '' as Ref>, - ChatInfo: '' as Ref> + ChatInfo: '' as Ref>, + InlineButton: '' as Ref>, + TypingInfo: '' as Ref> }, mixin: { ObjectChatPanel: '' as Ref>, @@ -179,7 +201,9 @@ export default plugin(chunterId, { Added: '' as IntlString, Removed: '' as IntlString, CreatedChannelOn: '' as IntlString, - YouJoinedChannel: '' as IntlString + YouJoinedChannel: '' as IntlString, + AndMore: '' as IntlString, + IsTyping: '' as IntlString }, ids: { DMNotification: '' as Ref, diff --git a/plugins/contact-resources/package.json b/plugins/contact-resources/package.json index 9c5a71487e..23b73e1ff9 100644 --- a/plugins/contact-resources/package.json +++ b/plugins/contact-resources/package.json @@ -52,6 +52,7 @@ "@hcengineering/presentation": "^0.6.3", "@hcengineering/setting": "^0.6.17", "@hcengineering/templates": "^0.6.11", + "@hcengineering/text": "^0.6.5", "@hcengineering/text-editor": "^0.6.0", "@hcengineering/text-editor-resources": "^0.6.0", "@hcengineering/theme": "^0.6.5", diff --git a/plugins/contact-resources/src/components/CreateOrganization.svelte b/plugins/contact-resources/src/components/CreateOrganization.svelte index f53c5b0e22..655635fb39 100644 --- a/plugins/contact-resources/src/components/CreateOrganization.svelte +++ b/plugins/contact-resources/src/components/CreateOrganization.svelte @@ -13,13 +13,22 @@ // limitations under the License. --> @@ -185,12 +198,16 @@ focusIndex={3} />
- +
+ +
, - fullDescription: '', + fullDescription: makeCollaborativeDoc(objectId, 'fullDescription'), location: '', type: typeId as Ref } @@ -103,7 +112,7 @@ $: typeId && templateQ.query(task.class.ProjectType, { _id: typeId }, (result) => { const { _class, _id, description, targetClass, ...templateData } = result[0] - vacancyData = { ...(templateData as unknown as Data), fullDescription: description } + vacancyData = { ...(templateData as unknown as Data) } if (appliedTemplateId !== typeId) { fullDescription = description ?? '' appliedTemplateId = typeId @@ -172,10 +181,11 @@ } const number = (incResult as any).object.sequence + const resId: Ref = generateId() const identifier = `${project?.identifier}-${number}` - const resId = await client.addCollection(tracker.class.Issue, space, parent, tracker.class.Issue, 'subIssues', { + const data: AttachedData = { title: template.title + ` (${name})`, - description: template.description, + description: makeCollaborativeDoc(resId, 'description'), assignee: template.assignee, component: template.component, milestone: template.milestone, @@ -195,7 +205,10 @@ childInfo: [], kind: taskType._id, identifier - }) + } + + await updateMarkup(data.description, { description: template.description }) + await client.addCollection(tracker.class.Issue, space, parent, tracker.class.Issue, 'subIssues', data, resId) if ((template.labels?.length ?? 0) > 0) { const tagElements = await client.findAll(tags.class.TagElement, { _id: { $in: template.labels } }) for (const label of tagElements) { @@ -224,7 +237,7 @@ ...vacancyData, name, description: template?.shortDescription ?? '', - fullDescription, + fullDescription: makeCollaborativeDoc(objectId, 'fullDescription'), private: false, archived: false, number: (incResult as any).object.sequence, @@ -235,6 +248,8 @@ type: typeId } + await updateMarkup(data.fullDescription, { fullDescription }) + const id = await client.createDoc(recruit.class.Vacancy, core.space.Space, data, objectId) Analytics.handleEvent(RecruitEvents.VacancyCreated, { diff --git a/plugins/recruit/src/types.ts b/plugins/recruit/src/types.ts index 471d2d872f..5e8c0a3e78 100644 --- a/plugins/recruit/src/types.ts +++ b/plugins/recruit/src/types.ts @@ -15,13 +15,13 @@ import { Event } from '@hcengineering/calendar' import type { Channel, Organization, Person } from '@hcengineering/contact' -import type { AttachedData, AttachedDoc, Markup, Ref, Status, Timestamp } from '@hcengineering/core' +import type { AttachedData, AttachedDoc, CollaborativeDoc, Markup, Ref, Status, Timestamp } from '@hcengineering/core' import { TagReference } from '@hcengineering/tags' import type { Project, Task } from '@hcengineering/task' /** @public */ export interface Vacancy extends Project { - fullDescription?: string + fullDescription: CollaborativeDoc attachments?: number dueTo?: Timestamp location?: string diff --git a/plugins/text-editor-resources/package.json b/plugins/text-editor-resources/package.json index 5f597ae2fc..b176d93471 100644 --- a/plugins/text-editor-resources/package.json +++ b/plugins/text-editor-resources/package.json @@ -77,6 +77,7 @@ "rfc6902": "^5.0.1", "diff": "^5.1.0", "slugify": "^1.6.6", - "lib0": "^0.2.88" + "lib0": "^0.2.88", + "y-indexeddb": "^9.0.12" } } diff --git a/plugins/text-editor-resources/src/components/CollaborativeAttributeBox.svelte b/plugins/text-editor-resources/src/components/CollaborativeAttributeBox.svelte index adb24249d3..530d504d4f 100644 --- a/plugins/text-editor-resources/src/components/CollaborativeAttributeBox.svelte +++ b/plugins/text-editor-resources/src/components/CollaborativeAttributeBox.svelte @@ -13,7 +13,7 @@ // limitations under the License. --> diff --git a/plugins/text-editor-resources/src/provider/indexeddb.ts b/plugins/text-editor-resources/src/provider/indexeddb.ts new file mode 100644 index 0000000000..14b342ac81 --- /dev/null +++ b/plugins/text-editor-resources/src/provider/indexeddb.ts @@ -0,0 +1,32 @@ +// +// Copyright © 2024 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 { collaborativeDocParse, type CollaborativeDoc } from '@hcengineering/core' +import { type Doc as YDoc } from 'yjs' +import { IndexeddbPersistence } from 'y-indexeddb' + +export class IndexeddbProvider extends IndexeddbPersistence { + loaded: Promise + + constructor (collaborativeDoc: CollaborativeDoc, doc: YDoc) { + const { documentId, versionId } = collaborativeDocParse(collaborativeDoc) + const name = `${documentId}/${versionId}` + + super(name, doc) + + this.loaded = new Promise((resolve) => { + this.on('synced', resolve) + }) + } +} diff --git a/plugins/text-editor-resources/src/provider/storage.ts b/plugins/text-editor-resources/src/provider/storage.ts deleted file mode 100644 index fcd4cd3165..0000000000 --- a/plugins/text-editor-resources/src/provider/storage.ts +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copyright © 2023, 2024 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 { collaborativeDocParse, type Blob, type CollaborativeDoc, type Ref } from '@hcengineering/core' -import { getFileUrl } from '@hcengineering/presentation' -import { ObservableV2 as Observable } from 'lib0/observable' -import { applyUpdate, type Doc as YDoc } from 'yjs' - -interface EVENTS { - synced: (...args: any[]) => void -} - -async function fetchContent (blob: Ref, doc: YDoc): Promise { - const update = await fetchBlobContent(blob) - if (update !== undefined) { - applyUpdate(doc, update) - return true - } - - return false -} - -async function fetchBlobContent (_id: Ref): Promise { - try { - const href = getFileUrl(_id) - const res = await fetch(href) - - if (res.ok) { - const blob = await res.blob() - const buffer = await blob.arrayBuffer() - return new Uint8Array(buffer) - } - } catch (err: any) { - console.error(err) - } - - return undefined -} - -export class DirectStorageProvider extends Observable { - loaded: Promise - - constructor (collaborativeDoc: CollaborativeDoc, doc: YDoc) { - super() - - this.loaded = new Promise((resolve) => { - this.on('synced', resolve) - }) - - const { documentId, versionId } = collaborativeDocParse(collaborativeDoc) - - if (versionId === 'HEAD') { - void fetchContent(documentId as Ref, doc).then((synced) => { - if (synced) { - this.emit('synced', [this]) - } - }) - } - } -} diff --git a/plugins/text-editor-resources/src/provider/utils.ts b/plugins/text-editor-resources/src/provider/utils.ts index fc419f7e1a..14bbcb4422 100644 --- a/plugins/text-editor-resources/src/provider/utils.ts +++ b/plugins/text-editor-resources/src/provider/utils.ts @@ -21,7 +21,6 @@ import { formatPlatformDocumentId as origFormatPlatformDocumentId } from '@hcengineering/collaborator-client' import { getCurrentLocation } from '@hcengineering/ui' -import { getClient } from '@hcengineering/presentation' function getWorkspace (): string { return getCurrentLocation().path[1] ?? '' @@ -37,6 +36,5 @@ export function formatPlatformDocumentId ( objectId: Ref, objectAttr: string ): PlatformDocumentId { - const objectDomain = getClient().getHierarchy().getDomain(objectClass) - return origFormatPlatformDocumentId(objectDomain, objectClass, objectId, objectAttr) + return origFormatPlatformDocumentId(objectClass, objectId, objectAttr) } diff --git a/plugins/tracker-resources/src/components/CreateIssue.svelte b/plugins/tracker-resources/src/components/CreateIssue.svelte index cdf5179154..926243b1f1 100644 --- a/plugins/tracker-resources/src/components/CreateIssue.svelte +++ b/plugins/tracker-resources/src/components/CreateIssue.svelte @@ -27,6 +27,7 @@ SortingOrder, fillDefaults, generateId, + makeCollaborativeDoc, toIdMap } from '@hcengineering/core' import { getResource, translate } from '@hcengineering/platform' @@ -41,7 +42,9 @@ MultipleDraftController, SpaceSelector, createQuery, - getClient + getClient, + getMarkup, + updateMarkup } from '@hcengineering/presentation' import tags, { TagElement, TagReference } from '@hcengineering/tags' import { TaskType, makeRank } from '@hcengineering/task' @@ -190,7 +193,6 @@ if (originalIssue !== undefined && !ignoreOriginal) { const res: IssueDraft = { ...base, - description: originalIssue.description, status: originalIssue.status, priority: originalIssue.priority, component: originalIssue.component, @@ -200,6 +202,9 @@ parentIssue: originalIssue.parents[0]?.parentId, title: `${originalIssue.title} (copy)` } + void getMarkup(originalIssue.description).then((res) => { + object.description = res.description + }) void client.findAll(tags.class.TagReference, { attachedTo: originalIssue._id }).then((p) => { object.labels = p }) @@ -471,7 +476,7 @@ const value: DocData = { title: getTitle(object.title), - description: object.description, + description: makeCollaborativeDoc(_id, 'description'), assignee: object.assignee, component: object.component, milestone: object.milestone, @@ -504,6 +509,8 @@ identifier } + await updateMarkup(value.description, { description: object.description }) + await docCreateManager.commit(operations, _id, currentProject, value, 'pre') await operations.addCollection( diff --git a/plugins/tracker-resources/src/components/SubIssues.svelte b/plugins/tracker-resources/src/components/SubIssues.svelte index 5249945407..db9569b58e 100644 --- a/plugins/tracker-resources/src/components/SubIssues.svelte +++ b/plugins/tracker-resources/src/components/SubIssues.svelte @@ -14,8 +14,8 @@ -->