@simitgroup/simpleapp-generator 1.6.4-e-alpha → 1.6.4-g-alpha
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/ReleaseNote.md +13 -0
- package/dist/buildinschemas/index.d.ts +0 -1
- package/dist/buildinschemas/index.d.ts.map +1 -1
- package/dist/buildinschemas/index.js +1 -3
- package/dist/buildinschemas/index.js.map +1 -1
- package/dist/buildinschemas/systemmessage.js +3 -3
- package/dist/buildinschemas/systemmessage.js.map +1 -1
- package/dist/buildinschemas/user.d.ts.map +1 -1
- package/dist/buildinschemas/user.js +7 -0
- package/dist/buildinschemas/user.js.map +1 -1
- package/dist/buildinschemas/webhook.d.ts.map +1 -1
- package/dist/buildinschemas/webhook.js +20 -5
- package/dist/buildinschemas/webhook.js.map +1 -1
- package/package.json +1 -1
- package/src/buildinschemas/index.ts +0 -1
- package/src/buildinschemas/systemmessage.ts +3 -3
- package/src/buildinschemas/user.ts +7 -0
- package/src/buildinschemas/webhook.ts +21 -7
- package/templates/basic/nest/controller.ts.eta +3 -3
- package/templates/nest/.env._eta +7 -0
- package/templates/nest/src/app.module.ts.eta +12 -5
- package/templates/nest/src/main.ts.eta +30 -0
- package/templates/nest/src/simpleapp/generate/apischemas/simpleapp.apischema.ts.eta +32 -12
- package/templates/nest/src/simpleapp/generate/commons/audittrail.service.ts.eta +31 -26
- package/templates/nest/src/simpleapp/generate/commons/customkeycloa.guard.ts.eta +43 -0
- package/templates/nest/src/simpleapp/generate/commons/dicts/documents.ts.eta +2 -1
- package/templates/nest/src/simpleapp/generate/commons/middlewares/tenant.middleware.ts.eta +31 -5
- package/templates/nest/src/simpleapp/generate/commons/robotuser.service.ts.eta +15 -17
- package/templates/nest/src/simpleapp/generate/commons/runwebhook.service.ts.eta +161 -29
- package/templates/nest/src/simpleapp/generate/commons/user.context.ts.eta +17 -2
- package/templates/nest/src/simpleapp/generate/processors/simpleapp.processor.ts.eta +4 -6
- package/templates/nest/src/simpleapp/generate/processors/webhook.processor.ts.eta +224 -0
- package/templates/nuxt/components/calendar/CalendarSmall.vue.eta +113 -110
- package/templates/nuxt/components/image/ImageAvatar.vue.eta._vue +47 -13
- package/templates/nuxt/components/image/ImageToBase64Uploader.vue.eta.vue +5 -5
- package/templates/nuxt/components/renderer/RendererDocHistories.vue.eta +8 -5
- package/templates/nuxt/components/simpleApp/SimpleAppInput.vue.eta +25 -18
- package/templates/nuxt/composables/stringHelper.generate.ts.eta +13 -9
- package/templates/nuxt/pages/profile.vue.eta +3 -1
- package/templates/nuxt/server/api/profile/[...].ts.eta +11 -2
- package/templates/nuxt/simpleapp/generate/commons/documents.ts.eta +3 -1
- package/templates/nuxt/types/others.ts.eta +1 -0
- package/templates/nuxt/types/simpleappinput.ts.eta +2 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/src/buildinschemas/webhookhistory.ts +0 -42
|
@@ -11,12 +11,12 @@ import { Model } from 'mongoose';
|
|
|
11
11
|
import { UserContext } from 'src/simpleapp/generate/commons/user.context';
|
|
12
12
|
import { User } from 'src/simpleapp/services/user.service';
|
|
13
13
|
import { Permission } from 'src/simpleapp/services/perm.service';
|
|
14
|
-
|
|
14
|
+
const Base64URL = require('@darkwolf/base64url');
|
|
15
15
|
@Injectable()
|
|
16
16
|
export class SimpleAppRobotUserService {
|
|
17
17
|
private systemAccessToken: string;
|
|
18
|
-
private setToken = (token:string) =>
|
|
19
|
-
private getToken = ()=>this.systemAccessToken
|
|
18
|
+
private setToken = (token: string) => (this.systemAccessToken = token);
|
|
19
|
+
private getToken = () => this.systemAccessToken;
|
|
20
20
|
private expired: string;
|
|
21
21
|
logger = new Logger();
|
|
22
22
|
@InjectModel('User') private readonly usermodel: Model<User>;
|
|
@@ -28,7 +28,7 @@ export class SimpleAppRobotUserService {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
async init() {
|
|
31
|
-
await this.refreshSystemToken();
|
|
31
|
+
// await this.refreshSystemToken();
|
|
32
32
|
}
|
|
33
33
|
async refreshSystemToken() {
|
|
34
34
|
enum GrantType {
|
|
@@ -76,25 +76,27 @@ export class SimpleAppRobotUserService {
|
|
|
76
76
|
return data;
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
-
this.setToken(tokens.access_token)
|
|
80
|
-
|
|
79
|
+
this.setToken(tokens.access_token);
|
|
80
|
+
console.log("access token ",this.getToken())
|
|
81
81
|
const nextrefresh = tokens.expires_in * 0.8;
|
|
82
82
|
const appuser = this.prepareAppUser(undefined);
|
|
83
|
-
|
|
84
|
-
if(tokens.access_token){
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
83
|
+
|
|
84
|
+
// if (tokens.access_token) {
|
|
85
|
+
// setTimeout(async () => {
|
|
86
|
+
// await this.refreshSystemToken();
|
|
87
|
+
// }, nextrefresh * 1000);
|
|
88
|
+
// }
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
prepareAppUser(data: any) {
|
|
92
|
+
console.log("prepareAppUserprepareAppUser");
|
|
92
93
|
const appuser = new UserContext(this.usermodel, this.permmodel);
|
|
93
94
|
appuser.setAsStaticUser(
|
|
94
95
|
'00000000-0000-0000-0000-000000000000',
|
|
95
96
|
'robot',
|
|
96
97
|
'Robot',
|
|
97
98
|
'robot@a.org',
|
|
99
|
+
Base64URL.encodeText('0-0-0')
|
|
98
100
|
);
|
|
99
101
|
|
|
100
102
|
const tenantId = data?.tenantId ?? 0;
|
|
@@ -103,11 +105,7 @@ export class SimpleAppRobotUserService {
|
|
|
103
105
|
|
|
104
106
|
appuser.setXorg(appuser.generateXorg(tenantId, orgId, branchId));
|
|
105
107
|
appuser.setUserToken(this.getToken());
|
|
106
|
-
|
|
107
|
-
// 'return user ' + appuser.getUname(),
|
|
108
|
-
// appuser.getXorg(),
|
|
109
|
-
// appuser.getUserToken().length,
|
|
110
|
-
// );
|
|
108
|
+
|
|
111
109
|
return appuser;
|
|
112
110
|
}
|
|
113
111
|
}
|
|
@@ -11,16 +11,27 @@ import {
|
|
|
11
11
|
Webhook,
|
|
12
12
|
WebhookService,
|
|
13
13
|
} from 'src/simpleapp/services/webhook.service';
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
WebhookhistoryService,
|
|
17
|
-
} from 'src/simpleapp/services/webhookhistory.service';
|
|
14
|
+
import {alldocuments} from 'src/simpleapp/generate/commons/dicts/documents'
|
|
15
|
+
|
|
18
16
|
@Injectable()
|
|
19
17
|
export class RunWebhookService {
|
|
18
|
+
private webhookApiKey = process.env.WEBHOOK_SERVER_APIKEY
|
|
19
|
+
private webhookAppId=process.env.WEBHOOK_SERVER_APP_ID
|
|
20
|
+
private webhookurl = process.env.WEBHOOK_SERVER_URL;
|
|
21
|
+
private webhookprefix = process.env.PROJECT_CODE+'.'
|
|
22
|
+
private webhookeventurl = `${process.env.WEBHOOK_SERVER_URL}/event/`;
|
|
23
|
+
private webhookeventtypeurl = `${process.env.WEBHOOK_SERVER_URL}/event_types/`;
|
|
24
|
+
private webhooksync:boolean = process.env.WEBHOOKER_SYNC_EVENTTYPE=='true'
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
20
28
|
public constructor(
|
|
21
|
-
private webhookService: WebhookService,
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
private webhookService: WebhookService,
|
|
30
|
+
) {
|
|
31
|
+
if(this.webhooksync) this.syncEventTypes()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
24
35
|
@OnEvent('webhook')
|
|
25
36
|
async loadWebhook(
|
|
26
37
|
appuser: UserContext,
|
|
@@ -28,42 +39,163 @@ export class RunWebhookService {
|
|
|
28
39
|
actionName: string,
|
|
29
40
|
data?: any,
|
|
30
41
|
) {
|
|
31
|
-
let subscribeall = false;
|
|
32
|
-
const webhooks = await this.webhookService.search(appuser, {
|
|
33
|
-
documentName: documentName,
|
|
34
|
-
active: true,
|
|
35
|
-
});
|
|
36
|
-
if (webhooks.length == 0) return;
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
//no webhook server implemented
|
|
44
|
+
if(!this.webhookApiKey) return true
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
const filter = {
|
|
48
|
+
eventTypes: documentName + '.' + actionName ,
|
|
49
|
+
active: true,
|
|
44
50
|
}
|
|
51
|
+
console.log("Load webhook",documentName,actionName,filter)
|
|
52
|
+
const webhooks = await this.webhookService.search(appuser, filter);
|
|
53
|
+
console.log("Loaded webhook",webhooks)
|
|
54
|
+
if (webhooks.length == 0) return;
|
|
45
55
|
|
|
46
|
-
|
|
47
|
-
|
|
56
|
+
for (let i = 0; i < webhooks.length; i++) {
|
|
57
|
+
const webhook = webhooks[i];
|
|
58
|
+
try {
|
|
59
|
+
const res = await this.runWebHook(
|
|
60
|
+
appuser,
|
|
61
|
+
webhook,
|
|
62
|
+
documentName,
|
|
63
|
+
actionName,
|
|
64
|
+
data,
|
|
65
|
+
);
|
|
66
|
+
} catch (e) {
|
|
67
|
+
return e;
|
|
68
|
+
}
|
|
48
69
|
}
|
|
49
70
|
}
|
|
50
71
|
|
|
51
|
-
async runWebHook(
|
|
52
|
-
|
|
53
|
-
|
|
72
|
+
async runWebHook(
|
|
73
|
+
appuser: UserContext,
|
|
74
|
+
webhook: Webhook,
|
|
75
|
+
documentName: string,
|
|
76
|
+
actionName: string,
|
|
77
|
+
data: any,
|
|
78
|
+
) {
|
|
79
|
+
|
|
80
|
+
const headers = {};
|
|
81
|
+
|
|
82
|
+
console.log("Run webhook ",documentName,actionName)
|
|
54
83
|
if (Array.isArray(webhook.headers)) {
|
|
55
84
|
webhook.headers.forEach((h) => {
|
|
56
85
|
headers[h.name] = h.value;
|
|
57
86
|
});
|
|
58
87
|
}
|
|
88
|
+
headers['accept'] = 'application/json';
|
|
89
|
+
headers['content-type'] = 'application/json';
|
|
90
|
+
headers['Authorization'] = this.webhookApiKey;
|
|
91
|
+
const eventname = `${this.webhookprefix}${documentName}.${actionName}`
|
|
59
92
|
const options = {
|
|
60
|
-
method: webhook.requestMethod,
|
|
61
|
-
body: JSON.stringify(data),
|
|
93
|
+
method: webhook.requestMethod.toUpperCase(),
|
|
62
94
|
headers: headers,
|
|
95
|
+
body: JSON.stringify({
|
|
96
|
+
labels: {
|
|
97
|
+
tenantId: appuser.getTenantId().toString(),
|
|
98
|
+
},
|
|
99
|
+
metadata: {
|
|
100
|
+
src: process.env.PROJECT_NAME,
|
|
101
|
+
user: appuser.getFullname(),
|
|
102
|
+
},
|
|
103
|
+
application_id: this.webhookAppId,
|
|
104
|
+
event_type: eventname,
|
|
105
|
+
event_id: crypto.randomUUID(),
|
|
106
|
+
occurred_at: new Date().toISOString(),
|
|
107
|
+
payload: JSON.stringify(data),
|
|
108
|
+
payload_content_type: 'application/json',
|
|
109
|
+
}),
|
|
110
|
+
};
|
|
111
|
+
try {
|
|
112
|
+
console.log("webhook request ",this.webhookeventurl,options)
|
|
113
|
+
const req = await fetch(this.webhookeventurl, options);
|
|
114
|
+
const statusCode = req.status;
|
|
115
|
+
if(statusCode>=300){
|
|
116
|
+
throw new InternalServerErrorException(`create event ${eventname} failed to webhook server`)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// const body = req.body;
|
|
120
|
+
} catch (e) {
|
|
121
|
+
return e
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
async syncEventTypes(){
|
|
128
|
+
const options = {
|
|
129
|
+
method: 'GET',
|
|
130
|
+
headers: {
|
|
131
|
+
accept: 'application/json',
|
|
132
|
+
Authorization: this.webhookApiKey
|
|
133
|
+
}};
|
|
134
|
+
// {
|
|
135
|
+
// "service_name": "billing",
|
|
136
|
+
// "resource_type_name": "invoice",
|
|
137
|
+
// "verb_name": "documentno",
|
|
138
|
+
// "event_type_name": "billing.invoice.documentno"
|
|
139
|
+
// },
|
|
140
|
+
try{
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
const res = await fetch(
|
|
144
|
+
`${this.webhookeventtypeurl}?application_id=${this.webhookAppId}`,
|
|
145
|
+
options)
|
|
146
|
+
|
|
147
|
+
const locallist = this.getSchemaEventTypes()
|
|
148
|
+
const eventlist = (await res.json()).map(item=>item.event_type_name)
|
|
149
|
+
|
|
150
|
+
for(let i=0; i<locallist.length;i++){
|
|
151
|
+
const item = locallist[i]
|
|
152
|
+
if(!eventlist.includes(item)){
|
|
153
|
+
//create remote event type
|
|
154
|
+
await this.createEventType(item)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
}catch(e){
|
|
160
|
+
throw new InternalServerErrorException(e)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
async createEventType(eventname:string){
|
|
168
|
+
const para = eventname.split('.')
|
|
169
|
+
const options = {
|
|
170
|
+
method: 'POST',
|
|
171
|
+
headers: {
|
|
172
|
+
accept: 'application/json',
|
|
173
|
+
'content-type': 'application/json',
|
|
174
|
+
Authorization: this.webhookApiKey
|
|
175
|
+
},
|
|
176
|
+
body: JSON.stringify({
|
|
177
|
+
application_id: this.webhookAppId,
|
|
178
|
+
resource_type: para[0],
|
|
179
|
+
service: para[1],
|
|
180
|
+
verb: para[2]
|
|
181
|
+
})
|
|
63
182
|
};
|
|
64
183
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
184
|
+
await fetch(this.webhookeventtypeurl, options)
|
|
185
|
+
// .then(response => response.json())
|
|
186
|
+
// .then(response => console.log(response))
|
|
187
|
+
// .catch(err => console.error(err));
|
|
188
|
+
}
|
|
189
|
+
getSchemaEventTypes(){
|
|
190
|
+
const alleventtypes:string[] = []
|
|
191
|
+
alldocuments.filter(item=>Array.isArray(item.webhook))
|
|
192
|
+
.forEach(item=>{
|
|
193
|
+
item.webhook.forEach(h=>{
|
|
194
|
+
alleventtypes.push(
|
|
195
|
+
`${this.webhookprefix}${item.docName.toLowerCase()}.${h}`)
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
return alleventtypes
|
|
199
|
+
|
|
68
200
|
}
|
|
69
201
|
}
|
|
@@ -58,6 +58,9 @@ export class UserContext {
|
|
|
58
58
|
protected ssoACL: any = {};
|
|
59
59
|
protected token: string = '';
|
|
60
60
|
protected refreshtoken: string = '';
|
|
61
|
+
//guest access token obtain from header 'x-guest-accesstoken', during use x-apikey/x-apisecret
|
|
62
|
+
protected guestToken?: string='';
|
|
63
|
+
protected guestInfo:{uid:string,uname:string,fullname:string,email:string} = {uid:'',uname:'',fullname:'',email:''}
|
|
61
64
|
protected groups: string[] = [];
|
|
62
65
|
protected branchCode: string = '';
|
|
63
66
|
protected branchName: string = '';
|
|
@@ -110,6 +113,7 @@ export class UserContext {
|
|
|
110
113
|
getOffsetMinute = () => this.offsetMinute;
|
|
111
114
|
getGroups = () => this.groups;
|
|
112
115
|
getCurrency = () => this.currency;
|
|
116
|
+
getGuestInfo = () => this.guestInfo;
|
|
113
117
|
getMoreProps = () => this.moreProps;
|
|
114
118
|
getRoles = () => this.roles;
|
|
115
119
|
getModifieds = () => this.modifiedRecords;
|
|
@@ -282,10 +286,11 @@ export class UserContext {
|
|
|
282
286
|
this.uid = tokeninfo?.sub ?? '';
|
|
283
287
|
this.email = tokeninfo?.email ?? '';
|
|
284
288
|
this.uname = tokeninfo?.preferred_username ?? '';
|
|
285
|
-
this.fullname = tokeninfo?.name ??
|
|
289
|
+
this.fullname = tokeninfo?.name ?? '';
|
|
286
290
|
this.ssoACL = tokeninfo?.resource_access ?? [];
|
|
287
291
|
this.logger.verbose(`set token ${this.uid}`);
|
|
288
292
|
//read current user from db
|
|
293
|
+
// console.log("Set User token")
|
|
289
294
|
// console.log("await this.obtainProfileFromDb()")
|
|
290
295
|
const userinfo = await this.obtainProfileFromDb();
|
|
291
296
|
this.logger.verbose(userinfo, 'obtainProfileFromDb result');
|
|
@@ -696,6 +701,7 @@ export class UserContext {
|
|
|
696
701
|
uname: string,
|
|
697
702
|
name: string,
|
|
698
703
|
email: string,
|
|
704
|
+
xorg:string
|
|
699
705
|
) => {
|
|
700
706
|
//define token info
|
|
701
707
|
this.token = '';
|
|
@@ -704,9 +710,18 @@ export class UserContext {
|
|
|
704
710
|
this.uname = uname;
|
|
705
711
|
this.fullname = name;
|
|
706
712
|
this.ssoACL = '';
|
|
707
|
-
this.roles = [Role.Everyone, Role.User];
|
|
713
|
+
this.roles = [Role.Everyone, Role.User,Role.SuperUser];
|
|
714
|
+
this.setXorg(xorg);
|
|
708
715
|
};
|
|
709
716
|
|
|
717
|
+
setGuestToken(tokenstr:string){
|
|
718
|
+
const tokeninfo = jwt.decode(tokenstr);
|
|
719
|
+
this.guestInfo.uid = tokeninfo?.sub ?? '';
|
|
720
|
+
this.guestInfo.email = tokeninfo?.email ?? '';
|
|
721
|
+
this.guestInfo.uname = tokeninfo?.preferred_username ?? '';
|
|
722
|
+
this.guestInfo.fullname = tokeninfo?.name ?? '';
|
|
723
|
+
|
|
724
|
+
}
|
|
710
725
|
/**
|
|
711
726
|
* define additional properties from user into moreProps
|
|
712
727
|
*/
|
|
@@ -367,7 +367,6 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
|
|
|
367
367
|
dbsession.startTransaction();
|
|
368
368
|
}
|
|
369
369
|
|
|
370
|
-
|
|
371
370
|
this.logger.debug(
|
|
372
371
|
'this.withDocNumberFormat :' +
|
|
373
372
|
this.withDocNumberFormat +
|
|
@@ -379,7 +378,6 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
|
|
|
379
378
|
await this.genNewDocNo(appuser, data);
|
|
380
379
|
}
|
|
381
380
|
|
|
382
|
-
|
|
383
381
|
let isolationFilter: any = { ...appuser.getCreateFilter() };
|
|
384
382
|
isolationFilter = this.polishIsolationFilter(isolationFilter, data);
|
|
385
383
|
|
|
@@ -623,14 +621,14 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
|
|
|
623
621
|
`You submit older version data "v${data.__v}"" but latest version = "v${existingdata.__v}"`,
|
|
624
622
|
);
|
|
625
623
|
}
|
|
626
|
-
this.logger.debug('warn1', existingdata);
|
|
624
|
+
// this.logger.debug('warn1', existingdata);
|
|
627
625
|
data.__v = existingdata.__v + 1;
|
|
628
626
|
// if (!existingdata) {
|
|
629
627
|
// throw new NotFoundException(`${id} not found`, 'not found');
|
|
630
628
|
// }
|
|
631
|
-
this.logger.debug('warn2');
|
|
629
|
+
// this.logger.debug('warn2');
|
|
632
630
|
if (this.hooks.beforeUpdate)
|
|
633
|
-
await this.hooks.beforeUpdate(appuser, id,
|
|
631
|
+
await this.hooks.beforeUpdate(appuser, id, existingdata,data);
|
|
634
632
|
|
|
635
633
|
const dbsession = appuser.getDBSession();
|
|
636
634
|
if (dbsession && !dbsession.inTransaction()) {
|
|
@@ -693,7 +691,7 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
|
|
|
693
691
|
data.__v = existingdata.__v + 1;
|
|
694
692
|
|
|
695
693
|
if (this.hooks.beforeUpdate)
|
|
696
|
-
await this.hooks.beforeUpdate(appuser, id, data
|
|
694
|
+
await this.hooks.beforeUpdate(appuser, id,existingdata, data);
|
|
697
695
|
|
|
698
696
|
const dbsession = appuser.getDBSession();
|
|
699
697
|
if (dbsession && !dbsession.inTransaction()) {
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file was automatically generated by simpleapp generator. Every
|
|
3
|
+
* MODIFICATION OVERRIDE BY GENERATEOR
|
|
4
|
+
* last change 2024-02-23
|
|
5
|
+
* Author: Ks Tan
|
|
6
|
+
*/
|
|
7
|
+
import { UserContext } from '../commons/user.context';
|
|
8
|
+
import * as sharelibs from '../sharelibs';
|
|
9
|
+
import { Injectable, InternalServerErrorException, } from '@nestjs/common';
|
|
10
|
+
import { InjectModel } from '@nestjs/mongoose';
|
|
11
|
+
import * as jsonpath from 'jsonpath';
|
|
12
|
+
import { Model } from 'mongoose';
|
|
13
|
+
import { WebhookJsonSchema } from '../jsonschemas/webhook.jsonschema';
|
|
14
|
+
import { SimpleAppService } from './simpleapp.processor';
|
|
15
|
+
import * as types from '../types';
|
|
16
|
+
import { DocNumberFormatGenerator } from '../commons/docnogenerator.service';
|
|
17
|
+
import {
|
|
18
|
+
WebhookBasicAuth,
|
|
19
|
+
WebhookHeaders,
|
|
20
|
+
Webhook,
|
|
21
|
+
} from '../types/webhook.type';
|
|
22
|
+
import {
|
|
23
|
+
DefaultWebhookBasicAuth,
|
|
24
|
+
DefaultWebhookHeaders,
|
|
25
|
+
DefaultWebhook,
|
|
26
|
+
} from '../defaults/webhook.default';
|
|
27
|
+
|
|
28
|
+
@Injectable()
|
|
29
|
+
export class WebhookProcessor extends SimpleAppService<Webhook> {
|
|
30
|
+
protected documentIdentityCode = 'title';
|
|
31
|
+
protected documentIdentityLabel = 'title';
|
|
32
|
+
private webhookApiKey = process.env.WEBHOOK_SERVER_APIKEY
|
|
33
|
+
private webhookAppId=process.env.WEBHOOK_SERVER_APP_ID
|
|
34
|
+
private webhookurl = process.env.WEBHOOK_SERVER_URL;
|
|
35
|
+
private webhookprefix = process.env.PROJECT_CODE+'.'
|
|
36
|
+
private webhooksubscribtionurl = `${process.env.WEBHOOK_SERVER_URL}/subscriptions/`;
|
|
37
|
+
|
|
38
|
+
protected hooks: types.WebhookHooks = {
|
|
39
|
+
beforeCreate: async (appuser: UserContext, data: types.Webhook) =>
|
|
40
|
+
this.beforeCreate(appuser, data),
|
|
41
|
+
beforeUpdate: async (
|
|
42
|
+
appuser: UserContext,
|
|
43
|
+
id: string,
|
|
44
|
+
prevdata: types.Webhook,
|
|
45
|
+
newdata: types.Webhook,
|
|
46
|
+
) => this.beforeUpdate(appuser, id, prevdata, newdata),
|
|
47
|
+
afterDelete: async (
|
|
48
|
+
appuser: UserContext,
|
|
49
|
+
result: types.DeleteResultType<types.Webhook>,
|
|
50
|
+
id: string,
|
|
51
|
+
) => this.afterDelete(appuser, result, id),
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
protected foreignkeys = {};
|
|
56
|
+
constructor(mydoc: Model<Webhook>) {
|
|
57
|
+
super('WEBHOOK', 'webhook', mydoc, types.IsolationType.tenant);
|
|
58
|
+
this.setSchema(WebhookJsonSchema);
|
|
59
|
+
this.setData(DefaultWebhook(crypto.randomUUID()));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
reCalculateValue(data: Webhook) {
|
|
63
|
+
//console.log('trigger new recalculate')
|
|
64
|
+
const $data = data;
|
|
65
|
+
const jsopbj = new jsonpath['JSONPath']();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async beforeCreate(appuser: UserContext, data: types.Webhook) {
|
|
69
|
+
const createResult = await this.addRemoteWebhook(appuser, data);
|
|
70
|
+
if (!createResult['subscription_id'])
|
|
71
|
+
throw new InternalServerErrorException(
|
|
72
|
+
'syncronize webhook server failed',
|
|
73
|
+
);
|
|
74
|
+
data.serverSubscriptionId = createResult.subscription_id;
|
|
75
|
+
data.serverSubscriptionSecret = createResult.secret;
|
|
76
|
+
// throw new BadRequestException("fail purposely")
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async beforeUpdate(
|
|
80
|
+
appuser: UserContext,
|
|
81
|
+
id: string,
|
|
82
|
+
prevdata: types.Webhook,
|
|
83
|
+
newdata: types.Webhook,
|
|
84
|
+
) {
|
|
85
|
+
|
|
86
|
+
if(!prevdata.serverSubscriptionId){
|
|
87
|
+
const createResult = await this.addRemoteWebhook(appuser, newdata);
|
|
88
|
+
if (!createResult['subscription_id'])
|
|
89
|
+
throw new InternalServerErrorException(
|
|
90
|
+
'syncronize webhook server failed',
|
|
91
|
+
);
|
|
92
|
+
newdata.serverSubscriptionId = createResult.subscription_id;
|
|
93
|
+
newdata.serverSubscriptionSecret = createResult.secret;
|
|
94
|
+
}else{
|
|
95
|
+
await this.updateRemoteWebhook(appuser,newdata)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async afterDelete(
|
|
100
|
+
appuser: UserContext,
|
|
101
|
+
result: types.DeleteResultType<types.Webhook>,
|
|
102
|
+
id: string,
|
|
103
|
+
) {
|
|
104
|
+
await this.removeRemoteWebhook(appuser,result,id)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async removeRemoteWebhook(
|
|
108
|
+
appuser: UserContext,
|
|
109
|
+
result: types.DeleteResultType<types.Webhook>,
|
|
110
|
+
id: string,
|
|
111
|
+
){
|
|
112
|
+
const options = {
|
|
113
|
+
method: 'DELETE',
|
|
114
|
+
headers: {Authorization: this.webhookApiKey}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
const subscriptionId=result.data.serverSubscriptionId
|
|
119
|
+
if(!subscriptionId)return
|
|
120
|
+
|
|
121
|
+
try{
|
|
122
|
+
const res = await fetch(
|
|
123
|
+
`${this.webhooksubscribtionurl}/${subscriptionId}?application_id=${this.webhookAppId}`,
|
|
124
|
+
options)
|
|
125
|
+
}catch(e){
|
|
126
|
+
throw new InternalServerErrorException(e)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
}
|
|
130
|
+
async addRemoteWebhook(appuser: UserContext, data: types.Webhook) {
|
|
131
|
+
const headers = {};
|
|
132
|
+
data.headers.forEach((h) => {
|
|
133
|
+
headers[h.name] = h.value;
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const options = {
|
|
137
|
+
method: 'POST',
|
|
138
|
+
headers: {
|
|
139
|
+
Authorization: this.webhookApiKey,
|
|
140
|
+
accept: 'application/json',
|
|
141
|
+
'content-type': 'application/json',
|
|
142
|
+
},
|
|
143
|
+
body: JSON.stringify({
|
|
144
|
+
application_id: this.webhookAppId,
|
|
145
|
+
description: data.title,
|
|
146
|
+
metadata: {
|
|
147
|
+
tenantId: data.tenantId.toString(),
|
|
148
|
+
creator: appuser.getUname(),
|
|
149
|
+
},
|
|
150
|
+
is_enabled: data.active,
|
|
151
|
+
event_types: data.eventTypes.map(
|
|
152
|
+
(item) => this.webhookprefix + item,
|
|
153
|
+
),
|
|
154
|
+
label_key: 'tenantId',
|
|
155
|
+
label_value: data.tenantId.toString(),
|
|
156
|
+
target: {
|
|
157
|
+
headers: headers,
|
|
158
|
+
type: 'http',
|
|
159
|
+
method: data.requestMethod.toUpperCase(),
|
|
160
|
+
url: data.url,
|
|
161
|
+
},
|
|
162
|
+
}),
|
|
163
|
+
};
|
|
164
|
+
try {
|
|
165
|
+
const res = await fetch(this.webhooksubscribtionurl, options);
|
|
166
|
+
return await res.json();
|
|
167
|
+
if (res.status >= 300) {
|
|
168
|
+
this.logger.error(res.statusText, 'create webhook failed');
|
|
169
|
+
throw new InternalServerErrorException('create webhook failed');
|
|
170
|
+
}
|
|
171
|
+
} catch (e) {
|
|
172
|
+
throw new InternalServerErrorException(e);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async updateRemoteWebhook(appuser: UserContext, data: types.Webhook) {
|
|
177
|
+
const headers = {};
|
|
178
|
+
data.headers.forEach((h) => {
|
|
179
|
+
headers[h.name] = h.value;
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const options = {
|
|
183
|
+
method: 'PUT',
|
|
184
|
+
headers: {
|
|
185
|
+
Authorization: this.webhookApiKey,
|
|
186
|
+
accept: 'application/json',
|
|
187
|
+
'content-type': 'application/json',
|
|
188
|
+
},
|
|
189
|
+
body: JSON.stringify({
|
|
190
|
+
application_id: this.webhookAppId,
|
|
191
|
+
description: data.title,
|
|
192
|
+
metadata: {
|
|
193
|
+
tenantId: data.tenantId.toString(),
|
|
194
|
+
creator: appuser.getUname(),
|
|
195
|
+
},
|
|
196
|
+
is_enabled: data.active,
|
|
197
|
+
event_types: data.eventTypes.map(
|
|
198
|
+
(item) => this.webhookprefix + item,
|
|
199
|
+
),
|
|
200
|
+
label_key: 'tenantId',
|
|
201
|
+
label_value: data.tenantId.toString(),
|
|
202
|
+
target: {
|
|
203
|
+
headers: headers,
|
|
204
|
+
type: 'http',
|
|
205
|
+
method: data.requestMethod.toUpperCase(),
|
|
206
|
+
url: data.url,
|
|
207
|
+
},
|
|
208
|
+
}),
|
|
209
|
+
};
|
|
210
|
+
try {
|
|
211
|
+
const subscriptionId = data.serverSubscriptionId
|
|
212
|
+
const res = await fetch(`${this.webhooksubscribtionurl}/${subscriptionId}`, options);
|
|
213
|
+
return await res.json();
|
|
214
|
+
if (res.status >= 300) {
|
|
215
|
+
this.logger.error(res.statusText, 'updateRemoteWebhook webhook failed');
|
|
216
|
+
throw new InternalServerErrorException('update webhook failed');
|
|
217
|
+
}
|
|
218
|
+
} catch (e) {
|
|
219
|
+
throw new InternalServerErrorException(e);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/***************************** additional execute *****************************************/
|
|
224
|
+
}
|