@simitgroup/simpleapp-generator 1.6.7-h-alpha → 1.6.7-j-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 +12 -0
- package/dist/generate.js +2 -0
- package/dist/generate.js.map +1 -1
- package/package.json +1 -1
- package/src/generate.ts +2 -0
- package/templates/basic/miniApi/resource.controller.ts.eta +122 -52
- package/templates/basic/miniApi/resource.service.ts.eta +61 -25
- package/templates/basic/nest/apischema.ts.eta +21 -6
- package/templates/basic/nest/controller.ts.eta +37 -16
- package/templates/basic/nuxt/resource-bridge.service.ts.eta +3 -2
- package/templates/miniApi/src/constants/api-scopes.ts.eta +22 -4
- package/templates/miniApi/src/constants/available-custom-field.ts.eta +22 -0
- package/templates/nest/src/simpleapp/generate/commons/middlewares/tenant.middleware.ts.eta +34 -14
- package/templates/nest/src/simpleapp/generate/commons/robotuser.service.ts.eta +4 -8
- package/templates/nest/src/simpleapp/generate/commons/user.context.ts.eta +154 -17
- package/templates/nest/src/simpleapp/generate/processors/simpleapp.processor.ts.eta +125 -441
- package/templates/nuxt/composables/getUserStore.generate.ts.eta +62 -66
- package/templates/nuxt/plugins/19.simpleapp-mini-app-store.ts.eta +240 -203
- package/templates/nuxt/plugins/20.simpleapp-userstore.ts.eta +1 -1
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppFeatureCustomField.vue.eta +34 -0
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppFeatureCustomPage.vue.eta +68 -0
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppFeatureScope.vue.eta +53 -0
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppIcon.vue.eta +49 -0
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppMenuButton.vue.eta +6 -4
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppPageIframe.vue.eta +9 -3
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppPermissionWrapper.vue.eta +4 -4
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppRestrictedWarning.vue.eta +3 -3
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppSettingLayout.vue.eta +3 -3
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppSettingPage.vue.eta +2 -2
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppWrapper.vue.eta +2 -32
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/integration/MiniAppIntegrationItem.vue.eta +6 -5
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/integration/MiniAppIntegrationItemBadge.vue.eta +15 -9
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/integration/MiniAppIntegrationItemGroup.vue.eta +4 -4
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/integration/MiniAppIntegrationPage.vue.eta +135 -22
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/types/miniApp.ts.eta +7 -7
- package/templates/nuxt/simpleapp/generate/features/miniApp/bridge/services/bridge-resource-accessor.service.ts.eta +3 -1
- package/templates/nuxt/simpleapp/generate/features/miniApp/bridge/services/bridge.service.ts.eta +3 -0
|
@@ -34,28 +34,41 @@ import {Role} from '../commons/roles/roles.enum'
|
|
|
34
34
|
import { Response } from 'express';
|
|
35
35
|
import {AppUser} from '../commons/decorators/appuser.decorator'
|
|
36
36
|
import {UserContext} from '../commons/user.context'
|
|
37
|
+
import { MiniAppScope } from 'src/modules/mini-app/mini-app-scope/mini-app-scope.decorator';
|
|
37
38
|
|
|
39
|
+
<%
|
|
40
|
+
const drawMiniAppScope = (actionName) => {
|
|
41
|
+
if(isWhitelistedMiniApp(actionName, it)){
|
|
42
|
+
return `@MiniAppScope('${it.resourceName}.${actionName}')`;
|
|
43
|
+
}
|
|
38
44
|
|
|
39
|
-
|
|
40
|
-
const systemtypes = ['Boolean','boolean','string','String','number','Number','object','Object','integer','Integer']
|
|
41
|
-
if(!typename) throw actionname +" undefine schema";
|
|
42
|
-
let tmptypename = typename.replace('[','').replace(']','')
|
|
43
|
-
|
|
44
|
-
if(systemtypes.includes(tmptypename)){
|
|
45
|
-
if(proptype=='type' ) tmptypename =tmptypename.toLowerCase();
|
|
46
|
-
}else if(proptype=='schema'){
|
|
47
|
-
tmptypename= 'schemas.'+tmptypename
|
|
48
|
-
}else{ //type
|
|
49
|
-
tmptypename= 'types.'+tmptypename
|
|
45
|
+
return '';
|
|
50
46
|
}
|
|
47
|
+
%>
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
|
|
50
|
+
<%
|
|
51
|
+
const getFieldType =(proptype, typename,actionname)=>{
|
|
52
|
+
const systemtypes = ['Boolean','boolean','string','String','number','Number','object','Object','integer','Integer']
|
|
53
|
+
if(!typename) throw actionname +" undefine schema";
|
|
54
|
+
let tmptypename = typename.replace('[','').replace(']','')
|
|
55
|
+
|
|
56
|
+
if(systemtypes.includes(tmptypename)){
|
|
57
|
+
if(proptype=='type' ) tmptypename =tmptypename.toLowerCase();
|
|
58
|
+
}else if(proptype=='schema'){
|
|
59
|
+
tmptypename= 'schemas.'+tmptypename
|
|
60
|
+
}else{ //type
|
|
61
|
+
tmptypename= 'types.'+tmptypename
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if(typename.includes('[')){
|
|
65
|
+
return proptype=='schema' ? `[${tmptypename}]` : `${tmptypename}[]`
|
|
66
|
+
}else{
|
|
67
|
+
return tmptypename
|
|
68
|
+
}
|
|
56
69
|
}
|
|
57
|
-
}
|
|
58
70
|
%>
|
|
71
|
+
|
|
59
72
|
<% let superadmindoctype = ['tenant','globaluser'] %>
|
|
60
73
|
const apiname = '<%= it.typename %>'.toUpperCase();
|
|
61
74
|
@ApiTags(apiname)
|
|
@@ -103,6 +116,7 @@ export class <%= it.typename %>Controller extends SimpleAppAbstractController<
|
|
|
103
116
|
@ApiQuery({ name: 'keyword', type:String})
|
|
104
117
|
@ApiBody({ description: 'Data', type: ()=>Object})
|
|
105
118
|
@ApiOperation({ operationId: 'autoComplete',description:"retrieve array of {_id, code, name}" })
|
|
119
|
+
<%~ drawMiniAppScope('autoComplete') %>
|
|
106
120
|
async autoComplete(@AppUser() appuser: UserContext,
|
|
107
121
|
@Query('keyword') keyword:string,
|
|
108
122
|
@Body() data: types.<%=it.typename%>,
|
|
@@ -127,6 +141,7 @@ export class <%= it.typename %>Controller extends SimpleAppAbstractController<
|
|
|
127
141
|
@ApiResponse({ status: 500, description: 'internal error' })
|
|
128
142
|
@ApiBody({ description: 'Data',type:schemas.<%= it.typename%> })
|
|
129
143
|
@ApiOperation({ operationId: 'runCreate' })
|
|
144
|
+
<%~ drawMiniAppScope('create') %>
|
|
130
145
|
async create(@AppUser() appuser: UserContext,@Body() data: schemas.<%= it.typename%>) {
|
|
131
146
|
return await this._create(appuser,data)
|
|
132
147
|
}
|
|
@@ -147,6 +162,7 @@ export class <%= it.typename %>Controller extends SimpleAppAbstractController<
|
|
|
147
162
|
@ApiResponse({ status: 500, description: 'internal error' })
|
|
148
163
|
@ApiBody({ description: 'Data', type: schemas.ApiSearchBody })
|
|
149
164
|
@ApiOperation({ operationId: 'runSearch' })
|
|
165
|
+
<%~ drawMiniAppScope('list') %>
|
|
150
166
|
async search(@AppUser() appuser: UserContext,@Body() data: types.SearchBody) {
|
|
151
167
|
return await this._search(appuser,data)
|
|
152
168
|
}
|
|
@@ -207,6 +223,7 @@ export class <%= it.typename %>Controller extends SimpleAppAbstractController<
|
|
|
207
223
|
@ApiQuery({name: "<%=api['queryPara'][q]%>",required: false,type: String})
|
|
208
224
|
<%}%>
|
|
209
225
|
<%}%>
|
|
226
|
+
<%~ drawMiniAppScope(api.action) %>
|
|
210
227
|
async <%=capitalizeFirstLetter(api.action) %>(
|
|
211
228
|
@AppUser() appuser: UserContext,
|
|
212
229
|
<%if(['post','put','patch'].includes(api.method.toLowerCase())){%>
|
|
@@ -284,6 +301,7 @@ export class <%= it.typename %>Controller extends SimpleAppAbstractController<
|
|
|
284
301
|
@ApiResponse({ status: 404, description: 'Document not found' })
|
|
285
302
|
@ApiResponse({ status: 500, description: 'Internal error' })
|
|
286
303
|
@ApiOperation({ operationId: 'runFindOne' })
|
|
304
|
+
<%~ drawMiniAppScope('detail') %>
|
|
287
305
|
async findOne(@AppUser() appuser: UserContext,@Param('id') id: string) {
|
|
288
306
|
const data = await this._findOne(appuser, id);
|
|
289
307
|
if(!data ){
|
|
@@ -308,6 +326,7 @@ export class <%= it.typename %>Controller extends SimpleAppAbstractController<
|
|
|
308
326
|
@ApiResponse({ status: 500, description: 'Internal error' })
|
|
309
327
|
@ApiBody({ description: 'Data',type: schemas.<%= it.typename%> })
|
|
310
328
|
@ApiOperation({ operationId: 'runUpdate' })
|
|
329
|
+
<%~ drawMiniAppScope('update') %>
|
|
311
330
|
async update(@AppUser() appuser: UserContext,@Param('id') id: string, @Body() data: schemas.<%= it.typename%>) {
|
|
312
331
|
return await this._update(appuser,id, data) ;
|
|
313
332
|
}
|
|
@@ -325,6 +344,7 @@ export class <%= it.typename %>Controller extends SimpleAppAbstractController<
|
|
|
325
344
|
@ApiResponse({ status: 500, description: 'Internal error' })
|
|
326
345
|
@ApiBody({ description: 'Data',type: schemas.<%= it.typename%> })
|
|
327
346
|
@ApiOperation({ operationId: 'runPatch' })
|
|
347
|
+
<%~ drawMiniAppScope('patch') %>
|
|
328
348
|
async patch(@AppUser() appuser: UserContext,@Param('id') id: string, @Body() data: schemas.<%= it.typename%>) {
|
|
329
349
|
return await this._patch(appuser,id, data) ;
|
|
330
350
|
}
|
|
@@ -343,6 +363,7 @@ export class <%= it.typename %>Controller extends SimpleAppAbstractController<
|
|
|
343
363
|
@ApiResponse({ status: 404, description: 'Document not found' })
|
|
344
364
|
@ApiResponse({ status: 500, description: 'Internal error' })
|
|
345
365
|
@ApiOperation({ operationId: 'runDelete' })
|
|
366
|
+
<%~ drawMiniAppScope('delete') %>
|
|
346
367
|
async delete(@AppUser() appuser: UserContext,@Param('id') id: string) {
|
|
347
368
|
return this._delete(appuser,id);
|
|
348
369
|
}
|
|
@@ -24,12 +24,13 @@ export type <%= typeActionName %> = (typeof actions)[number];
|
|
|
24
24
|
export class MiniApp<%= pascalName %>BridgeService {
|
|
25
25
|
protected api!: <%= apiName %>;
|
|
26
26
|
|
|
27
|
-
constructor(miniAppCode: string) {
|
|
27
|
+
constructor(miniAppCode: string, developerPortalAppId: string) {
|
|
28
28
|
const config = getAxiosConfig();
|
|
29
29
|
if(miniAppCode !== ""){
|
|
30
30
|
config.baseOptions = {
|
|
31
31
|
headers: {
|
|
32
|
-
"x-mini-app-code": miniAppCode
|
|
32
|
+
"x-mini-app-code": miniAppCode,
|
|
33
|
+
"x-mini-app-dev-portal-app-id": developerPortalAppId,
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
}
|
|
@@ -7,45 +7,63 @@
|
|
|
7
7
|
const resourcePascalName = upperFirstCase(simpleAppConfig.resourceName);
|
|
8
8
|
const resourceKebabName = camelToKebab(simpleAppConfig.resourceName);
|
|
9
9
|
const additionalApis = simpleAppConfig?.additionalApis || [];
|
|
10
|
+
const resourceTitleName = titleCase(resourcePascalName);
|
|
10
11
|
let availableApis = {};
|
|
11
12
|
|
|
12
13
|
Object.entries(miniAppWhitelistApis).forEach(([action, value]) => {
|
|
13
14
|
if (value !== true && typeof value !== 'object') { return; }
|
|
14
15
|
let method = '';
|
|
16
|
+
let description = '';
|
|
17
|
+
|
|
15
18
|
switch(action) {
|
|
16
|
-
case 'list':
|
|
17
19
|
case 'detail':
|
|
18
20
|
method = 'get';
|
|
21
|
+
description = `Retrieve a single ${resourceTitleName} record by ID.`;
|
|
22
|
+
break;
|
|
23
|
+
|
|
24
|
+
case 'list':
|
|
25
|
+
method = 'post';
|
|
26
|
+
description = `Retrieve ${resourceTitleName} records with optional filtering, sorting, and field selection.`;
|
|
19
27
|
break;
|
|
20
28
|
|
|
21
29
|
case 'create':
|
|
30
|
+
method = 'post';
|
|
31
|
+
description = `Create a new ${resourceTitleName} record.`;
|
|
32
|
+
break;
|
|
33
|
+
|
|
22
34
|
case 'autoComplete':
|
|
23
35
|
method = 'post';
|
|
36
|
+
description = `Retrieve a selectable list of ${resourceTitleName} options for dropdowns or autocomplete fields.`;
|
|
24
37
|
break;
|
|
25
38
|
|
|
26
39
|
case 'update':
|
|
27
40
|
method = 'put';
|
|
41
|
+
description = `Replace an existing ${resourceTitleName} record with new data.`;
|
|
28
42
|
break;
|
|
29
43
|
|
|
30
44
|
case 'patch':
|
|
31
45
|
method = 'patch';
|
|
46
|
+
description = `Update specific fields of an existing ${resourceTitleName} record.`;
|
|
32
47
|
break;
|
|
33
48
|
|
|
34
49
|
case 'delete':
|
|
35
50
|
method = 'delete';
|
|
51
|
+
description = `Delete an existing ${resourceTitleName} record.`;
|
|
36
52
|
break;
|
|
37
53
|
|
|
38
54
|
case 'current':
|
|
39
55
|
return;
|
|
40
|
-
break;
|
|
41
56
|
|
|
42
57
|
default:
|
|
43
|
-
|
|
58
|
+
const additionalApiConfig = (additionalApis ?? []).find(item => item.action === action);
|
|
59
|
+
method = additionalApiConfig?.method ?? '';
|
|
60
|
+
description = additionalApiConfig?.description ?? '';
|
|
44
61
|
break;
|
|
45
62
|
}
|
|
46
63
|
|
|
47
64
|
availableApis[action] = {
|
|
48
|
-
method: method
|
|
65
|
+
method: method,
|
|
66
|
+
description: description,
|
|
49
67
|
}
|
|
50
68
|
});
|
|
51
69
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<%
|
|
2
|
+
const modules = it.modules.sort((a, b) => a.docname.localeCompare(b.docname, undefined, { sensitivity: 'base' }));
|
|
3
|
+
%>
|
|
4
|
+
|
|
5
|
+
const customFields = [
|
|
6
|
+
<% modules.forEach(module => { %>
|
|
7
|
+
<%
|
|
8
|
+
const schema = module.schema;
|
|
9
|
+
const simpleAppConfig = schema['x-simpleapp-config'];
|
|
10
|
+
const isEnabled = simpleAppConfig?.customField?.isEnable || false;
|
|
11
|
+
%>
|
|
12
|
+
<% if(isEnabled) { %>
|
|
13
|
+
{
|
|
14
|
+
documentType: '<%= simpleAppConfig.documentType%>',
|
|
15
|
+
documentName: '<%= simpleAppConfig.documentName%>',
|
|
16
|
+
resourceName: '<%= simpleAppConfig.resourceName%>',
|
|
17
|
+
},
|
|
18
|
+
<% } %>
|
|
19
|
+
<% }); %>
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
export default customFields;
|
|
@@ -13,8 +13,10 @@ import { Model } from 'mongoose';
|
|
|
13
13
|
import { Permission } from '../../types/perm.type';
|
|
14
14
|
import { User } from '../../types/user.type';
|
|
15
15
|
import { Appintegration } from '../../types/appintegration.type';
|
|
16
|
-
import { Webhook } from '../../types';
|
|
16
|
+
import { Miniappinstallation, Webhook } from '../../types';
|
|
17
17
|
import { UserContext } from '../user.context';
|
|
18
|
+
import { ApiKeyService } from './api-key/api-key.service';
|
|
19
|
+
import { HEADER_X_API_KEY, HEADER_X_API_SECRET } from '../constants/header';
|
|
18
20
|
|
|
19
21
|
@Injectable()
|
|
20
22
|
export class TenantMiddleware implements NestMiddleware {
|
|
@@ -29,6 +31,8 @@ export class TenantMiddleware implements NestMiddleware {
|
|
|
29
31
|
@InjectModel('Permission') private readonly permModel: Model<Permission>,
|
|
30
32
|
@InjectModel('Appintegration') private readonly appModel: Model<Appintegration>,
|
|
31
33
|
@InjectModel('Webhook') private readonly webhookModel: Model<Webhook>,
|
|
34
|
+
@InjectModel('Miniappinstallation') private readonly miniAppInstallationModel: Model<Miniappinstallation>,
|
|
35
|
+
private readonly apiKeyService: ApiKeyService,
|
|
32
36
|
) {}
|
|
33
37
|
|
|
34
38
|
requireXOrg(baseurl: string): boolean {
|
|
@@ -51,11 +55,11 @@ export class TenantMiddleware implements NestMiddleware {
|
|
|
51
55
|
tokenStr = tokenStr.replace('Bearer ', '');
|
|
52
56
|
const xOrg = req.get('x-org') ?? this.defaultXOrg;
|
|
53
57
|
|
|
54
|
-
const user = new UserContext(this.userModel, this.permModel, this.appModel);
|
|
58
|
+
const user = new UserContext(this.userModel, this.permModel, this.appModel, this.miniAppInstallationModel);
|
|
55
59
|
|
|
56
60
|
if (req.baseUrl == '/graphql') {
|
|
57
61
|
if (tokenStr) {
|
|
58
|
-
await user.setCurrentUserInfo(tokenStr, xOrg,this.webhookModel);
|
|
62
|
+
await user.setCurrentUserInfo(tokenStr, xOrg, this.webhookModel);
|
|
59
63
|
}
|
|
60
64
|
req['sessionuser'] = user;
|
|
61
65
|
next();
|
|
@@ -63,22 +67,37 @@ export class TenantMiddleware implements NestMiddleware {
|
|
|
63
67
|
}
|
|
64
68
|
this.logger.debug(`Running TenantMiddleware for ${req.baseUrl}`);
|
|
65
69
|
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
70
|
+
// Request Using API Key & Secret
|
|
71
|
+
const isApiKeyRequest = process.env.X_APIKEY && req.get(HEADER_X_API_KEY) && req.get(HEADER_X_API_SECRET);
|
|
72
|
+
if (isApiKeyRequest) {
|
|
73
|
+
try {
|
|
74
|
+
await this.apiKeyService.validate(req, user);
|
|
75
|
+
|
|
73
76
|
req['sessionuser'] = user;
|
|
74
77
|
next();
|
|
75
78
|
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
throw new BadRequestException('Invalid API Key / Secret');
|
|
79
|
+
} catch (e) {
|
|
80
|
+
throw e;
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
83
|
|
|
84
|
+
// If X_APIKEY defined, and there is api key and secret supplied. use robot user
|
|
85
|
+
// if (process.env.X_APIKEY && req.get('x-apikey') && req.get('x-apisecret')) {
|
|
86
|
+
// if (req.get('x-apikey') == process.env.X_APIKEY && req.get('x-apisecret') == process.env.X_APISECRET) {
|
|
87
|
+
// user.setAsStaticUser('00000000-0000-0000-0000-000000000000', 'robot', 'Robot', 'robot@a.org', req.get('x-org') ?? this.defaultXOrg);
|
|
88
|
+
// if (req.get('x-guest-accesstoken')) {
|
|
89
|
+
// user.setGuestToken(req.get('x-guest-accesstoken'));
|
|
90
|
+
// }
|
|
91
|
+
|
|
92
|
+
// req['sessionuser'] = user;
|
|
93
|
+
// next();
|
|
94
|
+
// return;
|
|
95
|
+
// } else {
|
|
96
|
+
// this.logger.log('Invalid API Key / Secret');
|
|
97
|
+
// throw new BadRequestException('Invalid API Key / Secret');
|
|
98
|
+
// }
|
|
99
|
+
// }
|
|
100
|
+
|
|
82
101
|
if (!req.get('authorization')) {
|
|
83
102
|
this.logger.log('Undefine Bearer Token');
|
|
84
103
|
return res.status(401).send('Undefine Bearer Token');
|
|
@@ -90,7 +109,8 @@ export class TenantMiddleware implements NestMiddleware {
|
|
|
90
109
|
}
|
|
91
110
|
|
|
92
111
|
try {
|
|
93
|
-
await user.setCurrentUserInfo(tokenStr, xOrg,this.webhookModel);
|
|
112
|
+
await user.setCurrentUserInfo(tokenStr, xOrg, this.webhookModel);
|
|
113
|
+
user.detectMiniAppSdkRequest(req);
|
|
94
114
|
if (user.getId() == '' && this.requireXOrg(req.baseUrl)) {
|
|
95
115
|
this.logger.log('Access deny for user:', req.baseUrl);
|
|
96
116
|
return res.status(401).send('Access deny for user');
|
|
@@ -11,6 +11,7 @@ import { UserContext } from 'src/simpleapp/generate/commons/user.context';
|
|
|
11
11
|
import { Permission } from 'src/simpleapp/services/perm.service';
|
|
12
12
|
import { User } from 'src/simpleapp/services/user.service';
|
|
13
13
|
import { Appintegration } from 'src/simpleapp/services/appintegration.service';
|
|
14
|
+
import { Miniappinstallation } from '../types';
|
|
14
15
|
const Base64URL = require('@darkwolf/base64url');
|
|
15
16
|
@Injectable()
|
|
16
17
|
export class SimpleAppRobotUserService {
|
|
@@ -22,6 +23,7 @@ export class SimpleAppRobotUserService {
|
|
|
22
23
|
@InjectModel('User') private readonly usermodel: Model<User>;
|
|
23
24
|
@InjectModel('Permission') private readonly permmodel: Model<Permission>;
|
|
24
25
|
@InjectModel('Appintegration') private readonly appmodel: Model<Appintegration>;
|
|
26
|
+
@InjectModel('Miniappinstallation') private readonly miniAppInstallationModel: Model<Miniappinstallation>;
|
|
25
27
|
|
|
26
28
|
constructor() {
|
|
27
29
|
this.init();
|
|
@@ -91,14 +93,8 @@ export class SimpleAppRobotUserService {
|
|
|
91
93
|
|
|
92
94
|
prepareAppUser(data: any) {
|
|
93
95
|
// console.log('prepareAppUserprepareAppUser');
|
|
94
|
-
const appuser = new UserContext(this.usermodel, this.permmodel, this.appmodel);
|
|
95
|
-
appuser.setAsStaticUser(
|
|
96
|
-
'00000000-0000-0000-0000-000000000000',
|
|
97
|
-
'robot',
|
|
98
|
-
'Robot',
|
|
99
|
-
'robot@a.org',
|
|
100
|
-
Base64URL.encodeText('0-0-0'),
|
|
101
|
-
);
|
|
96
|
+
const appuser = new UserContext(this.usermodel, this.permmodel, this.appmodel, this.miniAppInstallationModel);
|
|
97
|
+
appuser.setAsStaticUser('00000000-0000-0000-0000-000000000000', 'robot', 'Robot', 'robot@a.org', Base64URL.encodeText('0-0-0'));
|
|
102
98
|
|
|
103
99
|
const tenantId = data?.tenantId ?? 0;
|
|
104
100
|
const orgId = data?.orgId ?? 0;
|
|
@@ -10,13 +10,14 @@ import { InjectModel } from '@nestjs/mongoose';
|
|
|
10
10
|
import { BadRequestException, ForbiddenException, Injectable, Logger, Scope } from '@nestjs/common';
|
|
11
11
|
import * as jwt from 'jsonwebtoken';
|
|
12
12
|
import { ClientSession, Model, PipelineStage } from 'mongoose';
|
|
13
|
-
import { Branch, Organization, Permission, Tenant, TenantClientSetting, User, Appintegration, Webhook } from 'src/simpleapp/generate/types';
|
|
13
|
+
import { Branch, Organization, Permission, Tenant, TenantClientSetting, User, Appintegration, Webhook, Miniappinstallation, TUserType, TMiniApiTokenInfo } from 'src/simpleapp/generate/types';
|
|
14
14
|
import { ProfileUserBranch, ProfileUserInvites } from '../../profile/profile.types';
|
|
15
15
|
import { ModifiedRecords } from '../types';
|
|
16
16
|
import { Role } from './roles/roles.enum';
|
|
17
17
|
import systemWebHooks from '../../webhooks';
|
|
18
18
|
import * as rolegroups from './roles/roles.group';
|
|
19
|
-
|
|
19
|
+
import { isEmpty } from 'lodash';
|
|
20
|
+
import { Request } from 'express';
|
|
20
21
|
|
|
21
22
|
@Injectable({ scope: Scope.REQUEST })
|
|
22
23
|
export class UserContext {
|
|
@@ -62,6 +63,10 @@ export class UserContext {
|
|
|
62
63
|
email: string;
|
|
63
64
|
} = { uid: '', uname: '', fullname: '', email: '' };
|
|
64
65
|
|
|
66
|
+
protected miniApiToken?: string;
|
|
67
|
+
|
|
68
|
+
protected miniApiTokenInfo?: TMiniApiTokenInfo;
|
|
69
|
+
|
|
65
70
|
protected groups: string[] = [];
|
|
66
71
|
|
|
67
72
|
protected branchCode: string = '';
|
|
@@ -92,7 +97,7 @@ export class UserContext {
|
|
|
92
97
|
|
|
93
98
|
protected package: string = '';
|
|
94
99
|
|
|
95
|
-
protected webhooks: Webhook[] = systemWebHooks
|
|
100
|
+
protected webhooks: Webhook[] = systemWebHooks;
|
|
96
101
|
protected clientSetting: TenantClientSetting = {
|
|
97
102
|
auditTrail: false,
|
|
98
103
|
support: false,
|
|
@@ -101,7 +106,7 @@ export class UserContext {
|
|
|
101
106
|
};
|
|
102
107
|
|
|
103
108
|
private dbsession: ClientSession;
|
|
104
|
-
|
|
109
|
+
|
|
105
110
|
protected modifiedRecords: ModifiedRecords = {
|
|
106
111
|
createds: {},
|
|
107
112
|
updateds: {},
|
|
@@ -113,10 +118,13 @@ export class UserContext {
|
|
|
113
118
|
einvoice: boolean;
|
|
114
119
|
} = { simbiz6: false, einvoice: false };
|
|
115
120
|
|
|
121
|
+
protected userType: TUserType = 'normal';
|
|
122
|
+
|
|
116
123
|
constructor(
|
|
117
|
-
private readonly userModel: Model<User>,
|
|
124
|
+
private readonly userModel: Model<User>,
|
|
118
125
|
private readonly permModel: Model<Permission>,
|
|
119
126
|
private readonly appModel: Model<Appintegration>,
|
|
127
|
+
private readonly miniAppInstallationModel: Model<Miniappinstallation>,
|
|
120
128
|
) {}
|
|
121
129
|
|
|
122
130
|
setDBSession = (dbsession: ClientSession) => {
|
|
@@ -206,14 +214,31 @@ export class UserContext {
|
|
|
206
214
|
return data;
|
|
207
215
|
};
|
|
208
216
|
|
|
209
|
-
|
|
210
|
-
|
|
217
|
+
getUserToken = () => this.token;
|
|
218
|
+
|
|
219
|
+
getMiniApiToken = () => this.miniApiToken;
|
|
220
|
+
|
|
221
|
+
getMiniApiTokenInfo = () => this.miniApiTokenInfo;
|
|
222
|
+
|
|
223
|
+
setMiniApiToken = (token: string) => {
|
|
224
|
+
this.miniApiToken = token;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
setMiniApiTokenInfo = (miniApiTokenInfo: TMiniApiTokenInfo) => {
|
|
228
|
+
this.miniApiTokenInfo = miniApiTokenInfo;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
setCurrentUserInfo = async (tokenstr: string, xOrg: string, webhookModel: Model<Webhook>) => {
|
|
211
232
|
this.setXOrg(xOrg);
|
|
212
|
-
await this.setUserToken(tokenstr);
|
|
213
|
-
|
|
233
|
+
await this.setUserToken(tokenstr);
|
|
234
|
+
this.setUserType('normal');
|
|
235
|
+
const wh = await webhookModel.find({ branchId: this.getBranchId(), active: true });
|
|
236
|
+
|
|
237
|
+
if (Array.isArray(wh) && wh.length > 0) this.webhooks = this.webhooks.concat(wh);
|
|
238
|
+
};
|
|
214
239
|
|
|
215
|
-
|
|
216
|
-
|
|
240
|
+
setPackage = (packageName: string) => {
|
|
241
|
+
this.package = packageName;
|
|
217
242
|
};
|
|
218
243
|
|
|
219
244
|
/**
|
|
@@ -267,7 +292,7 @@ export class UserContext {
|
|
|
267
292
|
as: 'currentTenant',
|
|
268
293
|
},
|
|
269
294
|
},
|
|
270
|
-
|
|
295
|
+
|
|
271
296
|
{
|
|
272
297
|
$unwind: '$currentTenant',
|
|
273
298
|
},
|
|
@@ -327,8 +352,6 @@ export class UserContext {
|
|
|
327
352
|
return userProfile;
|
|
328
353
|
};
|
|
329
354
|
|
|
330
|
-
getUserToken = () => this.token;
|
|
331
|
-
|
|
332
355
|
setUserToken = async (tokenStr: string) => {
|
|
333
356
|
this.logger.debug(`===setUserToken===`);
|
|
334
357
|
// Define token info
|
|
@@ -401,8 +424,8 @@ export class UserContext {
|
|
|
401
424
|
orgId: this.orgId,
|
|
402
425
|
};
|
|
403
426
|
};
|
|
404
|
-
getWebHooks = ()=> this.webhooks
|
|
405
|
-
|
|
427
|
+
getWebHooks = () => this.webhooks;
|
|
428
|
+
|
|
406
429
|
getWorkflowTaskFilter() {
|
|
407
430
|
return {
|
|
408
431
|
'data.tenantId': this.tenantId,
|
|
@@ -467,6 +490,28 @@ export class UserContext {
|
|
|
467
490
|
}
|
|
468
491
|
};
|
|
469
492
|
|
|
493
|
+
decodeXOrg = (xOrg: string) => {
|
|
494
|
+
try {
|
|
495
|
+
const decodedText: string = Base64URL.decodeText(xOrg);
|
|
496
|
+
const xOrgRealm = decodedText.includes('/') ? decodedText.split('/') : decodedText.split('-');
|
|
497
|
+
|
|
498
|
+
const tenantId = Number(xOrgRealm[0]);
|
|
499
|
+
const orgId = Number(xOrgRealm[1]);
|
|
500
|
+
const branchId = Number(xOrgRealm[2]);
|
|
501
|
+
if (xOrgRealm.length == 3 && !isNaN(tenantId) && !isNaN(orgId) && !isNaN(branchId)) {
|
|
502
|
+
return {
|
|
503
|
+
tenantId,
|
|
504
|
+
orgId,
|
|
505
|
+
branchId,
|
|
506
|
+
};
|
|
507
|
+
} else {
|
|
508
|
+
throw new BadRequestException('invalidXorg');
|
|
509
|
+
}
|
|
510
|
+
} catch (err) {
|
|
511
|
+
throw new BadRequestException(err);
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
|
|
470
515
|
async getUserInfo(): Promise<UserInfo> {
|
|
471
516
|
this.logger.debug('===getUserInfo===');
|
|
472
517
|
|
|
@@ -495,6 +540,7 @@ export class UserContext {
|
|
|
495
540
|
invites: this.getInvites(),
|
|
496
541
|
moreProps: this.getMoreProps(),
|
|
497
542
|
appintegration: this.getAppIntegration(),
|
|
543
|
+
userType: this.getUserType(),
|
|
498
544
|
};
|
|
499
545
|
|
|
500
546
|
if (this.getId() != '') {
|
|
@@ -749,6 +795,96 @@ export class UserContext {
|
|
|
749
795
|
this.guestInfo.email = <string>tokeninfo?.email ?? '';
|
|
750
796
|
this.guestInfo.uname = <string>tokeninfo?.preferred_username ?? '';
|
|
751
797
|
this.guestInfo.fullname = <string>tokeninfo?.name ?? '';
|
|
798
|
+
|
|
799
|
+
this.setUserType('guest');
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
getUserType = () => this.userType;
|
|
803
|
+
|
|
804
|
+
setUserType = (userType: TUserType) => {
|
|
805
|
+
this.userType = userType;
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
detectMiniAppSdkRequest(req: Request) {
|
|
809
|
+
if (!isEmpty(req.headers['x-mini-app-dev-portal-app-id'])) {
|
|
810
|
+
this.setUserType('miniAppSdk');
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
async setMiniApiUser(tokenStr: string, xOrg: string) {
|
|
815
|
+
const tokenInfo: TMiniApiTokenInfo = jwt.decode(tokenStr) as TMiniApiTokenInfo;
|
|
816
|
+
const appId = tokenInfo?.miniAppId ?? '';
|
|
817
|
+
|
|
818
|
+
this.setXOrg(xOrg);
|
|
819
|
+
this.setUserType('miniApi');
|
|
820
|
+
this.setMiniApiToken(tokenStr);
|
|
821
|
+
this.setMiniApiTokenInfo(tokenInfo);
|
|
822
|
+
|
|
823
|
+
const miniAppInstallation = await this.findIsCentreInstalledMiniApp(appId);
|
|
824
|
+
|
|
825
|
+
if (isEmpty(miniAppInstallation.miniApiUser) || isEmpty(miniAppInstallation.miniApiUser.uid)) {
|
|
826
|
+
throw new ForbiddenException('NOT found connected user');
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
this.token = tokenStr;
|
|
830
|
+
this.uid = miniAppInstallation.miniApiUser.uid;
|
|
831
|
+
|
|
832
|
+
await this.setUserProfileFromDB();
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
async findIsCentreInstalledMiniApp(developerPortalAppId: string) {
|
|
836
|
+
const miniAppInstallation = await this.miniAppInstallationModel.findOne({
|
|
837
|
+
'miniApp.developerPortalAppId': developerPortalAppId,
|
|
838
|
+
isActive: true,
|
|
839
|
+
tenantId: this.tenantId,
|
|
840
|
+
orgId: this.orgId,
|
|
841
|
+
branchId: this.branchId,
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
if (!miniAppInstallation) {
|
|
845
|
+
throw new ForbiddenException('NOT allowed to access this centre');
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
return miniAppInstallation;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
async setUserProfileFromDB() {
|
|
852
|
+
const userProfile = await this.obtainProfileFromDB();
|
|
853
|
+
if (userProfile) {
|
|
854
|
+
this.logger.debug(`User ${this.uid} exists in tenant (${this.tenantId})`);
|
|
855
|
+
|
|
856
|
+
this._id = userProfile._id.toString();
|
|
857
|
+
this.email = userProfile.email ?? '';
|
|
858
|
+
this.uname = userProfile.uname ?? '';
|
|
859
|
+
this.fullname = userProfile.fullname ?? '';
|
|
860
|
+
|
|
861
|
+
this.branchCode = userProfile['branchCode'] ?? '';
|
|
862
|
+
this.branchName = userProfile['branchName'] ?? '';
|
|
863
|
+
this.orgCode = userProfile['orgCode'] ?? '';
|
|
864
|
+
this.orgName = userProfile['orgName'] ?? '';
|
|
865
|
+
this.timeZone = userProfile['timeZone'] ?? '';
|
|
866
|
+
this.currency = userProfile['currency'] ?? '';
|
|
867
|
+
this.country = userProfile['country'] ?? '';
|
|
868
|
+
this.offsetMinute = userProfile['offsetMinute'] ?? 0;
|
|
869
|
+
this.orgRecordId = userProfile['orgRecordId'] ?? '';
|
|
870
|
+
this.branchRecordId = userProfile['branchRecordId'] ?? '';
|
|
871
|
+
this.groups = userProfile['groups'] ?? [];
|
|
872
|
+
this.clientSetting = userProfile['clientSetting'] ?? {};
|
|
873
|
+
this.roles = userProfile['roles'] ?? [Role.Everyone, Role.User];
|
|
874
|
+
this.moreProps = this.setMoreProps(userProfile);
|
|
875
|
+
this.package = userProfile['package'];
|
|
876
|
+
this.appintegration = await this.setAppIntegration();
|
|
877
|
+
} else {
|
|
878
|
+
this.logger.debug(`User ${this.uid} not exists in tenant (${this.tenantId})`);
|
|
879
|
+
this.logger.debug(`Set unknown id of current user`);
|
|
880
|
+
this.roles = [Role.Everyone, Role.Unknown];
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
if (this.isRealmAdmin() && !this.roles.includes(Role.SuperAdmin)) {
|
|
884
|
+
this.roles.push(Role.SuperAdmin);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
this.logger.verbose(`User ${this.uid} have _id (${this.getId()}), groups (${this.groups.join(',')}) and roles (${this.getRoles().join(',')}).`);
|
|
752
888
|
}
|
|
753
889
|
|
|
754
890
|
getAppIntegration = () => this.appintegration;
|
|
@@ -773,6 +909,7 @@ export class UserContext {
|
|
|
773
909
|
}
|
|
774
910
|
return this.appintegration;
|
|
775
911
|
}
|
|
912
|
+
|
|
776
913
|
/**
|
|
777
914
|
* Define additional properties from user into moreProps
|
|
778
915
|
*/
|
|
@@ -927,4 +1064,4 @@ type UserInfo = {
|
|
|
927
1064
|
type UserTenant = User & {
|
|
928
1065
|
tenantName: string;
|
|
929
1066
|
permissions: Permission[];
|
|
930
|
-
};
|
|
1067
|
+
};
|