@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.
Files changed (37) hide show
  1. package/ReleaseNote.md +12 -0
  2. package/dist/generate.js +2 -0
  3. package/dist/generate.js.map +1 -1
  4. package/package.json +1 -1
  5. package/src/generate.ts +2 -0
  6. package/templates/basic/miniApi/resource.controller.ts.eta +122 -52
  7. package/templates/basic/miniApi/resource.service.ts.eta +61 -25
  8. package/templates/basic/nest/apischema.ts.eta +21 -6
  9. package/templates/basic/nest/controller.ts.eta +37 -16
  10. package/templates/basic/nuxt/resource-bridge.service.ts.eta +3 -2
  11. package/templates/miniApi/src/constants/api-scopes.ts.eta +22 -4
  12. package/templates/miniApi/src/constants/available-custom-field.ts.eta +22 -0
  13. package/templates/nest/src/simpleapp/generate/commons/middlewares/tenant.middleware.ts.eta +34 -14
  14. package/templates/nest/src/simpleapp/generate/commons/robotuser.service.ts.eta +4 -8
  15. package/templates/nest/src/simpleapp/generate/commons/user.context.ts.eta +154 -17
  16. package/templates/nest/src/simpleapp/generate/processors/simpleapp.processor.ts.eta +125 -441
  17. package/templates/nuxt/composables/getUserStore.generate.ts.eta +62 -66
  18. package/templates/nuxt/plugins/19.simpleapp-mini-app-store.ts.eta +240 -203
  19. package/templates/nuxt/plugins/20.simpleapp-userstore.ts.eta +1 -1
  20. package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppFeatureCustomField.vue.eta +34 -0
  21. package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppFeatureCustomPage.vue.eta +68 -0
  22. package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppFeatureScope.vue.eta +53 -0
  23. package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppIcon.vue.eta +49 -0
  24. package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppMenuButton.vue.eta +6 -4
  25. package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppPageIframe.vue.eta +9 -3
  26. package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppPermissionWrapper.vue.eta +4 -4
  27. package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppRestrictedWarning.vue.eta +3 -3
  28. package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppSettingLayout.vue.eta +3 -3
  29. package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppSettingPage.vue.eta +2 -2
  30. package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/MiniAppWrapper.vue.eta +2 -32
  31. package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/integration/MiniAppIntegrationItem.vue.eta +6 -5
  32. package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/integration/MiniAppIntegrationItemBadge.vue.eta +15 -9
  33. package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/integration/MiniAppIntegrationItemGroup.vue.eta +4 -4
  34. package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/integration/MiniAppIntegrationPage.vue.eta +135 -22
  35. package/templates/nuxt/simpleapp/generate/features/miniApp/app/types/miniApp.ts.eta +7 -7
  36. package/templates/nuxt/simpleapp/generate/features/miniApp/bridge/services/bridge-resource-accessor.service.ts.eta +3 -1
  37. 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
- <% const getFieldType =(proptype, typename,actionname)=>{
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
- if(typename.includes('[')){
53
- return proptype=='schema' ? `[${tmptypename}]` : `${tmptypename}[]`
54
- }else{
55
- return tmptypename
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
- method = (additionalApis ?? []).find(item => item.action === action)?.method ?? '';
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
- // If X_APIKEY defined, and there is api key and secret supplied. use robot user
67
- if (process.env.X_APIKEY && req.get('x-apikey') && req.get('x-apisecret')) {
68
- if (req.get('x-apikey') == process.env.X_APIKEY && req.get('x-apisecret') == process.env.X_APISECRET) {
69
- user.setAsStaticUser('00000000-0000-0000-0000-000000000000', 'robot', 'Robot', 'robot@a.org', req.get('x-org') ?? this.defaultXOrg);
70
- if (req.get('x-guest-accesstoken')) {
71
- user.setGuestToken(req.get('x-guest-accesstoken'));
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
- } else {
77
- this.logger.log('Invalid API Key / Secret');
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
- setCurrentUserInfo = async (tokenstr: string, xOrg: string,webhookModel:Model<Webhook>) => {
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
- const wh = await webhookModel.find({branchId:this.getBranchId(),active:true})
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
- if(Array.isArray(wh) && wh.length>0)
216
- this.webhooks = this.webhooks.concat(wh)
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
+ };