@drax/crud-back 0.25.0 → 0.27.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/dist/controllers/AbstractFastifyController.js +52 -14
- package/package.json +6 -6
- package/src/controllers/AbstractFastifyController.ts +85 -23
- package/src/index.ts +4 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/types/controllers/AbstractFastifyController.d.ts +21 -7
- package/types/controllers/AbstractFastifyController.d.ts.map +1 -1
- package/types/index.d.ts +2 -0
- package/types/index.d.ts.map +1 -1
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
import { CommonConfig, DraxConfig, LimitError, NotFoundError, BadRequestError, CommonController } from "@drax/common-back";
|
|
2
2
|
import { join } from "path";
|
|
3
3
|
import QueryFilterRegex from "../regexs/QueryFilterRegex.js";
|
|
4
|
-
const BASE_FILE_DIR = DraxConfig.getOrLoad(CommonConfig.FileDir) || 'files';
|
|
5
|
-
const BASE_URL = DraxConfig.getOrLoad(CommonConfig.BaseUrl) ? DraxConfig.get(CommonConfig.BaseUrl).replace(/\/$/, '') : '';
|
|
6
4
|
class AbstractFastifyController extends CommonController {
|
|
7
5
|
constructor(service, permission) {
|
|
8
6
|
super();
|
|
7
|
+
this.baseFileDir = DraxConfig.getOrLoad(CommonConfig.FileDir) || 'files';
|
|
8
|
+
this.baseURL = DraxConfig.getOrLoad(CommonConfig.BaseUrl) ? DraxConfig.get(CommonConfig.BaseUrl).replace(/\/$/, '') : '';
|
|
9
9
|
this.tenantField = 'tenant';
|
|
10
10
|
this.userField = 'user';
|
|
11
11
|
this.tenantFilter = false;
|
|
12
|
-
this.userFilter = false;
|
|
13
12
|
this.tenantSetter = false;
|
|
14
|
-
this.userSetter = false;
|
|
15
13
|
this.tenantAssert = false;
|
|
14
|
+
/**
|
|
15
|
+
* userFilter is used to filter items by user field like createdBy with auth user. Used by find, search, paginate
|
|
16
|
+
*/
|
|
17
|
+
this.userFilter = false;
|
|
18
|
+
/**
|
|
19
|
+
* userSetter is used to set user like createdBy with auth user. Used by create
|
|
20
|
+
*/
|
|
21
|
+
this.userSetter = false;
|
|
22
|
+
/**
|
|
23
|
+
* userSetter is used to verify item user like createdBy with auth user. Used by update, delete, findById
|
|
24
|
+
*/
|
|
16
25
|
this.userAssert = false;
|
|
17
26
|
this.defaultLimit = 1000;
|
|
18
27
|
this.maximumLimit = 10000;
|
|
@@ -42,13 +51,22 @@ class AbstractFastifyController extends CommonController {
|
|
|
42
51
|
}
|
|
43
52
|
}
|
|
44
53
|
applyUserAndTenantFilters(filters, rbac) {
|
|
45
|
-
|
|
46
|
-
|
|
54
|
+
this.applyTenantFilter(filters, rbac);
|
|
55
|
+
this.applyUserFilter(filters, rbac);
|
|
56
|
+
}
|
|
57
|
+
applyUserFilter(filters, rbac) {
|
|
58
|
+
if (rbac.hasSomePermission([this.permission.All, this.permission.ViewAll])) {
|
|
59
|
+
return;
|
|
47
60
|
}
|
|
48
61
|
if (this.userFilter && rbac.userId) {
|
|
49
62
|
filters.push({ field: this.userField, operator: 'eq', value: rbac.userId });
|
|
50
63
|
}
|
|
51
64
|
}
|
|
65
|
+
applyTenantFilter(filters, rbac) {
|
|
66
|
+
if (this.tenantFilter && rbac.tenantId) {
|
|
67
|
+
filters.push({ field: this.tenantField, operator: 'eq', value: rbac.tenantId });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
52
70
|
assertTenant(item, rbac) {
|
|
53
71
|
if (this.tenantAssert) {
|
|
54
72
|
const itemTenantId = item[this.tenantField]?._id ? item[this.tenantField]._id.toString() : null;
|
|
@@ -94,10 +112,17 @@ class AbstractFastifyController extends CommonController {
|
|
|
94
112
|
}
|
|
95
113
|
const id = request.params.id;
|
|
96
114
|
const payload = request.body;
|
|
115
|
+
if (!request.rbac.hasSomePermission([this.permission.All, this.permission.UpdateAll])) {
|
|
116
|
+
let preItem = await this.service.findById(id);
|
|
117
|
+
if (!preItem) {
|
|
118
|
+
reply.statusCode = 404;
|
|
119
|
+
reply.send({ error: 'NOT_FOUND' });
|
|
120
|
+
}
|
|
121
|
+
this.assertUser(preItem, request.rbac);
|
|
122
|
+
}
|
|
123
|
+
//Definido el tenant/user en el create no debe modificarse en un update
|
|
97
124
|
delete payload[this.tenantField];
|
|
98
125
|
delete payload[this.userField];
|
|
99
|
-
//Una vez que un registro se crea con un tenant, no deberia actualizarse nunca mas
|
|
100
|
-
//this.applyUserAndTenantSetters(payload, request.rbac)
|
|
101
126
|
let item = await this.service.update(id, payload);
|
|
102
127
|
if (!item) {
|
|
103
128
|
throw new NotFoundError();
|
|
@@ -117,10 +142,17 @@ class AbstractFastifyController extends CommonController {
|
|
|
117
142
|
}
|
|
118
143
|
const id = request.params.id;
|
|
119
144
|
const payload = request.body;
|
|
145
|
+
if (!request.rbac.hasSomePermission([this.permission.All, this.permission.UpdateAll])) {
|
|
146
|
+
let preItem = await this.service.findById(id);
|
|
147
|
+
if (!preItem) {
|
|
148
|
+
reply.statusCode = 404;
|
|
149
|
+
reply.send({ error: 'NOT_FOUND' });
|
|
150
|
+
}
|
|
151
|
+
this.assertUser(preItem, request.rbac);
|
|
152
|
+
}
|
|
153
|
+
//Definido el tenant/user en el create no debe modificarse en un update
|
|
120
154
|
delete payload[this.tenantField];
|
|
121
155
|
delete payload[this.userField];
|
|
122
|
-
//Una vez que un registro se crea con un tenant, no deberia actualizarse nunca mas
|
|
123
|
-
//this.applyUserAndTenantSetters(payload, request.rbac)
|
|
124
156
|
let item = await this.service.updatePartial(id, payload);
|
|
125
157
|
if (!item) {
|
|
126
158
|
throw new NotFoundError();
|
|
@@ -144,7 +176,10 @@ class AbstractFastifyController extends CommonController {
|
|
|
144
176
|
reply.statusCode = 404;
|
|
145
177
|
reply.send({ error: 'NOT_FOUND' });
|
|
146
178
|
}
|
|
147
|
-
this.
|
|
179
|
+
if (!request.rbac.hasSomePermission([this.permission.All, this.permission.DeleteAll])) {
|
|
180
|
+
this.assertUser(item, request.rbac);
|
|
181
|
+
}
|
|
182
|
+
this.assertTenant(item, request.rbac);
|
|
148
183
|
await this.service.delete(id);
|
|
149
184
|
reply.send({
|
|
150
185
|
id: id,
|
|
@@ -169,7 +204,10 @@ class AbstractFastifyController extends CommonController {
|
|
|
169
204
|
if (!item) {
|
|
170
205
|
throw new NotFoundError();
|
|
171
206
|
}
|
|
172
|
-
this.
|
|
207
|
+
if (!request.rbac.hasSomePermission([this.permission.All, this.permission.ViewAll])) {
|
|
208
|
+
this.assertUser(item, request.rbac);
|
|
209
|
+
}
|
|
210
|
+
this.assertTenant(item, request.rbac);
|
|
173
211
|
return item;
|
|
174
212
|
}
|
|
175
213
|
catch (e) {
|
|
@@ -315,7 +353,7 @@ class AbstractFastifyController extends CommonController {
|
|
|
315
353
|
const year = (new Date().getFullYear()).toString();
|
|
316
354
|
const month = (new Date().getMonth() + 1).toString().padStart(2, '0');
|
|
317
355
|
const exportPath = 'exports';
|
|
318
|
-
const destinationPath = join(
|
|
356
|
+
const destinationPath = join(this.baseFileDir, 'exports', year, month);
|
|
319
357
|
let result = await this.service.export({
|
|
320
358
|
separator,
|
|
321
359
|
format,
|
|
@@ -326,7 +364,7 @@ class AbstractFastifyController extends CommonController {
|
|
|
326
364
|
search,
|
|
327
365
|
filters,
|
|
328
366
|
}, destinationPath);
|
|
329
|
-
const url = `${
|
|
367
|
+
const url = `${this.baseURL}/api/file/${exportPath}/${year}/${month}/${result.fileName}`;
|
|
330
368
|
return {
|
|
331
369
|
url: url,
|
|
332
370
|
rowCount: result.rowCount,
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.27.0",
|
|
7
7
|
"description": "Crud utils across modules",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"types": "types/index.d.ts",
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
"author": "Cristian Incarnato & Drax Team",
|
|
23
23
|
"license": "ISC",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@drax/common-back": "^0.
|
|
26
|
-
"@drax/common-share": "^0.
|
|
27
|
-
"@drax/identity-share": "^0.
|
|
28
|
-
"@drax/media-back": "^0.
|
|
25
|
+
"@drax/common-back": "^0.27.0",
|
|
26
|
+
"@drax/common-share": "^0.27.0",
|
|
27
|
+
"@drax/identity-share": "^0.27.0",
|
|
28
|
+
"@drax/media-back": "^0.27.0",
|
|
29
29
|
"@graphql-tools/load-files": "^7.0.0",
|
|
30
30
|
"@graphql-tools/merge": "^9.0.4",
|
|
31
31
|
"mongoose": "^8.6.3",
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"tsc-alias": "^1.8.10",
|
|
46
46
|
"typescript": "^5.6.2"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "b8c4047451d7bcae320e5b9e50b8e60fb69229a8"
|
|
49
49
|
}
|
|
@@ -40,25 +40,35 @@ type CustomRequest = FastifyRequest<{
|
|
|
40
40
|
}
|
|
41
41
|
}>
|
|
42
42
|
|
|
43
|
-
const BASE_FILE_DIR = DraxConfig.getOrLoad(CommonConfig.FileDir) || 'files';
|
|
44
|
-
const BASE_URL = DraxConfig.getOrLoad(CommonConfig.BaseUrl) ? DraxConfig.get(CommonConfig.BaseUrl).replace(/\/$/, '') : ''
|
|
45
|
-
|
|
46
43
|
|
|
47
44
|
class AbstractFastifyController<T, C, U> extends CommonController {
|
|
48
45
|
|
|
49
46
|
protected service: AbstractService<T, C, U>
|
|
50
47
|
protected permission: IDraxPermission
|
|
51
48
|
|
|
49
|
+
protected baseFileDir = DraxConfig.getOrLoad(CommonConfig.FileDir) || 'files';
|
|
50
|
+
protected baseURL = DraxConfig.getOrLoad(CommonConfig.BaseUrl) ? DraxConfig.get(CommonConfig.BaseUrl).replace(/\/$/, '') : ''
|
|
51
|
+
|
|
52
52
|
protected tenantField: string = 'tenant'
|
|
53
53
|
protected userField: string = 'user'
|
|
54
54
|
|
|
55
55
|
protected tenantFilter: boolean = false
|
|
56
|
+
protected tenantSetter: boolean = false
|
|
57
|
+
protected tenantAssert: boolean = false
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* userFilter is used to filter items by user field like createdBy with auth user. Used by find, search, paginate
|
|
61
|
+
*/
|
|
56
62
|
protected userFilter: boolean = false
|
|
57
63
|
|
|
58
|
-
|
|
64
|
+
/**
|
|
65
|
+
* userSetter is used to set user like createdBy with auth user. Used by create
|
|
66
|
+
*/
|
|
59
67
|
protected userSetter: boolean = false
|
|
60
68
|
|
|
61
|
-
|
|
69
|
+
/**
|
|
70
|
+
* userSetter is used to verify item user like createdBy with auth user. Used by update, delete, findById
|
|
71
|
+
*/
|
|
62
72
|
protected userAssert: boolean = false
|
|
63
73
|
|
|
64
74
|
protected defaultLimit: number = 1000
|
|
@@ -89,41 +99,55 @@ class AbstractFastifyController<T, C, U> extends CommonController {
|
|
|
89
99
|
})
|
|
90
100
|
return filters
|
|
91
101
|
} catch (e) {
|
|
92
|
-
console.error("parseFilters error",e)
|
|
102
|
+
console.error("parseFilters error", e)
|
|
93
103
|
throw e
|
|
94
104
|
}
|
|
95
105
|
}
|
|
96
106
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
107
|
+
protected applyUserAndTenantFilters(filters: IDraxFieldFilter[], rbac: IRbac) {
|
|
108
|
+
this.applyTenantFilter(filters, rbac)
|
|
109
|
+
this.applyUserFilter(filters, rbac)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
protected applyUserFilter(filters: IDraxFieldFilter[], rbac: IRbac) {
|
|
113
|
+
|
|
114
|
+
if(rbac.hasSomePermission([this.permission.All, this.permission.ViewAll])) {
|
|
115
|
+
return
|
|
100
116
|
}
|
|
101
117
|
|
|
102
|
-
if
|
|
118
|
+
if(this.userFilter && rbac.userId) {
|
|
103
119
|
filters.push({field: this.userField, operator: 'eq', value: rbac.userId})
|
|
104
120
|
}
|
|
121
|
+
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
protected applyTenantFilter(filters: IDraxFieldFilter[], rbac: IRbac) {
|
|
125
|
+
if (this.tenantFilter && rbac.tenantId) {
|
|
126
|
+
filters.push({field: this.tenantField, operator: 'eq', value: rbac.tenantId})
|
|
127
|
+
}
|
|
105
128
|
}
|
|
106
129
|
|
|
107
|
-
|
|
130
|
+
protected assertTenant(item: T, rbac: IRbac) {
|
|
108
131
|
if (this.tenantAssert) {
|
|
109
132
|
const itemTenantId = item[this.tenantField]?._id ? item[this.tenantField]._id.toString() : null
|
|
110
133
|
rbac.assertTenantId(itemTenantId)
|
|
111
134
|
}
|
|
112
135
|
}
|
|
113
136
|
|
|
114
|
-
|
|
137
|
+
protected assertUser(item: T, rbac: IRbac) {
|
|
138
|
+
|
|
115
139
|
if (this.userAssert) {
|
|
116
140
|
const itemUserId = item[this.userField]?._id ? item[this.userField]._id.toString() : null
|
|
117
141
|
rbac.assertUserId(itemUserId)
|
|
118
142
|
}
|
|
119
143
|
}
|
|
120
144
|
|
|
121
|
-
|
|
145
|
+
protected assertUserAndTenant(item: T, rbac: IRbac) {
|
|
122
146
|
this.assertTenant(item, rbac)
|
|
123
147
|
this.assertUser(item, rbac)
|
|
124
148
|
}
|
|
125
149
|
|
|
126
|
-
|
|
150
|
+
protected applyUserAndTenantSetters(payload: any, rbac: any) {
|
|
127
151
|
if (this.tenantSetter && rbac.tenantId) {
|
|
128
152
|
payload[this.tenantField] = rbac.tenantId
|
|
129
153
|
}
|
|
@@ -155,10 +179,23 @@ class AbstractFastifyController<T, C, U> extends CommonController {
|
|
|
155
179
|
}
|
|
156
180
|
const id = request.params.id
|
|
157
181
|
const payload = request.body
|
|
182
|
+
|
|
183
|
+
if (!request.rbac.hasSomePermission([this.permission.All, this.permission.UpdateAll])) {
|
|
184
|
+
|
|
185
|
+
let preItem = await this.service.findById(id)
|
|
186
|
+
|
|
187
|
+
if (!preItem) {
|
|
188
|
+
reply.statusCode = 404
|
|
189
|
+
reply.send({error: 'NOT_FOUND'})
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
this.assertUser(preItem, request.rbac)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
//Definido el tenant/user en el create no debe modificarse en un update
|
|
158
196
|
delete payload[this.tenantField]
|
|
159
197
|
delete payload[this.userField]
|
|
160
|
-
|
|
161
|
-
//this.applyUserAndTenantSetters(payload, request.rbac)
|
|
198
|
+
|
|
162
199
|
let item = await this.service.update(id, payload as U)
|
|
163
200
|
|
|
164
201
|
if (!item) {
|
|
@@ -180,10 +217,23 @@ class AbstractFastifyController<T, C, U> extends CommonController {
|
|
|
180
217
|
}
|
|
181
218
|
const id = request.params.id
|
|
182
219
|
const payload = request.body
|
|
220
|
+
|
|
221
|
+
if (!request.rbac.hasSomePermission([this.permission.All, this.permission.UpdateAll])) {
|
|
222
|
+
|
|
223
|
+
let preItem = await this.service.findById(id)
|
|
224
|
+
|
|
225
|
+
if (!preItem) {
|
|
226
|
+
reply.statusCode = 404
|
|
227
|
+
reply.send({error: 'NOT_FOUND'})
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
this.assertUser(preItem, request.rbac)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
//Definido el tenant/user en el create no debe modificarse en un update
|
|
183
234
|
delete payload[this.tenantField]
|
|
184
235
|
delete payload[this.userField]
|
|
185
|
-
|
|
186
|
-
//this.applyUserAndTenantSetters(payload, request.rbac)
|
|
236
|
+
|
|
187
237
|
let item = await this.service.updatePartial(id, payload as U)
|
|
188
238
|
if (!item) {
|
|
189
239
|
throw new NotFoundError()
|
|
@@ -201,6 +251,7 @@ class AbstractFastifyController<T, C, U> extends CommonController {
|
|
|
201
251
|
reply.statusCode = 400
|
|
202
252
|
reply.send({error: 'BAD REQUEST'})
|
|
203
253
|
}
|
|
254
|
+
|
|
204
255
|
const id = request.params.id
|
|
205
256
|
|
|
206
257
|
let item = await this.service.findById(id)
|
|
@@ -210,7 +261,12 @@ class AbstractFastifyController<T, C, U> extends CommonController {
|
|
|
210
261
|
reply.send({error: 'NOT_FOUND'})
|
|
211
262
|
}
|
|
212
263
|
|
|
213
|
-
this.
|
|
264
|
+
if (!request.rbac.hasSomePermission([this.permission.All, this.permission.DeleteAll])) {
|
|
265
|
+
this.assertUser(item, request.rbac)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
this.assertTenant(item, request.rbac)
|
|
269
|
+
|
|
214
270
|
|
|
215
271
|
await this.service.delete(id)
|
|
216
272
|
reply.send({
|
|
@@ -240,7 +296,11 @@ class AbstractFastifyController<T, C, U> extends CommonController {
|
|
|
240
296
|
throw new NotFoundError()
|
|
241
297
|
}
|
|
242
298
|
|
|
243
|
-
this.
|
|
299
|
+
if (!request.rbac.hasSomePermission([this.permission.All, this.permission.ViewAll])) {
|
|
300
|
+
this.assertUser(item, request.rbac)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
this.assertTenant(item, request.rbac)
|
|
244
304
|
|
|
245
305
|
return item
|
|
246
306
|
} catch (e) {
|
|
@@ -281,6 +341,7 @@ class AbstractFastifyController<T, C, U> extends CommonController {
|
|
|
281
341
|
const search = request.query.search ??= undefined
|
|
282
342
|
const filters = this.parseFilters(request.query.filters)
|
|
283
343
|
|
|
344
|
+
|
|
284
345
|
this.applyUserAndTenantFilters(filters, request.rbac);
|
|
285
346
|
|
|
286
347
|
let items = await this.service.find({search, filters, order, orderBy, limit})
|
|
@@ -353,7 +414,6 @@ class AbstractFastifyController<T, C, U> extends CommonController {
|
|
|
353
414
|
}
|
|
354
415
|
|
|
355
416
|
|
|
356
|
-
|
|
357
417
|
async search(request: CustomRequest, reply: FastifyReply) {
|
|
358
418
|
try {
|
|
359
419
|
request.rbac.assertPermission(this.permission.View)
|
|
@@ -416,7 +476,7 @@ class AbstractFastifyController<T, C, U> extends CommonController {
|
|
|
416
476
|
const month = (new Date().getMonth() + 1).toString().padStart(2, '0')
|
|
417
477
|
const exportPath = 'exports'
|
|
418
478
|
|
|
419
|
-
const destinationPath = join(
|
|
479
|
+
const destinationPath = join(this.baseFileDir, 'exports', year, month)
|
|
420
480
|
|
|
421
481
|
let result: IDraxExportResult = await this.service.export({
|
|
422
482
|
separator,
|
|
@@ -429,7 +489,7 @@ class AbstractFastifyController<T, C, U> extends CommonController {
|
|
|
429
489
|
filters,
|
|
430
490
|
}, destinationPath)
|
|
431
491
|
|
|
432
|
-
const url = `${
|
|
492
|
+
const url = `${this.baseURL}/api/file/${exportPath}/${year}/${month}/${result.fileName}`
|
|
433
493
|
|
|
434
494
|
|
|
435
495
|
return {
|
|
@@ -447,3 +507,5 @@ class AbstractFastifyController<T, C, U> extends CommonController {
|
|
|
447
507
|
|
|
448
508
|
export default AbstractFastifyController;
|
|
449
509
|
export {AbstractFastifyController}
|
|
510
|
+
|
|
511
|
+
export type {CustomRequest}
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import AbstractMongoRepository from "./repository/AbstractMongoRepository.js";
|
|
|
3
3
|
import AbstractSqliteRepository from "./repository/AbstractSqliteRepository.js";
|
|
4
4
|
import AbstractService from "./services/AbstractService.js";
|
|
5
5
|
import AbstractFastifyController from "./controllers/AbstractFastifyController.js";
|
|
6
|
+
import type {CustomRequest} from "./controllers/AbstractFastifyController.js";
|
|
6
7
|
|
|
7
8
|
//schemas
|
|
8
9
|
import {IdParamSchema} from "./schemas/IdParamSchema.js"
|
|
@@ -15,6 +16,9 @@ import {ExportBodyResponseSchema} from "./schemas/ExportBodyResponseSchema.js"
|
|
|
15
16
|
import {ErrorBodyResponseSchema, ValidationErrorBodyResponseSchema} from "./schemas/ErrorBodyResponseSchema.js"
|
|
16
17
|
import {CrudSchemaBuilder} from "./builders/CrudSchemaBuilder.js";
|
|
17
18
|
|
|
19
|
+
export type {
|
|
20
|
+
CustomRequest
|
|
21
|
+
}
|
|
18
22
|
|
|
19
23
|
export {
|
|
20
24
|
|