@simitgroup/simpleapp-generator 1.0.31 → 1.0.32
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/README.md +100 -12
- package/dist/framework.js +7 -2
- package/dist/framework.js.map +1 -1
- package/dist/generate.js +10 -1
- package/dist/generate.js.map +1 -1
- package/dist/processors/jsonschemabuilder.js +91 -15
- package/dist/processors/jsonschemabuilder.js.map +1 -1
- package/dist/type.js.map +1 -1
- package/package.json +1 -1
- package/src/framework.ts +7 -2
- package/src/generate.ts +12 -1
- package/src/processors/jsonschemabuilder.ts +113 -22
- package/src/type.ts +44 -1
- package/templates/basic/controller.eta +34 -17
- package/templates/basic/model.eta +2 -1
- package/templates/basic/pageindex.vue.eta +1 -1
- package/templates/basic/service.eta +18 -2
- package/templates/basic/simpleappclient.eta +19 -0
- package/templates/nest/SimpleAppService.eta +116 -70
- package/templates/nest/TenantMiddleware.eta +8 -13
- package/templates/nest/UserProvider.eta +127 -0
- package/templates/nest/Workflow.eta +75 -0
- package/templates/nest/app.controller.eta +12 -0
- package/templates/nest/app.module.eta +6 -2
- package/templates/nest/app.service.eta +8 -0
- package/templates/nest/nest.env.eta +7 -0
- package/templates/nuxt/components.debugdocdata.vue.eta +1 -1
- package/templates/nuxt/components.menus.vue.eta +22 -7
- package/templates/nuxt/pages.[xorg].index.vue.eta +9 -8
- package/templates/nuxt/pages.index.vue.eta +61 -8
- package/templates/nuxt/server.api.ts.eta +2 -1
- package/templates/nuxt/tailwind.css.eta +14 -1
- package/tsconfig.json +4 -1
- package/templates/nest/User.eta +0 -115
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
import { Injectable } from '@nestjs/common';
|
|
1
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
2
2
|
import { InjectModel } from '@nestjs/mongoose';
|
|
3
3
|
import { Model } from 'mongoose';
|
|
4
4
|
import Ajv from 'ajv';
|
|
5
5
|
import addFormats from 'ajv-formats';
|
|
6
6
|
import addErrors from 'ajv-errors';
|
|
7
7
|
import foreignkeys from '../dicts/foreignkeys.json';
|
|
8
|
-
|
|
8
|
+
import { Workflow } from './Workflow';
|
|
9
9
|
import {
|
|
10
10
|
NotFoundException,
|
|
11
11
|
InternalServerErrorException,
|
|
12
12
|
} from '@nestjs/common/exceptions';
|
|
13
|
-
import {
|
|
13
|
+
import { UserProvider } from './UserProvider';
|
|
14
14
|
|
|
15
15
|
export enum IsolationType {
|
|
16
|
+
'none' = 'none',
|
|
16
17
|
'org' = 'org',
|
|
17
18
|
'tenant' = 'tenant',
|
|
18
19
|
'branch' = 'branch',
|
|
19
20
|
}
|
|
20
21
|
export enum HookType {
|
|
21
|
-
'init'='init',
|
|
22
|
-
'beforeSearch'='beforeSearch',
|
|
23
|
-
'afterSearch'='afterSearch',
|
|
24
|
-
'beforeValidation'='beforeValidation',
|
|
25
|
-
'afterValidation'='afterValidation',
|
|
26
|
-
'beforeCreate'='beforeCreate',
|
|
27
|
-
'afterCreate'='afterCreate',
|
|
28
|
-
'beforeUpdate'='beforeUpdate',
|
|
29
|
-
'afterUpdate'='afterUpdate',
|
|
30
|
-
'beforeDelete'='beforeDelete',
|
|
31
|
-
'afterDelete'='afterDelete',
|
|
32
|
-
'beforeFetchRecord'='beforeFetchRecord',
|
|
33
|
-
'afterFetchRecord'='afterFetchRecord',
|
|
34
|
-
|
|
35
|
-
|
|
22
|
+
'init' = 'init',
|
|
23
|
+
'beforeSearch' = 'beforeSearch',
|
|
24
|
+
'afterSearch' = 'afterSearch',
|
|
25
|
+
'beforeValidation' = 'beforeValidation',
|
|
26
|
+
'afterValidation' = 'afterValidation',
|
|
27
|
+
'beforeCreate' = 'beforeCreate',
|
|
28
|
+
'afterCreate' = 'afterCreate',
|
|
29
|
+
'beforeUpdate' = 'beforeUpdate',
|
|
30
|
+
'afterUpdate' = 'afterUpdate',
|
|
31
|
+
'beforeDelete' = 'beforeDelete',
|
|
32
|
+
'afterDelete' = 'afterDelete',
|
|
33
|
+
'beforeFetchRecord' = 'beforeFetchRecord',
|
|
34
|
+
'afterFetchRecord' = 'afterFetchRecord',
|
|
36
35
|
}
|
|
37
36
|
export type MoreProjectionType = {
|
|
38
|
-
[key:string]:string
|
|
39
|
-
}
|
|
37
|
+
[key: string]: string;
|
|
38
|
+
};
|
|
40
39
|
@Injectable()
|
|
41
40
|
export class SimpleAppService<T extends { _id?: string }> {
|
|
41
|
+
protected logger = new Logger();
|
|
42
42
|
protected jsonschema = { type: 'object', properties: {}, required: [] };
|
|
43
43
|
protected documentIdentityCode = 'code';
|
|
44
44
|
protected documentIdentityName = 'label';
|
|
45
45
|
protected documentName = '-unknowndocname-';
|
|
46
46
|
protected documentType = '-unknowndoctype-';
|
|
47
47
|
protected LIMITPERPAGE = 20;
|
|
48
|
-
protected moreAutoCompleteField:MoreProjectionType={}
|
|
48
|
+
protected moreAutoCompleteField: MoreProjectionType = {};
|
|
49
49
|
protected isolationtype: IsolationType = IsolationType.org;
|
|
50
50
|
protected isolationFilter: any = {};
|
|
51
51
|
protected data: T = { _id: '' } as T;
|
|
52
52
|
private doc: Model<T>; //set private to prevent developer break data isolation control
|
|
53
53
|
protected errorlist = [];
|
|
54
54
|
constructor(
|
|
55
|
-
doctype:string,
|
|
56
|
-
docname:string,
|
|
55
|
+
doctype: string,
|
|
56
|
+
docname: string,
|
|
57
57
|
newdoc: Model<T>,
|
|
58
58
|
isolationtype: IsolationType = IsolationType.org,
|
|
59
59
|
) {
|
|
60
|
-
this.documentType=doctype
|
|
61
|
-
this.documentName=docname
|
|
60
|
+
this.documentType = doctype;
|
|
61
|
+
this.documentName = docname;
|
|
62
62
|
this.doc = newdoc;
|
|
63
63
|
this.isolationtype = isolationtype;
|
|
64
|
-
this.hook(HookType.init,this.documentName)
|
|
64
|
+
this.hook(HookType.init, this.documentName);
|
|
65
65
|
// this.tenantdoc = tenantdoc
|
|
66
66
|
}
|
|
67
67
|
getRecordId = (): string => this.data._id;
|
|
@@ -79,15 +79,18 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
79
79
|
getIsolationFilter = () => {
|
|
80
80
|
let isolationFilter = {};
|
|
81
81
|
switch (this.isolationtype) {
|
|
82
|
+
case 'none':
|
|
83
|
+
isolationFilter = {};
|
|
84
|
+
break;
|
|
82
85
|
case 'branch':
|
|
83
|
-
isolationFilter =
|
|
86
|
+
isolationFilter = UserProvider.getInstance().getBranchFilter();
|
|
84
87
|
break;
|
|
85
88
|
case 'tenant':
|
|
86
|
-
isolationFilter =
|
|
89
|
+
isolationFilter = UserProvider.getInstance().getTenantFilter();
|
|
87
90
|
break;
|
|
88
91
|
case 'org':
|
|
89
92
|
default:
|
|
90
|
-
isolationFilter =
|
|
93
|
+
isolationFilter = UserProvider.getInstance().getOrgFilter();
|
|
91
94
|
break;
|
|
92
95
|
}
|
|
93
96
|
return isolationFilter;
|
|
@@ -106,21 +109,21 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
106
109
|
throw new InternalServerErrorException(err.message);
|
|
107
110
|
}
|
|
108
111
|
}
|
|
109
|
-
addAutoCompleteField = (morefield:MoreProjectionType)=>{
|
|
110
|
-
const props = Object.getOwnPropertyNames(morefield)
|
|
111
|
-
console.log('addAutoCompleteField',props)
|
|
112
|
-
for(let i=0;i<props.length;i++){
|
|
113
|
-
const key = props[i]
|
|
114
|
-
this.moreAutoCompleteField[key] = '$'+morefield[key]
|
|
112
|
+
addAutoCompleteField = (morefield: MoreProjectionType) => {
|
|
113
|
+
const props = Object.getOwnPropertyNames(morefield);
|
|
114
|
+
console.log('addAutoCompleteField', props);
|
|
115
|
+
for (let i = 0; i < props.length; i++) {
|
|
116
|
+
const key = props[i];
|
|
117
|
+
this.moreAutoCompleteField[key] = '$' + morefield[key];
|
|
115
118
|
}
|
|
116
|
-
console.log(this.moreAutoCompleteField)
|
|
117
|
-
}
|
|
119
|
+
console.log(this.moreAutoCompleteField);
|
|
120
|
+
};
|
|
118
121
|
async getAutoComplete(keyword: string) {
|
|
119
122
|
try {
|
|
120
123
|
const filter1 = {};
|
|
121
124
|
const filter2 = {};
|
|
122
|
-
filter1[this.documentIdentityCode] = { $regex: keyword
|
|
123
|
-
filter2[this.documentIdentityName] = { $regex: keyword
|
|
125
|
+
filter1[this.documentIdentityCode] = { $regex: keyword, $options: 'i' };
|
|
126
|
+
filter2[this.documentIdentityName] = { $regex: keyword, $options: 'i' };
|
|
124
127
|
const filterobj = { $or: [filter1, filter2] };
|
|
125
128
|
|
|
126
129
|
Object.assign(filterobj, this.getIsolationFilter());
|
|
@@ -129,8 +132,8 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
129
132
|
label: `\$${this.documentIdentityCode}`,
|
|
130
133
|
name: `\$${this.documentIdentityName}`,
|
|
131
134
|
};
|
|
132
|
-
if(this.moreAutoCompleteField){
|
|
133
|
-
Object.assign(projections,this.moreAutoCompleteField)
|
|
135
|
+
if (this.moreAutoCompleteField) {
|
|
136
|
+
Object.assign(projections, this.moreAutoCompleteField);
|
|
134
137
|
}
|
|
135
138
|
const products = await this.doc.find(filterobj, projections, {
|
|
136
139
|
limit: this.LIMITPERPAGE,
|
|
@@ -145,14 +148,13 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
145
148
|
}
|
|
146
149
|
async search(filters: Object) {
|
|
147
150
|
try {
|
|
148
|
-
|
|
149
151
|
Object.assign(filters, this.getIsolationFilter());
|
|
150
|
-
await this.hook(HookType.beforeSearch,filters)
|
|
152
|
+
await this.hook(HookType.beforeSearch, filters);
|
|
151
153
|
const products = await this.doc.find(filters);
|
|
152
154
|
const productlist = products.map((p: T) => {
|
|
153
155
|
return p;
|
|
154
156
|
});
|
|
155
|
-
await this.hook(HookType.afterSearch,productlist)
|
|
157
|
+
await this.hook(HookType.afterSearch, productlist);
|
|
156
158
|
// console.log(products);
|
|
157
159
|
return productlist;
|
|
158
160
|
} catch (err) {
|
|
@@ -161,9 +163,9 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
161
163
|
// return this;
|
|
162
164
|
}
|
|
163
165
|
async findById(id: string) {
|
|
164
|
-
await this.hook(HookType.beforeFetchRecord,id)
|
|
166
|
+
await this.hook(HookType.beforeFetchRecord, id);
|
|
165
167
|
const data = await this.search({ _id: id });
|
|
166
|
-
await this.hook(HookType.afterFetchRecord,data)
|
|
168
|
+
await this.hook(HookType.afterFetchRecord, data);
|
|
167
169
|
if (data.length == 1) {
|
|
168
170
|
// console.log('data0', data[0]);
|
|
169
171
|
return data[0];
|
|
@@ -176,13 +178,13 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
176
178
|
let result;
|
|
177
179
|
|
|
178
180
|
try {
|
|
179
|
-
await this.hook(HookType.beforeCreate,this.data)
|
|
180
|
-
Object.assign(this.data,
|
|
181
|
+
await this.hook(HookType.beforeCreate, this.data);
|
|
182
|
+
Object.assign(this.data, UserProvider.getInstance().getCreateFilter());
|
|
181
183
|
delete this.data._id;
|
|
182
184
|
await this.validateData(this.data);
|
|
183
185
|
const newdoc = new this.doc(this.data);
|
|
184
186
|
result = await newdoc.save();
|
|
185
|
-
await this.hook(HookType.afterCreate,result)
|
|
187
|
+
await this.hook(HookType.afterCreate, result);
|
|
186
188
|
} catch (err) {
|
|
187
189
|
throw new InternalServerErrorException(err.message);
|
|
188
190
|
}
|
|
@@ -190,7 +192,7 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
190
192
|
}
|
|
191
193
|
async update() {
|
|
192
194
|
const id: string = this.getRecordId();
|
|
193
|
-
Object.assign(this.data,
|
|
195
|
+
Object.assign(this.data, UserProvider.getInstance().getUpdateFilter());
|
|
194
196
|
await this.findIdThenUpdate(id, this.data);
|
|
195
197
|
}
|
|
196
198
|
async delete() {
|
|
@@ -202,44 +204,56 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
202
204
|
return dependency;
|
|
203
205
|
}
|
|
204
206
|
}
|
|
205
|
-
hook = async (type: string, data?: any) =>{
|
|
206
|
-
console.log(
|
|
207
|
+
hook = async (type: string, data?: any) => {
|
|
208
|
+
// console.log('Default hook', data);
|
|
207
209
|
return true;
|
|
208
|
-
}
|
|
210
|
+
};
|
|
209
211
|
async validateData(data: T) {
|
|
210
212
|
const ajv = new Ajv({ allErrors: true });
|
|
211
213
|
addFormats(ajv);
|
|
212
214
|
addErrors(ajv);
|
|
213
215
|
|
|
214
216
|
ajv.addFormat('x-document-no', /.*$/);
|
|
215
|
-
ajv.addFormat('x-document-
|
|
216
|
-
ajv.addFormat('x-text', /.*$/);
|
|
217
|
+
ajv.addFormat('x-document-label', /.*$/);
|
|
217
218
|
ajv.addFormat('tel', /^$|^\d{7,15}$/gm);
|
|
219
|
+
ajv.addFormat('x-text', /.*$/);
|
|
218
220
|
ajv.addFormat('x-html', /.*$/);
|
|
219
|
-
ajv.addKeyword({
|
|
220
|
-
keyword: 'x-foreignkey',
|
|
221
|
-
type: 'string',
|
|
222
|
-
});
|
|
223
221
|
|
|
224
|
-
|
|
222
|
+
ajv.addKeyword({ keyword: 'x-document-status', type: 'array' });
|
|
223
|
+
ajv.addKeyword({ keyword: 'x-document-api', type: 'array' });
|
|
224
|
+
ajv.addKeyword({ keyword: 'x-ignore-autocomplete', type: 'boolean' });
|
|
225
|
+
ajv.addKeyword({ keyword: 'x-isolation-type', type: 'string' });
|
|
226
|
+
ajv.addKeyword({ keyword: 'x-document-type', type: 'string' });
|
|
227
|
+
ajv.addKeyword({ keyword: 'x-document-name', type: 'string' });
|
|
228
|
+
ajv.addKeyword({ keyword: 'x-collection-name', type: 'string' });
|
|
229
|
+
ajv.addKeyword({ keyword: 'x-autocomplete-field', type: 'boolean' });
|
|
230
|
+
ajv.addKeyword({ keyword: 'x-foreignkey', type: 'string' });
|
|
231
|
+
|
|
232
|
+
const issuccess = await this.hook(HookType.beforeValidation, data);
|
|
225
233
|
if (!issuccess) {
|
|
226
234
|
const erromsg: string[] = [];
|
|
227
235
|
for (let i = 0; i < this.errorlist.length; i++) {
|
|
228
236
|
erromsg.push(this.errorlist[i].message);
|
|
229
237
|
}
|
|
230
|
-
|
|
238
|
+
this.logger.log('run hook during validation');
|
|
231
239
|
throw new InternalServerErrorException(erromsg.join('\n'));
|
|
232
240
|
}
|
|
233
|
-
|
|
241
|
+
|
|
242
|
+
let validate;
|
|
243
|
+
try {
|
|
244
|
+
validate = ajv.compile(this.jsonschema);
|
|
245
|
+
} catch (err) {
|
|
246
|
+
this.logger.error('compile error', err);
|
|
247
|
+
}
|
|
248
|
+
|
|
234
249
|
const valid = validate(data);
|
|
235
|
-
//console.log(validate.errors);
|
|
236
250
|
if (!valid) {
|
|
237
|
-
|
|
251
|
+
this.logger.error(validate.errors);
|
|
238
252
|
const erromsg: string[] = [];
|
|
239
253
|
for (let i = 0; i < ajv.errors.length; i++) {
|
|
240
254
|
erromsg.push(ajv.errors[i].message);
|
|
241
255
|
}
|
|
242
|
-
|
|
256
|
+
this.logger.error('ajv erromsg: ', erromsg);
|
|
243
257
|
throw new InternalServerErrorException(erromsg.join('\n'));
|
|
244
258
|
}
|
|
245
259
|
await this.hook(HookType.afterValidation, data);
|
|
@@ -248,7 +262,7 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
248
262
|
async findIdThenDelete(id: string): Promise<any> {
|
|
249
263
|
// const data = await this.findById(id);
|
|
250
264
|
try {
|
|
251
|
-
await this.hook(HookType.beforeDelete,id)
|
|
265
|
+
await this.hook(HookType.beforeDelete, id);
|
|
252
266
|
//console.log('deletedeletedeletedelete');
|
|
253
267
|
const dependency = await this.getRelatedRecords(id);
|
|
254
268
|
if (!dependency) {
|
|
@@ -256,7 +270,7 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
256
270
|
filter['_id'] = id;
|
|
257
271
|
const deleteresult = await this.doc.deleteOne(filter);
|
|
258
272
|
// this.doc.findByIdAndDelete(id)
|
|
259
|
-
await this.hook(HookType.afterDelete,deleteresult)
|
|
273
|
+
await this.hook(HookType.afterDelete, deleteresult);
|
|
260
274
|
//this.doc.findByIdAndDelete(id);
|
|
261
275
|
return deleteresult;
|
|
262
276
|
} else {
|
|
@@ -273,15 +287,15 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
273
287
|
|
|
274
288
|
findIdThenUpdate = async (id: string, data: T) => {
|
|
275
289
|
const existingdata = await this.findById(id);
|
|
276
|
-
await this.hook(HookType.beforeUpdate,data)
|
|
290
|
+
await this.hook(HookType.beforeUpdate, data);
|
|
277
291
|
try {
|
|
278
|
-
Object.assign(data,
|
|
292
|
+
Object.assign(data, UserProvider.getInstance().getUpdateFilter());
|
|
279
293
|
Object.assign(existingdata, data);
|
|
280
294
|
await this.validateData(data);
|
|
281
295
|
let filter = this.getIsolationFilter();
|
|
282
296
|
filter['_id'] = id;
|
|
283
297
|
const result = await this.doc.findOneAndUpdate(filter, data);
|
|
284
|
-
await this.hook(HookType.afterUpdate,data)
|
|
298
|
+
await this.hook(HookType.afterUpdate, data);
|
|
285
299
|
return result;
|
|
286
300
|
} catch (err) {
|
|
287
301
|
throw new InternalServerErrorException(err.message);
|
|
@@ -317,4 +331,36 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
317
331
|
}
|
|
318
332
|
return Promise.resolve(null);
|
|
319
333
|
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* change property documentStatus for specific document, no workflow execution
|
|
337
|
+
* @param id
|
|
338
|
+
* @param docstatus
|
|
339
|
+
* @returns Promise
|
|
340
|
+
*/
|
|
341
|
+
async setDocumentStatus(id: string, docstatus: string) {
|
|
342
|
+
const partialdata: T = {} as T;
|
|
343
|
+
partialdata['documentStatus'] = docstatus;
|
|
344
|
+
return this.findIdThenUpdate(id, partialdata);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* change property documentStatus for specific document, no workflow execution
|
|
349
|
+
* @param id
|
|
350
|
+
* @param docstatus
|
|
351
|
+
* @returns Promise
|
|
352
|
+
*/
|
|
353
|
+
async executeWorkFlow(id: string, bpmnname: string, docstatus: string) {
|
|
354
|
+
const data = await this.findById(id);
|
|
355
|
+
return Workflow.getInstance().executeWorkFlow(
|
|
356
|
+
id,
|
|
357
|
+
bpmnname,
|
|
358
|
+
docstatus,
|
|
359
|
+
data,
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async getMyUserTask() {
|
|
364
|
+
return Workflow.getInstance().getMyUserTask();
|
|
365
|
+
}
|
|
320
366
|
}
|
|
@@ -2,38 +2,33 @@ import { Injectable, NestMiddleware, Scope } from '@nestjs/common';
|
|
|
2
2
|
import { Request, Response, NextFunction } from 'express';
|
|
3
3
|
// import * as jwt from 'nestjs-jwt'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
import { User } from './User';
|
|
5
|
+
import { UserProvider } from './UserProvider';
|
|
7
6
|
// import {KeycloakConfigService} from "../keycloak/keycloak.service"
|
|
8
7
|
@Injectable({
|
|
9
8
|
scope: Scope.REQUEST,
|
|
10
9
|
})
|
|
11
|
-
|
|
12
10
|
export class TenantMiddleware implements NestMiddleware {
|
|
13
11
|
use(req: Request, res: Response, next: NextFunction) {
|
|
14
12
|
if (req.baseUrl == '/oauth2-redirect.html') {
|
|
15
13
|
next();
|
|
16
|
-
return
|
|
14
|
+
return;
|
|
17
15
|
}
|
|
18
|
-
if(!req.headers['authorization']){
|
|
16
|
+
if (!req.headers['authorization']) {
|
|
19
17
|
return res.status(401).send('Undefine bearer token');
|
|
20
18
|
}
|
|
21
|
-
if (!req.headers['x-org']){
|
|
19
|
+
if (!req.headers['x-org']) {
|
|
22
20
|
return res.status(401).send('undefine header string x-org');
|
|
23
21
|
}
|
|
24
|
-
const u =
|
|
22
|
+
const u = UserProvider.getInstance();
|
|
25
23
|
|
|
26
24
|
try {
|
|
27
25
|
u.setXorg(req.headers['x-org'].toString());
|
|
28
|
-
let tokenstr:string = req.headers['authorization']
|
|
29
|
-
tokenstr = tokenstr.replace(
|
|
30
|
-
u.setUserToken(tokenstr);
|
|
26
|
+
let tokenstr: string = req.headers['authorization'];
|
|
27
|
+
tokenstr = tokenstr.replace('Bearer ', '');
|
|
28
|
+
u.setUserToken(tokenstr);
|
|
31
29
|
next();
|
|
32
30
|
} catch {
|
|
33
31
|
return res.status(401).send('Invalid x-org or user info');
|
|
34
32
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
33
|
}
|
|
39
34
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Injectable, Scope } from '@nestjs/common';
|
|
2
|
+
import Base64URL from '@darkwolf/base64url';
|
|
3
|
+
import * as jwt from 'jsonwebtoken';
|
|
4
|
+
|
|
5
|
+
@Injectable({
|
|
6
|
+
scope: Scope.REQUEST,
|
|
7
|
+
})
|
|
8
|
+
export class UserProvider {
|
|
9
|
+
private static instance: UserProvider;
|
|
10
|
+
protected uid: string = '';
|
|
11
|
+
protected uname: string = '';
|
|
12
|
+
protected email: string = '';
|
|
13
|
+
protected fullname: string = '';
|
|
14
|
+
protected xOrg: string = '';
|
|
15
|
+
protected tenantId: number = 0;
|
|
16
|
+
protected orgId: number = 0;
|
|
17
|
+
protected branchId: number = 0;
|
|
18
|
+
protected accessrights: any = {};
|
|
19
|
+
protected token: string = '';
|
|
20
|
+
protected refreshtoken: string = '';
|
|
21
|
+
protected groups: string[] = [];
|
|
22
|
+
constructor() {}
|
|
23
|
+
public static getInstance(): UserProvider {
|
|
24
|
+
if (!UserProvider.instance) {
|
|
25
|
+
UserProvider.instance = new UserProvider();
|
|
26
|
+
}
|
|
27
|
+
return UserProvider.instance;
|
|
28
|
+
}
|
|
29
|
+
getUid = () => this.uid;
|
|
30
|
+
getUname = () => this.uname;
|
|
31
|
+
getFullname = () => this.fullname;
|
|
32
|
+
getTenantId = () => this.tenantId;
|
|
33
|
+
getOrgId = () => this.orgId;
|
|
34
|
+
getBranchId = () => this.branchId;
|
|
35
|
+
getEmail = () => this.email;
|
|
36
|
+
getGroups = () => this.groups;
|
|
37
|
+
setUserToken = (tokenstr: string) => {
|
|
38
|
+
const tokeninfo = jwt.decode(tokenstr);
|
|
39
|
+
// realm_access: {
|
|
40
|
+
// roles: [
|
|
41
|
+
// 'default-roles-simitdeveloper',
|
|
42
|
+
// 'offline_access',
|
|
43
|
+
// 'uma_authorization'
|
|
44
|
+
// ]
|
|
45
|
+
// },
|
|
46
|
+
// resource_access: { account: { roles: [Array] } },
|
|
47
|
+
// scope: 'openid email profile',
|
|
48
|
+
// sid: '53192f53-d4af-413b-b8d7-1e186419fe53',
|
|
49
|
+
// email_verified: false,
|
|
50
|
+
// name: 'kstan kstan',
|
|
51
|
+
// preferred_username: 'kstan',
|
|
52
|
+
// given_name: 'kstan',
|
|
53
|
+
// family_name: 'kstan',
|
|
54
|
+
// email: 'kstan@simitgroup.com'
|
|
55
|
+
|
|
56
|
+
const u = UserProvider.getInstance();
|
|
57
|
+
u.token = tokenstr;
|
|
58
|
+
u.uid = tokeninfo.sub;
|
|
59
|
+
u.email = tokeninfo.email;
|
|
60
|
+
u.uname = tokeninfo.preferred_username;
|
|
61
|
+
u.fullname = tokeninfo.name;
|
|
62
|
+
u.groups = ['superuser', 'accountant', 'directors'];
|
|
63
|
+
u.accessrights = tokeninfo.resource_access;
|
|
64
|
+
};
|
|
65
|
+
getInfo = () => {
|
|
66
|
+
return UserProvider.getInstance();
|
|
67
|
+
};
|
|
68
|
+
getBranchFilter = () => {
|
|
69
|
+
return {
|
|
70
|
+
tenantId: UserProvider.getInstance().tenantId,
|
|
71
|
+
orgId: UserProvider.getInstance().orgId,
|
|
72
|
+
branchId: UserProvider.getInstance().branchId,
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
getTenantFilter = () => {
|
|
76
|
+
return { tenantId: UserProvider.getInstance().tenantId };
|
|
77
|
+
};
|
|
78
|
+
getOrgFilter = () => {
|
|
79
|
+
return {
|
|
80
|
+
tenantId: UserProvider.getInstance().tenantId,
|
|
81
|
+
orgId: UserProvider.getInstance().orgId,
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
getWorkflowTaskFilter() {
|
|
85
|
+
return {
|
|
86
|
+
'data.tenantId': UserProvider.getInstance().tenantId,
|
|
87
|
+
'assignments.assignee': UserProvider.getInstance().getUid(),
|
|
88
|
+
// 'assignments.assignee': User.getInstance().getUid(),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
getCreateFilter = () => {
|
|
92
|
+
const u = UserProvider.getInstance();
|
|
93
|
+
return {
|
|
94
|
+
tenantId: u.tenantId,
|
|
95
|
+
orgId: u.orgId,
|
|
96
|
+
branchId: u.branchId,
|
|
97
|
+
createdby: u.uid,
|
|
98
|
+
updatedby: u.uid,
|
|
99
|
+
created: new Date().getTime().toString(),
|
|
100
|
+
updated: new Date().getTime().toString(),
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
getUpdateFilter = () => {
|
|
104
|
+
const u = UserProvider.getInstance();
|
|
105
|
+
return {
|
|
106
|
+
updatedby: u.uid,
|
|
107
|
+
updated: new Date().getTime().toString(),
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
setXorg = (xorg) => {
|
|
111
|
+
try {
|
|
112
|
+
const decodedText: string = Base64URL.decodeText(xorg);
|
|
113
|
+
const arrXorg = decodedText.split('-');
|
|
114
|
+
|
|
115
|
+
if (arrXorg.length == 3) {
|
|
116
|
+
const u = UserProvider.getInstance();
|
|
117
|
+
u.tenantId = Number(arrXorg[0]);
|
|
118
|
+
u.orgId = Number(arrXorg[1]);
|
|
119
|
+
u.branchId = Number(arrXorg[2]);
|
|
120
|
+
} else {
|
|
121
|
+
throw 'invalid x-org';
|
|
122
|
+
}
|
|
123
|
+
} catch (err) {
|
|
124
|
+
throw err;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { BPMNClient } from "bpmn-client";
|
|
2
|
+
import { UserProvider } from './UserProvider';
|
|
3
|
+
import { Injectable } from '@nestjs/common';
|
|
4
|
+
|
|
5
|
+
export type UserTaskActorOptions={
|
|
6
|
+
userId:string
|
|
7
|
+
actGroups?:string[]
|
|
8
|
+
actUsers?:string[]
|
|
9
|
+
}
|
|
10
|
+
@Injectable()
|
|
11
|
+
export class Workflow{
|
|
12
|
+
private static instance: Workflow;
|
|
13
|
+
server:BPMNClient
|
|
14
|
+
|
|
15
|
+
constructor(){
|
|
16
|
+
this.server= new BPMNClient(process.env.BPMN_HOST, process.env.BPMN_PORT, process.env.BPMN_API_KEY);
|
|
17
|
+
}
|
|
18
|
+
public static getInstance(): Workflow {
|
|
19
|
+
if (!Workflow.instance) {
|
|
20
|
+
Workflow.instance = new Workflow();
|
|
21
|
+
}
|
|
22
|
+
return Workflow.instance;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async executeWorkFlow(id: string, bpmnname: string, docstatus: string,data:any) {
|
|
26
|
+
const workflowactoroptions : UserTaskActorOptions ={
|
|
27
|
+
userId:UserProvider.getInstance().getUid(),
|
|
28
|
+
actGroups:[],
|
|
29
|
+
actUsers:[]
|
|
30
|
+
}
|
|
31
|
+
var instance = await this.server.engine.start(bpmnname, data,null,workflowactoroptions);
|
|
32
|
+
const res = {instanceId:instance.id,data:instance.data,name:instance.name,status:instance.status}
|
|
33
|
+
return Promise.resolve(res);
|
|
34
|
+
}
|
|
35
|
+
async getMyUserTask(){
|
|
36
|
+
const uid = UserProvider.getInstance().getUid()
|
|
37
|
+
const groups = UserProvider.getInstance().getGroups()
|
|
38
|
+
// 'assignments.candidateUsers': User.getInstance().getUid(),
|
|
39
|
+
// 'assignments.candidateGroups': User.getInstance().getGroups
|
|
40
|
+
|
|
41
|
+
let anyof:any = [
|
|
42
|
+
{'items.assignments.assignee':uid},
|
|
43
|
+
]
|
|
44
|
+
let usersfilter={}
|
|
45
|
+
usersfilter[uid]={$in: 'items.assignments.candidateUsers'}
|
|
46
|
+
anyof.push(usersfilter)
|
|
47
|
+
//{'data.tenantId': 1,'$or':[{
|
|
48
|
+
// 'items.assignments.assignee': 'b2a49a8f-a943-4814-8087-60b1ef2f304f'
|
|
49
|
+
// }]}
|
|
50
|
+
//any of the group
|
|
51
|
+
for(let i=0;i<groups.length;i++){
|
|
52
|
+
const gname = groups[i]
|
|
53
|
+
const tmp = {}
|
|
54
|
+
tmp[gname]={ $in: 'items.assignments.candidateGroups'}
|
|
55
|
+
anyof.push(tmp)
|
|
56
|
+
}
|
|
57
|
+
const filters = {
|
|
58
|
+
'data.tenantId': UserProvider.getInstance().getTenantId(),
|
|
59
|
+
'$or':anyof
|
|
60
|
+
}
|
|
61
|
+
console.dir(filters)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
// 'assignments.assignee': User.getInstance().getUid(),
|
|
65
|
+
const client = new BPMNClient(process.env.BPMN_HOST, process.env.BPMN_PORT, process.env.BPMN_API_KEY);
|
|
66
|
+
const items = await client.datastore.findItems(filters)
|
|
67
|
+
let data=[]
|
|
68
|
+
for(let i=0;i<items.length;i++){
|
|
69
|
+
if(items[i].status=='wait'){
|
|
70
|
+
data.push(items[i])
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return Promise.resolve(data);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Controller, Get } from '@nestjs/common';
|
|
2
|
+
import { AppService } from './app.service';
|
|
3
|
+
|
|
4
|
+
@Controller()
|
|
5
|
+
export class AppController {
|
|
6
|
+
constructor(private readonly appService: AppService) {}
|
|
7
|
+
|
|
8
|
+
@Get('myworkflowtask')
|
|
9
|
+
async getHello() {
|
|
10
|
+
return await this.appService.getMyTask();
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -10,6 +10,8 @@ import { ConfigModule } from '@nestjs/config';
|
|
|
10
10
|
import { ServeStaticModule } from '@nestjs/serve-static';
|
|
11
11
|
import { join } from 'path';
|
|
12
12
|
import {TenantMiddleware} from './class/TenantMiddleware'
|
|
13
|
+
import {AppController} from './app.controller'
|
|
14
|
+
import {AppService} from './app.service'
|
|
13
15
|
<% for(let i=0;i<it.length; i++){ %>
|
|
14
16
|
import {<%= it[i].docname %>Module} from './docs/<%= it[i].doctype %>/<%= it[i].doctype %>.module'
|
|
15
17
|
<%}%>
|
|
@@ -32,8 +34,10 @@ import {<%= it[i].docname %>Module} from './docs/<%= it[i].doctype %>/<%= it[i].
|
|
|
32
34
|
tokenValidation: TokenValidation.ONLINE,
|
|
33
35
|
}),
|
|
34
36
|
<% for(let i=0;i<it.length; i++){ %><%= it[i].docname %>Module,<%}%>],
|
|
35
|
-
controllers: [],
|
|
36
|
-
providers: [
|
|
37
|
+
controllers: [AppController],
|
|
38
|
+
providers: [
|
|
39
|
+
AppService,
|
|
40
|
+
{
|
|
37
41
|
provide: APP_GUARD,
|
|
38
42
|
useClass: AuthGuard,
|
|
39
43
|
},
|
|
@@ -6,6 +6,13 @@ PROJECT_NAME=SimpleApp Demo1
|
|
|
6
6
|
PROJECT_DESCRIPTION=Try CRUD
|
|
7
7
|
PROJECT_Version=1.0.0
|
|
8
8
|
|
|
9
|
+
BPMN_HOST=<%=it.bpmnsetting.BPMN_HOST%>
|
|
10
|
+
|
|
11
|
+
BPMN_PORT=<%=it.bpmnsetting.BPMN_PORT%>
|
|
12
|
+
|
|
13
|
+
BPMN_API_KEY=<%=it.bpmnsetting.BPMN_API_KEY%>
|
|
14
|
+
|
|
15
|
+
|
|
9
16
|
OAUTH2_BASEURL=<%=it.keycloaksetting.OAUTH2_BASEURL%>
|
|
10
17
|
|
|
11
18
|
OAUTH2_REALM=<%=it.keycloaksetting.OAUTH2_REALM%>
|