mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 19:11:33 +03:00
Workspace Versioning
Closes #857 Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
ed1550839d
commit
7d1df3c36d
@ -244,6 +244,14 @@
|
|||||||
"safeForSimultaneousRushProcesses": true,
|
"safeForSimultaneousRushProcesses": true,
|
||||||
"shellCommand": "find .|grep tsconfig.tsbuildinfo | xargs rm | pwd"
|
"shellCommand": "find .|grep tsconfig.tsbuildinfo | xargs rm | pwd"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"commandKind": "global",
|
||||||
|
"name": "model-version",
|
||||||
|
"summary": "show model version",
|
||||||
|
"description": "show model version",
|
||||||
|
"safeForSimultaneousRushProcesses": true,
|
||||||
|
"shellCommand": "cd ./models/all/ && npx ts-node ./src/__showversion.ts"
|
||||||
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,6 +85,7 @@ specifiers:
|
|||||||
'@rush-temp/server-contact': file:./projects/server-contact.tgz
|
'@rush-temp/server-contact': file:./projects/server-contact.tgz
|
||||||
'@rush-temp/server-contact-resources': file:./projects/server-contact-resources.tgz
|
'@rush-temp/server-contact-resources': file:./projects/server-contact-resources.tgz
|
||||||
'@rush-temp/server-core': file:./projects/server-core.tgz
|
'@rush-temp/server-core': file:./projects/server-core.tgz
|
||||||
|
'@rush-temp/server-tool': file:./projects/server-tool.tgz
|
||||||
'@rush-temp/server-ws': file:./projects/server-ws.tgz
|
'@rush-temp/server-ws': file:./projects/server-ws.tgz
|
||||||
'@rush-temp/setting': file:./projects/setting.tgz
|
'@rush-temp/setting': file:./projects/setting.tgz
|
||||||
'@rush-temp/setting-assets': file:./projects/setting-assets.tgz
|
'@rush-temp/setting-assets': file:./projects/setting-assets.tgz
|
||||||
@ -275,6 +276,7 @@ dependencies:
|
|||||||
'@rush-temp/server-contact': file:projects/server-contact.tgz
|
'@rush-temp/server-contact': file:projects/server-contact.tgz
|
||||||
'@rush-temp/server-contact-resources': file:projects/server-contact-resources.tgz
|
'@rush-temp/server-contact-resources': file:projects/server-contact-resources.tgz
|
||||||
'@rush-temp/server-core': file:projects/server-core.tgz
|
'@rush-temp/server-core': file:projects/server-core.tgz
|
||||||
|
'@rush-temp/server-tool': file:projects/server-tool.tgz
|
||||||
'@rush-temp/server-ws': file:projects/server-ws.tgz
|
'@rush-temp/server-ws': file:projects/server-ws.tgz
|
||||||
'@rush-temp/setting': file:projects/setting.tgz
|
'@rush-temp/setting': file:projects/setting.tgz
|
||||||
'@rush-temp/setting-assets': file:projects/setting-assets.tgz
|
'@rush-temp/setting-assets': file:projects/setting-assets.tgz
|
||||||
@ -10679,7 +10681,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/account.tgz:
|
file:projects/account.tgz:
|
||||||
resolution: {integrity: sha512-/BBGpjzh87DsWYJmHFtYyTBH2FerxHMoGT3xQ4A5IHVtKmx2gTP5mdaY09h+vaNp+a4iTs5rqa7h+Y7l/5uX2w==, tarball: file:projects/account.tgz}
|
resolution: {integrity: sha512-VNN6Dc2PvOzjbchYoCP7QnVKnUXcy3ByayOx6l8aSdjxxTKBw8XQKrTFgMfBeozvFKzKqf9v7Zz2k9sxzrHw7g==, tarball: file:projects/account.tgz}
|
||||||
name: '@rush-temp/account'
|
name: '@rush-temp/account'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -11646,7 +11648,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
file:projects/model-all.tgz_typescript@4.5.4:
|
file:projects/model-all.tgz_typescript@4.5.4:
|
||||||
resolution: {integrity: sha512-5WhGhRWED9z51QVhlJWZJ321v4zkTY33gwVOdMVHidlhnmKJ2LPWfIN8Gs1K2I0Z2Gea3GfcgDbSrTYSGQd7bw==, tarball: file:projects/model-all.tgz}
|
resolution: {integrity: sha512-KRaYm65Scg01RukljQmwVKLg143zgzLtjWBJQizBtdAU0X8XoHFnW6cOK1Pa3O15Et812fs3HhWWTUj32GJnoQ==, tarball: file:projects/model-all.tgz}
|
||||||
id: file:projects/model-all.tgz
|
id: file:projects/model-all.tgz
|
||||||
name: '@rush-temp/model-all'
|
name: '@rush-temp/model-all'
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
@ -12543,6 +12545,34 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
file:projects/server-tool.tgz:
|
||||||
|
resolution: {integrity: sha512-jA31a+Q2vADtUml9IaD4IKCxzzqWKz74NIRkBVhY3VqQpiXup+g+Pa2Rf2f3J57yT8aQKCpf63YMysWR42iQjQ==, tarball: file:projects/server-tool.tgz}
|
||||||
|
name: '@rush-temp/server-tool'
|
||||||
|
version: 0.0.0
|
||||||
|
dependencies:
|
||||||
|
'@rushstack/heft': 0.41.8
|
||||||
|
'@types/heft-jest': 1.0.2
|
||||||
|
'@types/minio': 7.0.11
|
||||||
|
'@types/ws': 8.2.2
|
||||||
|
'@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
|
||||||
|
mongodb: 4.2.2
|
||||||
|
prettier: 2.5.1
|
||||||
|
typescript: 4.5.4
|
||||||
|
ws: 8.3.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- bufferutil
|
||||||
|
- supports-color
|
||||||
|
- utf-8-validate
|
||||||
|
dev: false
|
||||||
|
|
||||||
file:projects/server-ws.tgz:
|
file:projects/server-ws.tgz:
|
||||||
resolution: {integrity: sha512-MSFFpLjIMFt0oyH4+8JUkNOkCNtdEtMDoxcyN7+kDdz44wSZjSOmheJHYkXO6JTEffcaaRhQ9vO/e7MBNMeoxQ==, tarball: file:projects/server-ws.tgz}
|
resolution: {integrity: sha512-MSFFpLjIMFt0oyH4+8JUkNOkCNtdEtMDoxcyN7+kDdz44wSZjSOmheJHYkXO6JTEffcaaRhQ9vO/e7MBNMeoxQ==, tarball: file:projects/server-ws.tgz}
|
||||||
name: '@rush-temp/server-ws'
|
name: '@rush-temp/server-ws'
|
||||||
|
@ -96,6 +96,7 @@
|
|||||||
"@anticrm/server-contact-resources": "~0.6.0",
|
"@anticrm/server-contact-resources": "~0.6.0",
|
||||||
"@anticrm/templates": "~0.6.0",
|
"@anticrm/templates": "~0.6.0",
|
||||||
"@anticrm/templates-assets": "~0.6.0",
|
"@anticrm/templates-assets": "~0.6.0",
|
||||||
"@anticrm/templates-resources": "~0.6.0"
|
"@anticrm/templates-resources": "~0.6.0",
|
||||||
|
"@anticrm/core": "~0.6.16"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"ACCOUNTS_URL":"/account",
|
"ACCOUNTS_URL":"/account",
|
||||||
"UPLOAD_URL":"/files"
|
"UPLOAD_URL":"/files",
|
||||||
|
"MODEL_VERSION": null
|
||||||
}
|
}
|
@ -14,9 +14,10 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import { addLocation } from '@anticrm/platform'
|
import { addLocation } from '@anticrm/platform'
|
||||||
|
import {Data, Version} from '@anticrm/core'
|
||||||
|
|
||||||
import login, { loginId } from '@anticrm/login'
|
import login, { loginId } from '@anticrm/login'
|
||||||
import { workbenchId } from '@anticrm/workbench'
|
import workbench, { workbenchId } from '@anticrm/workbench'
|
||||||
import { viewId } from '@anticrm/view'
|
import { viewId } from '@anticrm/view'
|
||||||
import { taskId } from '@anticrm/task'
|
import { taskId } from '@anticrm/task'
|
||||||
import { contactId } from '@anticrm/contact'
|
import { contactId } from '@anticrm/contact'
|
||||||
@ -51,13 +52,15 @@ import '@anticrm/templates-assets'
|
|||||||
|
|
||||||
import { setMetadata } from '@anticrm/platform'
|
import { setMetadata } from '@anticrm/platform'
|
||||||
export async function configurePlatform() {
|
export async function configurePlatform() {
|
||||||
await fetch('/config.json').then(async (config) => {
|
const config = await (await fetch('/config.json')).json()
|
||||||
await config.json().then(value => {
|
console.log('loading configuration', config)
|
||||||
console.log('loading configuration', value)
|
setMetadata(login.metadata.AccountsUrl, config.ACCOUNTS_URL)
|
||||||
setMetadata(login.metadata.AccountsUrl, value.ACCOUNTS_URL)
|
setMetadata(login.metadata.UploadUrl, config.UPLOAD_URL)
|
||||||
setMetadata(login.metadata.UploadUrl, value.UPLOAD_URL)
|
|
||||||
})
|
if( config.MODEL_VERSION != null) {
|
||||||
})
|
console.log('Minimal Model version requirement', config.MODEL_VERSION)
|
||||||
|
setMetadata(workbench.metadata.RequiredVersion, config.MODEL_VERSION)
|
||||||
|
}
|
||||||
setMetadata(login.metadata.TelegramUrl, process.env.TELEGRAM_URL ?? 'http://localhost:8086')
|
setMetadata(login.metadata.TelegramUrl, process.env.TELEGRAM_URL ?? 'http://localhost:8086')
|
||||||
setMetadata(login.metadata.GmailUrl, process.env.GMAIL_URL ?? 'http://localhost:8087')
|
setMetadata(login.metadata.GmailUrl, process.env.GMAIL_URL ?? 'http://localhost:8087')
|
||||||
setMetadata(login.metadata.OverrideEndpoint, process.env.LOGIN_ENDPOINT)
|
setMetadata(login.metadata.OverrideEndpoint, process.env.LOGIN_ENDPOINT)
|
||||||
|
@ -71,6 +71,7 @@
|
|||||||
"mime-types": "~2.1.34",
|
"mime-types": "~2.1.34",
|
||||||
"@anticrm/attachment": "~0.6.1",
|
"@anticrm/attachment": "~0.6.1",
|
||||||
"@anticrm/server-contact": "~0.6.1",
|
"@anticrm/server-contact": "~0.6.1",
|
||||||
"@anticrm/server-contact-resources": "~0.6.0"
|
"@anticrm/server-contact-resources": "~0.6.0",
|
||||||
|
"@anticrm/server-tool": "~0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import accountPlugin, {
|
import {
|
||||||
ACCOUNT_DB,
|
ACCOUNT_DB,
|
||||||
assignWorkspace,
|
assignWorkspace,
|
||||||
createAccount,
|
createAccount,
|
||||||
@ -22,23 +22,20 @@ import accountPlugin, {
|
|||||||
dropAccount,
|
dropAccount,
|
||||||
dropWorkspace,
|
dropWorkspace,
|
||||||
getAccount,
|
getAccount,
|
||||||
|
listAccounts,
|
||||||
listWorkspaces,
|
listWorkspaces,
|
||||||
listAccounts
|
upgradeWorkspace
|
||||||
} from '@anticrm/account'
|
} from '@anticrm/account'
|
||||||
import { setMetadata } from '@anticrm/platform'
|
import { setMetadata } from '@anticrm/platform'
|
||||||
|
import toolPlugin, { prepareTools, version } from '@anticrm/server-tool'
|
||||||
import { program } from 'commander'
|
import { program } from 'commander'
|
||||||
import { Client } from 'minio'
|
|
||||||
import { Db, MongoClient } from 'mongodb'
|
import { Db, MongoClient } from 'mongodb'
|
||||||
import { rebuildElastic } from './elastic'
|
import { rebuildElastic } from './elastic'
|
||||||
import { importXml } from './importer'
|
import { importXml } from './importer'
|
||||||
import { clearTelegramHistory } from './telegram'
|
import { clearTelegramHistory } from './telegram'
|
||||||
import { diffWorkspace, dumpWorkspace, restoreWorkspace, upgradeWorkspace } from './workspace'
|
import { diffWorkspace, dumpWorkspace, restoreWorkspace } from './workspace'
|
||||||
|
|
||||||
const mongodbUri = process.env.MONGO_URL
|
const { mongodbUri, minio } = prepareTools()
|
||||||
if (mongodbUri === undefined) {
|
|
||||||
console.error('please provide mongodb url.')
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const transactorUrl = process.env.TRANSACTOR_URL
|
const transactorUrl = process.env.TRANSACTOR_URL
|
||||||
if (transactorUrl === undefined) {
|
if (transactorUrl === undefined) {
|
||||||
@ -46,40 +43,14 @@ if (transactorUrl === undefined) {
|
|||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const minioEndpoint = process.env.MINIO_ENDPOINT
|
|
||||||
if (minioEndpoint === undefined) {
|
|
||||||
console.error('please provide minio endpoint')
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const minioAccessKey = process.env.MINIO_ACCESS_KEY
|
|
||||||
if (minioAccessKey === undefined) {
|
|
||||||
console.error('please provide minio access key')
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const minioSecretKey = process.env.MINIO_SECRET_KEY
|
|
||||||
if (minioSecretKey === undefined) {
|
|
||||||
console.error('please provide minio secret key')
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const elasticUrl = process.env.ELASTIC_URL
|
const elasticUrl = process.env.ELASTIC_URL
|
||||||
if (elasticUrl === undefined) {
|
if (elasticUrl === undefined) {
|
||||||
console.error('please provide elastic url')
|
console.error('please provide elastic url')
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
setMetadata(accountPlugin.metadata.Endpoint, transactorUrl)
|
setMetadata(toolPlugin.metadata.Endpoint, transactorUrl)
|
||||||
setMetadata(accountPlugin.metadata.Transactor, transactorUrl)
|
setMetadata(toolPlugin.metadata.Transactor, transactorUrl)
|
||||||
|
|
||||||
const minio = new Client({
|
|
||||||
endPoint: minioEndpoint,
|
|
||||||
port: 9000,
|
|
||||||
useSSL: false,
|
|
||||||
accessKey: minioAccessKey,
|
|
||||||
secretKey: minioSecretKey
|
|
||||||
})
|
|
||||||
|
|
||||||
async function withDatabase (uri: string, f: (db: Db, client: MongoClient) => Promise<any>): Promise<void> {
|
async function withDatabase (uri: string, f: (db: Db, client: MongoClient) => Promise<any>): Promise<void> {
|
||||||
console.log(`connecting to database '${uri}'...`)
|
console.log(`connecting to database '${uri}'...`)
|
||||||
@ -139,7 +110,9 @@ program
|
|||||||
.command('upgrade-workspace <name>')
|
.command('upgrade-workspace <name>')
|
||||||
.description('upgrade workspace')
|
.description('upgrade workspace')
|
||||||
.action(async (workspace, cmd) => {
|
.action(async (workspace, cmd) => {
|
||||||
await upgradeWorkspace(mongodbUri, workspace, transactorUrl, minio)
|
return await withDatabase(mongodbUri, async (db) => {
|
||||||
|
await upgradeWorkspace(db, workspace)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
program
|
program
|
||||||
@ -158,6 +131,8 @@ program
|
|||||||
return await withDatabase(mongodbUri, async (db) => {
|
return await withDatabase(mongodbUri, async (db) => {
|
||||||
const workspacesJSON = JSON.stringify(await listWorkspaces(db), null, 2)
|
const workspacesJSON = JSON.stringify(await listWorkspaces(db), null, 2)
|
||||||
console.info(workspacesJSON)
|
console.info(workspacesJSON)
|
||||||
|
|
||||||
|
console.log('latest model version:', JSON.stringify(version))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -16,70 +16,16 @@
|
|||||||
|
|
||||||
import contact from '@anticrm/contact'
|
import contact from '@anticrm/contact'
|
||||||
import core, { DOMAIN_TX, Tx } from '@anticrm/core'
|
import core, { DOMAIN_TX, Tx } from '@anticrm/core'
|
||||||
import builder, { migrateOperations } from '@anticrm/model-all'
|
import builder, { version } from '@anticrm/model-all'
|
||||||
|
import { upgradeModel } from '@anticrm/server-tool'
|
||||||
import { existsSync } from 'fs'
|
import { existsSync } from 'fs'
|
||||||
import { mkdir, open, readFile, writeFile } from 'fs/promises'
|
import { mkdir, open, readFile, writeFile } from 'fs/promises'
|
||||||
import { Client } from 'minio'
|
import { Client } from 'minio'
|
||||||
import { Document, MongoClient } from 'mongodb'
|
import { Document, MongoClient } from 'mongodb'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { connect } from './connect'
|
import { rebuildElastic } from './elastic'
|
||||||
import { MigrateClientImpl } from './upgrade'
|
|
||||||
import { generateModelDiff, printDiff } from './mdiff'
|
import { generateModelDiff, printDiff } from './mdiff'
|
||||||
import { listMinioObjects, MinioWorkspaceItem } from './minio'
|
import { listMinioObjects, MinioWorkspaceItem } from './minio'
|
||||||
import { rebuildElastic } from './elastic'
|
|
||||||
|
|
||||||
const txes = JSON.parse(JSON.stringify(builder.getTxes())) as Tx[]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export async function upgradeWorkspace (
|
|
||||||
mongoUrl: string,
|
|
||||||
dbName: string,
|
|
||||||
transactorUrl: string,
|
|
||||||
minio: Client
|
|
||||||
): Promise<void> {
|
|
||||||
if (txes.some((tx) => tx.objectSpace !== core.space.Model)) {
|
|
||||||
throw Error('Model txes must target only core.space.Model')
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = new MongoClient(mongoUrl)
|
|
||||||
try {
|
|
||||||
await client.connect()
|
|
||||||
const db = client.db(dbName)
|
|
||||||
|
|
||||||
console.log('removing model...')
|
|
||||||
// we're preserving accounts (created by core.account.System).
|
|
||||||
const result = await db.collection(DOMAIN_TX).deleteMany({
|
|
||||||
objectSpace: core.space.Model,
|
|
||||||
modifiedBy: core.account.System,
|
|
||||||
objectClass: { $ne: contact.class.EmployeeAccount }
|
|
||||||
})
|
|
||||||
console.log(`${result.deletedCount} transactions deleted.`)
|
|
||||||
|
|
||||||
console.log('creating model...')
|
|
||||||
const model = txes
|
|
||||||
const insert = await db.collection(DOMAIN_TX).insertMany(model as Document[])
|
|
||||||
console.log(`${insert.insertedCount} model transactions inserted.`)
|
|
||||||
|
|
||||||
const migrateClient = new MigrateClientImpl(db)
|
|
||||||
for (const op of migrateOperations) {
|
|
||||||
await op.migrate(migrateClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Apply upgrade operations')
|
|
||||||
|
|
||||||
const connection = await connect(transactorUrl, dbName)
|
|
||||||
for (const op of migrateOperations) {
|
|
||||||
await op.upgrade(connection)
|
|
||||||
}
|
|
||||||
|
|
||||||
await connection.close()
|
|
||||||
} finally {
|
|
||||||
await client.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CollectionInfo {
|
interface CollectionInfo {
|
||||||
name: string
|
name: string
|
||||||
file: string
|
file: string
|
||||||
@ -107,7 +53,7 @@ export async function dumpWorkspace (mongoUrl: string, dbName: string, fileName:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const workspaceInfo: WorkspaceInfo = {
|
const workspaceInfo: WorkspaceInfo = {
|
||||||
version: '0.6.0',
|
version: `${version.major}.${version.minor}.${version.patch}`,
|
||||||
collections: [],
|
collections: [],
|
||||||
minioData: []
|
minioData: []
|
||||||
}
|
}
|
||||||
@ -215,7 +161,7 @@ export async function restoreWorkspace (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await upgradeWorkspace(mongoUrl, dbName, transactorUrl, minio)
|
await upgradeModel(dbName, transactorUrl)
|
||||||
|
|
||||||
await rebuildElastic(mongoUrl, dbName, minio, elasticUrl)
|
await rebuildElastic(mongoUrl, dbName, minio, elasticUrl)
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"build:watch": "tsc",
|
"build:watch": "tsc",
|
||||||
"lint:fix": "eslint --fix src",
|
"lint:fix": "eslint --fix src",
|
||||||
"genmodel": "ts-node src/__genmodel.ts",
|
"genmodel": "ts-node src/__genmodel.ts",
|
||||||
|
"version": "ts-node src/__showversion.ts",
|
||||||
"lint": "eslint src",
|
"lint": "eslint src",
|
||||||
"format": "prettier --write src && eslint --fix src"
|
"format": "prettier --write src && eslint --fix src"
|
||||||
},
|
},
|
||||||
|
17
models/all/src/__showversion.ts
Normal file
17
models/all/src/__showversion.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2020 Anticrm Platform Contributors.
|
||||||
|
//
|
||||||
|
// 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 { version } from '.'
|
||||||
|
console.log(`"${version.major}.${version.minor}.${version.patch}"`)
|
@ -13,61 +13,71 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import core, { Data, Version } from '@anticrm/core'
|
||||||
import { Builder } from '@anticrm/model'
|
import { Builder } from '@anticrm/model'
|
||||||
|
import { createModel as activityModel } from '@anticrm/model-activity'
|
||||||
import { createModel as coreModel } from '@anticrm/model-core'
|
|
||||||
import { createModel as viewModel } from '@anticrm/model-view'
|
|
||||||
import { createModel as workbenchModel } from '@anticrm/model-workbench'
|
|
||||||
import { createModel as contactModel } from '@anticrm/model-contact'
|
|
||||||
import { createModel as taskModel } from '@anticrm/model-task'
|
|
||||||
import { createModel as chunterModel } from '@anticrm/model-chunter'
|
|
||||||
import { createModel as recruitModel } from '@anticrm/model-recruit'
|
|
||||||
import { createModel as settingModel } from '@anticrm/model-setting'
|
|
||||||
import { createModel as telegramModel } from '@anticrm/model-telegram'
|
|
||||||
import { createModel as attachmentModel } from '@anticrm/model-attachment'
|
import { createModel as attachmentModel } from '@anticrm/model-attachment'
|
||||||
import { createModel as leadModel } from '@anticrm/model-lead'
|
import { createModel as chunterModel } from '@anticrm/model-chunter'
|
||||||
|
import { createModel as contactModel } from '@anticrm/model-contact'
|
||||||
|
import { createModel as coreModel } from '@anticrm/model-core'
|
||||||
|
import { createDemo } from '@anticrm/model-demo'
|
||||||
import { createModel as gmailModel } from '@anticrm/model-gmail'
|
import { createModel as gmailModel } from '@anticrm/model-gmail'
|
||||||
import { createModel as inventoryModel } from '@anticrm/model-inventory'
|
import { createModel as inventoryModel } from '@anticrm/model-inventory'
|
||||||
|
import { createModel as leadModel } from '@anticrm/model-lead'
|
||||||
import { createModel as presentationModel } from '@anticrm/model-presentation'
|
import { createModel as presentationModel } from '@anticrm/model-presentation'
|
||||||
import { createModel as templatesModel } from '@anticrm/model-templates'
|
import { createModel as recruitModel } from '@anticrm/model-recruit'
|
||||||
import { createModel as textEditorModel } from '@anticrm/model-text-editor'
|
|
||||||
|
|
||||||
import { createModel as serverCoreModel } from '@anticrm/model-server-core'
|
|
||||||
import { createModel as serverAttachmentModel } from '@anticrm/model-server-attachment'
|
import { createModel as serverAttachmentModel } from '@anticrm/model-server-attachment'
|
||||||
import { createModel as serverContactModel } from '@anticrm/model-server-contact'
|
import { createModel as serverContactModel } from '@anticrm/model-server-contact'
|
||||||
import { createModel as activityModel } from '@anticrm/model-activity'
|
import { createModel as serverCoreModel } from '@anticrm/model-server-core'
|
||||||
|
import { createModel as settingModel } from '@anticrm/model-setting'
|
||||||
import { createDemo } from '@anticrm/model-demo'
|
import { createModel as taskModel } from '@anticrm/model-task'
|
||||||
|
import { createModel as telegramModel } from '@anticrm/model-telegram'
|
||||||
|
import { createModel as templatesModel } from '@anticrm/model-templates'
|
||||||
|
import { createModel as textEditorModel } from '@anticrm/model-text-editor'
|
||||||
|
import { createModel as viewModel } from '@anticrm/model-view'
|
||||||
|
import { createModel as workbenchModel } from '@anticrm/model-workbench'
|
||||||
|
|
||||||
const builder = new Builder()
|
const builder = new Builder()
|
||||||
|
|
||||||
coreModel(builder)
|
const builders = [
|
||||||
activityModel(builder)
|
coreModel,
|
||||||
attachmentModel(builder)
|
activityModel,
|
||||||
viewModel(builder)
|
attachmentModel,
|
||||||
workbenchModel(builder)
|
viewModel,
|
||||||
contactModel(builder)
|
workbenchModel,
|
||||||
chunterModel(builder)
|
contactModel,
|
||||||
taskModel(builder)
|
chunterModel,
|
||||||
recruitModel(builder)
|
taskModel,
|
||||||
settingModel(builder)
|
recruitModel,
|
||||||
telegramModel(builder)
|
settingModel,
|
||||||
leadModel(builder)
|
telegramModel,
|
||||||
gmailModel(builder)
|
leadModel,
|
||||||
inventoryModel(builder)
|
gmailModel,
|
||||||
presentationModel(builder)
|
inventoryModel,
|
||||||
templatesModel(builder)
|
presentationModel,
|
||||||
textEditorModel(builder)
|
templatesModel,
|
||||||
|
textEditorModel,
|
||||||
|
|
||||||
serverCoreModel(builder)
|
serverCoreModel,
|
||||||
serverAttachmentModel(builder)
|
serverAttachmentModel,
|
||||||
serverContactModel(builder)
|
serverContactModel,
|
||||||
|
|
||||||
createDemo(builder)
|
createDemo
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const b of builders) {
|
||||||
|
b(builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const version: Data<Version> = {
|
||||||
|
major: 0,
|
||||||
|
minor: 6,
|
||||||
|
patch: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.createDoc(core.class.Version, core.space.Model, version, core.version.Model)
|
||||||
export default builder
|
export default builder
|
||||||
|
|
||||||
// Export upgrade procedures
|
// Export upgrade procedures
|
||||||
export { migrateOperations } from './migration'
|
|
||||||
|
|
||||||
export { createDeps } from './creation'
|
export { createDeps } from './creation'
|
||||||
|
export { migrateOperations } from './migration'
|
||||||
|
@ -31,7 +31,8 @@ import type {
|
|||||||
Collection,
|
Collection,
|
||||||
RefTo,
|
RefTo,
|
||||||
ArrOf,
|
ArrOf,
|
||||||
Interface
|
Interface,
|
||||||
|
Version
|
||||||
} from '@anticrm/core'
|
} from '@anticrm/core'
|
||||||
import { DOMAIN_MODEL } from '@anticrm/core'
|
import { DOMAIN_MODEL } from '@anticrm/core'
|
||||||
import { Model, Prop, TypeRef, TypeString, TypeTimestamp } from '@anticrm/model'
|
import { Model, Prop, TypeRef, TypeString, TypeTimestamp } from '@anticrm/model'
|
||||||
@ -128,3 +129,10 @@ export class TTypeTimestamp extends TType {}
|
|||||||
|
|
||||||
@Model(core.class.TypeDate, core.class.Type)
|
@Model(core.class.TypeDate, core.class.Type)
|
||||||
export class TTypeDate extends TType {}
|
export class TTypeDate extends TType {}
|
||||||
|
|
||||||
|
@Model(core.class.Version, core.class.Doc, DOMAIN_MODEL)
|
||||||
|
export class TVersion extends TDoc implements Version {
|
||||||
|
major!: number
|
||||||
|
minor!: number
|
||||||
|
patch!: number
|
||||||
|
}
|
||||||
|
@ -15,15 +15,42 @@
|
|||||||
|
|
||||||
import { Builder } from '@anticrm/model'
|
import { Builder } from '@anticrm/model'
|
||||||
import core from './component'
|
import core from './component'
|
||||||
import { TAttribute, TArrOf, TClass, TDoc, TMixin, TObj, TType, TTypeString, TTypeBoolean, TTypeTimestamp, TTypeDate, TAttachedDoc, TCollection, TRefTo, TInterface } from './core'
|
import {
|
||||||
import { TSpace, TAccount } from './security'
|
TArrOf,
|
||||||
import { TTx, TTxCreateDoc, TTxMixin, TTxUpdateDoc, TTxCUD, TTxPutBag, TTxRemoveDoc, TTxBulkWrite, TTxCollectionCUD } from './tx'
|
TAttachedDoc,
|
||||||
|
TAttribute,
|
||||||
|
TClass,
|
||||||
|
TCollection,
|
||||||
|
TDoc,
|
||||||
|
TInterface,
|
||||||
|
TMixin,
|
||||||
|
TObj,
|
||||||
|
TRefTo,
|
||||||
|
TType,
|
||||||
|
TTypeBoolean,
|
||||||
|
TTypeDate,
|
||||||
|
TTypeString,
|
||||||
|
TTypeTimestamp,
|
||||||
|
TVersion
|
||||||
|
} from './core'
|
||||||
|
import { TAccount, TSpace } from './security'
|
||||||
|
import {
|
||||||
|
TTx,
|
||||||
|
TTxBulkWrite,
|
||||||
|
TTxCollectionCUD,
|
||||||
|
TTxCreateDoc,
|
||||||
|
TTxCUD,
|
||||||
|
TTxMixin,
|
||||||
|
TTxPutBag,
|
||||||
|
TTxRemoveDoc,
|
||||||
|
TTxUpdateDoc
|
||||||
|
} from './tx'
|
||||||
|
|
||||||
export * from './core'
|
export * from './core'
|
||||||
|
export { coreOperation } from './migration'
|
||||||
export * from './security'
|
export * from './security'
|
||||||
export * from './tx'
|
export * from './tx'
|
||||||
export { core as default }
|
export { core as default }
|
||||||
export { coreOperation } from './migration'
|
|
||||||
|
|
||||||
export function createModel (builder: Builder): void {
|
export function createModel (builder: Builder): void {
|
||||||
builder.createModel(
|
builder.createModel(
|
||||||
@ -52,6 +79,7 @@ export function createModel (builder: Builder): void {
|
|||||||
TRefTo,
|
TRefTo,
|
||||||
TCollection,
|
TCollection,
|
||||||
TTypeDate,
|
TTypeDate,
|
||||||
TArrOf
|
TArrOf,
|
||||||
|
TVersion
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -221,3 +221,12 @@ export interface Space extends Doc {
|
|||||||
export interface Account extends Doc {
|
export interface Account extends Doc {
|
||||||
email: string
|
email: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface Version extends Doc {
|
||||||
|
major: number
|
||||||
|
minor: number
|
||||||
|
patch: number
|
||||||
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
//
|
//
|
||||||
import type { Plugin, StatusCode } from '@anticrm/platform'
|
import type { Plugin, StatusCode } from '@anticrm/platform'
|
||||||
import { plugin } from '@anticrm/platform'
|
import { plugin } from '@anticrm/platform'
|
||||||
import { Mixin } from '.'
|
import { Mixin, Version } from '.'
|
||||||
import type { Account, ArrOf, AnyAttribute, AttachedDoc, Class, Doc, Interface, Obj, PropertyType, Ref, Space, Timestamp, Type, Collection, RefTo } from './classes'
|
import type { Account, ArrOf, AnyAttribute, AttachedDoc, Class, Doc, Interface, Obj, PropertyType, Ref, Space, Timestamp, Type, Collection, RefTo } from './classes'
|
||||||
import type { Tx, TxBulkWrite, TxCollectionCUD, TxCreateDoc, TxCUD, TxMixin, TxPutBag, TxRemoveDoc, TxUpdateDoc } from './tx'
|
import type { Tx, TxBulkWrite, TxCollectionCUD, TxCreateDoc, TxCUD, TxMixin, TxPutBag, TxRemoveDoc, TxUpdateDoc } from './tx'
|
||||||
|
|
||||||
@ -50,7 +50,8 @@ export default plugin(coreId, {
|
|||||||
RefTo: '' as Ref<Class<RefTo<Doc>>>,
|
RefTo: '' as Ref<Class<RefTo<Doc>>>,
|
||||||
ArrOf: '' as Ref<Class<ArrOf<Doc>>>,
|
ArrOf: '' as Ref<Class<ArrOf<Doc>>>,
|
||||||
Collection: '' as Ref<Class<Collection<AttachedDoc>>>,
|
Collection: '' as Ref<Class<Collection<AttachedDoc>>>,
|
||||||
Bag: '' as Ref<Class<Type<Record<string, PropertyType>>>>
|
Bag: '' as Ref<Class<Type<Record<string, PropertyType>>>>,
|
||||||
|
Version: '' as Ref<Class<Version>>
|
||||||
},
|
},
|
||||||
space: {
|
space: {
|
||||||
Tx: '' as Ref<Space>,
|
Tx: '' as Ref<Space>,
|
||||||
@ -62,5 +63,8 @@ export default plugin(coreId, {
|
|||||||
status: {
|
status: {
|
||||||
ObjectNotFound: '' as StatusCode<{ _id: Ref<Doc> }>,
|
ObjectNotFound: '' as StatusCode<{ _id: Ref<Doc> }>,
|
||||||
ItemNotFound: '' as StatusCode<{ _id: Ref<Doc>, _localId: string }>
|
ItemNotFound: '' as StatusCode<{ _id: Ref<Doc>, _localId: string }>
|
||||||
|
},
|
||||||
|
version: {
|
||||||
|
Model: '' as Ref<Version>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -315,7 +315,7 @@ export class Builder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTxes (): Tx[] {
|
getTxes (): Tx[] {
|
||||||
return this.txes
|
return [...this.txes]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,8 +15,9 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
||||||
import { getResource } from '@anticrm/platform'
|
import { getMetadata, getResource } from '@anticrm/platform'
|
||||||
import type { Client } from '@anticrm/core'
|
import type { Client } from '@anticrm/core'
|
||||||
|
import core from '@anticrm/core'
|
||||||
import { setCurrentAccount } from '@anticrm/core'
|
import { setCurrentAccount } from '@anticrm/core'
|
||||||
import { navigate, Loading, fetchMetadataLocalStorage } from '@anticrm/ui'
|
import { navigate, Loading, fetchMetadataLocalStorage } from '@anticrm/ui'
|
||||||
|
|
||||||
@ -25,6 +26,9 @@ import login from '@anticrm/login'
|
|||||||
import contact from '@anticrm/contact'
|
import contact from '@anticrm/contact'
|
||||||
|
|
||||||
import Workbench from './Workbench.svelte'
|
import Workbench from './Workbench.svelte'
|
||||||
|
import workbench from '../plugin'
|
||||||
|
|
||||||
|
let versionError: string | undefined = ''
|
||||||
|
|
||||||
async function connect (): Promise<Client | undefined> {
|
async function connect (): Promise<Client | undefined> {
|
||||||
const token = fetchMetadataLocalStorage(login.metadata.LoginToken)
|
const token = fetchMetadataLocalStorage(login.metadata.LoginToken)
|
||||||
@ -39,6 +43,7 @@ async function connect(): Promise<Client | undefined> {
|
|||||||
const getClient = await getResource(client.function.GetClient)
|
const getClient = await getResource(client.function.GetClient)
|
||||||
const instance = await getClient(token, endpoint)
|
const instance = await getClient(token, endpoint)
|
||||||
console.log('logging in as', email)
|
console.log('logging in as', email)
|
||||||
|
|
||||||
const me = await instance.findOne(contact.class.EmployeeAccount, { email })
|
const me = await instance.findOne(contact.class.EmployeeAccount, { email })
|
||||||
if (me !== undefined) {
|
if (me !== undefined) {
|
||||||
console.log('login: employee account', me)
|
console.log('login: employee account', me)
|
||||||
@ -46,6 +51,32 @@ async function connect(): Promise<Client | undefined> {
|
|||||||
} else {
|
} else {
|
||||||
console.log('WARNING: no employee account found.')
|
console.log('WARNING: no employee account found.')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('checking model version')
|
||||||
|
const version = await instance.findOne(core.class.Version, {})
|
||||||
|
console.log('Model version', version)
|
||||||
|
|
||||||
|
const requirdVersion = getMetadata(workbench.metadata.RequiredVersion)
|
||||||
|
if (requirdVersion !== undefined) {
|
||||||
|
console.log('checking min model version', requirdVersion)
|
||||||
|
const versionStr = `${version?.major}.${version?.minor}.${version?.patch}`
|
||||||
|
|
||||||
|
if (version === undefined || requirdVersion !== versionStr) {
|
||||||
|
versionError = `${versionStr} => ${requirdVersion}`
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
console.log(err)
|
||||||
|
const requirdVersion = getMetadata(workbench.metadata.RequiredVersion)
|
||||||
|
console.log('checking min model version', requirdVersion)
|
||||||
|
if (requirdVersion !== undefined) {
|
||||||
|
versionError = `'unknown' => ${requirdVersion}`
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +85,27 @@ async function connect(): Promise<Client | undefined> {
|
|||||||
{#await connect()}
|
{#await connect()}
|
||||||
<Loading/>
|
<Loading/>
|
||||||
{:then client}
|
{:then client}
|
||||||
|
{#if !client && versionError}
|
||||||
|
<div class='antiPopup version-popup'>
|
||||||
|
<h1>Server is under maintenance.</h1>
|
||||||
|
{versionError}
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
<Workbench {client}/>
|
<Workbench {client}/>
|
||||||
|
{/if}
|
||||||
{:catch error}
|
{:catch error}
|
||||||
<div>{error} -- {error.stack}</div>
|
<div>{error} -- {error.stack}</div>
|
||||||
{/await}
|
{/await}
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.version-popup {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: fixed;
|
||||||
|
left: 25%;
|
||||||
|
right: 25%;
|
||||||
|
top: 25%;
|
||||||
|
bottom: 25%;
|
||||||
|
}
|
||||||
|
</style>
|
@ -13,8 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import type { IntlString, Asset, Plugin } from '@anticrm/platform'
|
import type { IntlString, Asset, Plugin, Metadata } from '@anticrm/platform'
|
||||||
import type { Ref, Class, Doc, Obj, Space, Mixin } from '@anticrm/core'
|
import type { Ref, Class, Doc, Obj, Space, Mixin, Data, Version } from '@anticrm/core'
|
||||||
|
|
||||||
import type { AnyComponent } from '@anticrm/ui'
|
import type { AnyComponent } from '@anticrm/ui'
|
||||||
|
|
||||||
@ -92,5 +92,8 @@ export default plugin(workbenchId, {
|
|||||||
},
|
},
|
||||||
component: {
|
component: {
|
||||||
WorkbenchApp: '' as AnyComponent
|
WorkbenchApp: '' as AnyComponent
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
RequiredVersion: '' as Metadata<Data<Version>>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
"koa": "^2.13.1",
|
"koa": "^2.13.1",
|
||||||
"koa-router": "^10.1.1",
|
"koa-router": "^10.1.1",
|
||||||
"koa-bodyparser": "^4.3.0",
|
"koa-bodyparser": "^4.3.0",
|
||||||
"@koa/cors": "^3.1.0"
|
"@koa/cors": "^3.1.0",
|
||||||
|
"@anticrm/server-tool": "~0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import accountPlugin, { ACCOUNT_DB, methods } from '@anticrm/account'
|
import { ACCOUNT_DB, methods } from '@anticrm/account'
|
||||||
|
import toolPlugin from '@anticrm/server-tool'
|
||||||
import platform, { Request, Response, serialize, setMetadata, Severity, Status } from '@anticrm/platform'
|
import platform, { Request, Response, serialize, setMetadata, Severity, Status } from '@anticrm/platform'
|
||||||
import cors from '@koa/cors'
|
import cors from '@koa/cors'
|
||||||
import { IncomingHttpHeaders } from 'http'
|
import { IncomingHttpHeaders } from 'http'
|
||||||
@ -37,8 +38,8 @@ if (transactorUri === undefined) {
|
|||||||
|
|
||||||
const endpointUri = process.env.ENDPOINT_URL ?? transactorUri
|
const endpointUri = process.env.ENDPOINT_URL ?? transactorUri
|
||||||
|
|
||||||
setMetadata(accountPlugin.metadata.Endpoint, endpointUri)
|
setMetadata(toolPlugin.metadata.Endpoint, endpointUri)
|
||||||
setMetadata(accountPlugin.metadata.Transactor, transactorUri)
|
setMetadata(toolPlugin.metadata.Transactor, transactorUri)
|
||||||
|
|
||||||
let client: MongoClient
|
let client: MongoClient
|
||||||
|
|
||||||
|
@ -965,6 +965,11 @@
|
|||||||
"packageName": "@anticrm/model-text-editor",
|
"packageName": "@anticrm/model-text-editor",
|
||||||
"projectFolder": "models/text-editor",
|
"projectFolder": "models/text-editor",
|
||||||
"shouldPublish": true
|
"shouldPublish": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"packageName": "@anticrm/server-tool",
|
||||||
|
"projectFolder": "server/tool",
|
||||||
|
"shouldPublish": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,8 @@
|
|||||||
"@anticrm/client-resources": "~0.6.4",
|
"@anticrm/client-resources": "~0.6.4",
|
||||||
"@anticrm/client": "~0.6.1",
|
"@anticrm/client": "~0.6.1",
|
||||||
"jwt-simple": "~0.5.6",
|
"jwt-simple": "~0.5.6",
|
||||||
"ws": "^8.2.0"
|
"ws": "^8.2.0",
|
||||||
|
"@anticrm/model": "~0.6.0",
|
||||||
|
"@anticrm/server-tool": "~0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,25 +14,23 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import contact, { combineName } from '@anticrm/contact'
|
||||||
|
import core, { TxOperations } from '@anticrm/core'
|
||||||
import platform, {
|
import platform, {
|
||||||
getMetadata,
|
getMetadata,
|
||||||
Metadata,
|
PlatformError,
|
||||||
Plugin,
|
Plugin,
|
||||||
|
plugin,
|
||||||
Request,
|
Request,
|
||||||
Response,
|
Response,
|
||||||
PlatformError,
|
|
||||||
plugin,
|
|
||||||
Severity,
|
Severity,
|
||||||
Status,
|
Status,
|
||||||
StatusCode
|
StatusCode
|
||||||
} from '@anticrm/platform'
|
} from '@anticrm/platform'
|
||||||
|
import toolPlugin, { connect, initModel, upgradeModel, version } from '@anticrm/server-tool'
|
||||||
import { pbkdf2Sync, randomBytes } from 'crypto'
|
import { pbkdf2Sync, randomBytes } from 'crypto'
|
||||||
import { decode, encode } from 'jwt-simple'
|
import { decode, encode } from 'jwt-simple'
|
||||||
import { Binary, Db, ObjectId } from 'mongodb'
|
import { Binary, Db, ObjectId } from 'mongodb'
|
||||||
import core, { TxOperations } from '@anticrm/core'
|
|
||||||
import contact, { combineName } from '@anticrm/contact'
|
|
||||||
import { connect } from './connect'
|
|
||||||
import { initWorkspace } from './tool'
|
|
||||||
|
|
||||||
const WORKSPACE_COLLECTION = 'workspace'
|
const WORKSPACE_COLLECTION = 'workspace'
|
||||||
const ACCOUNT_COLLECTION = 'account'
|
const ACCOUNT_COLLECTION = 'account'
|
||||||
@ -51,11 +49,6 @@ export const accountId = 'account' as Plugin
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
const accountPlugin = plugin(accountId, {
|
const accountPlugin = plugin(accountId, {
|
||||||
metadata: {
|
|
||||||
Endpoint: '' as Metadata<string>,
|
|
||||||
Transactor: '' as Metadata<string>,
|
|
||||||
Secret: '' as Metadata<string>
|
|
||||||
},
|
|
||||||
status: {
|
status: {
|
||||||
AccountNotFound: '' as StatusCode<{ account: string }>,
|
AccountNotFound: '' as StatusCode<{ account: string }>,
|
||||||
WorkspaceNotFound: '' as StatusCode<{ workspace: string }>,
|
WorkspaceNotFound: '' as StatusCode<{ workspace: string }>,
|
||||||
@ -66,11 +59,11 @@ const accountPlugin = plugin(accountId, {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const getSecret = (): string => {
|
const getSecret = (): string => {
|
||||||
return getMetadata(accountPlugin.metadata.Secret) ?? 'secret'
|
return getMetadata(toolPlugin.metadata.Secret) ?? 'secret'
|
||||||
}
|
}
|
||||||
|
|
||||||
const getEndpoint = (): string => {
|
const getEndpoint = (): string => {
|
||||||
const endpoint = getMetadata(accountPlugin.metadata.Endpoint)
|
const endpoint = getMetadata(toolPlugin.metadata.Endpoint)
|
||||||
if (endpoint === undefined) {
|
if (endpoint === undefined) {
|
||||||
throw new Error('Please provide transactor endpoint url')
|
throw new Error('Please provide transactor endpoint url')
|
||||||
}
|
}
|
||||||
@ -78,7 +71,7 @@ const getEndpoint = (): string => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getTransactor = (): string => {
|
const getTransactor = (): string => {
|
||||||
const transactor = getMetadata(accountPlugin.metadata.Transactor)
|
const transactor = getMetadata(toolPlugin.metadata.Transactor)
|
||||||
if (transactor === undefined) {
|
if (transactor === undefined) {
|
||||||
throw new Error('Please provide transactor url')
|
throw new Error('Please provide transactor url')
|
||||||
}
|
}
|
||||||
@ -307,13 +300,31 @@ export async function createWorkspace (db: Db, workspace: string, organisation:
|
|||||||
.collection(WORKSPACE_COLLECTION)
|
.collection(WORKSPACE_COLLECTION)
|
||||||
.insertOne({
|
.insertOne({
|
||||||
workspace,
|
workspace,
|
||||||
organisation
|
organisation,
|
||||||
|
version
|
||||||
})
|
})
|
||||||
.then((e) => e.insertedId.toHexString())
|
.then((e) => e.insertedId.toHexString())
|
||||||
await initWorkspace(getTransactor(), workspace)
|
await initModel(getTransactor(), workspace)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export async function upgradeWorkspace (db: Db, workspace: string): Promise<string> {
|
||||||
|
if ((await getWorkspace(db, workspace)) === null) {
|
||||||
|
throw new PlatformError(new Status(Severity.ERROR, accountPlugin.status.WorkspaceNotFound, { workspace }))
|
||||||
|
}
|
||||||
|
await db.collection(WORKSPACE_COLLECTION).updateOne(
|
||||||
|
{ workspace },
|
||||||
|
{
|
||||||
|
$set: { version }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await upgradeModel(getTransactor(), workspace)
|
||||||
|
return `${version.major}.${version.minor}.${version.patch}`
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"build": "heft build",
|
"build": "heft build",
|
||||||
"build:watch": "tsc",
|
"build:watch": "tsc",
|
||||||
"lint:fix": "eslint --fix src",
|
"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/ && rm ./dist/config.json",
|
"bundle": "esbuild src/__start.ts --define:process.env.MODEL_VERSION=$(rush model-version | tail -1) --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:build": "docker build -t anticrm/front .",
|
||||||
"docker:push": "docker push anticrm/front",
|
"docker:push": "docker push anticrm/front",
|
||||||
"lint": "eslint src",
|
"lint": "eslint src",
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
export ACCOUNTS_URL=http://localhost:3333
|
export ACCOUNTS_URL=http://localhost:3333
|
||||||
|
export UPLOAD_URL=http://localhost:3333/files
|
||||||
export TRANSACTOR_URL=ws://localhost:3333
|
export TRANSACTOR_URL=ws://localhost:3333
|
||||||
export ELASTIC_URL=http://elastic:9200
|
export ELASTIC_URL=http://elastic:9200
|
||||||
export MINIO_ENDPOINT=minio
|
export MINIO_ENDPOINT=minio
|
||||||
export MINIO_ACCESS_KEY=minioadmin
|
export MINIO_ACCESS_KEY=minioadmin
|
||||||
export MINIO_SECRET_KEY=minioadmin
|
export MINIO_SECRET_KEY=minioadmin
|
||||||
|
|
||||||
node ./bundle.js
|
node ./bundle.js
|
@ -67,6 +67,12 @@ if (uploadUrl === undefined) {
|
|||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = { transactorEndpoint, elasticUrl, minio, accountsUrl, uploadUrl }
|
const modelVersion = process.env.MODEL_VERSION
|
||||||
|
if (modelVersion === undefined) {
|
||||||
|
console.error('please provide model version requirement')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = { transactorEndpoint, elasticUrl, minio, accountsUrl, uploadUrl, modelVersion }
|
||||||
console.log('Starting Front service with', config)
|
console.log('Starting Front service with', config)
|
||||||
start(config, 8080)
|
start(config, 8080)
|
||||||
|
@ -14,22 +14,20 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { resolve, join } from 'path'
|
import attachment from '@anticrm/attachment'
|
||||||
import https from 'https'
|
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 cors from 'cors'
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import fileUpload, { UploadedFile } from 'express-fileupload'
|
import fileUpload, { UploadedFile } from 'express-fileupload'
|
||||||
import cors from 'cors'
|
import https from 'https'
|
||||||
import { v4 as uuid } from 'uuid'
|
|
||||||
import { decode } from 'jwt-simple'
|
import { decode } from 'jwt-simple'
|
||||||
|
|
||||||
import { Space, Ref, Doc, Account } from '@anticrm/core'
|
|
||||||
// import { TxFactory } from '@anticrm/core'
|
|
||||||
import type { Token, IndexedDoc } from '@anticrm/server-core'
|
|
||||||
import { createElasticAdapter } from '@anticrm/elastic'
|
|
||||||
import attachment from '@anticrm/attachment'
|
|
||||||
// import { createContributingClient } from '@anticrm/contrib'
|
// import { createContributingClient } from '@anticrm/contrib'
|
||||||
|
|
||||||
import { Client, ItemBucketMetadata } from 'minio'
|
import { Client, ItemBucketMetadata } from 'minio'
|
||||||
|
import { join, resolve } from 'path'
|
||||||
|
import { v4 as uuid } from 'uuid'
|
||||||
|
|
||||||
// import { createElasticAdapter } from '@anticrm/elastic'
|
// import { createElasticAdapter } from '@anticrm/elastic'
|
||||||
|
|
||||||
@ -79,7 +77,7 @@ async function minioUpload (minio: Client, workspace: string, file: UploadedFile
|
|||||||
* @public
|
* @public
|
||||||
* @param port -
|
* @param port -
|
||||||
*/
|
*/
|
||||||
export function start (config: { transactorEndpoint: string, elasticUrl: string, minio: Client, accountsUrl: string, uploadUrl: 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()
|
const app = express()
|
||||||
|
|
||||||
app.use(cors())
|
app.use(cors())
|
||||||
@ -92,7 +90,8 @@ export function start (config: { transactorEndpoint: string, elasticUrl: string,
|
|||||||
res.json(
|
res.json(
|
||||||
{
|
{
|
||||||
ACCOUNTS_URL: config.accountsUrl,
|
ACCOUNTS_URL: config.accountsUrl,
|
||||||
UPLOAD_URL: config.uploadUrl
|
UPLOAD_URL: config.uploadUrl,
|
||||||
|
MODEL_VERSION: config.modelVersion
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
7
server/tool/.eslintrc.js
Normal file
7
server/tool/.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/tool/.npmignore
Normal file
4
server/tool/.npmignore
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*
|
||||||
|
!/lib/**
|
||||||
|
!CHANGELOG.md
|
||||||
|
/lib/**/__tests__/
|
18
server/tool/config/rig.json
Normal file
18
server/tool/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"
|
||||||
|
}
|
43
server/tool/package.json
Normal file
43
server/tool/package.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "@anticrm/server-tool",
|
||||||
|
"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",
|
||||||
|
"@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",
|
||||||
|
"@types/minio": "^7.0.10",
|
||||||
|
"@types/ws": "^8.2.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mongodb": "^4.1.1",
|
||||||
|
"@anticrm/platform": "~0.6.5",
|
||||||
|
"@anticrm/model-all": "~0.6.0",
|
||||||
|
"minio": "^7.0.19",
|
||||||
|
"@anticrm/core": "~0.6.14",
|
||||||
|
"@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"
|
||||||
|
}
|
||||||
|
}
|
@ -19,18 +19,22 @@ import clientResources from '@anticrm/client-resources'
|
|||||||
import { Client } from '@anticrm/core'
|
import { Client } from '@anticrm/core'
|
||||||
import { getMetadata, setMetadata } from '@anticrm/platform'
|
import { getMetadata, setMetadata } from '@anticrm/platform'
|
||||||
import { encode } from 'jwt-simple'
|
import { encode } from 'jwt-simple'
|
||||||
import accountPlugin from '.'
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
import toolPlugin from './plugin'
|
||||||
const WebSocket = require('ws')
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
export async function connect (transactorUrl: string, workspace: string, email?: string): Promise<Client> {
|
export async function connect (transactorUrl: string, workspace: string, email?: string): Promise<Client> {
|
||||||
const token = encode(
|
const token = encode(
|
||||||
{ email: email ?? 'anticrm@hc.engineering', workspace },
|
{ email: email ?? 'anticrm@hc.engineering', workspace },
|
||||||
getMetadata(accountPlugin.metadata.Secret) ?? 'secret'
|
getMetadata(toolPlugin.metadata.Secret) ?? 'secret'
|
||||||
)
|
)
|
||||||
|
|
||||||
// We need to override default factory with 'ws' one.
|
// We need to override default factory with 'ws' one.
|
||||||
|
// eslint-disable-next-line
|
||||||
|
const WebSocket = require('ws')
|
||||||
|
|
||||||
setMetadata(client.metadata.ClientSocketFactory, (url) => new WebSocket(url))
|
setMetadata(client.metadata.ClientSocketFactory, (url) => new WebSocket(url))
|
||||||
return await (await clientResources()).function.GetClient(token, transactorUrl)
|
return await (await clientResources()).function.GetClient(token, transactorUrl)
|
||||||
}
|
}
|
@ -1,6 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
// Copyright © 2022 Hardcore Engineering Inc.
|
||||||
// Copyright © 2021, 2022 Hardcore Engineering Inc.
|
|
||||||
//
|
//
|
||||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
// 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
|
// you may not use this file except in compliance with the License. You may
|
||||||
@ -14,13 +13,24 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import contact from '@anticrm/contact'
|
||||||
import core, { DOMAIN_TX, Tx } from '@anticrm/core'
|
import core, { DOMAIN_TX, Tx } from '@anticrm/core'
|
||||||
import builder, { createDeps } from '@anticrm/model-all'
|
import builder, { createDeps, migrateOperations } from '@anticrm/model-all'
|
||||||
import { Client } from 'minio'
|
import { Client } from 'minio'
|
||||||
import { Document, MongoClient } from 'mongodb'
|
import { Document, MongoClient } from 'mongodb'
|
||||||
import { connect } from './connect'
|
import { connect } from './connect'
|
||||||
|
import { MigrateClientImpl } from './upgrade'
|
||||||
|
import toolPlugin from './plugin'
|
||||||
|
|
||||||
export async function initWorkspace (transactorUrl: string, dbName: string): Promise<void> {
|
export { version } from '@anticrm/model-all'
|
||||||
|
export * from './plugin'
|
||||||
|
export { toolPlugin as default }
|
||||||
|
export * from './connect'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function prepareTools (): { mongodbUri: string, minio: Client, txes: Tx[] } {
|
||||||
const minioEndpoint = process.env.MINIO_ENDPOINT
|
const minioEndpoint = process.env.MINIO_ENDPOINT
|
||||||
if (minioEndpoint === undefined) {
|
if (minioEndpoint === undefined) {
|
||||||
console.error('please provide minio endpoint')
|
console.error('please provide minio endpoint')
|
||||||
@ -54,7 +64,14 @@ export async function initWorkspace (transactorUrl: string, dbName: string): Pro
|
|||||||
})
|
})
|
||||||
|
|
||||||
const txes = JSON.parse(JSON.stringify(builder.getTxes())) as Tx[]
|
const txes = JSON.parse(JSON.stringify(builder.getTxes())) as Tx[]
|
||||||
|
return { mongodbUri, minio, txes }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export async function initModel (transactorUrl: string, dbName: string): Promise<void> {
|
||||||
|
const { mongodbUri, minio, txes } = prepareTools()
|
||||||
if (txes.some((tx) => tx.objectSpace !== core.space.Model)) {
|
if (txes.some((tx) => tx.objectSpace !== core.space.Model)) {
|
||||||
throw Error('Model txes must target only core.space.Model')
|
throw Error('Model txes must target only core.space.Model')
|
||||||
}
|
}
|
||||||
@ -90,3 +107,52 @@ export async function initWorkspace (transactorUrl: string, dbName: string): Pro
|
|||||||
await client.close()
|
await client.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export async function upgradeModel (
|
||||||
|
transactorUrl: string,
|
||||||
|
dbName: string
|
||||||
|
): Promise<void> {
|
||||||
|
const { mongodbUri, txes } = prepareTools()
|
||||||
|
if (txes.some((tx) => tx.objectSpace !== core.space.Model)) {
|
||||||
|
throw Error('Model txes must target only core.space.Model')
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = new MongoClient(mongodbUri)
|
||||||
|
try {
|
||||||
|
await client.connect()
|
||||||
|
const db = client.db(dbName)
|
||||||
|
|
||||||
|
console.log('removing model...')
|
||||||
|
// we're preserving accounts (created by core.account.System).
|
||||||
|
const result = await db.collection(DOMAIN_TX).deleteMany({
|
||||||
|
objectSpace: core.space.Model,
|
||||||
|
modifiedBy: core.account.System,
|
||||||
|
objectClass: { $ne: contact.class.EmployeeAccount }
|
||||||
|
})
|
||||||
|
console.log(`${result.deletedCount} transactions deleted.`)
|
||||||
|
|
||||||
|
console.log('creating model...')
|
||||||
|
const model = txes
|
||||||
|
const insert = await db.collection(DOMAIN_TX).insertMany(model as Document[])
|
||||||
|
console.log(`${insert.insertedCount} model transactions inserted.`)
|
||||||
|
|
||||||
|
const migrateClient = new MigrateClientImpl(db)
|
||||||
|
for (const op of migrateOperations) {
|
||||||
|
await op.migrate(migrateClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Apply upgrade operations')
|
||||||
|
|
||||||
|
const connection = await connect(transactorUrl, dbName)
|
||||||
|
for (const op of migrateOperations) {
|
||||||
|
await op.upgrade(connection)
|
||||||
|
}
|
||||||
|
|
||||||
|
await connection.close()
|
||||||
|
} finally {
|
||||||
|
await client.close()
|
||||||
|
}
|
||||||
|
}
|
19
server/tool/src/plugin.ts
Normal file
19
server/tool/src/plugin.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { Metadata, plugin, Plugin } from '@anticrm/platform'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export const toolId = 'tool' as Plugin
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
const toolPlugin = plugin(toolId, {
|
||||||
|
metadata: {
|
||||||
|
Endpoint: '' as Metadata<string>,
|
||||||
|
Transactor: '' as Metadata<string>,
|
||||||
|
Secret: '' as Metadata<string>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default toolPlugin
|
8
server/tool/tsconfig.json
Normal file
8
server/tool/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "./node_modules/@anticrm/platform-rig/profiles/default/tsconfig.json",
|
||||||
|
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./lib"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user