mirror of
https://github.com/hcengineering/platform.git
synced 2025-01-03 00:43:59 +03:00
move workers to another repo (#7560)
Signed-off-by: Nikolay Chunosov <Chunosov.N@gmail.com>
This commit is contained in:
parent
8c4c4ad9b3
commit
2d64f89584
@ -164,15 +164,6 @@ dependencies:
|
||||
'@rush-temp/cloud-transactor':
|
||||
specifier: file:./projects/cloud-transactor.tgz
|
||||
version: file:projects/cloud-transactor.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(ts-node@10.9.2)(utf-8-validate@6.0.4)
|
||||
'@rush-temp/cloud-transactor-api':
|
||||
specifier: file:./projects/cloud-transactor-api.tgz
|
||||
version: file:projects/cloud-transactor-api.tgz(esbuild@0.24.2)(ts-node@10.9.2)
|
||||
'@rush-temp/cloud-transactor-api-demo':
|
||||
specifier: file:./projects/cloud-transactor-api-demo.tgz
|
||||
version: file:projects/cloud-transactor-api-demo.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.24.2)(ts-node@10.9.2)(utf-8-validate@6.0.4)
|
||||
'@rush-temp/cloud-transactor-http-api':
|
||||
specifier: file:./projects/cloud-transactor-http-api.tgz
|
||||
version: file:projects/cloud-transactor-http-api.tgz(bufferutil@4.0.8)(esbuild@0.24.2)(ts-node@10.9.2)(utf-8-validate@6.0.4)
|
||||
'@rush-temp/collaboration':
|
||||
specifier: file:./projects/collaboration.tgz
|
||||
version: file:projects/collaboration.tgz(esbuild@0.24.2)(ts-node@10.9.2)
|
||||
@ -23038,106 +23029,6 @@ packages:
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
file:projects/cloud-transactor-api-demo.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.24.2)(ts-node@10.9.2)(utf-8-validate@6.0.4):
|
||||
resolution: {integrity: sha512-jDFW5bSJFL4PuJt0ESJ50jyMHtQKSd9eLfjoUXslmIUdAEITbEEz8N0cstMBujEybfmSghSi6haE2GnG/p4rHg==, tarball: file:projects/cloud-transactor-api-demo.tgz}
|
||||
id: file:projects/cloud-transactor-api-demo.tgz
|
||||
name: '@rush-temp/cloud-transactor-api-demo'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@cloudflare/workers-types': 4.20241022.0
|
||||
'@types/jest': 29.5.12
|
||||
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.6.2)
|
||||
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.6.2)
|
||||
eslint: 8.56.0
|
||||
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.6.2)
|
||||
eslint-plugin-import: 2.29.1(eslint@8.56.0)
|
||||
eslint-plugin-n: 15.7.0(eslint@8.56.0)
|
||||
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
|
||||
itty-router: 5.0.18
|
||||
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2)
|
||||
prettier: 3.2.5
|
||||
ts-jest: 29.1.2(esbuild@0.24.2)(jest@29.7.0)(typescript@5.6.2)
|
||||
typescript: 5.6.2
|
||||
wrangler: 3.97.0(@cloudflare/workers-types@4.20241022.0)(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- '@jest/types'
|
||||
- '@types/node'
|
||||
- babel-jest
|
||||
- babel-plugin-macros
|
||||
- bufferutil
|
||||
- esbuild
|
||||
- node-notifier
|
||||
- supports-color
|
||||
- ts-node
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
file:projects/cloud-transactor-api.tgz(esbuild@0.24.2)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-KdCN6yGHiBNfGsSWtSwQuIfOQGGr0P40cj5u8Z5p5AQz0MbeoRJ1gFnpzI8VJjOhKfNKn3JR8Zdwg+JTq29yWQ==, tarball: file:projects/cloud-transactor-api.tgz}
|
||||
id: file:projects/cloud-transactor-api.tgz
|
||||
name: '@rush-temp/cloud-transactor-api'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@types/jest': 29.5.12
|
||||
'@types/node': 20.11.19
|
||||
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.6.2)
|
||||
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.6.2)
|
||||
eslint: 8.56.0
|
||||
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.6.2)
|
||||
eslint-plugin-import: 2.29.1(eslint@8.56.0)
|
||||
eslint-plugin-n: 15.7.0(eslint@8.56.0)
|
||||
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
|
||||
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2)
|
||||
prettier: 3.2.5
|
||||
ts-jest: 29.1.2(esbuild@0.24.2)(jest@29.7.0)(typescript@5.6.2)
|
||||
typescript: 5.6.2
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- '@jest/types'
|
||||
- babel-jest
|
||||
- babel-plugin-macros
|
||||
- esbuild
|
||||
- node-notifier
|
||||
- supports-color
|
||||
- ts-node
|
||||
dev: false
|
||||
|
||||
file:projects/cloud-transactor-http-api.tgz(bufferutil@4.0.8)(esbuild@0.24.2)(ts-node@10.9.2)(utf-8-validate@6.0.4):
|
||||
resolution: {integrity: sha512-4vV2jdYPYycGKl0jomdYmnEvZgancPc2pdhJ6khPP3qR4FZFhHx5lF/uLuREoZ3d7GuQqNs4J+fKL/+iriCkaA==, tarball: file:projects/cloud-transactor-http-api.tgz}
|
||||
id: file:projects/cloud-transactor-http-api.tgz
|
||||
name: '@rush-temp/cloud-transactor-http-api'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@cloudflare/workers-types': 4.20241022.0
|
||||
'@types/jest': 29.5.12
|
||||
'@types/node': 20.11.19
|
||||
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.6.2)
|
||||
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.6.2)
|
||||
eslint: 8.56.0
|
||||
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.6.2)
|
||||
eslint-plugin-import: 2.29.1(eslint@8.56.0)
|
||||
eslint-plugin-n: 15.7.0(eslint@8.56.0)
|
||||
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
|
||||
itty-router: 5.0.18
|
||||
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2)
|
||||
prettier: 3.2.5
|
||||
ts-jest: 29.1.2(esbuild@0.24.2)(jest@29.7.0)(typescript@5.6.2)
|
||||
typescript: 5.6.2
|
||||
wrangler: 3.97.0(@cloudflare/workers-types@4.20241022.0)(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- '@jest/types'
|
||||
- babel-jest
|
||||
- babel-plugin-macros
|
||||
- bufferutil
|
||||
- esbuild
|
||||
- node-notifier
|
||||
- supports-color
|
||||
- ts-node
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
file:projects/cloud-transactor.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(ts-node@10.9.2)(utf-8-validate@6.0.4):
|
||||
resolution: {integrity: sha512-6kBv/3KfTsOvsDrfVXQvQwtLM72ltx4oPHc3cJ9xtG82XLeQLLpeJmCy6CNDVOtXaqnagwF5TKm/zCw4sc/dFQ==, tarball: file:projects/cloud-transactor.tgz}
|
||||
id: file:projects/cloud-transactor.tgz
|
||||
|
15
rush.json
15
rush.json
@ -2230,21 +2230,6 @@
|
||||
"packageName": "@hcengineering/cloud-transactor",
|
||||
"projectFolder": "workers/transactor",
|
||||
"shouldPublish": false
|
||||
},
|
||||
{
|
||||
"packageName": "@hcengineering/cloud-transactor-api",
|
||||
"projectFolder": "workers/transactor-api",
|
||||
"shouldPublish": false
|
||||
},
|
||||
{
|
||||
"packageName": "@hcengineering/cloud-transactor-api-demo",
|
||||
"projectFolder": "workers/transactor-api-demo",
|
||||
"shouldPublish": false
|
||||
},
|
||||
{
|
||||
"packageName": "@hcengineering/cloud-transactor-http-api",
|
||||
"projectFolder": "workers/transactor-http-api",
|
||||
"shouldPublish": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig"
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
|
||||
roots: ["./src"],
|
||||
coverageReporters: ["text-summary", "html"]
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
{
|
||||
"name": "@hcengineering/cloud-transactor-api-demo",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"template": "cloud",
|
||||
"scripts": {
|
||||
"deploy": "wrangler deploy",
|
||||
"dev": "wrangler dev --port 4050",
|
||||
"dev-local": "wrangler dev --port 4050 --local --upstream-protocol=http",
|
||||
"start": "wrangler dev --port 4050",
|
||||
"cf-typegen": "wrangler types",
|
||||
"build": "compile",
|
||||
"build:watch": "compile",
|
||||
"test": "jest --passWithNoTests --silent --forceExit",
|
||||
"format": "format src",
|
||||
"_phase:build": "compile transpile src",
|
||||
"_phase:test": "jest --passWithNoTests --silent --forceExit",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20241022.0",
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@types/jest": "^29.5.5",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint": "^8.54.0",
|
||||
"jest": "^29.7.0",
|
||||
"prettier": "^3.1.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"typescript": "^5.3.3",
|
||||
"wrangler": "^3.97.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/core": "^0.6.32",
|
||||
"@hcengineering/contact": "^0.6.24",
|
||||
"@hcengineering/cloud-transactor-api": "^0.6.0",
|
||||
"itty-router": "^5.0.18"
|
||||
}
|
||||
}
|
@ -1,166 +0,0 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import {
|
||||
type ConnectOptions,
|
||||
type TransactorRawApi,
|
||||
type TransactorService,
|
||||
createHttpClient,
|
||||
createRpcClient,
|
||||
getWorkspaceLogin,
|
||||
unpackModel
|
||||
} from '@hcengineering/cloud-transactor-api'
|
||||
import contact, { AvatarType, type Person } from '@hcengineering/contact'
|
||||
import core, { type AccountClient, type Ref, type TxCreateDoc, TxOperations, generateId } from '@hcengineering/core'
|
||||
import { Router, error, json } from 'itty-router'
|
||||
|
||||
async function callClient<T> (client: T, method: () => Promise<any>): Promise<Response> {
|
||||
try {
|
||||
return json(await method())
|
||||
} catch (error) {
|
||||
console.error({ error })
|
||||
throw error
|
||||
} finally {
|
||||
if (Symbol.dispose in (client as any)) {
|
||||
;(client as any)[Symbol.dispose]()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch (request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
||||
const transactorService = env.TRANSACTOR_SERVICE as any as TransactorService
|
||||
|
||||
function getConnectOpts (params: Record<string, any>): ConnectOptions {
|
||||
return {
|
||||
authOptions: {
|
||||
email: params.email,
|
||||
password: params.password,
|
||||
workspace: params.workspace
|
||||
},
|
||||
serverConfig: {
|
||||
ACCOUNTS_URL: env.ACCOUNTS_URL
|
||||
},
|
||||
workspaceId: params.workspace,
|
||||
loadModel: params.loadModel === true
|
||||
}
|
||||
}
|
||||
|
||||
async function rawClient (params: Record<string, any>): Promise<TransactorRawApi> {
|
||||
const email = params.email
|
||||
const password = params.password
|
||||
const workspace = params.workspace
|
||||
const info = await getWorkspaceLogin('', { email, password, workspace }, { ACCOUNTS_URL: env.ACCOUNTS_URL })
|
||||
return await transactorService.openRpc(info.token, params.workspace)
|
||||
}
|
||||
|
||||
async function rpcClient (params: Record<string, any>): Promise<AccountClient> {
|
||||
return await createRpcClient(transactorService, getConnectOpts(params))
|
||||
}
|
||||
|
||||
async function httpClient (params: Record<string, any>): Promise<AccountClient> {
|
||||
return await createHttpClient(env.HTTP_API_URL, getConnectOpts(params))
|
||||
}
|
||||
|
||||
const router = Router()
|
||||
router
|
||||
.get('/demo-find-raw/:email/:password/:workspace', async ({ params }) => {
|
||||
const client = await rawClient(params)
|
||||
return await callClient(client, async () => {
|
||||
return await client.findAll(contact.class.Person, {})
|
||||
})
|
||||
})
|
||||
.get('/demo-find-rpc/:email/:password/:workspace', async ({ params }) => {
|
||||
const client = await rpcClient(params)
|
||||
return await callClient(client, async () => {
|
||||
return await client.findAll(contact.class.Person, {})
|
||||
})
|
||||
})
|
||||
.get('/demo-find-http/:email/:password/:workspace', async ({ params }) => {
|
||||
const client = await httpClient(params)
|
||||
return await callClient(client, async () => {
|
||||
return await client.findAll(contact.class.Person, {})
|
||||
})
|
||||
})
|
||||
.get('/demo-get-model-raw/:email/:password/:workspace', async ({ params }) => {
|
||||
const client = await rawClient(params)
|
||||
return await callClient(client, async () => {
|
||||
return await unpackModel(await client.getModel())
|
||||
})
|
||||
})
|
||||
.get('/demo-get-model-rpc/:email/:password/:workspace', async ({ params }) => {
|
||||
const client = await rpcClient({ ...params, loadModel: true })
|
||||
return await callClient(client, async () => {
|
||||
return client.getModel()
|
||||
})
|
||||
})
|
||||
.get('/demo-get-model-http/:email/:password/:workspace', async ({ params }) => {
|
||||
const client = await httpClient({ ...params, loadModel: true })
|
||||
return await callClient(client, async () => {
|
||||
return client.getModel()
|
||||
})
|
||||
})
|
||||
.get('/demo-tx-raw/:email/:password/:workspace', async ({ params }) => {
|
||||
const client = await rawClient(params)
|
||||
return await callClient(client, async () => {
|
||||
const account = await client.getAccount()
|
||||
const id = generateId()
|
||||
const tx: TxCreateDoc<Person> = {
|
||||
_id: id as Ref<TxCreateDoc<Person>>,
|
||||
_class: core.class.TxCreateDoc,
|
||||
space: core.space.Tx,
|
||||
objectId: id as Ref<Person>,
|
||||
objectClass: contact.class.Person,
|
||||
objectSpace: account.space,
|
||||
modifiedOn: Date.now(),
|
||||
modifiedBy: account._id,
|
||||
createdBy: account._id,
|
||||
attributes: {
|
||||
name: 'Person ' + id,
|
||||
city: 'Unknown',
|
||||
avatarType: AvatarType.COLOR
|
||||
}
|
||||
}
|
||||
return await client.tx(tx)
|
||||
})
|
||||
})
|
||||
.get('/demo-tx-rpc/:email/:password/:workspace', async ({ params }) => {
|
||||
const client = await rpcClient({ ...params, loadModel: true })
|
||||
return await callClient(client, async () => {
|
||||
const account = await client.getAccount()
|
||||
const txops = new TxOperations(client, account._id)
|
||||
return await txops.createDoc(contact.class.Person, account.space, {
|
||||
name: 'Person ' + generateId(),
|
||||
city: 'Unknown',
|
||||
avatarType: AvatarType.COLOR
|
||||
})
|
||||
})
|
||||
})
|
||||
.get('/demo-tx-http/:email/:password/:workspace', async ({ params }) => {
|
||||
const client = await httpClient({ ...params, loadModel: true })
|
||||
return await callClient(client, async () => {
|
||||
const account = await client.getAccount()
|
||||
const txops = new TxOperations(client, account._id)
|
||||
return await txops.createDoc(contact.class.Person, account.space, {
|
||||
name: 'Person ' + generateId(),
|
||||
city: 'Unknown',
|
||||
avatarType: AvatarType.COLOR
|
||||
})
|
||||
})
|
||||
})
|
||||
.all('*', () => error(404))
|
||||
return await router.fetch(request).catch(error)
|
||||
}
|
||||
} satisfies ExportedHandler<Env>
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo",
|
||||
"types": ["@cloudflare/workers-types", "jest"]
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
// Generated by Wrangler by running `wrangler types`
|
||||
|
||||
interface Env {
|
||||
ACCOUNTS_URL: "http://127.0.0.1:3000";
|
||||
HTTP_API_URL: "http://127.0.0.1:4040";
|
||||
TRANSACTOR_SERVICE: Fetcher;
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
#:schema node_modules/wrangler/config-schema.json
|
||||
name = "cloud-transactor-api-demo"
|
||||
main = "src/index.ts"
|
||||
compatibility_date = "2024-09-23"
|
||||
compatibility_flags = ["nodejs_compat"]
|
||||
keep_vars = true
|
||||
|
||||
[vars]
|
||||
ACCOUNTS_URL = "http://127.0.0.1:3000"
|
||||
HTTP_API_URL = "http://127.0.0.1:4040"
|
||||
|
||||
[[services]]
|
||||
binding = "TRANSACTOR_SERVICE"
|
||||
service = "cloud-transactor"
|
||||
entrypoint = "TransactorRpc"
|
@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/node/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig",
|
||||
"rigProfile": "node"
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
|
||||
roots: ["./src"],
|
||||
coverageReporters: ["text-summary", "html"]
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
{
|
||||
"name": "@hcengineering/cloud-transactor-api",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"template": "node",
|
||||
"scripts": {
|
||||
"build": "compile",
|
||||
"build:watch": "compile",
|
||||
"test": "jest --passWithNoTests --silent --forceExit",
|
||||
"format": "format src",
|
||||
"_phase:build": "compile transpile src",
|
||||
"_phase:test": "jest --passWithNoTests --silent --forceExit",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@types/jest": "^29.5.5",
|
||||
"@types/node": "~20.11.16",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint": "^8.54.0",
|
||||
"jest": "^29.7.0",
|
||||
"prettier": "^3.1.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/core": "^0.6.32"
|
||||
}
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { concatLink } from '@hcengineering/core'
|
||||
|
||||
/**
|
||||
* Configuration options for password-based authentication
|
||||
* @public
|
||||
*/
|
||||
export interface PasswordAuthOptions {
|
||||
/** User's email address */
|
||||
email: string
|
||||
|
||||
/** User's password */
|
||||
password: string
|
||||
|
||||
/** Workspace name */
|
||||
workspace: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for token-based authentication
|
||||
* @public
|
||||
*/
|
||||
export interface TokenAuthOptions {
|
||||
/** Authentication token */
|
||||
token: string
|
||||
|
||||
/** Workspace name */
|
||||
workspace: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Union type representing all authentication options
|
||||
* Can be either password-based or token-based authentication
|
||||
* @public
|
||||
*/
|
||||
export type AuthOptions = PasswordAuthOptions | TokenAuthOptions
|
||||
|
||||
/** @public */
|
||||
export interface LoginInfo {
|
||||
token: string
|
||||
endpoint: string
|
||||
confirmed: boolean
|
||||
email: string
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface WorkspaceLoginInfo extends LoginInfo {
|
||||
workspace: string
|
||||
workspaceId: string
|
||||
}
|
||||
|
||||
export interface ServerConfig {
|
||||
ACCOUNTS_URL: string
|
||||
}
|
||||
|
||||
async function loadServerConfig (url: string): Promise<ServerConfig> {
|
||||
const configUrl = concatLink(url, '/config.json')
|
||||
const res = await fetch(configUrl)
|
||||
if (res.ok) {
|
||||
return (await res.json()) as ServerConfig
|
||||
}
|
||||
throw new Error('Failed to fetch config')
|
||||
}
|
||||
|
||||
export async function getWorkspaceLogin (
|
||||
configUrl: string,
|
||||
options: AuthOptions,
|
||||
serverConfig?: ServerConfig
|
||||
): Promise<WorkspaceLoginInfo> {
|
||||
serverConfig ??= await loadServerConfig(configUrl)
|
||||
|
||||
let token: string
|
||||
|
||||
if ('token' in options) {
|
||||
token = options.token
|
||||
} else {
|
||||
const { email, password, workspace } = options
|
||||
token = await login(serverConfig.ACCOUNTS_URL, email, password, workspace)
|
||||
}
|
||||
|
||||
if (token === undefined) {
|
||||
throw new Error('Login failed')
|
||||
}
|
||||
|
||||
const ws = await selectWorkspace(serverConfig.ACCOUNTS_URL, token, options.workspace)
|
||||
if (ws === undefined) {
|
||||
throw new Error('Workspace not found')
|
||||
}
|
||||
|
||||
return ws
|
||||
}
|
||||
|
||||
async function login (accountsUrl: string, user: string, password: string, workspace: string): Promise<string> {
|
||||
const response = await fetch(accountsUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
method: 'login',
|
||||
params: [user, password, workspace]
|
||||
})
|
||||
})
|
||||
|
||||
const result = await response.json()
|
||||
return result.result?.token
|
||||
}
|
||||
|
||||
async function selectWorkspace (accountsUrl: string, token: string, workspace: string): Promise<WorkspaceLoginInfo> {
|
||||
const response = await fetch(accountsUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: 'Bearer ' + token
|
||||
},
|
||||
body: JSON.stringify({
|
||||
method: 'selectWorkspace',
|
||||
params: [workspace, 'external']
|
||||
})
|
||||
})
|
||||
const result = await response.json()
|
||||
return result.result as WorkspaceLoginInfo
|
||||
}
|
@ -1,164 +0,0 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import {
|
||||
type Account,
|
||||
type AccountClient,
|
||||
type Class,
|
||||
type Doc,
|
||||
type DocumentQuery,
|
||||
type FindOptions,
|
||||
type FindResult,
|
||||
type ModelDb,
|
||||
type Tx,
|
||||
type TxResult,
|
||||
type Ref,
|
||||
type Hierarchy,
|
||||
type SearchQuery,
|
||||
type SearchOptions,
|
||||
type SearchResult,
|
||||
type WithLookup
|
||||
} from '@hcengineering/core'
|
||||
import { type ConnectOptions } from './types'
|
||||
import { getWorkspaceLogin } from './account'
|
||||
import { decodeModel } from './utils'
|
||||
|
||||
export async function createHttpClient (httpApiWorkerUrl: string, options: ConnectOptions): Promise<AccountClient> {
|
||||
let token = options.workspaceToken
|
||||
if (token === undefined) {
|
||||
if (options.authOptions === undefined) {
|
||||
throw new Error('Either workspaceToken or authOptions must be provided')
|
||||
}
|
||||
if (options.configUrl === '' && options.serverConfig === undefined) {
|
||||
throw new Error('Either configUrl or serverConfig must be provided')
|
||||
}
|
||||
const ws = await getWorkspaceLogin(options.configUrl ?? '', options.authOptions, options.serverConfig)
|
||||
token = ws.token
|
||||
}
|
||||
const client = new TransactorHttpClient(token, options.workspaceId ?? '', httpApiWorkerUrl)
|
||||
if (options.loadModel ?? false) {
|
||||
await client.loadModel()
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
class TransactorHttpClient implements AccountClient {
|
||||
private model: ModelDb | undefined
|
||||
private hierarchy: Hierarchy | undefined
|
||||
|
||||
constructor (
|
||||
private readonly token: string,
|
||||
private readonly workspaceId: string,
|
||||
private readonly httpApiWorkerUrl: string
|
||||
) {}
|
||||
|
||||
private url (method: string): string {
|
||||
return `${this.httpApiWorkerUrl}/${method}/${encodeURIComponent(this.workspaceId)}`
|
||||
}
|
||||
|
||||
async loadModel (): Promise<void> {
|
||||
const response = await fetch(this.url('model'), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/octet-stream',
|
||||
Authorization: 'Bearer ' + this.token
|
||||
}
|
||||
})
|
||||
const compressed = await (response as any).bytes()
|
||||
const { model, hierarchy } = await decodeModel(compressed)
|
||||
this.model = model
|
||||
this.hierarchy = hierarchy
|
||||
}
|
||||
|
||||
notify (...tx: Tx[]): void {
|
||||
// does nothing
|
||||
}
|
||||
|
||||
getHierarchy (): Hierarchy {
|
||||
if (this.hierarchy === undefined) {
|
||||
throw new Error('Hierarchy is not loaded, please use loadModel=true when initializing client')
|
||||
}
|
||||
return this.hierarchy
|
||||
}
|
||||
|
||||
getModel (): ModelDb {
|
||||
if (this.model === undefined) {
|
||||
throw new Error('Model is not loaded, please use loadModel=true when initializing client')
|
||||
}
|
||||
return this.model
|
||||
}
|
||||
|
||||
async findOne<T extends Doc>(
|
||||
_class: Ref<Class<T>>,
|
||||
query: DocumentQuery<T>,
|
||||
options?: FindOptions<T>
|
||||
): Promise<WithLookup<T> | undefined> {
|
||||
return (await this.findAll(_class, query, options)).shift()
|
||||
}
|
||||
|
||||
async close (): Promise<void> {
|
||||
// does nothing
|
||||
}
|
||||
|
||||
async findAll<T extends Doc>(
|
||||
_class: Ref<Class<T>>,
|
||||
query: DocumentQuery<T>,
|
||||
options?: FindOptions<T>
|
||||
): Promise<FindResult<T>> {
|
||||
const response = await fetch(this.url('find-all'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: 'Bearer ' + this.token
|
||||
},
|
||||
body: JSON.stringify({ _class, query, options })
|
||||
})
|
||||
return await response.json()
|
||||
}
|
||||
|
||||
async tx (tx: Tx): Promise<TxResult> {
|
||||
const response = await fetch(this.url('tx'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: 'Bearer ' + this.token
|
||||
},
|
||||
body: JSON.stringify(tx)
|
||||
})
|
||||
return await response.json()
|
||||
}
|
||||
|
||||
async searchFulltext (query: SearchQuery, options: SearchOptions): Promise<SearchResult> {
|
||||
// TODO
|
||||
const result: SearchResult = {
|
||||
docs: [],
|
||||
total: 0
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async getAccount (): Promise<Account> {
|
||||
const response = await fetch(this.url('account'), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: 'Bearer ' + this.token
|
||||
}
|
||||
})
|
||||
return await response.json()
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
export * from './http'
|
||||
export * from './account'
|
||||
export * from './rpc'
|
||||
export * from './types'
|
||||
export * from './utils'
|
@ -1,169 +0,0 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import {
|
||||
type Account,
|
||||
type AccountClient,
|
||||
type Class,
|
||||
type Doc,
|
||||
type DocumentQuery,
|
||||
type FindOptions,
|
||||
type FindResult,
|
||||
type Ref,
|
||||
type SearchOptions,
|
||||
type SearchQuery,
|
||||
type SearchResult,
|
||||
type Storage,
|
||||
type Tx,
|
||||
type TxResult,
|
||||
type WithLookup,
|
||||
type Hierarchy,
|
||||
type ModelDb
|
||||
} from '@hcengineering/core'
|
||||
import { decodeModel } from './utils'
|
||||
import { type ConnectOptions } from './types'
|
||||
import { getWorkspaceLogin } from './account'
|
||||
|
||||
export interface TransactorService {
|
||||
openRpc: (token: string, workspaceId: string) => Promise<TransactorRawApi>
|
||||
}
|
||||
|
||||
export interface TransactorRawApi extends Storage {
|
||||
getModel: () => Promise<Buffer>
|
||||
getAccount: () => Promise<Account>
|
||||
}
|
||||
|
||||
export async function createRpcClient (
|
||||
transactorService: TransactorService,
|
||||
options: ConnectOptions
|
||||
): Promise<AccountClient> {
|
||||
let token = options.workspaceToken
|
||||
if (token === undefined) {
|
||||
if (options.authOptions === undefined) {
|
||||
throw new Error('Either workspaceToken or authOptions must be provided')
|
||||
}
|
||||
if (options.configUrl === '' && options.serverConfig === undefined) {
|
||||
throw new Error('Either configUrl or serverConfig must be provided')
|
||||
}
|
||||
const ws = await getWorkspaceLogin(options.configUrl ?? '', options.authOptions, options.serverConfig)
|
||||
token = ws.token
|
||||
}
|
||||
const client = new TransactorRpcClient(token, options.workspaceId ?? '', transactorService)
|
||||
if (options.loadModel === true) {
|
||||
await client.loadModel()
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
class TransactorRpcClient implements AccountClient {
|
||||
private disposed = false
|
||||
private model: ModelDb | undefined
|
||||
private hierarchy: Hierarchy | undefined
|
||||
private transactorRpcStub: TransactorRawApi | undefined
|
||||
private account: Account | undefined
|
||||
|
||||
constructor (
|
||||
private readonly token: string,
|
||||
private readonly workspaceId: string,
|
||||
private readonly transactorService: TransactorService
|
||||
) {}
|
||||
|
||||
private async transactorStub (): Promise<TransactorRawApi> {
|
||||
if (this.transactorRpcStub === undefined) {
|
||||
this.transactorRpcStub = await this.transactorService.openRpc(this.token, this.workspaceId)
|
||||
}
|
||||
return this.transactorRpcStub
|
||||
}
|
||||
|
||||
async loadModel (): Promise<void> {
|
||||
const stub = await this.transactorStub()
|
||||
const compressed = await stub.getModel()
|
||||
const { model, hierarchy } = await decodeModel(compressed)
|
||||
this.model = model
|
||||
this.hierarchy = hierarchy
|
||||
}
|
||||
|
||||
notify (...tx: Tx[]): void {
|
||||
// does nothing
|
||||
}
|
||||
|
||||
getHierarchy (): Hierarchy {
|
||||
if (this.hierarchy === undefined) {
|
||||
throw new Error('Hierarchy is not loaded, please use loadModel=true when initializing client')
|
||||
}
|
||||
return this.hierarchy
|
||||
}
|
||||
|
||||
getModel (): ModelDb {
|
||||
if (this.model === undefined) {
|
||||
throw new Error('Model is not loaded, please use loadModel=true when initializing client')
|
||||
}
|
||||
return this.model
|
||||
}
|
||||
|
||||
async findOne<T extends Doc>(
|
||||
_class: Ref<Class<T>>,
|
||||
query: DocumentQuery<T>,
|
||||
options?: FindOptions<T>
|
||||
): Promise<WithLookup<T> | undefined> {
|
||||
return (await this.findAll(_class, query, options)).shift()
|
||||
}
|
||||
|
||||
async close (): Promise<void> {
|
||||
this.dispose()
|
||||
}
|
||||
|
||||
async findAll<T extends Doc>(
|
||||
_class: Ref<Class<T>>,
|
||||
query: DocumentQuery<T>,
|
||||
options?: FindOptions<T>
|
||||
): Promise<FindResult<T>> {
|
||||
const stub = await this.transactorStub()
|
||||
return await stub.findAll(_class, query, options)
|
||||
}
|
||||
|
||||
async tx (tx: Tx): Promise<TxResult> {
|
||||
const stub = await this.transactorStub()
|
||||
return await stub.tx(tx)
|
||||
}
|
||||
|
||||
async searchFulltext (query: SearchQuery, options: SearchOptions): Promise<SearchResult> {
|
||||
// TODO
|
||||
const result: SearchResult = {
|
||||
docs: [],
|
||||
total: 0
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async getAccount (): Promise<Account> {
|
||||
if (this.account === undefined) {
|
||||
const stub = await this.transactorStub()
|
||||
this.account = await stub.getAccount()
|
||||
}
|
||||
return this.account
|
||||
}
|
||||
|
||||
private dispose (): void {
|
||||
if (!this.disposed && this.transactorRpcStub !== undefined && Symbol.dispose in this.transactorRpcStub) {
|
||||
this.disposed = true
|
||||
;(this.transactorRpcStub as any)[Symbol.dispose]()
|
||||
}
|
||||
}
|
||||
|
||||
[Symbol.dispose] (): void {
|
||||
this.dispose()
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { type AuthOptions, type ServerConfig } from './account'
|
||||
|
||||
export interface ConnectOptions {
|
||||
workspaceToken?: string
|
||||
|
||||
workspaceId?: string
|
||||
|
||||
loadModel?: boolean
|
||||
|
||||
authOptions: AuthOptions
|
||||
|
||||
configUrl?: string
|
||||
|
||||
serverConfig?: ServerConfig
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { gunzip } from 'zlib'
|
||||
import { promisify } from 'util'
|
||||
import {
|
||||
Hierarchy,
|
||||
ModelDb,
|
||||
type MeasureContext,
|
||||
type MeasureLogger,
|
||||
type ParamsType,
|
||||
type Tx
|
||||
} from '@hcengineering/core'
|
||||
|
||||
export async function unpackModel (compressed: Buffer | Uint8Array): Promise<Tx[]> {
|
||||
const ungzipAsync = promisify(gunzip)
|
||||
const buffer = await ungzipAsync(new Uint8Array(compressed))
|
||||
const decoder = new TextDecoder()
|
||||
const jsonString = decoder.decode(buffer)
|
||||
const model = JSON.parse(jsonString) as Tx[]
|
||||
return model
|
||||
}
|
||||
|
||||
export async function decodeModel (compressed: Buffer | Uint8Array): Promise<{ model: ModelDb, hierarchy: Hierarchy }> {
|
||||
const txes = await unpackModel(compressed)
|
||||
const hierarchy = new Hierarchy()
|
||||
for (const tx of txes) {
|
||||
hierarchy.tx(tx)
|
||||
}
|
||||
const model = new ModelDb(hierarchy)
|
||||
const ctx = createDummyMeasureContext()
|
||||
model.addTxes(ctx, txes, false)
|
||||
return { model, hierarchy }
|
||||
}
|
||||
|
||||
function createConsoleLogger (): MeasureLogger {
|
||||
return {
|
||||
info: (message: string, obj?: Record<string, any>) => {
|
||||
console.info(message, obj)
|
||||
},
|
||||
error: (message: string, obj?: Record<string, any>) => {
|
||||
console.error(message, obj)
|
||||
},
|
||||
warn: (message: string, obj?: Record<string, any>) => {
|
||||
console.warn(message, obj)
|
||||
},
|
||||
logOperation: (operation: string, time: number, params: ParamsType) => {
|
||||
console.info(operation, { time, ...params })
|
||||
},
|
||||
close: async () => {}
|
||||
}
|
||||
}
|
||||
|
||||
function createDummyMeasureContext (): MeasureContext {
|
||||
const ctx: MeasureContext = {
|
||||
id: '',
|
||||
contextData: {},
|
||||
newChild: (name, params, fullParams, logger) => {
|
||||
return ctx
|
||||
},
|
||||
with: <T>(name: any, params: any, op: any, fullParams: any) => {
|
||||
return Promise.resolve() as Promise<T>
|
||||
},
|
||||
withSync: (name, params, op, fullParams) => {
|
||||
return op(ctx)
|
||||
},
|
||||
withLog: <T>(name: any, params: any, op: any, fullParams: any) => {
|
||||
return Promise.resolve() as Promise<T>
|
||||
},
|
||||
logger: createConsoleLogger(),
|
||||
measure: (name, value, override) => {},
|
||||
error: (message, args) => {
|
||||
console.error(message, args)
|
||||
},
|
||||
info: (message, args) => {
|
||||
console.info(message, args)
|
||||
},
|
||||
warn: (message, args) => {
|
||||
console.warn(message, args)
|
||||
},
|
||||
end: () => {}
|
||||
}
|
||||
return ctx
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/node/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo"
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig"
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
|
||||
roots: ["./src"],
|
||||
coverageReporters: ["text-summary", "html"]
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
{
|
||||
"name": "@hcengineering/cloud-transactor-http-api",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"template": "cloud",
|
||||
"scripts": {
|
||||
"deploy": "wrangler deploy",
|
||||
"dev": "wrangler dev --port 4040",
|
||||
"dev-local": "wrangler dev --port 4040 --local --upstream-protocol=http",
|
||||
"start": "wrangler dev --port 4040",
|
||||
"cf-typegen": "wrangler types",
|
||||
"build": "compile",
|
||||
"build:watch": "compile",
|
||||
"test": "jest --passWithNoTests --silent --forceExit",
|
||||
"format": "format src",
|
||||
"_phase:build": "compile transpile src",
|
||||
"_phase:test": "jest --passWithNoTests --silent --forceExit",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20241022.0",
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@types/jest": "^29.5.5",
|
||||
"@types/node": "~20.11.16",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint": "^8.54.0",
|
||||
"jest": "^29.7.0",
|
||||
"prettier": "^3.1.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"typescript": "^5.3.3",
|
||||
"wrangler": "^3.97.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/analytics": "^0.6.0",
|
||||
"@hcengineering/core": "^0.6.32",
|
||||
"itty-router": "^5.0.18"
|
||||
}
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { type Account, type Storage } from '@hcengineering/core'
|
||||
import { WorkerEntrypoint } from 'cloudflare:workers'
|
||||
import { type IRequest, type IRequestStrict, type RequestHandler, Router, cors, error, html, json } from 'itty-router'
|
||||
|
||||
const { preflight, corsify } = cors({
|
||||
maxAge: 86400
|
||||
})
|
||||
|
||||
const router = Router<IRequestStrict, [Env, ExecutionContext], Response>({
|
||||
before: [preflight],
|
||||
finally: [corsify]
|
||||
})
|
||||
|
||||
type WorkspaceRequest = {
|
||||
workspaceId: string
|
||||
token: string
|
||||
} & IRequestStrict
|
||||
|
||||
interface TransactorService {
|
||||
openRpc: (token: string, workspaceId: string) => Promise<TransactorRawApi>
|
||||
}
|
||||
|
||||
interface TransactorRawApi extends Storage {
|
||||
getModel: () => Promise<Buffer>
|
||||
getAccount: () => Promise<Account>
|
||||
}
|
||||
|
||||
const withWorkspace: RequestHandler<WorkspaceRequest> = (request: WorkspaceRequest) => {
|
||||
if (request.params.workspaceId === undefined || request.params.workspaceId === '') {
|
||||
return error(400, 'Missing workspace')
|
||||
}
|
||||
const token = request.headers.get('Authorization')
|
||||
if (token === null) {
|
||||
return error(401, 'Missing Authorization header')
|
||||
}
|
||||
request.workspaceId = decodeURIComponent(request.params.workspaceId)
|
||||
request.token = token.split(' ')[1]
|
||||
}
|
||||
|
||||
async function callTransactor (
|
||||
request: WorkspaceRequest,
|
||||
env: Env,
|
||||
method: (transactorRpc: TransactorRawApi) => Promise<Response>
|
||||
): Promise<Response> {
|
||||
const transactorService = env.TRANSACTOR_SERVICE as any as TransactorService
|
||||
const transactorRpc = await transactorService.openRpc(request.token, request.workspaceId)
|
||||
try {
|
||||
return await method(transactorRpc)
|
||||
} finally {
|
||||
if (Symbol.dispose in transactorRpc) {
|
||||
;(transactorRpc as any)[Symbol.dispose]()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
router.post('/find-all/:workspaceId', withWorkspace, async (request: WorkspaceRequest, env: Env) => {
|
||||
return await callTransactor(request, env, async (transactorRpc) => {
|
||||
if (request.body === null) {
|
||||
return error(400, 'Missing body')
|
||||
}
|
||||
const { _class, query, options }: any = await request.json()
|
||||
const result = await transactorRpc.findAll(_class, query, options)
|
||||
return json(result)
|
||||
})
|
||||
})
|
||||
|
||||
router.post('/tx/:workspaceId', withWorkspace, async (request: WorkspaceRequest, env: Env) => {
|
||||
return await callTransactor(request, env, async (transactorRpc) => {
|
||||
if (request.body === null) {
|
||||
return error(400, 'Missing body')
|
||||
}
|
||||
const tx: any = await request.json()
|
||||
const result = await transactorRpc.tx(tx)
|
||||
return json(result)
|
||||
})
|
||||
})
|
||||
|
||||
router.get('/model/:workspaceId', withWorkspace, async (request: WorkspaceRequest, env: Env) => {
|
||||
return await callTransactor(request, env, async (transactorRpc) => {
|
||||
const model = await transactorRpc.getModel()
|
||||
return new Response(model, {
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Content-Length': model.length.toString()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
router.get('/account/:workspaceId', withWorkspace, async (request: WorkspaceRequest, env: Env) => {
|
||||
return await callTransactor(request, env, async (transactorRpc) => {
|
||||
const account = await transactorRpc.getAccount()
|
||||
return json(account)
|
||||
})
|
||||
})
|
||||
|
||||
router.all('/', () =>
|
||||
html(
|
||||
`Huly® Transactor API™ <a href="https://huly.io">https://huly.io</a>
|
||||
© 2024 <a href="https://hulylabs.com">Huly Labs</a>`
|
||||
)
|
||||
)
|
||||
|
||||
router.all('*', () => error(404))
|
||||
|
||||
export default class TransactorHttpApiWorker extends WorkerEntrypoint {
|
||||
async fetch (request: IRequest): Promise<Response> {
|
||||
return await router.fetch(request, this.env, this.ctx).catch(error)
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo",
|
||||
"types": ["@cloudflare/workers-types", "node", "jest"]
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
// Generated by Wrangler by running `wrangler types`
|
||||
|
||||
interface Env {
|
||||
TRANSACTOR_SERVICE: Fetcher;
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
#:schema node_modules/wrangler/config-schema.json
|
||||
name = "cloud-transactor-http-api"
|
||||
main = "src/index.ts"
|
||||
compatibility_date = "2024-09-23"
|
||||
compatibility_flags = ["nodejs_compat"]
|
||||
keep_vars = true
|
||||
|
||||
[[services]]
|
||||
binding = "TRANSACTOR_SERVICE"
|
||||
service = "cloud-transactor"
|
||||
entrypoint = "TransactorRpc"
|
Loading…
Reference in New Issue
Block a user