mirror of
https://github.com/hcengineering/platform.git
synced 2025-06-09 01:10:17 +00:00
Update 'secret' token usage
1. Put 'secret' into one place, server-token 2. Fix server crash on rare unknown workspaces cases. 3. Upgrade now kick all clients and reload transactions. 4. Fix Front shutdown Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
80943700ed
commit
659063dd01
@ -86,6 +86,7 @@ specifiers:
|
||||
'@rush-temp/server-contact': file:./projects/server-contact.tgz
|
||||
'@rush-temp/server-contact-resources': file:./projects/server-contact-resources.tgz
|
||||
'@rush-temp/server-core': file:./projects/server-core.tgz
|
||||
'@rush-temp/server-token': file:./projects/server-token.tgz
|
||||
'@rush-temp/server-tool': file:./projects/server-tool.tgz
|
||||
'@rush-temp/server-ws': file:./projects/server-ws.tgz
|
||||
'@rush-temp/setting': file:./projects/setting.tgz
|
||||
@ -162,6 +163,7 @@ specifiers:
|
||||
intl-messageformat: ^9.7.1
|
||||
jpeg-js: ~0.4.3
|
||||
just-clone: ^3.2.1
|
||||
jwt-simple: ~0.5.6
|
||||
koa: ^2.13.1
|
||||
koa-bodyparser: ^4.3.0
|
||||
koa-router: ^10.1.1
|
||||
@ -278,6 +280,7 @@ dependencies:
|
||||
'@rush-temp/server-contact': file:projects/server-contact.tgz
|
||||
'@rush-temp/server-contact-resources': file:projects/server-contact-resources.tgz
|
||||
'@rush-temp/server-core': file:projects/server-core.tgz
|
||||
'@rush-temp/server-token': file:projects/server-token.tgz
|
||||
'@rush-temp/server-tool': file:projects/server-tool.tgz
|
||||
'@rush-temp/server-ws': file:projects/server-ws.tgz
|
||||
'@rush-temp/setting': file:projects/setting.tgz
|
||||
@ -354,6 +357,7 @@ dependencies:
|
||||
intl-messageformat: 9.10.0
|
||||
jpeg-js: 0.4.3
|
||||
just-clone: 3.2.1
|
||||
jwt-simple: 0.5.6
|
||||
koa: 2.13.4
|
||||
koa-bodyparser: 4.3.0
|
||||
koa-router: 10.1.1
|
||||
@ -11422,7 +11426,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/account.tgz:
|
||||
resolution: {integrity: sha512-T69SM17sZnYKxu3FyjI47F+wFix2aKjpenQxDThYp5qNkNgZ4/1+znXlJUhUUL+GQpmcLDJ3kcnu/2dkcM1NGg==, tarball: file:projects/account.tgz}
|
||||
resolution: {integrity: sha512-5AF4BBNU65wTk1D+dIOhXhzHGi6QDuGpi/DOEAwF4A1I0f6+J3bq9T5SU/2zXY0i9TLYhD3szFi5zrGcx/KGAg==, tarball: file:projects/account.tgz}
|
||||
name: '@rush-temp/account'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -11811,7 +11815,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/dev-account.tgz:
|
||||
resolution: {integrity: sha512-gKJqJzfPozNrp2KQpEqOwYvlO7xVnxH8/re0ECfdLyDMlHtC3fypTxdprYL/7FheupKZp3YTY4T3MCG2+SG40w==, tarball: file:projects/dev-account.tgz}
|
||||
resolution: {integrity: sha512-hsYsHT6KU6FPYdWhJ6Npwqu2rZmjOoj8FqbV4q3Mie9kG/CAuKAMm2nFw7Vv2E9lpIaeLAJyP4sVI3WkBIGkOg==, tarball: file:projects/dev-account.tgz}
|
||||
name: '@rush-temp/dev-account'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -11979,7 +11983,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/front.tgz:
|
||||
resolution: {integrity: sha512-iSBgFbScuEX0S/r4FJh/z02rKgeaMPKCe0Z7Unr/g2AzgeO65k1G6uaT/mtlr1ulaYixN+dMMmaxzkJT5psDeQ==, tarball: file:projects/front.tgz}
|
||||
resolution: {integrity: sha512-RXsa4jlZB6UdPjSIAHmf07BEcWlH6N26QnAVFQ3QL5VdqLi73ohsPQV9seKz36c5jGsA//Z0BS9QYVCETuHdgA==, tarball: file:projects/front.tgz}
|
||||
name: '@rush-temp/front'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -12012,7 +12016,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/generator.tgz:
|
||||
resolution: {integrity: sha512-Difehi/KDbulPB9s3UOP1fSeLfvWQwpSUr77OnMF31EoCLAXT+9ugFduvlveFdnYCttYKpgGO2bvaxBaH7krdA==, tarball: file:projects/generator.tgz}
|
||||
resolution: {integrity: sha512-FfLadJ6fn6vv/PvtsqrXqC/kRieldqLFfWmeb1Q2wPTnMmWv7r71Hj9tcM8G9BG7HBNolsdqt2yHHHm+SowMTg==, tarball: file:projects/generator.tgz}
|
||||
name: '@rush-temp/generator'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -13292,6 +13296,31 @@ packages:
|
||||
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
jwt-simple: 0.5.6
|
||||
minio: 7.0.26
|
||||
prettier: 2.5.1
|
||||
typescript: 4.5.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/server-token.tgz:
|
||||
resolution: {integrity: sha512-74lvKW1J8vMQI7r+UUFCO8KDItftmBVTc9ecLL9kbWHdFT/kfR1ua57O8XG7MMJDmBVsWylHmM1kr6lxwNpNBA==, tarball: file:projects/server-token.tgz}
|
||||
name: '@rush-temp/server-token'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.41.8
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@types/minio': 7.0.11
|
||||
'@types/node': 16.11.14
|
||||
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
|
||||
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
|
||||
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
jwt-simple: 0.5.6
|
||||
minio: 7.0.26
|
||||
prettier: 2.5.1
|
||||
typescript: 4.5.4
|
||||
@ -13300,7 +13329,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/server-tool.tgz:
|
||||
resolution: {integrity: sha512-jA31a+Q2vADtUml9IaD4IKCxzzqWKz74NIRkBVhY3VqQpiXup+g+Pa2Rf2f3J57yT8aQKCpf63YMysWR42iQjQ==, tarball: file:projects/server-tool.tgz}
|
||||
resolution: {integrity: sha512-Cib8Y9814bARmY3GxSBzMKpmJvQiz149kzwokA3IIbvAqHw+ndpuZYF7gcOG6c6y8975CNltyx4wSlUiojLFfg==, tarball: file:projects/server-tool.tgz}
|
||||
name: '@rush-temp/server-tool'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -13328,7 +13357,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/server-ws.tgz:
|
||||
resolution: {integrity: sha512-MSFFpLjIMFt0oyH4+8JUkNOkCNtdEtMDoxcyN7+kDdz44wSZjSOmheJHYkXO6JTEffcaaRhQ9vO/e7MBNMeoxQ==, tarball: file:projects/server-ws.tgz}
|
||||
resolution: {integrity: sha512-kesBl2gLp031syY1dFuI26w339buhEKja2/9LjlYpV4sX1E8l8+a2RmPOlE6tBRhQ+bEEq/l5YIYNHxBBhGuxg==, tarball: file:projects/server-ws.tgz}
|
||||
name: '@rush-temp/server-ws'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -13763,7 +13792,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/tool.tgz:
|
||||
resolution: {integrity: sha512-yAIi2mb58Lhm+ysa7lZxq+at3RSkc5UlkZhhcPRX9BUq+8dHV0PyJZQb+sH2ys77588F+4CV7jCpxmKERe3HYQ==, tarball: file:projects/tool.tgz}
|
||||
resolution: {integrity: sha512-tQSiv1J1yUul6SD2WNyzzSCJeS1VCZgWkun9xGIJELvQ+iZN6S4fznI35eMLBppJWD0XIYHmeOzNCfjX4PWw7g==, tarball: file:projects/tool.tgz}
|
||||
name: '@rush-temp/tool'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
|
@ -28,6 +28,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"jwt-simple": "^0.5.6"
|
||||
"@anticrm/server-token": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
import type { Request, Response } from '@anticrm/platform'
|
||||
import platform, { Status, Severity } from '@anticrm/platform'
|
||||
|
||||
import { encode } from 'jwt-simple'
|
||||
import { generateToken } from '@anticrm/server-token'
|
||||
|
||||
interface LoginInfo {
|
||||
token: string
|
||||
@ -37,7 +37,7 @@ function login (endpoint: string, email: string, password: string, workspace: st
|
||||
return { error: new Status(Severity.ERROR, platform.status.Unauthorized, {}) }
|
||||
}
|
||||
|
||||
const token = encode({ email, workspace }, 'secret')
|
||||
const token = generateToken(email, workspace)
|
||||
return { result: { token, endpoint } }
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,6 @@
|
||||
"dependencies": {
|
||||
"commander": "^8.1.0",
|
||||
"@anticrm/account": "~0.6.0",
|
||||
"jwt-simple": "^0.5.6",
|
||||
"@anticrm/core": "~0.6.11",
|
||||
"@anticrm/contact": "~0.6.2",
|
||||
"@anticrm/model-all": "~0.6.0",
|
||||
@ -59,6 +58,7 @@
|
||||
"minio": "^7.0.19",
|
||||
"@types/pdfkit": "~0.12.3",
|
||||
"@anticrm/task": "~0.6.0",
|
||||
"jpeg-js": "~0.4.3"
|
||||
"jpeg-js": "~0.4.3",
|
||||
"@anticrm/server-token": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -3,14 +3,14 @@ import client from '@anticrm/client'
|
||||
import clientResources from '@anticrm/client-resources'
|
||||
import { Client } from '@anticrm/core'
|
||||
import { setMetadata } from '@anticrm/platform'
|
||||
import { encode } from 'jwt-simple'
|
||||
import { generateToken } from '@anticrm/server-token'
|
||||
|
||||
// eslint-disable-next-line
|
||||
const WebSocket = require('ws')
|
||||
|
||||
export async function connect (transactorUrl: string, workspace: string): Promise<Client> {
|
||||
console.log('connecting to transactor...')
|
||||
const token = encode({ email: 'anticrm@hc.engineering', workspace }, 'secret')
|
||||
const token = generateToken('anticrm@hc.engineering', workspace)
|
||||
|
||||
// We need to override default factory with 'ws' one.
|
||||
setMetadata(client.metadata.ClientSocketFactory, (url) => new WebSocket(url))
|
||||
|
@ -42,7 +42,6 @@
|
||||
"mongodb": "^4.1.1",
|
||||
"commander": "^8.1.0",
|
||||
"@anticrm/account": "~0.6.0",
|
||||
"jwt-simple": "^0.5.6",
|
||||
"@anticrm/core": "~0.6.11",
|
||||
"@anticrm/contact": "~0.6.2",
|
||||
"minio": "^7.0.19",
|
||||
@ -58,6 +57,7 @@
|
||||
"@elastic/elasticsearch": "^7.14.0",
|
||||
"@anticrm/elastic": "~0.6.0",
|
||||
"@anticrm/server-core": "~0.6.1",
|
||||
"@anticrm/server-token": "~0.6.0",
|
||||
"@anticrm/model-attachment": "~0.6.0",
|
||||
"@anticrm/mongo": "~0.6.0",
|
||||
"@anticrm/dev-storage": "~0.6.0",
|
||||
|
@ -3,14 +3,14 @@ import client from '@anticrm/client'
|
||||
import clientResources from '@anticrm/client-resources'
|
||||
import { Client } from '@anticrm/core'
|
||||
import { setMetadata } from '@anticrm/platform'
|
||||
import { encode } from 'jwt-simple'
|
||||
import { generateToken } from '@anticrm/server-token'
|
||||
|
||||
// eslint-disable-next-line
|
||||
const WebSocket = require('ws')
|
||||
|
||||
export async function connect (transactorUrl: string, workspace: string): Promise<Client> {
|
||||
console.log('connecting to transactor...')
|
||||
const token = encode({ email: 'anticrm@hc.engineering', workspace }, 'secret')
|
||||
const token = generateToken('anticrm@hc.engineering', workspace)
|
||||
|
||||
// We need to override default factory with 'ws' one.
|
||||
setMetadata(client.metadata.ClientSocketFactory, (url) => new WebSocket(url))
|
||||
|
@ -28,7 +28,8 @@ import {
|
||||
upgradeWorkspace
|
||||
} from '@anticrm/account'
|
||||
import { setMetadata } from '@anticrm/platform'
|
||||
import toolPlugin, { generateToken, prepareTools, version } from '@anticrm/server-tool'
|
||||
import { generateToken } from '@anticrm/server-token'
|
||||
import toolPlugin, { prepareTools, version } from '@anticrm/server-tool'
|
||||
import { program } from 'commander'
|
||||
import { Db, MongoClient } from 'mongodb'
|
||||
import { rebuildElastic } from './elastic'
|
||||
|
@ -496,6 +496,11 @@
|
||||
"projectFolder": "server/core",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/server-token",
|
||||
"projectFolder": "server/token",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/server",
|
||||
"projectFolder": "server/server",
|
||||
|
@ -36,9 +36,9 @@
|
||||
"@anticrm/contact": "~0.6.2",
|
||||
"@anticrm/client-resources": "~0.6.4",
|
||||
"@anticrm/client": "~0.6.1",
|
||||
"jwt-simple": "~0.5.6",
|
||||
"ws": "^8.2.0",
|
||||
"@anticrm/model": "~0.6.0",
|
||||
"@anticrm/server-tool": "~0.6.0"
|
||||
"@anticrm/server-tool": "~0.6.0",
|
||||
"@anticrm/server-token": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,8 @@ import platform, {
|
||||
Status,
|
||||
StatusCode
|
||||
} from '@anticrm/platform'
|
||||
import toolPlugin, { connect, initModel, upgradeModel, version, decodeToken, generateToken } from '@anticrm/server-tool'
|
||||
import toolPlugin, { connect, initModel, upgradeModel, version } from '@anticrm/server-tool'
|
||||
import { decodeToken, generateToken } from '@anticrm/server-token'
|
||||
import { pbkdf2Sync, randomBytes } from 'crypto'
|
||||
import { Binary, Db, ObjectId } from 'mongodb'
|
||||
|
||||
@ -393,7 +394,7 @@ export async function assignWorkspace (db: Db, email: string, workspace: string)
|
||||
}
|
||||
|
||||
async function createEmployeeAccount (account: Account, workspace: string): Promise<void> {
|
||||
const connection = await connect(getTransactor(), workspace, account.email)
|
||||
const connection = await connect(getTransactor(), workspace, false, account.email)
|
||||
try {
|
||||
const ops = new TxOperations(connection, core.account.System)
|
||||
|
||||
@ -471,7 +472,7 @@ export async function changeName (db: Db, token: string, first: string, last: st
|
||||
}
|
||||
|
||||
async function updateEmployeeAccount (account: Account, workspace: string): Promise<void> {
|
||||
const connection = await connect(getTransactor(), workspace, account.email)
|
||||
const connection = await connect(getTransactor(), workspace, false, account.email)
|
||||
try {
|
||||
const ops = new TxOperations(connection, core.account.System)
|
||||
|
||||
|
@ -76,13 +76,6 @@ export interface FullTextAdapter {
|
||||
*/
|
||||
export type FullTextAdapterFactory = (url: string, workspace: string) => Promise<FullTextAdapter>
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Token {
|
||||
workspace: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
@ -43,8 +43,8 @@
|
||||
"uuid": "^8.3.2",
|
||||
"cors": "^2.8.5",
|
||||
"@anticrm/elastic": "~0.6.0",
|
||||
"jwt-simple": "^0.5.6",
|
||||
"@anticrm/server-core": "~0.6.1",
|
||||
"@anticrm/server-token": "~0.6.0",
|
||||
"@anticrm/attachment": "~0.6.0",
|
||||
"@anticrm/contrib": "~0.6.0",
|
||||
"minio": "^7.0.19"
|
||||
|
@ -14,8 +14,8 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { start } from './app'
|
||||
import { Client } from 'minio'
|
||||
import { start } from './app'
|
||||
|
||||
const SERVER_PORT = parseInt(process.env.SERVER_PORT ?? '8080')
|
||||
|
||||
@ -77,4 +77,14 @@ if (modelVersion === undefined) {
|
||||
|
||||
const config = { transactorEndpoint, elasticUrl, minio, accountsUrl, uploadUrl, modelVersion }
|
||||
console.log('Starting Front service with', config)
|
||||
start(config, SERVER_PORT)
|
||||
const shutdown = start(config, SERVER_PORT)
|
||||
|
||||
const close = (): void => {
|
||||
console.trace('Exiting from server')
|
||||
console.log('Shutdown request accepted')
|
||||
shutdown()
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
process.on('SIGINT', close)
|
||||
process.on('SIGTERM', close)
|
||||
|
@ -17,36 +17,16 @@
|
||||
import attachment from '@anticrm/attachment'
|
||||
import { Account, Doc, Ref, Space } from '@anticrm/core'
|
||||
import { createElasticAdapter } from '@anticrm/elastic'
|
||||
// import { TxFactory } from '@anticrm/core'
|
||||
import type { IndexedDoc, Token } from '@anticrm/server-core'
|
||||
import type { IndexedDoc } from '@anticrm/server-core'
|
||||
import { decodeToken } from '@anticrm/server-token'
|
||||
import cors from 'cors'
|
||||
import express from 'express'
|
||||
import fileUpload, { UploadedFile } from 'express-fileupload'
|
||||
import https from 'https'
|
||||
import { decode } from 'jwt-simple'
|
||||
// import { createContributingClient } from '@anticrm/contrib'
|
||||
import { Client, ItemBucketMetadata } from 'minio'
|
||||
import { join, resolve } from 'path'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
// import { createElasticAdapter } from '@anticrm/elastic'
|
||||
|
||||
// const BUCKET = 'anticrm-upload-9e4e89c'
|
||||
|
||||
// async function awsUpload (file: UploadedFile): Promise<string> {
|
||||
// const id = uuid()
|
||||
// const s3 = new S3()
|
||||
// const resp = await s3.upload({
|
||||
// Bucket: BUCKET,
|
||||
// Key: id,
|
||||
// Body: file.data,
|
||||
// ContentType: file.mimetype,
|
||||
// ACL: 'public-read'
|
||||
// }).promise()
|
||||
// console.log(resp)
|
||||
// return id
|
||||
// }
|
||||
|
||||
async function minioUpload (minio: Client, workspace: string, file: UploadedFile): Promise<string> {
|
||||
const id = uuid()
|
||||
const meta: ItemBucketMetadata = {
|
||||
@ -59,25 +39,11 @@ async function minioUpload (minio: Client, workspace: string, file: UploadedFile
|
||||
return id
|
||||
}
|
||||
|
||||
// async function createAttachment (endpoint: string, token: string, account: Ref<Account>, space: Ref<Space>, attachedTo: Ref<Doc>, collection: string, name: string, file: string): Promise<void> {
|
||||
// const txFactory = new TxFactory(account)
|
||||
// const tx = txFactory.createTxCreateDoc(chunter.class.Attachment, space, {
|
||||
// attachedTo,
|
||||
// collection,
|
||||
// name,
|
||||
// file
|
||||
// })
|
||||
// const url = new URL(`/${token}`, endpoint)
|
||||
// const client = await createContributingClient(url.href)
|
||||
// await client.tx(tx)
|
||||
// client.close()
|
||||
// }
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param port -
|
||||
*/
|
||||
export function start (config: { transactorEndpoint: string, elasticUrl: string, minio: Client, accountsUrl: string, uploadUrl: string, modelVersion: string }, port: number): void {
|
||||
export function start (config: { transactorEndpoint: string, elasticUrl: string, minio: Client, accountsUrl: string, uploadUrl: string, modelVersion: string }, port: number): () => void {
|
||||
const app = express()
|
||||
|
||||
app.use(cors())
|
||||
@ -104,7 +70,7 @@ export function start (config: { transactorEndpoint: string, elasticUrl: string,
|
||||
app.get('/files', async (req, res) => {
|
||||
try {
|
||||
const token = req.query.token as string
|
||||
const payload = decode(token, 'secret', false) as Token
|
||||
const payload = decodeToken(token)
|
||||
const uuid = req.query.file as string
|
||||
|
||||
const stat = await config.minio.statObject(payload.workspace, uuid)
|
||||
@ -153,7 +119,7 @@ export function start (config: { transactorEndpoint: string, elasticUrl: string,
|
||||
|
||||
try {
|
||||
const token = authHeader.split(' ')[1]
|
||||
const payload = decode(token ?? '', 'secret', false) as Token
|
||||
const payload = decodeToken(token)
|
||||
// const fileId = await awsUpload(file as UploadedFile)
|
||||
const uuid = await minioUpload(config.minio, payload.workspace, file)
|
||||
console.log('uploaded uuid', uuid)
|
||||
@ -207,7 +173,7 @@ export function start (config: { transactorEndpoint: string, elasticUrl: string,
|
||||
}
|
||||
|
||||
const token = authHeader.split(' ')[1]
|
||||
const payload = decode(token ?? '', 'secret', false) as Token
|
||||
const payload = decodeToken(token)
|
||||
const uuid = req.query.file as string
|
||||
|
||||
await config.minio.removeObject(payload.workspace, uuid)
|
||||
@ -227,7 +193,7 @@ export function start (config: { transactorEndpoint: string, elasticUrl: string,
|
||||
return
|
||||
}
|
||||
const token = authHeader.split(' ')[1]
|
||||
const payload = decode(token ?? '', 'secret', false) as Token
|
||||
const payload = decodeToken(token)
|
||||
const url = req.query.url as string
|
||||
const cookie = req.query.cookie as string | undefined
|
||||
const attachedTo = req.query.attachedTo as Ref<Doc> | undefined
|
||||
@ -305,7 +271,7 @@ export function start (config: { transactorEndpoint: string, elasticUrl: string,
|
||||
return
|
||||
}
|
||||
const token = authHeader.split(' ')[1]
|
||||
const payload = decode(token ?? '', 'secret', false) as Token
|
||||
const payload = decodeToken(token)
|
||||
const { url, cookie, attachedTo, space } = req.body
|
||||
|
||||
console.log('importing from', url)
|
||||
@ -377,5 +343,8 @@ export function start (config: { transactorEndpoint: string, elasticUrl: string,
|
||||
response.sendFile(join(dist, 'index.html'))
|
||||
})
|
||||
|
||||
app.listen(port)
|
||||
const server = app.listen(port)
|
||||
return () => {
|
||||
server.close()
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,6 @@
|
||||
"elastic-apm-node": "~3.26.0",
|
||||
"minio": "~7.0.26",
|
||||
"@anticrm/server-contact": "~0.6.1",
|
||||
"@anticrm/server-contact-resources": "~0.6.0"
|
||||
"@anticrm/server-contact-resources": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
7
server/token/.eslintrc.js
Normal file
7
server/token/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/platform-rig/profiles/default/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
4
server/token/.npmignore
Normal file
4
server/token/.npmignore
Normal file
@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
18
server/token/config/rig.json
Normal file
18
server/token/config/rig.json
Normal file
@ -0,0 +1,18 @@
|
||||
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
|
||||
/**
|
||||
* (Required) The name of the rig package to inherit from.
|
||||
* It should be an NPM package name with the "-rig" suffix.
|
||||
*/
|
||||
"rigPackageName": "@anticrm/platform-rig"
|
||||
|
||||
/**
|
||||
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||
* If omitted, then the "default" profile will be used."
|
||||
*/
|
||||
// "rigProfile": "your-profile-name"
|
||||
}
|
36
server/token/package.json
Normal file
36
server/token/package.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "@anticrm/server-token",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "heft build",
|
||||
"build:watch": "tsc",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"lint": "eslint src",
|
||||
"format": "prettier --write src && eslint --fix src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/platform-rig": "~0.6.0",
|
||||
"@types/heft-jest": "^1.0.2",
|
||||
"@types/node": "^16.4.10",
|
||||
"@types/minio": "~7.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"prettier": "^2.4.1",
|
||||
"@rushstack/heft": "^0.41.1",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/core": "~0.6.11",
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"minio": "~7.0.26",
|
||||
"jwt-simple": "~0.5.6"
|
||||
}
|
||||
}
|
18
server/token/src/index.ts
Normal file
18
server/token/src/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
export { default } from './plugin'
|
||||
export * from './token'
|
33
server/token/src/plugin.ts
Normal file
33
server/token/src/plugin.ts
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Metadata, Plugin } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const serverTokenId = 'server-token' as Plugin
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
const serverToken = plugin(serverTokenId, {
|
||||
metadata: {
|
||||
Secret: '' as Metadata<string>
|
||||
}
|
||||
})
|
||||
|
||||
export default serverToken
|
32
server/token/src/token.ts
Normal file
32
server/token/src/token.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { getMetadata } from '@anticrm/platform'
|
||||
import serverPlugin from './plugin'
|
||||
import { encode, decode } from 'jwt-simple'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Token {
|
||||
email: string
|
||||
workspace: string
|
||||
extra?: Record<string, string>
|
||||
}
|
||||
|
||||
const getSecret = (): string => {
|
||||
return getMetadata(serverPlugin.metadata.Secret) ?? 'secret'
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function generateToken (email: string, workspace: string, extra?: Record<string, string>): string {
|
||||
return encode({ ...(extra ?? {}), email, workspace }, getSecret())
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function decodeToken (token: string): Token {
|
||||
const value = decode(token, getSecret(), false)
|
||||
const { email, workspace, ...extra } = value
|
||||
return { email, workspace, extra }
|
||||
}
|
10
server/token/tsconfig.json
Normal file
10
server/token/tsconfig.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./node_modules/@anticrm/platform-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"target": "ES2019",
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
@ -36,8 +36,8 @@
|
||||
"@anticrm/contact": "~0.6.2",
|
||||
"@anticrm/client-resources": "~0.6.4",
|
||||
"@anticrm/client": "~0.6.1",
|
||||
"jwt-simple": "~0.5.6",
|
||||
"ws": "^8.2.0",
|
||||
"@anticrm/model": "~0.6.0"
|
||||
"@anticrm/model": "~0.6.0",
|
||||
"@anticrm/server-token": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -17,19 +17,14 @@
|
||||
import client from '@anticrm/client'
|
||||
import clientResources from '@anticrm/client-resources'
|
||||
import { Client } from '@anticrm/core'
|
||||
import { getMetadata, setMetadata } from '@anticrm/platform'
|
||||
import { encode } from 'jwt-simple'
|
||||
|
||||
import toolPlugin from './plugin'
|
||||
import { setMetadata } from '@anticrm/platform'
|
||||
import { generateToken } from '@anticrm/server-token'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function connect (transactorUrl: string, workspace: string, email?: string): Promise<Client> {
|
||||
const token = encode(
|
||||
{ email: email ?? 'anticrm@hc.engineering', workspace },
|
||||
getMetadata(toolPlugin.metadata.Secret) ?? 'secret'
|
||||
)
|
||||
export async function connect (transactorUrl: string, workspace: string, reloadModel: boolean, email?: string): Promise<Client> {
|
||||
const token = generateToken(email ?? 'anticrm@hc.engineering', workspace, reloadModel ? { model: 'reload' } : undefined)
|
||||
|
||||
// We need to override default factory with 'ws' one.
|
||||
// eslint-disable-next-line
|
||||
|
@ -16,8 +16,6 @@
|
||||
import contact from '@anticrm/contact'
|
||||
import core, { DOMAIN_TX, Tx } from '@anticrm/core'
|
||||
import builder, { createDeps, migrateOperations } from '@anticrm/model-all'
|
||||
import { getMetadata } from '@anticrm/platform'
|
||||
import { decode, encode } from 'jwt-simple'
|
||||
import { Client } from 'minio'
|
||||
import { Document, MongoClient } from 'mongodb'
|
||||
import { connect } from './connect'
|
||||
@ -99,7 +97,7 @@ export async function initModel (transactorUrl: string, dbName: string): Promise
|
||||
console.log(`${result.insertedCount} model transactions inserted.`)
|
||||
|
||||
console.log('creating data...')
|
||||
const connection = await connect(transactorUrl, dbName)
|
||||
const connection = await connect(transactorUrl, dbName, true)
|
||||
try {
|
||||
await createDeps(connection)
|
||||
} catch (e) {
|
||||
@ -155,7 +153,7 @@ export async function upgradeModel (
|
||||
|
||||
console.log('Apply upgrade operations')
|
||||
|
||||
const connection = await connect(transactorUrl, dbName)
|
||||
const connection = await connect(transactorUrl, dbName, true)
|
||||
for (const op of migrateOperations) {
|
||||
await op.upgrade(connection)
|
||||
}
|
||||
@ -165,21 +163,3 @@ export async function upgradeModel (
|
||||
await client.close()
|
||||
}
|
||||
}
|
||||
|
||||
const getSecret = (): string => {
|
||||
return getMetadata(toolPlugin.metadata.Secret) ?? 'secret'
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function generateToken (email: string, workspace: string): string {
|
||||
return encode({ email, workspace }, getSecret())
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function decodeToken (token: string): { email: string, workspace: string} {
|
||||
return decode(token, getSecret())
|
||||
}
|
||||
|
@ -29,10 +29,10 @@
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"jwt-simple": "^0.5.6",
|
||||
"ws": "^8.0.0",
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"@anticrm/core": "~0.6.11",
|
||||
"@anticrm/server-core": "~0.6.1"
|
||||
"@anticrm/server-core": "~0.6.1",
|
||||
"@anticrm/server-token": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
import { readResponse, serialize } from '@anticrm/platform'
|
||||
import type { Token } from '@anticrm/server-core'
|
||||
import { encode } from 'jwt-simple'
|
||||
import WebSocket from 'ws'
|
||||
|
||||
describe('server', () => {
|
||||
|
@ -16,8 +16,7 @@
|
||||
|
||||
import { readResponse, serialize } from '@anticrm/platform'
|
||||
import { start, disableLogging } from '../server'
|
||||
import type { Token } from '@anticrm/server-core'
|
||||
import { encode } from 'jwt-simple'
|
||||
import { generateToken } from '@anticrm/server-token'
|
||||
import WebSocket from 'ws'
|
||||
|
||||
import type { Doc, Ref, Class, DocumentQuery, FindOptions, FindResult, Tx, TxResult, MeasureContext } from '@anticrm/core'
|
||||
@ -38,11 +37,8 @@ describe('server', () => {
|
||||
}), 3333)
|
||||
|
||||
function connect (): WebSocket {
|
||||
const payload: Token = {
|
||||
workspace: 'latest'
|
||||
}
|
||||
const token = encode(payload, 'secret')
|
||||
return new WebSocket('ws://localhost:3333/' + token)
|
||||
const token: string = generateToken('', 'latest')
|
||||
return new WebSocket(`ws://localhost:3333/${token}`)
|
||||
}
|
||||
|
||||
it('should connect to server', (done) => {
|
||||
|
@ -16,12 +16,11 @@
|
||||
|
||||
import { Class, Doc, DocumentQuery, FindOptions, FindResult, MeasureContext, Ref, ServerStorage, Tx, TxResult } from '@anticrm/core'
|
||||
import { readRequest, Response, serialize, unknownError } from '@anticrm/platform'
|
||||
import type { Token } from '@anticrm/server-core'
|
||||
import { decodeToken, Token } from '@anticrm/server-token'
|
||||
import { createServer, IncomingMessage } from 'http'
|
||||
import { decode } from 'jwt-simple'
|
||||
import WebSocket, { Server } from 'ws'
|
||||
|
||||
let LOGGING_ENABLED = false
|
||||
let LOGGING_ENABLED = true
|
||||
|
||||
export function disableLogging (): void { LOGGING_ENABLED = false }
|
||||
|
||||
@ -60,38 +59,57 @@ class SessionManager {
|
||||
async addSession (ws: WebSocket, token: Token, storageFactory: (ws: string) => Promise<ServerStorage>): Promise<Session> {
|
||||
const workspace = this.workspaces.get(token.workspace)
|
||||
if (workspace === undefined) {
|
||||
const storage = await storageFactory(token.workspace)
|
||||
const session = new Session(this, token, storage)
|
||||
const workspace: Workspace = {
|
||||
storage,
|
||||
sessions: [[session, ws]]
|
||||
}
|
||||
this.workspaces.set(token.workspace, workspace)
|
||||
return session
|
||||
return await this.createWorkspace(storageFactory, token, ws)
|
||||
} else {
|
||||
if (token.extra?.model === 'reload') {
|
||||
console.log('reloading workspace', JSON.stringify(token))
|
||||
// If upgrade client is used.
|
||||
// Drop all existing clients
|
||||
if (workspace.sessions.length > 0) {
|
||||
for (const s of workspace.sessions) {
|
||||
this.close(s[1], token.workspace, 0, 'upgrade')
|
||||
}
|
||||
}
|
||||
return await this.createWorkspace(storageFactory, token, ws)
|
||||
}
|
||||
|
||||
const session = new Session(this, token, workspace.storage)
|
||||
workspace.sessions.push([session, ws])
|
||||
return session
|
||||
}
|
||||
}
|
||||
|
||||
close (ws: WebSocket, token: Token, code: number, reason: string): void {
|
||||
private async createWorkspace (storageFactory: (ws: string) => Promise<ServerStorage>, token: Token, ws: WebSocket): Promise<Session> {
|
||||
const storage = await storageFactory(token.workspace)
|
||||
const session = new Session(this, token, storage)
|
||||
const workspace: Workspace = {
|
||||
storage,
|
||||
sessions: [[session, ws]]
|
||||
}
|
||||
this.workspaces.set(token.workspace, workspace)
|
||||
return session
|
||||
}
|
||||
|
||||
close (ws: WebSocket, workspaceId: string, code: number, reason: string): void {
|
||||
if (LOGGING_ENABLED) console.log(`closing websocket, code: ${code}, reason: ${reason}`)
|
||||
const workspace = this.workspaces.get(token.workspace)
|
||||
const workspace = this.workspaces.get(workspaceId)
|
||||
if (workspace === undefined) {
|
||||
throw new Error('internal: cannot find sessions')
|
||||
console.error(new Error('internal: cannot find sessions'))
|
||||
return
|
||||
}
|
||||
workspace.sessions = workspace.sessions.filter(session => session[1] !== ws)
|
||||
if (workspace.sessions.length === 0) {
|
||||
if (LOGGING_ENABLED) console.log('no sessions for workspace', token.workspace)
|
||||
this.workspaces.delete(token.workspace)
|
||||
if (LOGGING_ENABLED) console.log('no sessions for workspace', workspaceId)
|
||||
this.workspaces.delete(workspaceId)
|
||||
workspace.storage.close().catch(err => console.error(err))
|
||||
}
|
||||
}
|
||||
|
||||
broadcast (from: Session | null, token: Token, resp: Response<any>): void {
|
||||
const workspace = this.workspaces.get(token.workspace)
|
||||
if (workspace === undefined) {
|
||||
throw new Error('internal: cannot find sessions')
|
||||
console.error(new Error('internal: cannot find sessions'))
|
||||
return
|
||||
}
|
||||
if (LOGGING_ENABLED) console.log(`server broadcasting to ${workspace.sessions.length} clients...`)
|
||||
const msg = serialize(resp)
|
||||
@ -138,7 +156,7 @@ export function start (ctx: MeasureContext, storageFactory: (workspace: string)
|
||||
const session = await sessions.addSession(ws, token, storageFactory)
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
ws.on('message', async (msg: string) => await handleRequest(ctx, session, ws, msg))
|
||||
ws.on('close', (code: number, reason: string) => sessions.close(ws, token, code, reason))
|
||||
ws.on('close', (code: number, reason: string) => sessions.close(ws, token.workspace, code, reason))
|
||||
|
||||
for (const msg of buffer) {
|
||||
await handleRequest(ctx, session, ws, msg)
|
||||
@ -149,7 +167,7 @@ export function start (ctx: MeasureContext, storageFactory: (workspace: string)
|
||||
server.on('upgrade', (request: IncomingMessage, socket: any, head: Buffer) => {
|
||||
const token = request.url?.substring(1) // remove leading '/'
|
||||
try {
|
||||
const payload = decode(token ?? '', 'secret', false)
|
||||
const payload = decodeToken(token ?? '')
|
||||
console.log('client connected with payload', payload)
|
||||
wss.handleUpgrade(request, socket, head, ws => wss.emit('connection', ws, request, payload))
|
||||
} catch (err) {
|
||||
|
Loading…
Reference in New Issue
Block a user