@cuboapp/api-backend 1.0.9 → 1.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -7
- package/src/constants/index.ts +2 -1
- package/src/crdt/index.ts +418 -0
- package/src/crdt/types.ts +34 -0
- package/src/helpers/convert.ts +2 -1
- package/src/helpers/data.ts +1 -0
- package/src/helpers/index.ts +38 -71
- package/src/index.ts +100 -60
- package/src/types/augmentation.ts +7 -0
- package/src/types/db.ts +4 -0
- package/src/types/index.ts +28 -0
- package/.vscode/settings.json +0 -18
- package/package-lock.json +0 -512
package/src/index.ts
CHANGED
|
@@ -1,57 +1,49 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { CuboApiEntitiesMap
|
|
1
|
+
import { QueryTypes } from '@cuboapp/database'
|
|
2
|
+
import { CuboApiEntitiesMap } from '@cuboapp/types'
|
|
3
3
|
|
|
4
|
+
import { CuboCrdt } from './crdt'
|
|
4
5
|
import { ApiHelpers } from './helpers'
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
export * from './types'
|
|
8
|
-
export * from './utils'
|
|
9
|
-
|
|
10
|
-
export type CuboBackendApiOptions<T extends CuboApiEntitiesMap<T>> = {
|
|
11
|
-
api: {
|
|
12
|
-
base_url: string
|
|
13
|
-
token: string
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
db: {
|
|
17
|
-
connection?: Database
|
|
18
|
-
options?: Options
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
entities: CuboEntity[]
|
|
22
|
-
|
|
23
|
-
auth?: {
|
|
24
|
-
base_url?: string
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
augmentations?: Partial<CuboCrudAugmentationsStore<T>>
|
|
28
|
-
}
|
|
6
|
+
import { CuboBackendApiAuth, CuboBackendApiOptions, CuboCrudGetManyResponse, CuboCrudMethodOptions, CuboCrudRequest } from './types'
|
|
29
7
|
|
|
30
8
|
export class CuboBackendApi<T extends CuboApiEntitiesMap<T>> {
|
|
31
|
-
constructor(public options: CuboBackendApiOptions<T>) {
|
|
9
|
+
constructor(public options: CuboBackendApiOptions<T>) {
|
|
10
|
+
if (options.crdt) {
|
|
11
|
+
this.crdt = new CuboCrdt<T>(this)
|
|
12
|
+
this.crdt.listen()
|
|
13
|
+
}
|
|
14
|
+
}
|
|
32
15
|
|
|
16
|
+
public auth: CuboBackendApiAuth
|
|
17
|
+
public crdt?: CuboCrdt<T>
|
|
33
18
|
public helpers = new ApiHelpers<T>(this)
|
|
34
19
|
|
|
35
20
|
async init() {
|
|
36
|
-
|
|
21
|
+
try {
|
|
22
|
+
this.auth = await this.request<{ variables: Record<string, any> }>('/auth/me?with=variables')
|
|
37
23
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
24
|
+
if (!this.auth) {
|
|
25
|
+
throw { code: 403, text: 'Авторизация Cubo-Backend не пройдена' }
|
|
26
|
+
}
|
|
42
27
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
28
|
+
if (this.auth.variables.db !== undefined) {
|
|
29
|
+
this.options.db = this.options.db || {}
|
|
46
30
|
|
|
47
|
-
|
|
48
|
-
|
|
31
|
+
this.options.db.options = {
|
|
32
|
+
...(this.options.db.options || {}),
|
|
33
|
+
...(this.auth.variables.db || {})
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
} catch (e) {
|
|
37
|
+
console.error('[API-BACKEND] startup error', e)
|
|
38
|
+
}
|
|
49
39
|
}
|
|
50
40
|
|
|
51
|
-
|
|
41
|
+
public async request<T>(url: string, opts?: RequestInit & { debug?: boolean; withoutContentType?: boolean }) {
|
|
42
|
+
const baseUrl = this.options?.api?.base_url || 'https://api.cubo.sh'
|
|
43
|
+
|
|
52
44
|
const headers: Record<string, any> = {
|
|
53
45
|
...(opts?.headers || {}),
|
|
54
|
-
|
|
46
|
+
...(this.options.api?.headers || {})
|
|
55
47
|
}
|
|
56
48
|
|
|
57
49
|
if (!headers['Content-Type'] && !opts?.withoutContentType) {
|
|
@@ -59,33 +51,57 @@ export class CuboBackendApi<T extends CuboApiEntitiesMap<T>> {
|
|
|
59
51
|
headers['Content-Type'] = 'application/json'
|
|
60
52
|
}
|
|
61
53
|
|
|
62
|
-
const
|
|
54
|
+
const requestUrl = baseUrl.replace(/\/$/, '') + '/' + url.replace(/^\//, '')
|
|
55
|
+
const requestOpts = {
|
|
63
56
|
...(opts || {}),
|
|
64
57
|
headers
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if ([201, 200].includes(response.status)) {
|
|
68
|
-
try {
|
|
69
|
-
const json = await response.json()
|
|
58
|
+
}
|
|
70
59
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const text = await response.text()
|
|
60
|
+
try {
|
|
61
|
+
const response = await fetch(requestUrl, requestOpts)
|
|
74
62
|
|
|
63
|
+
if ([201, 200].includes(response.status)) {
|
|
75
64
|
try {
|
|
76
|
-
const json =
|
|
65
|
+
const json = await response.json()
|
|
77
66
|
|
|
78
67
|
return json as Promise<T>
|
|
79
|
-
} catch {
|
|
80
|
-
|
|
68
|
+
} catch (e) {
|
|
69
|
+
const text = await response.text()
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const json = text ? JSON.parse(text) : null
|
|
73
|
+
|
|
74
|
+
return json as Promise<T>
|
|
75
|
+
} catch {
|
|
76
|
+
throw { status: 500, error: 'Invalid response', text }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
throw {
|
|
81
|
+
status: response.status,
|
|
82
|
+
error: response.statusText,
|
|
83
|
+
text: await response.text()
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
} catch (e) {
|
|
87
|
+
if (opts?.debug) {
|
|
88
|
+
console.log(
|
|
89
|
+
'[API BACKEND]',
|
|
90
|
+
JSON.stringify(
|
|
91
|
+
{
|
|
92
|
+
request: {
|
|
93
|
+
url: requestUrl,
|
|
94
|
+
opts: requestOpts
|
|
95
|
+
},
|
|
96
|
+
error: e
|
|
97
|
+
},
|
|
98
|
+
null,
|
|
99
|
+
2
|
|
100
|
+
)
|
|
101
|
+
)
|
|
88
102
|
}
|
|
103
|
+
|
|
104
|
+
throw e
|
|
89
105
|
}
|
|
90
106
|
}
|
|
91
107
|
|
|
@@ -223,14 +239,27 @@ export class CuboBackendApi<T extends CuboApiEntitiesMap<T>> {
|
|
|
223
239
|
throw { status: 400, text: 'Unable to create entity' }
|
|
224
240
|
}
|
|
225
241
|
|
|
226
|
-
|
|
242
|
+
let response = await this.getOne<K>(entityAlias, { query: { id: created_id } }, opts)
|
|
227
243
|
|
|
228
244
|
if (!response) {
|
|
229
245
|
throw { status: 400, text: 'Unable to find entity after create' }
|
|
230
246
|
}
|
|
231
247
|
|
|
232
248
|
if (augmentation?.afterCreate && !opts?.excludeHooks?.includes('afterCreate')) {
|
|
233
|
-
|
|
249
|
+
response = await augmentation.afterCreate(response, req, opts)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (opts.crdt !== false && this.crdt.isCrdtSupported(entity)) {
|
|
253
|
+
const state = await this.crdt.createState({ entityAlias, id: created_id, entity }, response)
|
|
254
|
+
|
|
255
|
+
const fieldKey = entity.fields.find((f) => f.type === 'binary').alias
|
|
256
|
+
|
|
257
|
+
const db = await this.helpers.getConnection('read')
|
|
258
|
+
await db.update(entity!.alias, 'id=:id', { id: created_id }, { [fieldKey]: state }, opts)
|
|
259
|
+
|
|
260
|
+
if (augmentation?.afterCrdtCreate && !opts?.excludeHooks?.includes('afterCrdtCreate')) {
|
|
261
|
+
augmentation.afterCrdtCreate(response, req, opts)
|
|
262
|
+
}
|
|
234
263
|
}
|
|
235
264
|
|
|
236
265
|
return response
|
|
@@ -263,13 +292,22 @@ export class CuboBackendApi<T extends CuboApiEntitiesMap<T>> {
|
|
|
263
292
|
const db = await this.helpers.getConnection('write')
|
|
264
293
|
await db.update(entity.alias, 'id = :id', { id: item.id }, dto, { transaction: opts?.transaction, log: opts?.log })
|
|
265
294
|
|
|
266
|
-
|
|
295
|
+
let response = await this.getOne<K>(entityAlias, req, opts)
|
|
267
296
|
if (!response) {
|
|
268
297
|
throw { status: 400, text: 'Unable to find entity after update' }
|
|
269
298
|
}
|
|
270
299
|
|
|
271
300
|
if (augmentation?.afterUpdate && !opts?.excludeHooks?.includes('afterUpdate')) {
|
|
272
|
-
|
|
301
|
+
response = await augmentation.afterUpdate(response, req, opts)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (opts.crdt !== false && this.crdt.isCrdtSupported(entity)) {
|
|
305
|
+
const context = { auth: opts?.extra?.auth }
|
|
306
|
+
await this.crdt.updateDocument({ entityAlias, id: item.id, entity }, response, context)
|
|
307
|
+
|
|
308
|
+
if (augmentation?.afterCrdtUpdate && !opts?.excludeHooks?.includes('afterCrdtUpdate')) {
|
|
309
|
+
augmentation.afterCrdtUpdate(response, req, opts)
|
|
310
|
+
}
|
|
273
311
|
}
|
|
274
312
|
|
|
275
313
|
return response
|
|
@@ -309,4 +347,6 @@ export class CuboBackendApi<T extends CuboApiEntitiesMap<T>> {
|
|
|
309
347
|
}
|
|
310
348
|
}
|
|
311
349
|
|
|
350
|
+
export * from './crdt'
|
|
312
351
|
export * from './helpers'
|
|
352
|
+
export * from './types'
|
|
@@ -2,6 +2,7 @@ import { CuboApiEntitiesMap } from '@cuboapp/types'
|
|
|
2
2
|
|
|
3
3
|
import { CuboCrudGetManyResponse, CuboCrudRequest } from './basic'
|
|
4
4
|
import { CuboCrudMethodOptions, CuboCrudQueryOptions } from './db'
|
|
5
|
+
import { onChangePayload, onLoadDocumentPayload, onStoreDocumentPayload } from '@hocuspocus/server'
|
|
5
6
|
|
|
6
7
|
export type CuboCrudAugmentationsStore<T extends CuboApiEntitiesMap<T>> = {
|
|
7
8
|
[K in Extract<keyof T, string>]?: CuboCrudAugmentationInstance<T[K]>
|
|
@@ -20,10 +21,16 @@ export interface CuboCrudAugmentationInstance<T = unknown> {
|
|
|
20
21
|
|
|
21
22
|
beforeCreate?: (req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<CuboCrudQueryOptions>
|
|
22
23
|
afterCreate?: (result: T, req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<T>
|
|
24
|
+
afterCrdtCreate?: (result: T, req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<void>
|
|
23
25
|
|
|
24
26
|
beforeUpdate?: (req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<CuboCrudQueryOptions>
|
|
25
27
|
afterUpdate?: (result: T, req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<T>
|
|
28
|
+
afterCrdtUpdate?: (result: T, req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<void>
|
|
26
29
|
|
|
27
30
|
beforeDelete?: (req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<CuboCrudQueryOptions>
|
|
28
31
|
afterDelete?: (result: T, req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<boolean>
|
|
32
|
+
|
|
33
|
+
onLoadDocument?: (data: onLoadDocumentPayload) => Promise<void>
|
|
34
|
+
onChangeDocument?: (data: onChangePayload) => Promise<void>
|
|
35
|
+
onStoreDocument?: (data: onStoreDocumentPayload) => Promise<void>
|
|
29
36
|
}
|
package/src/types/db.ts
CHANGED
|
@@ -27,9 +27,13 @@ export type CuboCrudMethodOptions<E extends object = {}> = {
|
|
|
27
27
|
| 'afterGetMany'
|
|
28
28
|
| 'afterGetOne'
|
|
29
29
|
| 'beforeUpdate'
|
|
30
|
+
| 'afterCrdtCreate'
|
|
31
|
+
| 'afterCrdtUpdate'
|
|
30
32
|
)[]
|
|
31
33
|
|
|
32
34
|
log?: boolean
|
|
35
|
+
replace?: boolean
|
|
36
|
+
crdt?: boolean
|
|
33
37
|
|
|
34
38
|
queryOptions?: CuboCrudQueryOptions
|
|
35
39
|
} & E
|
package/src/types/index.ts
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
import { Database, DatabaseOptions, Options } from '@cuboapp/database'
|
|
2
|
+
import { CuboApiEntitiesMap, CuboAuthTokenType, CuboEntity } from '@cuboapp/types'
|
|
3
|
+
|
|
4
|
+
import { CuboCrdtConfiguration } from '../crdt'
|
|
5
|
+
|
|
6
|
+
import { CuboCrudAugmentationsStore } from './augmentation'
|
|
7
|
+
|
|
1
8
|
export * from './augmentation'
|
|
2
9
|
export * from './basic'
|
|
3
10
|
export * from './db'
|
|
11
|
+
|
|
12
|
+
export type CuboBackendApiOptions<T extends CuboApiEntitiesMap<T>> = {
|
|
13
|
+
api: {
|
|
14
|
+
base_url?: string
|
|
15
|
+
headers: Partial<Record<`cubo-${CuboAuthTokenType}`, string>> & Record<string, any>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
entities?: CuboEntity[]
|
|
19
|
+
|
|
20
|
+
db?: {
|
|
21
|
+
connection?: Database
|
|
22
|
+
options?: Options & DatabaseOptions
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
crdt?: CuboCrdtConfiguration<T>
|
|
26
|
+
augmentations?: Partial<CuboCrudAugmentationsStore<T>>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type CuboBackendApiAuth = {
|
|
30
|
+
variables: Record<string, any>
|
|
31
|
+
}
|
package/.vscode/settings.json
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"editor.minimap.enabled": false,
|
|
3
|
-
"editor.formatOnSave": true,
|
|
4
|
-
"editor.codeActionsOnSave": {
|
|
5
|
-
"source.fixAll.eslint": "explicit",
|
|
6
|
-
"source.fixAll.stylelint": "explicit",
|
|
7
|
-
"source.organizeImports": "explicit",
|
|
8
|
-
"source.organizeExports": "explicit"
|
|
9
|
-
},
|
|
10
|
-
"emmet.includeLanguages": {
|
|
11
|
-
"javascriptreact": "html",
|
|
12
|
-
"typescriptreact": "html"
|
|
13
|
-
},
|
|
14
|
-
"files.exclude": {
|
|
15
|
-
"node_modules/": true
|
|
16
|
-
},
|
|
17
|
-
"typescript.tsdk": "node_modules/typescript/lib"
|
|
18
|
-
}
|