@cuboapp/api-backend 1.0.0
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/.prettierrc +8 -0
- package/package.json +25 -0
- package/pnpm-lock.yaml +378 -0
- package/src/constants/backend.ts +74 -0
- package/src/constants/base.ts +11 -0
- package/src/constants/index.ts +2 -0
- package/src/helpers/convert.ts +135 -0
- package/src/helpers/data.ts +147 -0
- package/src/helpers/entities.ts +30 -0
- package/src/helpers/index.ts +381 -0
- package/src/helpers/query.ts +145 -0
- package/src/helpers/users.ts +38 -0
- package/src/helpers/withes.ts +292 -0
- package/src/index.ts +367 -0
- package/src/types/augmentation.ts +29 -0
- package/src/types/basic.ts +59 -0
- package/src/types/db.ts +24 -0
- package/src/types/index.ts +3 -0
- package/src/utils/index.ts +128 -0
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { CuboApiEntitiesMap } from '@cuboapp/types'
|
|
2
|
+
|
|
3
|
+
import { CuboCrudGetManyResponse, CuboCrudRequest } from './basic'
|
|
4
|
+
import { CuboCrudMethodOptions, CuboCrudQueryOptions } from './db'
|
|
5
|
+
|
|
6
|
+
export type CuboCrudAugmentationsStore<T extends CuboApiEntitiesMap<T>> = {
|
|
7
|
+
[K in Extract<keyof T, string>]?: CuboCrudAugmentationInstance<T[K]>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface CuboCrudAugmentationInstance<T = unknown> {
|
|
11
|
+
beforeGetMany?: (req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<CuboCrudQueryOptions>
|
|
12
|
+
afterGetMany?: (
|
|
13
|
+
result: CuboCrudGetManyResponse<T>,
|
|
14
|
+
req: CuboCrudRequest,
|
|
15
|
+
opts?: CuboCrudMethodOptions
|
|
16
|
+
) => Promise<CuboCrudGetManyResponse<T>>
|
|
17
|
+
|
|
18
|
+
beforeGetOne?: (req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<CuboCrudQueryOptions>
|
|
19
|
+
afterGetOne?: (result: T, req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<T | undefined>
|
|
20
|
+
|
|
21
|
+
beforeCreate?: (req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<CuboCrudQueryOptions>
|
|
22
|
+
afterCreate?: (result: T, req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<T>
|
|
23
|
+
|
|
24
|
+
beforeUpdate?: (req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<CuboCrudQueryOptions>
|
|
25
|
+
afterUpdate?: (result: T, req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<T>
|
|
26
|
+
|
|
27
|
+
beforeDelete?: (req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<CuboCrudQueryOptions>
|
|
28
|
+
afterDelete?: (result: T, req: CuboCrudRequest, opts?: CuboCrudMethodOptions) => Promise<boolean>
|
|
29
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { CuboEntity, CuboEntityField } from '@cuboapp/types'
|
|
2
|
+
import { type Transaction } from 'sequelize'
|
|
3
|
+
|
|
4
|
+
export type CuboCrudWith = {
|
|
5
|
+
entity: CuboEntity | { id: undefined; alias: string; fields: CuboEntityField[] }
|
|
6
|
+
key: string
|
|
7
|
+
full_key: string
|
|
8
|
+
table_name: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type CuboCrudQueryDefaults = {
|
|
12
|
+
query?: string
|
|
13
|
+
sort?: string
|
|
14
|
+
limit?: number
|
|
15
|
+
page?: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type CuboCrudRequest = {
|
|
19
|
+
headers?: Record<string, any>
|
|
20
|
+
query?: Record<string, any>
|
|
21
|
+
params?: Record<string, any>
|
|
22
|
+
body?: any
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type CuboCrudOptions = {
|
|
26
|
+
user_id?: number
|
|
27
|
+
transaction?: Transaction
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type CuboCrudDto = {
|
|
31
|
+
entity: CuboEntity
|
|
32
|
+
request: CuboCrudRequest
|
|
33
|
+
options: CuboCrudOptions
|
|
34
|
+
state: any
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type CuboCrudFindQuery = {
|
|
38
|
+
selects: string[]
|
|
39
|
+
conditions: string[]
|
|
40
|
+
replacements: Record<string, any>
|
|
41
|
+
sorts: string[]
|
|
42
|
+
offset: number
|
|
43
|
+
limit: number
|
|
44
|
+
|
|
45
|
+
joins: string[]
|
|
46
|
+
withes: CuboCrudWith[]
|
|
47
|
+
|
|
48
|
+
withDeleted?: boolean
|
|
49
|
+
is_count: boolean
|
|
50
|
+
sql: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type CuboCrudGetManyResponse<T, E extends object = object> = {
|
|
54
|
+
rows: T[]
|
|
55
|
+
totals: {
|
|
56
|
+
count: number
|
|
57
|
+
}
|
|
58
|
+
extra?: E
|
|
59
|
+
}
|
package/src/types/db.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Transaction } from 'sequelize'
|
|
2
|
+
|
|
3
|
+
export type CuboCrudQueryOptions = {
|
|
4
|
+
selects?: string[]
|
|
5
|
+
conditions?: string[]
|
|
6
|
+
replacements?: Record<string, any>
|
|
7
|
+
joins?: string[]
|
|
8
|
+
withes?: string[]
|
|
9
|
+
sorts?: string[]
|
|
10
|
+
withDeleted?: boolean
|
|
11
|
+
|
|
12
|
+
extra?: any
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type CuboCrudMethodOptions<E extends object = {}> = {
|
|
16
|
+
transaction?: Transaction
|
|
17
|
+
user_id?: number
|
|
18
|
+
extra?: any
|
|
19
|
+
excludeHooks?: ('beforeGetMany' | 'beforeGetOne' | 'afterCreate' | 'afterUpdate' | 'afterDelete' | 'afterGetMany' | 'afterGetOne')[]
|
|
20
|
+
|
|
21
|
+
log?: boolean
|
|
22
|
+
|
|
23
|
+
queryOptions?: CuboCrudQueryOptions
|
|
24
|
+
} & E
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { QueryTypes, Sequelize, Transaction } from 'sequelize'
|
|
2
|
+
|
|
3
|
+
export function dbPrepareUpdateQueryString(obj: Record<string, any>) {
|
|
4
|
+
const keys: string[] = []
|
|
5
|
+
const replacements: Record<string, any> = {}
|
|
6
|
+
|
|
7
|
+
for (const key in obj) {
|
|
8
|
+
const value = obj[key]
|
|
9
|
+
|
|
10
|
+
keys.push(`${key} = :${key}`)
|
|
11
|
+
|
|
12
|
+
let val = null
|
|
13
|
+
|
|
14
|
+
if (typeof value === 'string') {
|
|
15
|
+
val = !!value ? value : null
|
|
16
|
+
} else if (typeof value === 'number') {
|
|
17
|
+
val = !isNaN(value) ? value : null
|
|
18
|
+
} else if (typeof value === 'boolean') {
|
|
19
|
+
val = value
|
|
20
|
+
} else if (Array.isArray(value) || typeof value === 'object') {
|
|
21
|
+
val = value ? JSON.stringify(value) : null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
replacements[key] = val
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return { query: keys.join(', '), replacements }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function dbPrepareInsertQueryString(obj: Record<string, any>) {
|
|
31
|
+
const values: string[] = []
|
|
32
|
+
const replacements: Record<string, any> = {}
|
|
33
|
+
|
|
34
|
+
for (const key in obj) {
|
|
35
|
+
const value = obj[key]
|
|
36
|
+
|
|
37
|
+
values.push(`:${key}`)
|
|
38
|
+
|
|
39
|
+
let val = null
|
|
40
|
+
|
|
41
|
+
if (typeof value === 'string') {
|
|
42
|
+
val = !!value ? value : null
|
|
43
|
+
} else if (typeof value === 'number') {
|
|
44
|
+
val = !isNaN(value) ? value : null
|
|
45
|
+
} else if (typeof value === 'boolean') {
|
|
46
|
+
val = value
|
|
47
|
+
} else if (Array.isArray(value) || typeof value === 'object') {
|
|
48
|
+
val = value ? JSON.stringify(value) : null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
replacements[key] = val
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { keys: Object.keys(obj).join(', '), values: values.join(','), replacements }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function dbCreate(
|
|
58
|
+
db: Sequelize,
|
|
59
|
+
table: string,
|
|
60
|
+
arInsert: any,
|
|
61
|
+
opts?: { log?: boolean; transaction?: Transaction }
|
|
62
|
+
): Promise<{ id: number }> {
|
|
63
|
+
if (Object.keys(arInsert).length > 0) {
|
|
64
|
+
const dialect = db.getDialect()
|
|
65
|
+
|
|
66
|
+
const { keys, values, replacements } = dbPrepareInsertQueryString(arInsert)
|
|
67
|
+
|
|
68
|
+
let sql = ''
|
|
69
|
+
|
|
70
|
+
switch (dialect) {
|
|
71
|
+
case 'postgres':
|
|
72
|
+
sql = `insert into ${table} (${keys}) values (${values}) returning id`
|
|
73
|
+
break
|
|
74
|
+
case 'mysql':
|
|
75
|
+
sql = `insert into ${table} (${keys}) values (${values})`
|
|
76
|
+
break
|
|
77
|
+
default:
|
|
78
|
+
throw ''
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (opts?.log) {
|
|
82
|
+
console.warn(`QUERY: ${sql} [${JSON.stringify(replacements)}]`)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return db
|
|
86
|
+
.query(sql, {
|
|
87
|
+
type: QueryTypes.INSERT,
|
|
88
|
+
replacements,
|
|
89
|
+
transaction: opts?.transaction
|
|
90
|
+
})
|
|
91
|
+
.then((res: any) => {
|
|
92
|
+
switch (dialect) {
|
|
93
|
+
case 'postgres':
|
|
94
|
+
return { id: +res?.[0]?.[0].id }
|
|
95
|
+
case 'mysql':
|
|
96
|
+
return { id: +res[0] }
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
throw new Error('QUERY: db create array empty')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function dbUpdate(
|
|
105
|
+
db: Sequelize,
|
|
106
|
+
table: string,
|
|
107
|
+
where: string,
|
|
108
|
+
replacements: Record<string, any>,
|
|
109
|
+
arUpdate: any,
|
|
110
|
+
opts?: { log?: boolean; transaction?: Transaction }
|
|
111
|
+
) {
|
|
112
|
+
if (Object.keys(arUpdate).length > 0) {
|
|
113
|
+
const prepared = dbPrepareUpdateQueryString(arUpdate)
|
|
114
|
+
|
|
115
|
+
if (opts?.log) {
|
|
116
|
+
console.warn(`QUERY: update ${table} set ${prepared.query} where ${where} [${JSON.stringify(replacements)}]`)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await db.query(`update ${table} set ${prepared.query} where ${where}`, {
|
|
120
|
+
type: QueryTypes.UPDATE,
|
|
121
|
+
replacements: {
|
|
122
|
+
...(replacements || {}),
|
|
123
|
+
...(prepared.replacements || {})
|
|
124
|
+
},
|
|
125
|
+
transaction: opts?.transaction
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "commonjs",
|
|
4
|
+
"declaration": true,
|
|
5
|
+
"removeComments": true,
|
|
6
|
+
"emitDecoratorMetadata": true,
|
|
7
|
+
"experimentalDecorators": true,
|
|
8
|
+
"allowSyntheticDefaultImports": true,
|
|
9
|
+
"target": "ES2021",
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"outDir": "./dist",
|
|
12
|
+
"baseUrl": "./",
|
|
13
|
+
"incremental": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"paths": {
|
|
16
|
+
"@/*": ["src/*"]
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"include": ["src/**/*"],
|
|
20
|
+
"exclude": ["node_modules", "dist"]
|
|
21
|
+
}
|