diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..ff08c12b59 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,22 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Debug server", + "type": "node", + "request": "launch", + "args": ["src/__start.ts"], + "env": { + "ELASTIC_URL": "http://localhost:9200", + "MONGO_URL": "mongodb://localhost:27017" + }, + "runtimeArgs": ["--nolazy", "-r", "ts-node/register"], + "sourceMaps": true, + "cwd": "${workspaceRoot}/server/server", + "protocol": "inspector" + } + ] +} diff --git a/README.md b/README.md index 728095b24b..711bf028c1 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,40 @@ You need Microsoft's [rush](https://rushjs.io) to install application. Install [ cd dev/prod rushx dev ``` + Then go to http://localhost:8080/login:component:LoginApp and use following credentials to login into the system: -* login: rosamund@hc.engineering -* pass: 1111 -* workspace: trx40 +- login: rosamund@hc.engineering +- pass: 1111 +- workspace: trx40 + +To connect to running local server `dev-server` command should be used instead. + +## Build and run inside docker + +It is possible to setup all environment required with local docker containers. +Supported both amd64 and armv8 containers. + +```bash +rush build # Will build all required packages. +rush bundle # Will prepare bundles. +rush docker:build # Will build docker containers for all applications. +cd ./dev/ +docker-compose up -d --force-recreate # Will setup all containers + +# we a few seconds delay, to be sure elastic is up and running. +./deploy/setup-es-attachment-pipeline.sh # Setup elastic search plugin configuration. +``` + +By default docker volumes `dev_db` `dev_elastic` `dev_files` will be created for mongo/elastic/minio instances. + +Before we could start we need to create workspace/account and associate it with workspace. + +```bash +cd ./dev/tools +rushx run-local create-workspace ws1 -o DevWorkspace # Create workspace +rushx run-local create-account user1 -p 1234 -f John -l Appleseed # Create account +rushx run-local assign-workspace user1 ws1 # Assign worksapce to user +``` + +Following URL http://localhost:8081/login:component:LoginApp will lead us to app. diff --git a/common/config/rush/command-line.json b/common/config/rush/command-line.json index 2418864902..ab83d5b996 100644 --- a/common/config/rush/command-line.json +++ b/common/config/rush/command-line.json @@ -207,6 +207,27 @@ "ignoreDependencyOrder": false, "ignoreMissingScript": true }, + + { + "commandKind": "bulk", + "name": "docker:build", + "summary": "docker:build", + "description": "Build and make docker containers", + "enableParallelism": true, + "incremental": true, + "ignoreDependencyOrder": false, + "ignoreMissingScript": true + }, + { + "commandKind": "bulk", + "name": "bundle", + "summary": "bundle", + "description": "Build bundles", + "enableParallelism": true, + "incremental": true, + "ignoreDependencyOrder": false, + "ignoreMissingScript": true + }, ], /** diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index eee29f61b3..4c62446880 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -101,6 +101,7 @@ specifiers: '@types/ws': ^7.4.7 autoprefixer: ^10.2.6 commander: ^8.1.0 + compression-webpack-plugin: ~9.0.0 cors: ^2.8.5 cross-env: ^7.0.3 css-loader: ^5.2.1 @@ -241,6 +242,7 @@ dependencies: '@types/ws': 7.4.7 autoprefixer: 10.3.7_postcss@8.3.9 commander: 8.2.0 + compression-webpack-plugin: 9.0.1_webpack@5.57.1 cors: 2.8.5 cross-env: 7.0.3 css-loader: 5.2.7_webpack@5.57.1 @@ -2226,6 +2228,15 @@ packages: ajv: 6.12.6 dev: false + /ajv-formats/2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependenciesMeta: + ajv: + optional: true + dependencies: + ajv: 8.8.1 + dev: false + /ajv-keywords/3.5.2_ajv@6.12.6: resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} peerDependencies: @@ -2234,6 +2245,15 @@ packages: ajv: 6.12.6 dev: false + /ajv-keywords/5.0.0_ajv@8.8.1: + resolution: {integrity: sha512-ULd1QMjRoH6JDNUQIfDLrlE+OgZlFaxyYCjzt58uNuUQtKXt8/U+vK/8Ql0gyn/C5mqZzUWtKMqr/4YquvTrWA==} + peerDependencies: + ajv: ^8.0.0 + dependencies: + ajv: 8.8.1 + fast-deep-equal: 3.1.3 + dev: false + /ajv/6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} dependencies: @@ -2252,6 +2272,15 @@ packages: uri-js: 4.4.1 dev: false + /ajv/8.8.1: + resolution: {integrity: sha512-6CiMNDrzv0ZR916u2T+iRunnD60uWmNn8SkdB44/6stVORUg0aAkWO7PkOhpCmjmW8f2I/G/xnowD66fxGyQJg==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: false + /ansi-colors/3.2.4: resolution: {integrity: sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==} engines: {node: '>=6'} @@ -3013,6 +3042,17 @@ packages: mime-db: 1.50.0 dev: false + /compression-webpack-plugin/9.0.1_webpack@5.57.1: + resolution: {integrity: sha512-vqlhZIPSyCpy6eaYWy8iPhteLWpARKotRiN5B/jr7lLowJv1GVc98Snn1Dcxe0+SKbfydLu7qZcnNuP+AyG19Q==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.1.0 + dependencies: + schema-utils: 4.0.0 + serialize-javascript: 6.0.0 + webpack: 5.57.1_webpack-cli@4.8.0 + dev: false + /compression/1.7.4: resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==} engines: {node: '>= 0.8.0'} @@ -7674,6 +7714,16 @@ packages: ajv-keywords: 3.5.2_ajv@6.12.6 dev: false + /schema-utils/4.0.0: + resolution: {integrity: sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==} + engines: {node: '>= 12.13.0'} + dependencies: + '@types/json-schema': 7.0.9 + ajv: 8.8.1 + ajv-formats: 2.1.1 + ajv-keywords: 5.0.0_ajv@8.8.1 + dev: false + /secure-json-parse/2.4.0: resolution: {integrity: sha512-Q5Z/97nbON5t/L/sH6mY2EacfjVGwrCcSi5D3btRO2GZ8pf1K1UN7Z9H5J57hjVU2Qzxr1xO+FmBhOvEkzCMmg==} dev: false @@ -9832,7 +9882,7 @@ packages: dev: false file:projects/front.tgz_typescript@4.4.3: - resolution: {integrity: sha512-qKmnx2yFt1hFJRZWJNaPPbb+p/Q0lnyA6u9AAgeqDbOjmWjp48NxqX6jIbiEyQi56IDHViE3w4/NQb0c/mp/rA==, tarball: file:projects/front.tgz} + resolution: {integrity: sha512-8DxO3cOzfmEfVPsjIfOfA0mT3OrRDlBCuobV2mlj5+NzofW4yjJicsGlU8jCZ/fG2CMqfpWK5Hdino0LTX+T7g==, tarball: file:projects/front.tgz} id: file:projects/front.tgz name: '@rush-temp/front' version: 0.0.0 @@ -9844,20 +9894,21 @@ packages: '@types/minio': 7.0.10 '@types/node': 16.10.3 '@types/uuid': 8.3.1 - '@typescript-eslint/eslint-plugin': 4.33.0_eslint@7.32.0+typescript@4.4.3 + '@typescript-eslint/eslint-plugin': 5.4.0_87dbf04088b125598d0271706532eaf3 + '@typescript-eslint/parser': 5.4.0_eslint@7.32.0+typescript@4.4.3 cors: 2.8.5 esbuild: 0.12.29 eslint: 7.32.0 + eslint-config-standard-with-typescript: 21.0.1_05a8ea1454e6ca4c9f98b94b8f3abf9c eslint-plugin-import: 2.25.3_eslint@7.32.0 eslint-plugin-node: 11.1.0_eslint@7.32.0 - eslint-plugin-promise: 4.3.1 + eslint-plugin-promise: 5.1.1_eslint@7.32.0 express: 4.17.1 express-fileupload: 1.2.1 jwt-simple: 0.5.6 minio: 7.0.19 uuid: 8.3.2 transitivePeerDependencies: - - '@typescript-eslint/parser' - supports-color - typescript dev: false @@ -9999,7 +10050,7 @@ packages: dev: false file:projects/model-demo.tgz_typescript@4.4.3: - resolution: {integrity: sha512-yo76yELzjfVH7a4Ba62J3M4cYP9yN/Xbe+O1jpwgVqRyqUHpowMrykgvrMDh9meuEsSsJ3NNFrClan9TEaY17A==, tarball: file:projects/model-demo.tgz} + resolution: {integrity: sha512-vXuP1gNtnqOBsOcxF7YtmkX2JFGlRRRxlCt7Jf/uLkAdU2h87nhkoRdllrvkGXPCqJYae47xLvpEpIxpgj6OBQ==, tarball: file:projects/model-demo.tgz} id: file:projects/model-demo.tgz name: '@rush-temp/model-demo' version: 0.0.0 @@ -10330,13 +10381,14 @@ packages: dev: false file:projects/prod.tgz_sass@1.42.1+typescript@4.4.3: - resolution: {integrity: sha512-BNsiZcy9EorJrRU+a1vLrsdXIWfjlSOKDzxwvsiORGg+DJhyBuWRPMl1ngR/JuiDMPIThzTp3DHhmcKDPvQfVw==, tarball: file:projects/prod.tgz} + resolution: {integrity: sha512-x7H3hwhd/DGstkIJ2QG1AJMUyFKeP//3BVCL24c8w4Z1xMD+fnfsg5pf3V1c/LkHk/e3D3YyPTqBOAt5+u1mJQ==, tarball: file:projects/prod.tgz} id: file:projects/prod.tgz name: '@rush-temp/prod' version: 0.0.0 dependencies: '@types/node': 14.17.21 autoprefixer: 10.3.7_postcss@8.3.9 + compression-webpack-plugin: 9.0.1_webpack@5.57.1 cross-env: 7.0.3 css-loader: 5.2.7_webpack@5.57.1 dotenv-webpack: 7.0.3_webpack@5.57.1 diff --git a/dev/docker-compose.yaml b/dev/docker-compose.yaml new file mode 100644 index 0000000000..26ad2eb6f6 --- /dev/null +++ b/dev/docker-compose.yaml @@ -0,0 +1,73 @@ +services: + mongodb: + image: mongo + container_name: mongodb + environment: + - PUID=1000 + - PGID=1000 + volumes: + - db:/data/db + ports: + - 27017:27017 + restart: unless-stopped + minio: + image: 'minio/minio' + command: server /data --address ":9000" --console-address ":9001" + ports: + - 9000:9000 + volumes: + - files:/data + elastic: + image: 'elasticsearch:7.14.0' + command: | + /bin/sh -c "./bin/elasticsearch-plugin list | grep -q ingest-attachment || yes | ./bin/elasticsearch-plugin install --silent ingest-attachment; + /usr/local/bin/docker-entrypoint.sh eswrapper" + volumes: + - elastic:/usr/share/elasticsearch/data + ports: + - 9200:9200 + environment: + - ELASTICSEARCH_PORT_NUMBER=9200 + - BITNAMI_DEBUG=true + - discovery.type=single-node + account: + image: anticrm/account + links: + - mongodb + ports: + - 3000:3000 + environment: + - MONGO_URL=mongodb://mongodb:27017 + - TRANSACTOR_URL=ws://localhost:3333 + front: + image: anticrm/front + links: + - mongodb + - minio + - elastic + - server + - upload + ports: + - 8081:8080 + environment: + - ACCOUNTS_URL=http://localhost:3000 + - UPLOAD_URL=/files + - TRANSACTOR_URL=ws://localhost:3333 + - ELASTIC_URL=http://elastic:9200 + - MINIO_ENDPOINT=minio + - MINIO_ACCESS_KEY=minioadmin + - MINIO_SECRET_KEY=minioadmin + transactor: + image: anticrm/transactor + links: + - mongodb + - elastic + ports: + - 3333:3333 + environment: + - ELASTIC_URL=http://elastic:9200 + - MONGO_URL=mongodb://mongodb:27017 +volumes: + db: + files: + elastic: diff --git a/dev/prod/.env b/dev/prod/.env index fbc8053278..ef5af27eab 100644 --- a/dev/prod/.env +++ b/dev/prod/.env @@ -1,7 +1,5 @@ -CLIENT_TYPE=dev -ACCOUNTS_URL=/account -UPLOAD_URL=/upload -LOGIN_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6InJvc2FtdW5kQGhjLmVuZ2luZWVyaW5nIiwid29ya3NwYWNlIjoidHJ4NDAifQ.dYsCF2VRbuc-zmRt0yLAww1_--xtX4P1EqPFREEzCjQ -# LOGIN_ENDPOINT=ws://localhost:3333 -LOGIN_ENDPOINT=wss://transactor.hc.engineering/ +LOGIN_ENDPOINT=ws://localhost:3333 + +LOGIN_TOKEN_DEV=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6InJvc2FtdW5kQGhjLmVuZ2luZWVyaW5nIiwid29ya3NwYWNlIjoidHJ4NDAifQ.dYsCF2VRbuc-zmRt0yLAww1_--xtX4P1EqPFREEzCjQ +LOGIN_ENDPOINT_DEV=wss://transactor.hc.engineering/ diff --git a/dev/prod/.env-prod b/dev/prod/.env-prod index f169c875ee..e69de29bb2 100644 --- a/dev/prod/.env-prod +++ b/dev/prod/.env-prod @@ -1,5 +0,0 @@ - - -#ACCOUNTS_URL=https://ftwm71rwag.execute-api.us-west-2.amazonaws.com/stage/ -ACCOUNTS_URL=https://account.hc.engineering/ -UPLOAD_URL=/files diff --git a/dev/prod/config.json b/dev/prod/config.json new file mode 100644 index 0000000000..d915d990f8 --- /dev/null +++ b/dev/prod/config.json @@ -0,0 +1 @@ +{"ACCOUNTS_URL":"http://localhost:3000","UPLOAD_URL":"/files"} \ No newline at end of file diff --git a/dev/prod/package.json b/dev/prod/package.json index 64c216dbbc..03cce49c38 100644 --- a/dev/prod/package.json +++ b/dev/prod/package.json @@ -3,11 +3,11 @@ "version": "1.0.1", "license": "EPL-2.0", "scripts": { - "build": "cross-env NODE_ENV=production webpack --stats-error-details && echo 'done'", + "build": "cross-env NODE_ENV=development webpack --stats-error-details && echo 'done'", "analyze": "cross-env NODE_ENV=production webpack --json > stats.json", "show": "webpack-bundle-analyzer stats.json dist", - "dev": "cross-env webpack serve --content-base public", - "dev-server": "cross-env CLIENT=server webpack serve --content-base public", + "dev": "cross-env CLIENT_TYPE=dev webpack serve --content-base public", + "dev-server": "cross-env CLIENT_TYPE=dev-server webpack serve --content-base public", "start": "cross-env NODE_ENV=production webpack serve --content-base public", "preformat-svelte": "prettier -w src/**/*.svelte", "lint": "eslint --max-warnings=0 src", @@ -33,7 +33,8 @@ "autoprefixer": "^10.2.6", "postcss": "^8.3.4", "postcss-loader": "^6.1.0", - "postcss-load-config": "^3.1.0" + "postcss-load-config": "^3.1.0", + "compression-webpack-plugin": "~9.0.0" }, "dependencies": { "@anticrm/platform": "~0.6.5", diff --git a/dev/prod/public/config.json b/dev/prod/public/config.json new file mode 100644 index 0000000000..72b566762d --- /dev/null +++ b/dev/prod/public/config.json @@ -0,0 +1,4 @@ +{ + "ACCOUNTS_URL":"/account", + "UPLOAD_URL":"/files" +} \ No newline at end of file diff --git a/dev/prod/public/index.html b/dev/prod/public/index.html index 1474b2b6f8..c7bf9124d2 100644 --- a/dev/prod/public/index.html +++ b/dev/prod/public/index.html @@ -12,6 +12,7 @@ + diff --git a/dev/prod/src/main.ts b/dev/prod/src/main.ts index 9dffc79f9e..c5647f645d 100644 --- a/dev/prod/src/main.ts +++ b/dev/prod/src/main.ts @@ -14,41 +14,9 @@ // limitations under the License. // -// import { setMetadata } from '@anticrm/platform' import { createApp } from '@anticrm/ui' -// import login from '@anticrm/login' -// import pluginCore from '@anticrm/plugin-core' -// import meetingPlugin from '@anticrm/meeting' - import { configurePlatform } from './platform' configurePlatform() -// const accountsUrl = process.env.APP_ACCOUNTS_URL -// const appHost = process.env.APP_WSHOST -// const appPort = process.env.APP_WSPORT -// const appToken = process.env.APP_TOKEN -// const meetingHost = process.env.MEETING_WSHOST -// const meetingPort = process.env.MEETING_WSPORT - -// setMetadata(login.metadata.AccountsUrl, accountsUrl) -// setMetadata(pluginCore.metadata.ClientUrl, `${appHost}:${appPort}/${appToken}`) -// setMetadata(meetingPlugin.metadata.ClientUrl, `${meetingHost}:${meetingPort}`) -// platform.setMetadata(core.metadata.WSHost, host) -// platform.setMetadata(core.metadata.WSPort, port) - -// const loginInfo = currentAccount() -// if (loginInfo) { -// platform.setMetadata(core.metadata.WhoAmI, loginInfo.email) -// platform.setMetadata(core.metadata.Token, loginInfo.token) -// } - -// async function boot (): Promise { -// uiService.createApp(document.body) -// } - -// boot().catch(err => { -// new ErrorPage({ target: document.body, props: { error: err.message } }) -// }) - createApp(document.body) diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index 2ef08fa33d..1f8011dd30 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -38,20 +38,30 @@ import '@anticrm/recruit-assets' import '@anticrm/activity-assets' import { setMetadata } from '@anticrm/platform' - -export function configurePlatform() { - - setMetadata(login.metadata.AccountsUrl, process.env.ACCOUNTS_URL) - setMetadata(login.metadata.UploadUrl, process.env.UPLOAD_URL) - setMetadata(login.metadata.OverrideLoginToken, process.env.LOGIN_TOKEN) +export function configurePlatform() { + fetch('/config.json').then(config => { + config.json().then(value => { + console.log('loading configuration', value) + setMetadata(login.metadata.AccountsUrl, value.ACCOUNTS_URL) + setMetadata(login.metadata.UploadUrl, value.UPLOAD_URL) + }) + }) setMetadata(login.metadata.OverrideEndpoint, process.env.LOGIN_ENDPOINT) if (process.env.CLIENT_TYPE === 'dev') { + setMetadata(login.metadata.OverrideLoginToken, process.env.LOGIN_TOKEN_DEV) + setMetadata(login.metadata.OverrideEndpoint, process.env.LOGIN_ENDPOINT_DEV) + console.log('Use DEV server') addLocation(clientId, () => import(/* webpackChunkName: "client-dev" */ '@anticrm/dev-client-resources')) addLocation(serverChunterId, () => import(/* webpackChunkName: "server-chunter" */ '@anticrm/dev-server-chunter-resources')) addLocation(serverRecruitId, () => import(/* webpackChunkName: "server-recruit" */ '@anticrm/server-recruit-resources')) addLocation(serverViewId, () => import(/* webpackChunkName: "server-view" */ '@anticrm/server-view-resources')) } else { + if (process.env.CLIENT_TYPE === 'dev-server') { + console.log('Use Endpoint override:', process.env.LOGIN_ENDPOINT) + setMetadata(login.metadata.OverrideEndpoint, process.env.LOGIN_ENDPOINT) + } + console.log('Use server') addLocation(clientId, () => import(/* webpackChunkName: "client" */ '@anticrm/client-resources')) } diff --git a/dev/prod/webpack.config.js b/dev/prod/webpack.config.js index 808e0945c9..b9e225351e 100644 --- a/dev/prod/webpack.config.js +++ b/dev/prod/webpack.config.js @@ -17,9 +17,12 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin') const Dotenv = require('dotenv-webpack') const path = require('path') const autoprefixer = require('autoprefixer') +const CompressionPlugin = require('compression-webpack-plugin') +const DefinePlugin = require('webpack').DefinePlugin const mode = process.env.NODE_ENV || 'development' const prod = mode === 'production' +const devServer = (process.env.CLIENT_TYPE ?? '') === 'dev-server' module.exports = { entry: { @@ -42,6 +45,22 @@ module.exports = { chunkFilename: '[name].[id].js', publicPath: '/' }, + optimization: { + minimize: prod, + usedExports: prod, + splitChunks: { + chunks: 'all', + maxAsyncRequests: 5, + maxInitialRequests: 3, + cacheGroups: { + vendors: { + name: 'vendors', + test: /[\\/]node_modules[\\/]/, + priority: 20 + } + } + } + }, module: { rules: [ { @@ -149,10 +168,14 @@ module.exports = { }, mode, plugins: [ + ...(prod ? [new CompressionPlugin()] : []), new MiniCssExtractPlugin({ filename: '[name].css' }), - new Dotenv({path: prod ? '.env-prod' : '.env'}) + new Dotenv({path: prod ? '.env-prod' : '.env'}), + new DefinePlugin({ + 'process.env.CLIENT_TYPE': JSON.stringify(process.env.CLIENT_TYPE) + }) ], devtool: prod ? false : 'source-map', devServer: { @@ -160,7 +183,19 @@ module.exports = { historyApiFallback: { disableDotRule: true }, - proxy: { + proxy: devServer ? { + '/account': { + target: 'http://localhost:3000', + changeOrigin: true, + pathRewrite: { '^/account': '' }, + logLevel: 'debug' + }, + '/files': { + target: 'http://localhost:8081', + changeOrigin: true, + logLevel: 'debug' + }, + } : { '/account': { // target: 'https://ftwm71rwag.execute-api.us-west-2.amazonaws.com/stage/', target: 'https://account.hc.engineering/', @@ -168,12 +203,12 @@ module.exports = { pathRewrite: { '^/account': '' }, logLevel: 'debug' }, - '/upload': { + '/files': { // target: 'https://anticrm-upload.herokuapp.com/', - // target: 'http://localhost:3000/', + // target: 'http://localhost:3000/', target: 'https://front.hc.engineering/files', changeOrigin: true, - pathRewrite: { '^/upload': '' }, + pathRewrite: { '^/files': '' }, logLevel: 'debug' }, } diff --git a/dev/tool/package.json b/dev/tool/package.json index 8b4fc01968..51bd1648a3 100644 --- a/dev/tool/package.json +++ b/dev/tool/package.json @@ -9,9 +9,10 @@ "build:watch": "tsc", "lint:fix": "eslint --fix src", "start": "ts-node src/index.ts", - "bundle": "esbuild src/index.ts --bundle --minify --platform=node > bundle.js", + "bundle": "esbuild src/index.ts --bundle --minify --platform=node > bundle.js", "docker:build": "docker build -t anticrm/tool .", - "docker:push": "docker push anticrm/tool" + "docker:push": "docker push anticrm/tool", + "run-local": "MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin MONGO_URL=mongodb://localhost:27017 TRANSACTOR_URL=ws:/localhost:3333 MINIO_ENDPOINT=localhost ts-node ./src/index.ts" }, "devDependencies": { "@anticrm/platform-rig":"~0.6.0", diff --git a/pods/account/src/index.ts b/pods/account/src/index.ts index a5ee211a22..cd3763fe11 100644 --- a/pods/account/src/index.ts +++ b/pods/account/src/index.ts @@ -14,15 +14,13 @@ // limitations under the License. // -import { ACCOUNT_DB, methods } from '@anticrm/account' -import platform, { Response, Request, serialize, Status, Severity } from '@anticrm/platform' -import { MongoClient, Db } from 'mongodb' - -import Koa from 'koa' -import Router from 'koa-router' - -import bodyParser from 'koa-bodyparser' +import accountPlugin, { ACCOUNT_DB, methods } from '@anticrm/account' +import platform, { Request, Response, serialize, setMetadata, Severity, Status } from '@anticrm/platform' import cors from '@koa/cors' +import Koa from 'koa' +import bodyParser from 'koa-bodyparser' +import Router from 'koa-router' +import { Db, MongoClient } from 'mongodb' const dbUri = process.env.MONGO_URL if (dbUri === undefined) { @@ -30,6 +28,14 @@ if (dbUri === undefined) { process.exit(1) } +const transactorUri = process.env.TRANSACTOR_URL +if (dbUri === undefined) { + console.log('Please provide transactor url') + process.exit(1) +} + +setMetadata(accountPlugin.metadata.Endpoint, transactorUri) + let client: MongoClient const app = new Koa() @@ -60,6 +66,13 @@ app.use(cors()) app.use(bodyParser()) app.use(router.routes()).use(router.allowedMethods()) -app.listen(3000, () => { +const server = app.listen(3000, () => { console.log('server started on port 3000') }) + +const close = (): void => { + server.close() +} +process.on('SIGINT', close) +process.on('SIGTERM', close) +process.on('exit', close) diff --git a/server/account/src/index.ts b/server/account/src/index.ts index 35c969da84..dc9c4482d1 100644 --- a/server/account/src/index.ts +++ b/server/account/src/index.ts @@ -14,18 +14,14 @@ // limitations under the License. // -import type { Plugin, StatusCode, Request, Response } from '@anticrm/platform' -import { PlatformError, Severity, Status, plugin, unknownStatus } from '@anticrm/platform' -import { Binary, Db, ObjectId } from 'mongodb' +import { getMetadata, Metadata, Plugin, Request, Response, StatusCode, PlatformError, plugin, Severity, Status, unknownStatus } from '@anticrm/platform' import { pbkdf2Sync, randomBytes } from 'crypto' import { encode } from 'jwt-simple' +import { Binary, Db, ObjectId } from 'mongodb' const WORKSPACE_COLLECTION = 'workspace' const ACCOUNT_COLLECTION = 'account' -const endpoint = 'wss://transactor.hc.engineering/' -const secret = 'secret' - /** * @public */ @@ -40,6 +36,10 @@ export const accountId = 'account' as Plugin * @public */ const accountPlugin = plugin(accountId, { + metadata: { + Endpoint: '' as Metadata, + Secret: '' as Metadata + }, status: { AccountNotFound: '' as StatusCode<{account: string}>, WorkspaceNotFound: '' as StatusCode<{workspace: string}>, @@ -50,6 +50,14 @@ const accountPlugin = plugin(accountId, { } }) +const getSecret = (): string => { + return getMetadata(accountPlugin.metadata.Secret) ?? 'secret' +} + +const getEndpoint = (): string => { + return getMetadata(accountPlugin.metadata.Endpoint) ?? 'wss://transactor.hc.engineering/' +} + /** * @public */ @@ -132,7 +140,7 @@ async function getAccountInfo (db: Db, email: string, password: string): Promise } function generateToken (email: string, workspace: string): string { - return encode({ email, workspace }, secret) + return encode({ email, workspace }, getSecret()) } /** @@ -153,7 +161,7 @@ export async function login (db: Db, email: string, password: string, workspace: for (const w of workspaces) { if (w.equals(workspaceInfo._id)) { const result = { - endpoint, + endpoint: getEndpoint(), email, token: generateToken(email, workspace) } diff --git a/server/elastic/src/__init.ts b/server/elastic/src/__init.ts index 8f541b474e..a5a16a4fae 100644 --- a/server/elastic/src/__init.ts +++ b/server/elastic/src/__init.ts @@ -22,7 +22,7 @@ if (url === undefined) { process.exit(1) } -const client = new Client({ node: 'http://45.32.149.163:9200/' }) +const client = new Client({ node: url }) client.ingest.putPipeline({ id: 'anticrm-pipeline', diff --git a/server/elastic/src/adapter.ts b/server/elastic/src/adapter.ts index 0be44dee9e..897f010960 100644 --- a/server/elastic/src/adapter.ts +++ b/server/elastic/src/adapter.ts @@ -66,38 +66,51 @@ class ElasticAdapter implements FullTextAdapter { async index (doc: IndexedDoc): Promise { console.log('eastic: index', doc) if (doc.data === undefined) { - const resp = await this.client.index({ - index: this.db, - id: doc.id, - type: '_doc', - body: doc - }) - console.log('resp', resp) - console.log('error', (resp.meta as any)?.body?.error) + try { + const resp = await this.client.index({ + index: this.db, + id: doc.id, + type: '_doc', + body: doc + }) + console.log('resp', resp) + console.log('error', (resp.meta as any)?.body?.error) + } catch (err: any) { + console.log('elastic-exception', err) + } } else { console.log('attachment pipeline') - const resp = await this.client.index({ - index: this.db, - id: doc.id, - type: '_doc', - pipeline: 'attachment', - body: doc - }) - console.log('resp', resp) - console.log('error', (resp.meta as any)?.body?.error) + try { + const resp = await this.client.index({ + index: this.db, + id: doc.id, + type: '_doc', + pipeline: 'attachment', + body: doc + }) + console.log('resp', resp) + console.log('error', (resp.meta as any)?.body?.error) + } catch (err: any) { + console.log('elastic-exception', err) + } } return {} } async update (id: Ref, update: Record): Promise { - const resp = await this.client.update({ - index: this.db, - id, - body: { - doc: update - } - }) - console.log('update', resp) + try { + const resp = await this.client.update({ + index: this.db, + id, + body: { + doc: update + } + }) + console.log('update', resp) + } catch (err: any) { + console.log('elastic-exception', err) + } + return {} } } diff --git a/server/front/kube/front.yml b/server/front/kube/front.yml index 0eb6c3eadf..212767d173 100644 --- a/server/front/kube/front.yml +++ b/server/front/kube/front.yml @@ -20,6 +20,10 @@ spec: - containerPort: 8080 imagePullPolicy: Always env: + - name: ACCOUNTS_URL + value: https://account.hc.engineering/ + - name: UPLOAD_URL + value: /files - name: TRANSACTOR_URL value: ws://transactor/ - name: ELASTIC_URL diff --git a/server/front/package.json b/server/front/package.json index 6df36221fd..67d9e5c141 100644 --- a/server/front/package.json +++ b/server/front/package.json @@ -8,7 +8,7 @@ "build": "heft build", "build:watch": "tsc", "lint:fix": "eslint --fix src", - "bundle": "esbuild src/__start.ts --bundle --minify --platform=node > bundle.js & rm -rf ./dist && cp -r ../../dev/prod/dist . && cp -r ../../dev/prod/public/* ./dist/", + "bundle": "esbuild src/__start.ts --bundle --minify --platform=node > bundle.js & rm -rf ./dist && cp -r ../../dev/prod/dist . && cp -r ../../dev/prod/public/* ./dist/ && rm ./dist/config.json", "docker:build": "docker build -t anticrm/front .", "docker:push": "docker push anticrm/front" }, @@ -16,11 +16,13 @@ "@anticrm/platform-rig":"~0.6.0", "@types/heft-jest":"^1.0.2", "@types/node": "^16.4.10", - "@typescript-eslint/eslint-plugin":"4", - "eslint-plugin-import":"2", - "eslint-plugin-promise":"4", - "eslint-plugin-node":"11", - "eslint":"^7.32.0", + "@typescript-eslint/eslint-plugin": "^5.4.0", + "@typescript-eslint/parser": "^5.4.0", + "eslint-config-standard-with-typescript": "^21.0.1", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^5.1.1", + "eslint": "^7.32.0", "@types/express":"^4.17.13", "@types/express-fileupload":"^1.1.7", "@types/uuid":"^8.3.1", diff --git a/server/front/run.sh b/server/front/run.sh new file mode 100755 index 0000000000..3bf8911fef --- /dev/null +++ b/server/front/run.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +export ACCOUNTS_URL=http://localhost:3333 +export TRANSACTOR_URL=ws://localhost:3333 +export ELASTIC_URL=http://elastic:9200 +export MINIO_ENDPOINT=minio +export MINIO_ACCESS_KEY=minioadmin +export MINIO_SECRET_KEY=minioadmin +node ./bundle.js \ No newline at end of file diff --git a/server/front/src/__start.ts b/server/front/src/__start.ts index 7f9dacb089..11f6bf3b4e 100644 --- a/server/front/src/__start.ts +++ b/server/front/src/__start.ts @@ -17,8 +17,8 @@ import { start } from './app' import { Client } from 'minio' -const url = process.env.TRANSACTOR_URL -if (url === undefined) { +const transactorEndpoint = process.env.TRANSACTOR_URL +if (transactorEndpoint === undefined) { console.error('please provide transactor url') process.exit(1) } @@ -55,4 +55,18 @@ const minio = new Client({ secretKey: minioSecretKey }) -start(url, elasticUrl, minio, 8080) +const accountsUrl = process.env.ACCOUNTS_URL +if (accountsUrl === undefined) { + console.error('please provide accounts url') + process.exit(1) +} + +const uploadUrl = process.env.UPLOAD_URL +if (uploadUrl === undefined) { + console.error('please provide upload url') + process.exit(1) +} + +const config = { transactorEndpoint, elasticUrl, minio, accountsUrl, uploadUrl } +console.log('Starting Front service with', config) +start(config, 8080) diff --git a/server/front/src/app.ts b/server/front/src/app.ts index 0a38f1b738..4ce1387955 100644 --- a/server/front/src/app.ts +++ b/server/front/src/app.ts @@ -79,12 +79,24 @@ async function minioUpload (minio: Client, workspace: string, file: UploadedFile * @public * @param port - */ -export function start (transactorEndpoint: string, elasticUrl: string, minio: Client, port: number): void { +export function start (config: { transactorEndpoint: string, elasticUrl: string, minio: Client, accountsUrl: string, uploadUrl: string }, port: number): void { const app = express() app.use(cors()) app.use(fileUpload()) + // eslint-disable-next-line @typescript-eslint/no-misused-promises + app.get('/config.json', async (req, res) => { + res.status(200) + res.set('Cache-Control', 'no-cache') + res.json( + { + ACCOUNTS_URL: config.accountsUrl, + UPLOAD_URL: config.uploadUrl + } + ) + }) + const dist = resolve(__dirname, 'dist') console.log('serving static files from', dist) app.use(express.static(dist, { maxAge: '10m' })) @@ -96,8 +108,8 @@ export function start (transactorEndpoint: string, elasticUrl: string, minio: Cl const payload = decode(token, 'secret', false) as Token const uuid = req.query.file as string - const stat = await minio.statObject(payload.workspace, uuid) - minio.getObject(payload.workspace, uuid, function (err, dataStream) { + const stat = await config.minio.statObject(payload.workspace, uuid) + config.minio.getObject(payload.workspace, uuid, function (err, dataStream) { if (err !== null) { return console.log(err) } @@ -143,7 +155,7 @@ export function start (transactorEndpoint: string, elasticUrl: string, minio: Cl const token = authHeader.split(' ')[1] const payload = decode(token ?? '', 'secret', false) as Token // const fileId = await awsUpload(file as UploadedFile) - const uuid = await minioUpload(minio, payload.workspace, file) + const uuid = await minioUpload(config.minio, payload.workspace, file) console.log('uploaded uuid', uuid) const name = req.query.name as string @@ -162,7 +174,7 @@ export function start (transactorEndpoint: string, elasticUrl: string, minio: Cl // fileId // ) - const elastic = await createElasticAdapter(elasticUrl, payload.workspace) + const elastic = await createElasticAdapter(config.elasticUrl, payload.workspace) const indexedDoc: IndexedDoc = { id: generateId() + '/attachments/' + name, @@ -224,7 +236,7 @@ export function start (transactorEndpoint: string, elasticUrl: string, minio: Cl }).on('end', function () { const buffer = Buffer.concat(data) // eslint-disable-next-line @typescript-eslint/no-misused-promises - minio.putObject(payload.workspace, id, buffer, 0, meta, async (err, objInfo) => { + config.minio.putObject(payload.workspace, id, buffer, 0, meta, async (err, objInfo) => { if (err !== null) { console.log('minio putObject error', err) res.status(500).send(err) @@ -233,7 +245,7 @@ export function start (transactorEndpoint: string, elasticUrl: string, minio: Cl if (attachedTo !== undefined) { const space = req.query.space as Ref - const elastic = await createElasticAdapter(elasticUrl, payload.workspace) + const elastic = await createElasticAdapter(config.elasticUrl, payload.workspace) const indexedDoc: IndexedDoc = { id: generateId() + '/attachments/' + 'Profile.pdf', diff --git a/server/front/src/index.ts b/server/front/src/index.ts index 8d7a5f6b3a..8844274b23 100644 --- a/server/front/src/index.ts +++ b/server/front/src/index.ts @@ -30,5 +30,12 @@ app.get('*', function (request, response) { response.sendFile(join(dist, 'index.html')) }) -app.listen(port) +const server = app.listen(port) console.log(`server started on port ${port}`) + +const close = (): void => { + server.close() +} +process.on('SIGINT', close) +process.on('SIGTERM', close) +process.on('exit', close) diff --git a/server/server/src/__start.ts b/server/server/src/__start.ts index 36c97f243a..58763c7a1a 100644 --- a/server/server/src/__start.ts +++ b/server/server/src/__start.ts @@ -29,4 +29,14 @@ if (elasticUrl === undefined) { } // eslint-disable-next-line @typescript-eslint/no-floating-promises -start(url, elasticUrl, 3333) +const shutdown = start(url, elasticUrl, 3333) + +const close = (): void => { + console.error(new Error().stack) + console.log('Shutdown request accepted') + shutdown() + process.exit(0) +} +process.on('SIGINT', close) +process.on('SIGTERM', close) +process.on('exit', close) diff --git a/server/server/src/server.ts b/server/server/src/server.ts index 407d8e9fd9..a5bbff27af 100644 --- a/server/server/src/server.ts +++ b/server/server/src/server.ts @@ -39,12 +39,12 @@ async function createNullAdapter (hierarchy: Hierarchy, url: string, db: string, /** * @public */ -export async function start (dbUrl: string, fullTextUrl: string, port: number, host?: string): Promise { +export function start (dbUrl: string, fullTextUrl: string, port: number, host?: string): () => void { addLocation(serverChunterId, () => import('@anticrm/server-chunter-resources')) addLocation(serverRecruitId, () => import('@anticrm/server-recruit-resources')) addLocation(serverViewId, () => import('@anticrm/server-view-resources')) - startJsonRpc((workspace: string) => { + return startJsonRpc((workspace: string) => { const conf: DbConfiguration = { domains: { [DOMAIN_TX]: 'MongoTx', diff --git a/server/upload/src/__start.ts b/server/upload/src/__start.ts index a5fd5bc507..ecafe9540f 100644 --- a/server/upload/src/__start.ts +++ b/server/upload/src/__start.ts @@ -55,4 +55,12 @@ const minio = new Client({ secretKey: minioSecretKey }) -start(url, elasticUrl, minio, 3000) +const shutdown = start(url, elasticUrl, minio, 3000) + +const close = (): void => { + shutdown() + process.exit(0) +} +process.on('SIGINT', close) +process.on('SIGTERM', close) +process.on('exit', close) diff --git a/server/upload/src/app.ts b/server/upload/src/app.ts index ee49264b4f..9572328526 100644 --- a/server/upload/src/app.ts +++ b/server/upload/src/app.ts @@ -77,7 +77,7 @@ async function minioUpload (minio: Client, workspace: string, file: UploadedFile * @public * @param port - */ -export function start (transactorEndpoint: string, elasticUrl: string, minio: Client, port: number): void { +export function start (transactorEndpoint: string, elasticUrl: string, minio: Client, port: number): () => void { const app = express() app.use(cors()) @@ -177,5 +177,8 @@ export function start (transactorEndpoint: string, elasticUrl: string, minio: Cl } }) - app.listen(port) + const server = app.listen(port) + return () => { + server.close() + } } diff --git a/server/ws/src/server.ts b/server/ws/src/server.ts index 5d5a7e3fb5..97d1bd0e9d 100644 --- a/server/ws/src/server.ts +++ b/server/ws/src/server.ts @@ -115,7 +115,7 @@ async function handleRequest (service: S, ws: WebSocket, msg: string): Promis * @param port - * @param host - */ -export function start (storageFactory: (workspace: string) => Promise, port: number, host?: string): void { +export function start (storageFactory: (workspace: string) => Promise, port: number, host?: string): () => void { console.log(`starting server on port ${port} ...`) const sessions = new SessionManager() @@ -151,4 +151,7 @@ export function start (storageFactory: (workspace: string) => Promise { + server.close() + } }