@simitgroup/simpleapp-generator 2.0.2-v-alpha → 2.0.2-x-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 +14 -0
- package/dist/buildinschemas/autoincreament.d.ts +3 -0
- package/dist/buildinschemas/autoincreament.d.ts.map +1 -0
- package/dist/buildinschemas/autoincreament.js +39 -0
- package/dist/buildinschemas/autoincreament.js.map +1 -0
- package/dist/buildinschemas/docnoformat.d.ts +3 -0
- package/dist/buildinschemas/docnoformat.d.ts.map +1 -0
- package/dist/buildinschemas/docnoformat.js +58 -0
- package/dist/buildinschemas/docnoformat.js.map +1 -0
- package/dist/buildinschemas/documentevent.d.ts +3 -0
- package/dist/buildinschemas/documentevent.d.ts.map +1 -0
- package/dist/buildinschemas/documentevent.js +48 -0
- package/dist/buildinschemas/documentevent.js.map +1 -0
- package/dist/buildinschemas/webhooklog.d.ts +3 -0
- package/dist/buildinschemas/webhooklog.d.ts.map +1 -0
- package/dist/buildinschemas/webhooklog.js +79 -0
- package/dist/buildinschemas/webhooklog.js.map +1 -0
- package/dist/framework.d.ts.map +1 -1
- package/dist/framework.js +8 -18
- package/dist/framework.js.map +1 -1
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +8 -18
- package/dist/generate.js.map +1 -1
- package/dist/index.js +7 -17
- package/dist/index.js.map +1 -1
- package/dist/libs.d.ts.map +1 -1
- package/dist/processors/bpmnbuilder.d.ts.map +1 -1
- package/dist/processors/bpmnbuilder.js +7 -17
- package/dist/processors/bpmnbuilder.js.map +1 -1
- package/dist/processors/jrxmlbuilder.d.ts.map +1 -1
- package/dist/processors/jrxmlbuilder.js +7 -17
- package/dist/processors/jrxmlbuilder.js.map +1 -1
- package/dist/processors/jsonschemabuilder.d.ts.map +1 -1
- package/dist/processors/jsonschemabuilder.js.map +1 -1
- package/package.json +1 -1
- package/templates/nest/src/simple-app/_core/features/document-no-format/document-no-format.service.ts.eta +68 -62
- package/templates/nest/src/simple-app/_core/features/mini-app/mini-app-manager/mini-app-manager.controller.ts.eta +2 -3
- package/templates/nest/src/simple-app/_core/features/profile/profile.controller.ts.eta +7 -7
- package/templates/nest/src/simple-app/_core/features/profile/profile.schema.ts.eta +38 -6
- package/templates/nest/src/simple-app/_core/features/user-context/user-context.type.ts.eta +43 -13
- package/templates/nest/src/simple-app/_core/features/user-context/user.context.ts.eta +57 -37
- package/templates/nest/src/simple-app/_core/framework/schemas/simple-app.schema.ts.eta +34 -25
- package/templates/nest/src/simple-app/_core/framework/simple-app.interceptor.ts.eta +23 -20
- package/templates/nest/src/simple-app/_core/utils/dayjs.ts.eta +14 -9
- package/templates/nuxt/app.vue.eta +46 -47
- package/templates/nuxt/plugins/10.simpleapp-event.ts.eta +102 -92
- package/templates/nuxt/plugins/20.simpleapp-userstore.ts.eta +69 -51
- package/templates/nuxt/server/api/auth/[...].ts.eta +1 -0
- package/templates/nuxt/server/api/auth/logout.ts.eta +20 -7
|
@@ -6,32 +6,31 @@
|
|
|
6
6
|
* 2.
|
|
7
7
|
*/
|
|
8
8
|
import Base64URL from '@darkwolf/base64url';
|
|
9
|
-
import { isEmpty } from 'lodash';
|
|
10
|
-
import { Request } from 'express';
|
|
11
|
-
import { InjectModel } from '@nestjs/mongoose';
|
|
12
9
|
import { BadRequestException, ForbiddenException, Injectable, Logger, Scope } from '@nestjs/common';
|
|
10
|
+
import { Request } from 'express';
|
|
13
11
|
import * as jwt from 'jsonwebtoken';
|
|
12
|
+
import { isEmpty } from 'lodash';
|
|
14
13
|
import { ClientSession, Model, PipelineStage } from 'mongoose';
|
|
15
14
|
|
|
16
15
|
// import { Branch, Organization, Permission, Tenant, TenantClientSetting, User, Appintegration, Webhook, Miniappinstallation, TUserType, TMiniApiTokenInfo } from 'src/simpleapp/generate/types';
|
|
17
|
-
import {
|
|
16
|
+
import { TMiniApiTokenInfo, TUserType } from 'src/simple-app/_core/framework/schemas';
|
|
18
17
|
import { MiniAppInstallation } from 'src/simple-app/_core/resources/mini-app-installation/mini-app-installation.schema';
|
|
19
18
|
|
|
20
|
-
import { Tenant, TenantClientSetting } from 'src/simple-app/_core/resources/tenant/tenant.schema';
|
|
21
19
|
import { Organization } from 'src/simple-app/_core/resources/organization/organization.schema';
|
|
22
|
-
import {
|
|
20
|
+
import { Tenant, TenantClientSetting } from 'src/simple-app/_core/resources/tenant/tenant.schema';
|
|
23
21
|
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
22
|
+
import { Environment } from '@core-features/maintenance/schemas';
|
|
23
|
+
import { TenantLicenseEnum } from '@resources/tenant/tenant.enum';
|
|
24
|
+
import { ProfileUserBranch, TenantHealth, TenantPermission, UserContextInfo } from 'src/simple-app/_core/features/profile/profile.schema';
|
|
26
25
|
import { Webhook } from 'src/simple-app/_core/features/webhook/schemas';
|
|
27
|
-
import {
|
|
26
|
+
import { Permission, StepData } from 'src/simple-app/_core/resources/permission/permission.schema';
|
|
27
|
+
import { User } from 'src/simple-app/_core/resources/user/user.schema';
|
|
28
28
|
import { ModifiedRecords } from '../../framework/schemas';
|
|
29
|
+
import { SimpleAppDbRevertService } from '../../framework/simple-app-db-revert.service';
|
|
29
30
|
import { Role } from '../auth/role-guard/roles.enum';
|
|
30
|
-
import { Environment } from '@core-features/maintenance/schemas';
|
|
31
31
|
import * as rolegroups from '../auth/role-guard/roles.group';
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import { StepData } from 'src/simple-app/_core/resources/permission/permission.schema';
|
|
32
|
+
import { TenantHealthStatusEnum } from './user-context.enum';
|
|
33
|
+
import { ENV_EMAIL_ROLE_MAP, EnvEmailFieldKey, RoleGroupResolver, TenantPermissionAggregateRow } from './user-context.type';
|
|
35
34
|
|
|
36
35
|
// import systemWebHooks from '../../webhooks';
|
|
37
36
|
@Injectable({ scope: Scope.REQUEST })
|
|
@@ -136,6 +135,16 @@ export class UserContext extends UserContextInfo {
|
|
|
136
135
|
|
|
137
136
|
protected userType: TUserType = 'normal';
|
|
138
137
|
|
|
138
|
+
tenantHealth?: TenantHealth = {
|
|
139
|
+
status: TenantHealthStatusEnum.ACTIVE,
|
|
140
|
+
message: 'User context initial',
|
|
141
|
+
isLicenseRenewable: false,
|
|
142
|
+
isLicenseDowngradable: false,
|
|
143
|
+
isLicenseCancellable: false,
|
|
144
|
+
isLicenseUpgradable: true,
|
|
145
|
+
isLicenseAddOnAllowed: false,
|
|
146
|
+
};
|
|
147
|
+
|
|
139
148
|
constructor(
|
|
140
149
|
private readonly userModel: Model<User>,
|
|
141
150
|
private readonly permModel: Model<Permission>,
|
|
@@ -215,7 +224,7 @@ export class UserContext extends UserContextInfo {
|
|
|
215
224
|
|
|
216
225
|
setLicense = (license: TenantLicenseEnum) => {
|
|
217
226
|
if (!this.tenantInfo) {
|
|
218
|
-
this.tenantInfo = {} as
|
|
227
|
+
this.tenantInfo = {} as Tenant;
|
|
219
228
|
}
|
|
220
229
|
|
|
221
230
|
this.tenantInfo.license = license;
|
|
@@ -260,6 +269,10 @@ export class UserContext extends UserContextInfo {
|
|
|
260
269
|
this.offsetMinute = offsetMinute;
|
|
261
270
|
};
|
|
262
271
|
|
|
272
|
+
setTenantHealth = (data: TenantHealth) => {
|
|
273
|
+
this.tenantHealth = data;
|
|
274
|
+
};
|
|
275
|
+
|
|
263
276
|
// async obtainNoTenantProfile() {
|
|
264
277
|
// const pipelines: PipelineStage[] = [{ $match: { uid: this.uid, tenantId: this.tenantId } }];
|
|
265
278
|
// try {
|
|
@@ -288,8 +301,8 @@ export class UserContext extends UserContextInfo {
|
|
|
288
301
|
// return this.obtainNoTenantProfile();
|
|
289
302
|
// }
|
|
290
303
|
const pipelines: PipelineStage[] = [
|
|
291
|
-
//get profile
|
|
292
|
-
{ $match: { uid: this.uid, tenantId: this.tenantId } },
|
|
304
|
+
//get profile (inactive users must not load org/branch/groups)
|
|
305
|
+
{ $match: { uid: this.uid, tenantId: this.tenantId, active: { $ne: false } } },
|
|
293
306
|
{
|
|
294
307
|
$lookup: {
|
|
295
308
|
from: 'tenant',
|
|
@@ -497,7 +510,7 @@ export class UserContext extends UserContextInfo {
|
|
|
497
510
|
},
|
|
498
511
|
];
|
|
499
512
|
|
|
500
|
-
const userProfiles
|
|
513
|
+
const userProfiles = await this.userModel.aggregate<UserContextInfo>(pipelines).exec();
|
|
501
514
|
return userProfiles[0];
|
|
502
515
|
}
|
|
503
516
|
|
|
@@ -539,7 +552,7 @@ export class UserContext extends UserContextInfo {
|
|
|
539
552
|
});
|
|
540
553
|
this.branches = userProfile['branches'];
|
|
541
554
|
this.groups = userProfile['groups'] ?? [];
|
|
542
|
-
this.clientSetting = userProfile
|
|
555
|
+
this.clientSetting = userProfile.tenantInfo?.clientSetting ?? {
|
|
543
556
|
auditTrail: false,
|
|
544
557
|
disableClassReminder: true,
|
|
545
558
|
webhook: false,
|
|
@@ -552,24 +565,24 @@ export class UserContext extends UserContextInfo {
|
|
|
552
565
|
}
|
|
553
566
|
if (this.tenantInfo?.company && this.tenantInfo.company.billUserEmail === this.email) {
|
|
554
567
|
this.roles.push(Role.Billing);
|
|
568
|
+
this.roles.push(Role.Tenant_search);
|
|
569
|
+
this.roles.push(Role.TenantInvoice_search);
|
|
555
570
|
}
|
|
556
571
|
if (systemEnv) {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
};
|
|
563
|
-
for (const f of Object.keys(emailFields)) {
|
|
564
|
-
if (Array.isArray(systemEnv[f]) && systemEnv[f].includes(this.email) && !this.roles.includes(Role[emailFields[f]])) {
|
|
565
|
-
this.roles.push(Role[emailFields[f]]);
|
|
572
|
+
for (const f of Object.keys(ENV_EMAIL_ROLE_MAP) as EnvEmailFieldKey[]) {
|
|
573
|
+
const emails = systemEnv[f];
|
|
574
|
+
const targetRole = ENV_EMAIL_ROLE_MAP[f];
|
|
575
|
+
if (Array.isArray(emails) && emails.includes(this.email) && !this.roles.includes(targetRole)) {
|
|
576
|
+
this.roles.push(targetRole);
|
|
566
577
|
}
|
|
567
578
|
}
|
|
568
579
|
}
|
|
569
580
|
|
|
570
581
|
// console.log('this.roles', this.roles);
|
|
571
582
|
for (const group of this.groups) {
|
|
572
|
-
const
|
|
583
|
+
const roleResolver = (rolegroups as Record<string, RoleGroupResolver | undefined>)[group];
|
|
584
|
+
if (!roleResolver) continue;
|
|
585
|
+
const roles: Role[] = roleResolver();
|
|
573
586
|
//(<() => Role[]>rolegroups[group])();
|
|
574
587
|
for (let r = 0; r < roles.length; r++) {
|
|
575
588
|
if (!this.roles.includes(roles[r])) {
|
|
@@ -797,6 +810,7 @@ export class UserContext extends UserContextInfo {
|
|
|
797
810
|
branches: this.getBranches(),
|
|
798
811
|
invites: this.getInvites(),
|
|
799
812
|
moreProps: this.moreProps,
|
|
813
|
+
tenantHealth: this.tenantHealth,
|
|
800
814
|
};
|
|
801
815
|
return result;
|
|
802
816
|
}
|
|
@@ -887,6 +901,8 @@ export class UserContext extends UserContextInfo {
|
|
|
887
901
|
tenantId: {
|
|
888
902
|
$gt: 0,
|
|
889
903
|
},
|
|
904
|
+
// Inactive user rows must not appear on pick-tenant / org switcher
|
|
905
|
+
active: { $ne: false },
|
|
890
906
|
},
|
|
891
907
|
},
|
|
892
908
|
{
|
|
@@ -964,10 +980,10 @@ export class UserContext extends UserContextInfo {
|
|
|
964
980
|
},
|
|
965
981
|
];
|
|
966
982
|
|
|
967
|
-
const datas = await this.userModel.aggregate(pipelines);
|
|
983
|
+
const datas = await this.userModel.aggregate<TenantPermissionAggregateRow>(pipelines);
|
|
968
984
|
for (const r of datas) {
|
|
969
985
|
const perms: ProfileUserBranch[] = [];
|
|
970
|
-
for (const p of r.perms) {
|
|
986
|
+
for (const p of r.perms ?? []) {
|
|
971
987
|
perms.push({
|
|
972
988
|
permissionId: p._id,
|
|
973
989
|
tenantId: p.tenantId,
|
|
@@ -1039,7 +1055,7 @@ export class UserContext extends UserContextInfo {
|
|
|
1039
1055
|
const pipeLine: PipelineStage[] = [{ $match: { tenantId: -1 } }, { $unionWith: { coll: 'organization', pipeline: [{ $match: { orgId: this.orgId } }] } }, { $limit: 1 }];
|
|
1040
1056
|
|
|
1041
1057
|
// console.log("set as static user", pipeLine)
|
|
1042
|
-
const result
|
|
1058
|
+
const result = await this.userModel.aggregate<Organization>(pipeLine).exec();
|
|
1043
1059
|
// console.log("result",result)
|
|
1044
1060
|
if (result && result.length > 0) {
|
|
1045
1061
|
const res = result[0];
|
|
@@ -1150,7 +1166,7 @@ export class UserContext extends UserContextInfo {
|
|
|
1150
1166
|
});
|
|
1151
1167
|
this.branches = userProfile['branches'];
|
|
1152
1168
|
this.groups = userProfile['groups'] ?? [];
|
|
1153
|
-
this.clientSetting = userProfile
|
|
1169
|
+
this.clientSetting = userProfile.tenantInfo?.clientSetting ?? {
|
|
1154
1170
|
auditTrail: false,
|
|
1155
1171
|
disableClassReminder: true,
|
|
1156
1172
|
webhook: false,
|
|
@@ -1159,7 +1175,9 @@ export class UserContext extends UserContextInfo {
|
|
|
1159
1175
|
this.roles = userProfile['roles'] ?? [Role.Everyone, Role.User];
|
|
1160
1176
|
|
|
1161
1177
|
for (const group of this.groups) {
|
|
1162
|
-
const
|
|
1178
|
+
const roleResolver = (rolegroups as Record<string, RoleGroupResolver | undefined>)[group];
|
|
1179
|
+
if (!roleResolver) continue;
|
|
1180
|
+
const roles: Role[] = roleResolver();
|
|
1163
1181
|
for (let r = 0; r < roles.length; r++) {
|
|
1164
1182
|
if (!this.roles.includes(roles[r])) {
|
|
1165
1183
|
this.roles.push(roles[r]);
|
|
@@ -1233,8 +1251,9 @@ export class UserContext extends UserContextInfo {
|
|
|
1233
1251
|
/**
|
|
1234
1252
|
* Define additional properties from user into moreProps
|
|
1235
1253
|
*/
|
|
1236
|
-
setMoreProps(userProfile: UserContextInfo): Record<string,
|
|
1254
|
+
setMoreProps(userProfile: UserContextInfo): Record<string, unknown> {
|
|
1237
1255
|
const allprops = Object.keys(userProfile);
|
|
1256
|
+
const profileRecord = userProfile as unknown as Record<string, unknown>;
|
|
1238
1257
|
const excludekeys = [
|
|
1239
1258
|
'created',
|
|
1240
1259
|
'createdBy',
|
|
@@ -1265,11 +1284,11 @@ export class UserContext extends UserContextInfo {
|
|
|
1265
1284
|
'uid',
|
|
1266
1285
|
'orgRecordId',
|
|
1267
1286
|
];
|
|
1268
|
-
const data: Record<string,
|
|
1287
|
+
const data: Record<string, unknown> = {};
|
|
1269
1288
|
for (let i = 0; i < allprops.length; i++) {
|
|
1270
1289
|
const key = allprops[i];
|
|
1271
1290
|
if (!excludekeys.includes(key)) {
|
|
1272
|
-
data[key] =
|
|
1291
|
+
data[key] = profileRecord[key];
|
|
1273
1292
|
}
|
|
1274
1293
|
}
|
|
1275
1294
|
|
|
@@ -1293,7 +1312,7 @@ export class UserContext extends UserContextInfo {
|
|
|
1293
1312
|
return isodate;
|
|
1294
1313
|
}
|
|
1295
1314
|
|
|
1296
|
-
addTransactionStep(action: string, collection: string, id: string[], data:
|
|
1315
|
+
addTransactionStep(action: string, collection: string, id: string[], data: StepData['data']) {
|
|
1297
1316
|
this.transSteps.push({ action: action, collection: collection, id: id, data: data });
|
|
1298
1317
|
}
|
|
1299
1318
|
|
|
@@ -1326,6 +1345,7 @@ export class UserContext extends UserContextInfo {
|
|
|
1326
1345
|
if (this.dbsession.inTransaction()) await this.dbsession.abortTransaction();
|
|
1327
1346
|
}
|
|
1328
1347
|
}
|
|
1348
|
+
|
|
1329
1349
|
async endSession() {
|
|
1330
1350
|
await this.dbsession.endSession();
|
|
1331
1351
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { Field, ObjectType } from '@nestjs/graphql';
|
|
1
2
|
import { ApiProperty } from '@nestjs/swagger';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
3
|
+
import { JSONSchema7 } from 'json-schema';
|
|
4
|
+
import { PipelineStage } from 'mongoose';
|
|
4
5
|
import { Permission } from 'src/simple-app/_core/resources/permission/permission.schema';
|
|
5
6
|
|
|
6
7
|
export class ModifiedCollection {
|
|
@@ -18,12 +19,14 @@ export class DocNumberFormatResult {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export class SearchBody {
|
|
21
|
-
filter?:
|
|
22
|
+
filter?: PipelineStage.Match['$match'];
|
|
22
23
|
|
|
23
|
-
fields?:
|
|
24
|
+
fields?: PipelineStage.Project['$project'];
|
|
25
|
+
|
|
26
|
+
sorts?: PipelineStage.Sort['$sort'];
|
|
27
|
+
|
|
28
|
+
lookup?: PipelineStage.Lookup['$lookup'];
|
|
24
29
|
|
|
25
|
-
sorts?: any[];
|
|
26
|
-
lookup?: object;
|
|
27
30
|
pagination?: {
|
|
28
31
|
pageSize?: number;
|
|
29
32
|
pageNo?: number;
|
|
@@ -39,22 +42,6 @@ export enum IsolationType {
|
|
|
39
42
|
export class MoreProjectionType {
|
|
40
43
|
[key: string]: string;
|
|
41
44
|
}
|
|
42
|
-
export class ApiEvent {
|
|
43
|
-
_id: string;
|
|
44
|
-
created: string;
|
|
45
|
-
updated?: string;
|
|
46
|
-
duration: number;
|
|
47
|
-
createdBy: string;
|
|
48
|
-
path: string;
|
|
49
|
-
ip: string;
|
|
50
|
-
method: string;
|
|
51
|
-
headers: any;
|
|
52
|
-
data?: any;
|
|
53
|
-
statusCode: number;
|
|
54
|
-
status: string;
|
|
55
|
-
errMsg?: string;
|
|
56
|
-
errData?: any;
|
|
57
|
-
}
|
|
58
45
|
|
|
59
46
|
export class DeleteResultType<T> {
|
|
60
47
|
data: T;
|
|
@@ -70,6 +57,7 @@ export class DocumentStatus {
|
|
|
70
57
|
readOnly: boolean;
|
|
71
58
|
actions: string[]; //api name ['confirm','revert','close','void' and etc]
|
|
72
59
|
}
|
|
60
|
+
|
|
73
61
|
export enum RESTMethods {
|
|
74
62
|
'post' = 'post',
|
|
75
63
|
'get' = 'get',
|
|
@@ -126,14 +114,21 @@ export class ForeignKey {
|
|
|
126
114
|
@Field()
|
|
127
115
|
@ApiProperty({ type: () => String })
|
|
128
116
|
_id?: string;
|
|
117
|
+
|
|
129
118
|
@Field()
|
|
130
119
|
@ApiProperty({ type: () => String })
|
|
131
120
|
code?: string;
|
|
121
|
+
|
|
132
122
|
@Field()
|
|
133
123
|
@ApiProperty({ type: () => String })
|
|
134
124
|
label?: string;
|
|
135
|
-
|
|
125
|
+
|
|
126
|
+
// ! DO NOT use an index signature on your own schemas. ForeignKey is intentionally strict — only
|
|
127
|
+
// ! `_id`, `code`, and `label` are allowed. For fields with extra dynamic properties, extend this
|
|
128
|
+
// ! class or use the appropriate XxxAutoComplete type instead.
|
|
129
|
+
// [key: string]: any;
|
|
136
130
|
}
|
|
131
|
+
|
|
137
132
|
export class MyForeignKey {
|
|
138
133
|
[collectionname: string]: string[];
|
|
139
134
|
}
|
|
@@ -141,6 +136,9 @@ export class MyForeignKey {
|
|
|
141
136
|
export class NoParam {}
|
|
142
137
|
|
|
143
138
|
export class DynamicParam {
|
|
139
|
+
// ! `any` is intentional here. DynamicParam represents a fully open key-value map where both
|
|
140
|
+
// ! the keys and value types are unknown at compile time. Do not use this class for typed fields.
|
|
141
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
144
142
|
[key: string]: any;
|
|
145
143
|
}
|
|
146
144
|
|
|
@@ -163,11 +161,11 @@ export class SchemaFields {
|
|
|
163
161
|
updatedBy?: string;
|
|
164
162
|
__v?: number;
|
|
165
163
|
documentStatus?: string;
|
|
166
|
-
[key: string]: any; //SimpleAppJSONSchema7 | SimpleAppJSONSchema7[] | undefined;
|
|
167
164
|
}
|
|
168
165
|
|
|
169
166
|
export class SimpleAppJSONSchema7 implements JSONSchema7 {
|
|
170
167
|
'x-simpleapp-config': SchemaConfig;
|
|
168
|
+
type?: JSONSchema7['type'];
|
|
171
169
|
}
|
|
172
170
|
export const SimpleAppJSONSchema7Definition = SimpleAppJSONSchema7;
|
|
173
171
|
|
|
@@ -176,7 +174,18 @@ export class BranchPermission extends Permission {
|
|
|
176
174
|
branch: ForeignKey;
|
|
177
175
|
}
|
|
178
176
|
|
|
179
|
-
export type StepData = {
|
|
177
|
+
export type StepData = {
|
|
178
|
+
action: string;
|
|
179
|
+
|
|
180
|
+
collection: string;
|
|
181
|
+
|
|
182
|
+
id: string[];
|
|
183
|
+
|
|
184
|
+
// ! `any[]` is intentional here. Each step in a SAGA transaction can carry data from any
|
|
185
|
+
// ! MongoDB collection, so the shape is not known until runtime.
|
|
186
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
187
|
+
data: any[];
|
|
188
|
+
};
|
|
180
189
|
|
|
181
190
|
export type DocumentDictEntry = {
|
|
182
191
|
docName: string;
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
* last change 2023-03-17
|
|
5
5
|
* Author: Ks Tan
|
|
6
6
|
*/
|
|
7
|
+
import { ApiEvent } from '@core-features/log/schemas';
|
|
7
8
|
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
|
|
8
9
|
import { InjectConnection, InjectModel } from '@nestjs/mongoose';
|
|
10
|
+
import type { Response as ExpressResponse } from 'express';
|
|
9
11
|
import { Connection, Model } from 'mongoose';
|
|
10
|
-
import { Observable } from 'rxjs';
|
|
11
12
|
import { catchError, map, tap } from 'rxjs/operators';
|
|
12
13
|
import { UserContext } from '../features/user-context/user.context';
|
|
13
|
-
import { ApiEvent } from './schemas';
|
|
14
14
|
import { SimpleAppDbRevertService } from './simple-app-db-revert.service';
|
|
15
15
|
@Injectable()
|
|
16
16
|
export class SimpleAppInterceptor implements NestInterceptor {
|
|
@@ -20,40 +20,32 @@ export class SimpleAppInterceptor implements NestInterceptor {
|
|
|
20
20
|
private simpleAppDbRevertService: SimpleAppDbRevertService,
|
|
21
21
|
) {}
|
|
22
22
|
|
|
23
|
-
async intercept(context: ExecutionContext, next: CallHandler)
|
|
23
|
+
async intercept(context: ExecutionContext, next: CallHandler) {
|
|
24
24
|
//not http request then exclude such as graphql
|
|
25
25
|
if (context.getType() != 'http') {
|
|
26
26
|
// obtain usersession here
|
|
27
|
-
return next.handle().pipe(
|
|
28
|
-
tap(async () => {
|
|
29
|
-
//console.log("none http, do nothing at interceptor")
|
|
30
|
-
}),
|
|
31
|
-
);
|
|
27
|
+
return next.handle().pipe(tap(() => {}));
|
|
32
28
|
}
|
|
33
|
-
const req = context.switchToHttp().getRequest();
|
|
34
|
-
const resp = context.switchToHttp().getResponse();
|
|
29
|
+
const req = context.switchToHttp().getRequest<Request>();
|
|
30
|
+
const resp = context.switchToHttp().getResponse<ExpressResponse>();
|
|
35
31
|
//console.log("want to get user session:", Object.keys(req))
|
|
36
32
|
|
|
37
33
|
if (req.url == '/health') {
|
|
38
|
-
return next.handle().pipe(
|
|
39
|
-
tap(async () => {
|
|
40
|
-
//console.log("none http, do nothing at interceptor")
|
|
41
|
-
}),
|
|
42
|
-
);
|
|
34
|
+
return next.handle().pipe(tap(() => {}));
|
|
43
35
|
}
|
|
44
36
|
|
|
45
|
-
const usersession
|
|
37
|
+
const usersession = req['sessionuser'] as UserContext;
|
|
46
38
|
//console.log("after read user session:",usersession)
|
|
47
39
|
const method = req['method'];
|
|
48
40
|
const headers = { ...req['headers'] };
|
|
49
|
-
const ip = req['ip'];
|
|
41
|
+
const ip = req['ip'] as string;
|
|
50
42
|
const url = req['url'];
|
|
51
43
|
// let { url, method, headers, body }
|
|
52
44
|
const session = await this.connection.startSession();
|
|
53
45
|
if (!session['runCount']) {
|
|
54
46
|
session['runCount'] = 0;
|
|
55
47
|
} else {
|
|
56
|
-
session['runCount'] = session['runCount'] + 1;
|
|
48
|
+
session['runCount'] = (session['runCount'] as number) + 1;
|
|
57
49
|
}
|
|
58
50
|
usersession.setDBSession(session);
|
|
59
51
|
// const session: ClientSession = usersession.getDBSession();
|
|
@@ -83,8 +75,10 @@ export class SimpleAppInterceptor implements NestInterceptor {
|
|
|
83
75
|
return next.handle().pipe(
|
|
84
76
|
map((data) => {
|
|
85
77
|
if (data && typeof data === 'object' && 'pagination' in data && 'items' in data) {
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
86
79
|
return data;
|
|
87
80
|
}
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
88
82
|
return data;
|
|
89
83
|
}),
|
|
90
84
|
catchError(async (err, caught) => {
|
|
@@ -97,20 +91,29 @@ export class SimpleAppInterceptor implements NestInterceptor {
|
|
|
97
91
|
}
|
|
98
92
|
|
|
99
93
|
const responseBody = {
|
|
94
|
+
// eslint-disable-next-line
|
|
100
95
|
message: err.message,
|
|
101
96
|
timestamp: new Date().toISOString(),
|
|
102
97
|
path: url,
|
|
103
|
-
|
|
98
|
+
// eslint-disable-next-line
|
|
99
|
+
error: err.options as object,
|
|
104
100
|
};
|
|
105
101
|
|
|
102
|
+
// eslint-disable-next-line
|
|
106
103
|
eventObj.statusCode = err.status;
|
|
104
|
+
// eslint-disable-next-line
|
|
107
105
|
eventObj.errMsg = responseBody.message;
|
|
108
106
|
eventObj.data = req.body;
|
|
109
107
|
eventObj.status = 'NG';
|
|
110
108
|
eventObj.errData = responseBody.error;
|
|
111
|
-
|
|
109
|
+
|
|
110
|
+
resp.status(
|
|
111
|
+
// eslint-disable-next-line
|
|
112
|
+
err?.status ?? 500,
|
|
113
|
+
);
|
|
112
114
|
return responseBody;
|
|
113
115
|
}),
|
|
116
|
+
// eslint-disable-next-line
|
|
114
117
|
tap(async () => {
|
|
115
118
|
// console.log("============interceptor tap",method,url)
|
|
116
119
|
const endtime = new Date();
|
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
import dayjs from 'dayjs';
|
|
2
2
|
import duration from 'dayjs/plugin/duration';
|
|
3
|
+
import isBetween from 'dayjs/plugin/isBetween';
|
|
3
4
|
import timezone from 'dayjs/plugin/timezone';
|
|
4
5
|
import utc from 'dayjs/plugin/utc';
|
|
5
6
|
|
|
6
7
|
dayjs.extend(utc);
|
|
7
8
|
dayjs.extend(duration);
|
|
8
9
|
dayjs.extend(timezone);
|
|
10
|
+
dayjs.extend(isBetween);
|
|
9
11
|
|
|
10
12
|
export const getDayJs = () => {
|
|
11
13
|
return dayjs;
|
|
12
14
|
};
|
|
13
15
|
|
|
14
|
-
|
|
15
16
|
/**
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
* this function is used to convert user time date to UTC
|
|
18
|
+
* @param date
|
|
19
|
+
* @param timeZone from appuser, example Asia/Malaysia
|
|
20
|
+
* @returns
|
|
21
|
+
*/
|
|
21
22
|
export const convertToStartOfDayUTC = (date: string, timeZone: string): string => {
|
|
22
23
|
const startOfDay = dayjs.tz(date, timeZone).startOf('day');
|
|
23
24
|
const utcTime = startOfDay.utc().format();
|
|
24
25
|
return utcTime;
|
|
25
|
-
}
|
|
26
|
+
};
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* this function is used to convert user time date to UTC
|
|
@@ -34,7 +35,7 @@ export const convertToEndOfDayUTC = (date: string, timeZone: string): string =>
|
|
|
34
35
|
const endOfDay = dayjs.tz(date, timeZone).endOf('day');
|
|
35
36
|
const utcTime = endOfDay.utc().format();
|
|
36
37
|
return utcTime;
|
|
37
|
-
}
|
|
38
|
+
};
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
41
|
* this function is used to convert UTC time to user timezone
|
|
@@ -44,4 +45,8 @@ export const convertToEndOfDayUTC = (date: string, timeZone: string): string =>
|
|
|
44
45
|
export const convertUTCToTimezone = (date: string, timeZone: string): string => {
|
|
45
46
|
const localTime = dayjs(date).tz(timeZone).format();
|
|
46
47
|
return localTime;
|
|
47
|
-
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const utcToLocalOffset = (utcTime: string, timeZoneOffset: number) => {
|
|
51
|
+
return dayjs.utc(utcTime).utcOffset(Number(timeZoneOffset));
|
|
52
|
+
};
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<NuxtLayout>
|
|
3
|
-
|
|
2
|
+
<NuxtLayout>
|
|
4
3
|
<!-- <NuxtLayout :name="getLayout()"> -->
|
|
5
|
-
<ConfirmDialog></ConfirmDialog>
|
|
4
|
+
<ConfirmDialog></ConfirmDialog>
|
|
6
5
|
<DynamicDialog />
|
|
7
|
-
<SessionBlock/>
|
|
8
|
-
<OverlayHoldscreen/>
|
|
9
|
-
<EventDocumentViewer></EventDocumentViewer>
|
|
10
|
-
<EventDecision/>
|
|
11
|
-
<EventNotification/>
|
|
12
|
-
<
|
|
13
|
-
<
|
|
14
|
-
<NuxtPage/>
|
|
6
|
+
<SessionBlock />
|
|
7
|
+
<OverlayHoldscreen />
|
|
8
|
+
<EventDocumentViewer></EventDocumentViewer>
|
|
9
|
+
<EventDecision />
|
|
10
|
+
<EventNotification />
|
|
11
|
+
<SubscriptionFreeBanner />
|
|
12
|
+
<SubscriptionGracePeriodBanner />
|
|
13
|
+
<NuxtPage />
|
|
15
14
|
</NuxtLayout>
|
|
16
15
|
</template>
|
|
17
16
|
|
|
@@ -22,50 +21,50 @@
|
|
|
22
21
|
* last change 2024-03-17
|
|
23
22
|
* Author: Ks Tan
|
|
24
23
|
*/
|
|
25
|
-
let currentXorg = getCurrentXorg()
|
|
26
|
-
watch(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if(getCurrentXorg()===''){
|
|
34
|
-
navigateTo('/picktenant')
|
|
35
|
-
}else if(!getUserProfile()?.currentGroup){
|
|
36
|
-
goTo('pickgroup')
|
|
24
|
+
let currentXorg = getCurrentXorg();
|
|
25
|
+
watch(
|
|
26
|
+
() => useRoute().fullPath,
|
|
27
|
+
async (newval, oldvalue) => {
|
|
28
|
+
if (getCurrentXorg() != currentXorg) {
|
|
29
|
+
currentXorg = getCurrentXorg();
|
|
30
|
+
await reloadUserStore();
|
|
37
31
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
if (getPathPara("xorg", "") != "") {
|
|
33
|
+
if (getCurrentXorg() === "") {
|
|
34
|
+
navigateTo("/picktenant");
|
|
35
|
+
} else if (!getUserProfile()?.currentGroup) {
|
|
36
|
+
goTo("pickgroup");
|
|
37
|
+
}
|
|
38
|
+
setGraphqlServer();
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
);
|
|
41
42
|
|
|
42
|
-
const getLayout = ()=>{
|
|
43
|
-
// const { status } = useAuth();
|
|
44
|
-
|
|
43
|
+
const getLayout = () => {
|
|
44
|
+
// const { status } = useAuth();
|
|
45
45
|
// if(status.value=='unauthenticated') return 'loginlayout'
|
|
46
46
|
// else {
|
|
47
47
|
// if (isMobile()) return 'mobile'
|
|
48
48
|
// else 'default'
|
|
49
|
-
// }
|
|
50
|
-
}
|
|
49
|
+
// }
|
|
50
|
+
};
|
|
51
51
|
|
|
52
|
-
onMounted(async()=>{
|
|
53
|
-
|
|
54
|
-
if(x.data.value?.expires && x.data.value?.expires > new Date().toISOString()){
|
|
55
|
-
await reloadUserStore()
|
|
56
|
-
const currentgroup = useCookie(
|
|
52
|
+
onMounted(async () => {
|
|
53
|
+
const x = useAuthState();
|
|
54
|
+
if (x.data.value?.expires && x.data.value?.expires > new Date().toISOString()) {
|
|
55
|
+
await reloadUserStore();
|
|
56
|
+
const currentgroup = useCookie("currentGroup").value;
|
|
57
57
|
//if no xorg, no enforce pick group
|
|
58
|
-
if(getCurrentXorg()===
|
|
59
|
-
navigateTo(
|
|
60
|
-
}else if(!currentgroup){
|
|
61
|
-
goTo(
|
|
58
|
+
if (getCurrentXorg() === "") {
|
|
59
|
+
navigateTo("/picktenant");
|
|
60
|
+
} else if (!currentgroup) {
|
|
61
|
+
goTo("pickgroup");
|
|
62
62
|
}
|
|
63
|
-
setGraphqlServer()
|
|
64
|
-
}else{
|
|
65
|
-
if(useRoute().path!=
|
|
63
|
+
setGraphqlServer();
|
|
64
|
+
} else {
|
|
65
|
+
if (useRoute().path != "/login") {
|
|
66
66
|
navigateTo("/login");
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
})
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
71
70
|
</script>
|