@simitgroup/simpleapp-generator 2.0.3-m-alpha → 2.0.3-n-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 +5 -0
- package/package.json +1 -1
- package/templates/nest/src/simple-app/_core/framework/base/simple-app.service.ts.eta +148 -126
- package/templates/nuxt/composables/getMenus.generate.ts.eta +123 -118
- package/templates/nuxt/simpleapp/generate/features/miniApp/app/components/integration/MiniAppIntegrationPage.vue.eta +11 -12
package/ReleaseNote.md
CHANGED
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
|
|
|
13
13
|
import Ajv from 'ajv';
|
|
14
14
|
import addErrors from 'ajv-errors';
|
|
15
15
|
import addFormats from 'ajv-formats';
|
|
16
|
-
import { AnyKeys, AnyObject, FilterQuery, Model, PipelineStage, mongo } from 'mongoose';
|
|
16
|
+
import { AnyKeys, AnyObject, FilterQuery, Model, PipelineStage, SortOrder, mongo } from 'mongoose';
|
|
17
17
|
// import { CloudApiService } from 'src/cloudapi/cloudapi.service';
|
|
18
18
|
import { foreignkeys } from '../../features/foreign-key/foreignkeys.dict';
|
|
19
19
|
|
|
@@ -26,6 +26,31 @@ import { RunWebhookService } from '../../features/webhook/run-webhook.service';
|
|
|
26
26
|
import { DeleteResultType, IsolationType, MoreProjectionType, PatchManyRequest, SchemaFields, TextSearchBody, UniqueKeyExistResponse } from '../schemas';
|
|
27
27
|
import { SearchWithRelation } from './dto/simple-app-search-with-relation.dto';
|
|
28
28
|
|
|
29
|
+
type DynamicRecord = Record<string, unknown>;
|
|
30
|
+
type LooseRecord = ReturnType<typeof JSON.parse>;
|
|
31
|
+
type SimpleAppJsonSchema = DynamicRecord & {
|
|
32
|
+
properties?: Record<string, DynamicRecord>;
|
|
33
|
+
'x-simpleapp-config': {
|
|
34
|
+
resourceName?: string;
|
|
35
|
+
} & DynamicRecord;
|
|
36
|
+
};
|
|
37
|
+
type ErrorWithMessage = { message: string };
|
|
38
|
+
type EventResult = { name?: string };
|
|
39
|
+
type SortOption = string | Record<string, SortOrder> | [string, SortOrder][];
|
|
40
|
+
type AggregateTotalResult = { total?: number };
|
|
41
|
+
type RelatedRecord = { _id?: string } & DynamicRecord;
|
|
42
|
+
type ForeignKeySettings = Record<string, string[]>;
|
|
43
|
+
type ResourceForeignKeyDictionary = Record<string, string[]>;
|
|
44
|
+
type GlobalForeignKeyDictionary = Record<string, ForeignKeySettings>;
|
|
45
|
+
type ForeignKeyAggregateRow = { _id: string; collection: string };
|
|
46
|
+
|
|
47
|
+
const getErrorMessage = (err: unknown): string => {
|
|
48
|
+
if (err instanceof Error) return err.message;
|
|
49
|
+
if (typeof err === 'string') return err;
|
|
50
|
+
return JSON.stringify(err);
|
|
51
|
+
};
|
|
52
|
+
const isEventResult = (value: unknown): value is EventResult => typeof value === 'object' && value !== null && 'name' in value;
|
|
53
|
+
|
|
29
54
|
@Injectable()
|
|
30
55
|
export class SimpleAppService<T extends SchemaFields> {
|
|
31
56
|
// @Inject(EventEmitter2)
|
|
@@ -42,7 +67,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
42
67
|
protected docnogenerator: SimpleAppDocumentNoFormatService;
|
|
43
68
|
protected logger = new Logger();
|
|
44
69
|
protected strictIsolation = true;
|
|
45
|
-
protected jsonschema:
|
|
70
|
+
protected jsonschema: SimpleAppJsonSchema = {
|
|
46
71
|
type: 'object',
|
|
47
72
|
'x-simpleapp-config': {},
|
|
48
73
|
properties: {},
|
|
@@ -57,12 +82,12 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
57
82
|
protected MAX_PAGE_SIZE = 100000;
|
|
58
83
|
protected moreAutoCompleteField: MoreProjectionType = {};
|
|
59
84
|
protected isolationtype: IsolationType = IsolationType.org;
|
|
60
|
-
protected isolationFilter:
|
|
85
|
+
protected isolationFilter: FilterQuery<T> = {};
|
|
61
86
|
protected data: T = { _id: '' } as T;
|
|
62
87
|
protected doc: Model<T>; //set private to prevent developer break data isolation control
|
|
63
|
-
protected errorlist = [];
|
|
88
|
+
protected errorlist: ErrorWithMessage[] = [];
|
|
64
89
|
protected withDocNumberFormat = false;
|
|
65
|
-
protected foreignkeys = {};
|
|
90
|
+
protected foreignkeys: ResourceForeignKeyDictionary = {};
|
|
66
91
|
private eventEmitter: EventEmitter2;
|
|
67
92
|
private logService: SimpleAppLogService;
|
|
68
93
|
// protected userprovider = new UserContext() ;
|
|
@@ -91,7 +116,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
91
116
|
getDocumentType = () => this.documentType;
|
|
92
117
|
getDocumentName = (capFirst: boolean = false) => (capFirst ? _.upperFirst(this.documentName) : this.documentName);
|
|
93
118
|
getRecordId = (): string => this.data?._id ?? '';
|
|
94
|
-
setSchema = (newschema) => (this.jsonschema = newschema);
|
|
119
|
+
setSchema = (newschema: SimpleAppJsonSchema) => (this.jsonschema = newschema);
|
|
95
120
|
getSchema = () => this.doc.schema.obj;
|
|
96
121
|
getJsonSchema = () => this.jsonschema;
|
|
97
122
|
|
|
@@ -110,19 +135,19 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
110
135
|
return false;
|
|
111
136
|
}
|
|
112
137
|
reCalculateValue(data: T) {}
|
|
113
|
-
getIsolationFilter = (appUser: UserContext) => {
|
|
114
|
-
let isolationFilter = {};
|
|
138
|
+
getIsolationFilter = (appUser: UserContext): FilterQuery<T> => {
|
|
139
|
+
let isolationFilter: FilterQuery<T> = {};
|
|
115
140
|
switch (this.isolationtype) {
|
|
116
|
-
case
|
|
141
|
+
case IsolationType.none:
|
|
117
142
|
isolationFilter = {};
|
|
118
143
|
break;
|
|
119
|
-
case
|
|
144
|
+
case IsolationType.branch:
|
|
120
145
|
isolationFilter = appUser.getBranchFilter();
|
|
121
146
|
break;
|
|
122
|
-
case
|
|
147
|
+
case IsolationType.tenant:
|
|
123
148
|
isolationFilter = appUser.getTenantFilter();
|
|
124
149
|
break;
|
|
125
|
-
case
|
|
150
|
+
case IsolationType.org:
|
|
126
151
|
default:
|
|
127
152
|
isolationFilter = appUser.getOrgFilter();
|
|
128
153
|
break;
|
|
@@ -140,7 +165,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
140
165
|
// console.log(products);
|
|
141
166
|
return productlist;
|
|
142
167
|
} catch (err) {
|
|
143
|
-
throw new InternalServerErrorException(err
|
|
168
|
+
throw new InternalServerErrorException(getErrorMessage(err));
|
|
144
169
|
}
|
|
145
170
|
}
|
|
146
171
|
addAutoCompleteField = (morefield: MoreProjectionType) => {
|
|
@@ -154,22 +179,22 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
154
179
|
};
|
|
155
180
|
async getAutoComplete(appUser: UserContext, keyword: string, data?: T) {
|
|
156
181
|
try {
|
|
157
|
-
const filter1:
|
|
158
|
-
const filter2:
|
|
159
|
-
const filters:
|
|
160
|
-
if (this.jsonschema.properties[this.documentIdentityCode]
|
|
182
|
+
const filter1: DynamicRecord = {};
|
|
183
|
+
const filter2: DynamicRecord = {};
|
|
184
|
+
const filters: DynamicRecord[] = [];
|
|
185
|
+
if (this.jsonschema.properties?.[this.documentIdentityCode]?.type == 'string') {
|
|
161
186
|
filter1[this.documentIdentityCode] = { $regex: keyword, $options: 'i' };
|
|
162
187
|
filters.push(filter1);
|
|
163
188
|
}
|
|
164
189
|
filter2[this.documentIdentityLabel] = { $regex: keyword, $options: 'i' };
|
|
165
190
|
filters.push(filter2);
|
|
166
191
|
|
|
167
|
-
const filterobj = { $or: filters }
|
|
192
|
+
const filterobj = { $or: filters } as FilterQuery<T>;
|
|
168
193
|
Object.assign(filterobj, data);
|
|
169
194
|
Object.assign(filterobj, this.getIsolationFilter(appUser));
|
|
170
195
|
const projections = {
|
|
171
|
-
label:
|
|
172
|
-
code:
|
|
196
|
+
label: `$${this.documentIdentityLabel}`,
|
|
197
|
+
code: `$${this.documentIdentityCode}`,
|
|
173
198
|
};
|
|
174
199
|
if (this.moreAutoCompleteField) {
|
|
175
200
|
Object.assign(projections, this.moreAutoCompleteField);
|
|
@@ -183,7 +208,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
183
208
|
});
|
|
184
209
|
return productlist;
|
|
185
210
|
} catch (err) {
|
|
186
|
-
throw new InternalServerErrorException(err
|
|
211
|
+
throw new InternalServerErrorException(getErrorMessage(err));
|
|
187
212
|
}
|
|
188
213
|
}
|
|
189
214
|
|
|
@@ -205,7 +230,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
205
230
|
// console.log(products);
|
|
206
231
|
return productlist;
|
|
207
232
|
} catch (err) {
|
|
208
|
-
throw new InternalServerErrorException(err
|
|
233
|
+
throw new InternalServerErrorException(getErrorMessage(err));
|
|
209
234
|
}
|
|
210
235
|
// return this;
|
|
211
236
|
}
|
|
@@ -224,7 +249,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
224
249
|
throw new InternalServerErrorException('first aggregate pipelinestage shall use $match');
|
|
225
250
|
}
|
|
226
251
|
}
|
|
227
|
-
async aggregate<
|
|
252
|
+
async aggregate<TAggregateResult = LooseRecord>(appUser: UserContext, pipeline: PipelineStage[]) {
|
|
228
253
|
if (pipeline[0] && pipeline[0]['$match']) {
|
|
229
254
|
try {
|
|
230
255
|
const isolationFilter = { ...this.getIsolationFilter(appUser) };
|
|
@@ -236,7 +261,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
236
261
|
// if(appUser.getId() == ''){
|
|
237
262
|
// console.log(JSON.stringify(pipeline))
|
|
238
263
|
// }
|
|
239
|
-
const res = await this.doc.aggregate<
|
|
264
|
+
const res = await this.doc.aggregate<TAggregateResult>(pipeline, {
|
|
240
265
|
session: appUser.getDBSession(),
|
|
241
266
|
});
|
|
242
267
|
|
|
@@ -255,7 +280,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
255
280
|
appUser: UserContext,
|
|
256
281
|
filters: FilterQuery<T>,
|
|
257
282
|
projection: string[] = undefined,
|
|
258
|
-
sort:
|
|
283
|
+
sort: SortOption = undefined,
|
|
259
284
|
lookup: { [key: string]: string } = undefined,
|
|
260
285
|
pagination?: { pageSize?: number; pageNo?: number },
|
|
261
286
|
) {
|
|
@@ -281,7 +306,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
281
306
|
|
|
282
307
|
searchResults = await query.exec();
|
|
283
308
|
} else {
|
|
284
|
-
const pipelines = this.searchToAggregate(appUser, newfilters, projection, sort, lookup, pagination);
|
|
309
|
+
const pipelines = this.searchToAggregate(appUser, newfilters, projection, this.toAggregateSort(sort), lookup, pagination);
|
|
285
310
|
this.logger.debug('after aggregate', pipelines);
|
|
286
311
|
searchResults = await this.aggregate(appUser, pipelines);
|
|
287
312
|
}
|
|
@@ -295,7 +320,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
295
320
|
// console.log(products);
|
|
296
321
|
return list;
|
|
297
322
|
} catch (err) {
|
|
298
|
-
throw new BadRequestException(err
|
|
323
|
+
throw new BadRequestException(getErrorMessage(err));
|
|
299
324
|
}
|
|
300
325
|
// return this;
|
|
301
326
|
}
|
|
@@ -308,7 +333,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
308
333
|
appUser: UserContext,
|
|
309
334
|
filters: FilterQuery<T> = {},
|
|
310
335
|
projection: string[] = undefined,
|
|
311
|
-
sort:
|
|
336
|
+
sort: SortOption = undefined,
|
|
312
337
|
lookup: { [key: string]: string } = undefined,
|
|
313
338
|
pagination?: { pageSize?: number; pageNo?: number },
|
|
314
339
|
) {
|
|
@@ -345,7 +370,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
345
370
|
items = rows;
|
|
346
371
|
total = count;
|
|
347
372
|
} else {
|
|
348
|
-
const pipelines = this.searchToAggregate(appUser, newfilters, projection
|
|
373
|
+
const pipelines = this.searchToAggregate(appUser, newfilters, projection, this.toAggregateSort(sort), lookup, { pageSize, pageNo });
|
|
349
374
|
|
|
350
375
|
const countPipeline: PipelineStage[] = [{ $match: newfilters }];
|
|
351
376
|
const collections = Object.keys(lookup);
|
|
@@ -368,15 +393,15 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
368
393
|
|
|
369
394
|
this.logger.debug('after aggregate searchWithPageInfo', pipelines);
|
|
370
395
|
|
|
371
|
-
const [rows, countResult] = await Promise.all([this.aggregate(appUser, pipelines), this.aggregate(appUser, countPipeline)]);
|
|
396
|
+
const [rows, countResult] = await Promise.all([this.aggregate<T>(appUser, pipelines), this.aggregate<AggregateTotalResult>(appUser, countPipeline)]);
|
|
372
397
|
|
|
373
|
-
items = rows
|
|
374
|
-
total = countResult && countResult.length > 0 ? countResult[0]
|
|
398
|
+
items = rows;
|
|
399
|
+
total = countResult && countResult.length > 0 ? countResult[0].total || 0 : 0;
|
|
375
400
|
}
|
|
376
401
|
|
|
377
402
|
return items;
|
|
378
403
|
} catch (err) {
|
|
379
|
-
throw new BadRequestException(err
|
|
404
|
+
throw new BadRequestException(getErrorMessage(err));
|
|
380
405
|
}
|
|
381
406
|
}
|
|
382
407
|
|
|
@@ -441,7 +466,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
441
466
|
const keyword = body.keyword;
|
|
442
467
|
// console.log("bodybody",body)
|
|
443
468
|
// Prepare regex filter
|
|
444
|
-
const orConditions:
|
|
469
|
+
const orConditions: DynamicRecord[] = [];
|
|
445
470
|
const regex = new RegExp(keyword, 'i');
|
|
446
471
|
|
|
447
472
|
if (body.filters && body.filters.length > 0) {
|
|
@@ -477,7 +502,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
477
502
|
async findById(appUser: UserContext, id: string) {
|
|
478
503
|
// if (this.hooks.beforeFetchRecord) await this.hooks.beforeFetchRecord(appUser, id);
|
|
479
504
|
|
|
480
|
-
const data = await this.search(appUser, { _id: id
|
|
505
|
+
const data = await this.search(appUser, { _id: id });
|
|
481
506
|
// if (this.hooks.afterFetchRecord) await this.hooks.afterFetchRecord(appUser, data[0]);
|
|
482
507
|
|
|
483
508
|
if (data.length == 1) {
|
|
@@ -491,7 +516,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
491
516
|
async findByIdNoIsolation(appUser: UserContext, id: string) {
|
|
492
517
|
// if (this.hooks.beforeFetchRecord) await this.hooks.beforeFetchRecord(appUser, id);
|
|
493
518
|
|
|
494
|
-
const data = await this.searchNoIsolation(appUser, { _id: id
|
|
519
|
+
const data = await this.searchNoIsolation(appUser, { _id: id });
|
|
495
520
|
// if (this.hooks.afterFetchRecord) await this.hooks.afterFetchRecord(appUser, data[0]);
|
|
496
521
|
|
|
497
522
|
if (data.length == 1) {
|
|
@@ -511,7 +536,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
511
536
|
if (Array.isArray(datas)) {
|
|
512
537
|
for (let i = 0; i < datas.length; i++) {
|
|
513
538
|
const data = datas[i];
|
|
514
|
-
let isolationFilter:
|
|
539
|
+
let isolationFilter: FilterQuery<T> = { ...appUser.getCreateFilter() };
|
|
515
540
|
isolationFilter = this.polishIsolationFilter(isolationFilter, data);
|
|
516
541
|
|
|
517
542
|
Object.assign(data, isolationFilter);
|
|
@@ -524,28 +549,25 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
524
549
|
appUser.startTransaction();
|
|
525
550
|
}
|
|
526
551
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
appUser.addTransactionStep('createMany', this.documentName, allIds, allLogData);
|
|
532
|
-
|
|
533
|
-
await this.addManyAuditEvents(appUser, this.documentName, 'createMany', datas);
|
|
534
|
-
for (const data of datas) {
|
|
535
|
-
appUser.addInsertedRecordId(this.documentName, data._id);
|
|
536
|
-
}
|
|
552
|
+
const result = await this.doc.insertMany(datas, { session: dbsession });
|
|
553
|
+
const allIds = datas.map((item) => item._id);
|
|
554
|
+
const allLogData = datas.map((item) => null);
|
|
555
|
+
appUser.addTransactionStep('createMany', this.documentName, allIds, allLogData);
|
|
537
556
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
557
|
+
await this.addManyAuditEvents(appUser, this.documentName, 'createMany', datas);
|
|
558
|
+
for (const data of datas) {
|
|
559
|
+
appUser.addInsertedRecordId(this.documentName, data._id);
|
|
541
560
|
}
|
|
561
|
+
|
|
562
|
+
return result;
|
|
563
|
+
|
|
542
564
|
} else {
|
|
543
565
|
throw new BadRequestException(this.getDocumentType() + ': create many only support array');
|
|
544
566
|
}
|
|
545
567
|
}
|
|
546
568
|
|
|
547
569
|
async create(appUser: UserContext, data: T, noStartTransaction: boolean = false) {
|
|
548
|
-
let result;
|
|
570
|
+
let result: T;
|
|
549
571
|
|
|
550
572
|
if (!data._id) {
|
|
551
573
|
data._id = crypto.randomUUID();
|
|
@@ -560,7 +582,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
560
582
|
await this.genNewDocNo(appUser, data);
|
|
561
583
|
}
|
|
562
584
|
|
|
563
|
-
let isolationFilter:
|
|
585
|
+
let isolationFilter: FilterQuery<T> = { ...appUser.getCreateFilter() };
|
|
564
586
|
isolationFilter = this.polishIsolationFilter(isolationFilter, data);
|
|
565
587
|
|
|
566
588
|
this.logger.debug('isolationFilter', 'SimpleAppService');
|
|
@@ -580,36 +602,41 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
580
602
|
const newdoc = new this.doc(data);
|
|
581
603
|
await this.identifyForeignKeys(appUser, data);
|
|
582
604
|
try {
|
|
583
|
-
result = await newdoc.save({ session: dbsession });
|
|
605
|
+
result = (await newdoc.save({ session: dbsession })) as T;
|
|
606
|
+
const resultId = result._id ?? data._id ?? '';
|
|
584
607
|
appUser.addTransactionStep('create', this.documentName, [data._id], [null]);
|
|
585
|
-
await this.addAuditEvent(appUser, this.documentName,
|
|
586
|
-
appUser.addInsertedRecordId(this.documentName,
|
|
608
|
+
await this.addAuditEvent(appUser, this.documentName, resultId, 'create', data);
|
|
609
|
+
appUser.addInsertedRecordId(this.documentName, resultId);
|
|
587
610
|
await this.runEvent(appUser, 'afterCreate', { data: result }, false);
|
|
588
611
|
await this.callWebhook(appUser, 'create', result);
|
|
589
|
-
return result
|
|
612
|
+
return result;
|
|
590
613
|
} catch (err) {
|
|
591
614
|
await this.runErrorEvent(appUser, 'errorCreate', data, err);
|
|
592
615
|
throw new InternalServerErrorException(`createHook ${this.documentType} error: ${JSON.stringify(err)}`, `${this.documentType} createHook error`);
|
|
593
616
|
}
|
|
594
617
|
}
|
|
595
618
|
|
|
596
|
-
applyNestedDateTime = (appUser: UserContext, data:
|
|
597
|
-
const
|
|
619
|
+
applyNestedDateTime = (appUser: UserContext, data: object, transtype: string) => {
|
|
620
|
+
const record = data as DynamicRecord;
|
|
621
|
+
const props = Object.getOwnPropertyNames(record);
|
|
598
622
|
for (let i = 0; i < props.length; i++) {
|
|
599
623
|
const key = props[i];
|
|
600
624
|
//need to apply nested
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
625
|
+
const value = record[key];
|
|
626
|
+
if (Array.isArray(value) && value.length > 0 && typeof value[0] == 'object' && value[0] !== null) {
|
|
627
|
+
for (let j = 0; j < value.length; j++) {
|
|
628
|
+
if (typeof value[j] == 'object' && value[j] !== null) {
|
|
629
|
+
this.applyNestedDateTime(appUser, value[j] as DynamicRecord, transtype);
|
|
630
|
+
}
|
|
604
631
|
}
|
|
605
632
|
} else if (key == 'created') {
|
|
606
|
-
|
|
633
|
+
record['created'] = transtype == 'create' || !record['created'] ? new Date().toISOString() : record['created'];
|
|
607
634
|
} else if (key == 'createdBy') {
|
|
608
|
-
|
|
635
|
+
record['createdBy'] = transtype == 'create' || !record['createdBy'] ? appUser.getUid() : record['createdBy'];
|
|
609
636
|
} else if (key == 'updated') {
|
|
610
|
-
|
|
637
|
+
record['updated'] = new Date().toISOString();
|
|
611
638
|
} else if (key == 'updatedBy') {
|
|
612
|
-
|
|
639
|
+
record['updatedBy'] = appUser.getUid();
|
|
613
640
|
}
|
|
614
641
|
}
|
|
615
642
|
};
|
|
@@ -631,26 +658,14 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
631
658
|
ajv.addKeyword({ keyword: 'x-remote', schemaType: 'object' });
|
|
632
659
|
ajv.addKeyword({ keyword: 'x-readonly', schemaType: 'boolean' });
|
|
633
660
|
this.logger.debug('run hook during validation');
|
|
634
|
-
|
|
635
|
-
// if (this.hooks.beforeValidation) {
|
|
636
|
-
// issuccess = await this.hooks.beforeValidation(appUser, data, _id);
|
|
637
|
-
// }
|
|
638
|
-
// const issuccess = await this.hook(appUser, HookType.beforeValidation, data);
|
|
639
|
-
if (!issuccess) {
|
|
640
|
-
const errormsg: string[] = [];
|
|
641
|
-
for (let i = 0; i < this.errorlist.length; i++) {
|
|
642
|
-
errormsg.push(this.errorlist[i].message);
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
throw new BadRequestException(errormsg as HttpExceptionOptions, 'Before validation hook failed');
|
|
646
|
-
}
|
|
661
|
+
await this.runEvent(appUser, 'beforeValidation', { data, id: _id }, false);
|
|
647
662
|
|
|
648
|
-
let validate
|
|
663
|
+
let validate: ReturnType<Ajv['compile']>;
|
|
649
664
|
try {
|
|
650
665
|
validate = ajv.compile(this.jsonschema);
|
|
651
666
|
} catch (err) {
|
|
652
667
|
this.logger.error('compile error', err);
|
|
653
|
-
throw new ForbiddenException(err
|
|
668
|
+
throw new ForbiddenException(getErrorMessage(err));
|
|
654
669
|
}
|
|
655
670
|
const valid = validate(data);
|
|
656
671
|
if (!valid) {
|
|
@@ -660,13 +675,13 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
660
675
|
}
|
|
661
676
|
}
|
|
662
677
|
|
|
663
|
-
polishIsolationFilter = (filterIsolation:
|
|
664
|
-
if (this.isolationtype ==
|
|
678
|
+
polishIsolationFilter = <TFilter extends FilterQuery<T>>(filterIsolation: TFilter, data: Partial<T> = {}): TFilter => {
|
|
679
|
+
if (this.isolationtype == IsolationType.none) {
|
|
665
680
|
delete filterIsolation['branchId'];
|
|
666
681
|
delete filterIsolation['orgId'];
|
|
667
682
|
delete filterIsolation['tenantId'];
|
|
668
683
|
}
|
|
669
|
-
if (this.isolationtype ==
|
|
684
|
+
if (this.isolationtype == IsolationType.tenant && !this.strictIsolation) {
|
|
670
685
|
// delete filterIsolation['tenantId']
|
|
671
686
|
if (data['tenantId']) {
|
|
672
687
|
filterIsolation['tenantId'] = data['tenantId'];
|
|
@@ -674,7 +689,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
674
689
|
delete filterIsolation['branchId'];
|
|
675
690
|
delete filterIsolation['orgId'];
|
|
676
691
|
}
|
|
677
|
-
if (this.isolationtype ==
|
|
692
|
+
if (this.isolationtype == IsolationType.org && !this.strictIsolation) {
|
|
678
693
|
// delete filterIsolation['tenantId']
|
|
679
694
|
if (data['tenantId']) {
|
|
680
695
|
filterIsolation['tenantId'] = data['tenantId'];
|
|
@@ -687,7 +702,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
687
702
|
}
|
|
688
703
|
return filterIsolation;
|
|
689
704
|
};
|
|
690
|
-
async findIdThenDelete(appUser: UserContext, id: string): Promise<
|
|
705
|
+
async findIdThenDelete(appUser: UserContext, id: string): Promise<LooseRecord> {
|
|
691
706
|
const deletedata = await this.findById(appUser, id);
|
|
692
707
|
if (!deletedata) {
|
|
693
708
|
throw new NotFoundException(`${id} not found`, 'not found');
|
|
@@ -794,12 +809,12 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
794
809
|
// Add audit event
|
|
795
810
|
await this.addAuditEvent(appUser, this.documentName, id, 'update', data);
|
|
796
811
|
|
|
797
|
-
appUser.addUpdatedRecordId(this.documentName,
|
|
812
|
+
appUser.addUpdatedRecordId(this.documentName, id);
|
|
798
813
|
|
|
799
814
|
return result;
|
|
800
815
|
} catch (err) {
|
|
801
816
|
this.logger.error(err);
|
|
802
|
-
throw new InternalServerErrorException(err
|
|
817
|
+
throw new InternalServerErrorException(getErrorMessage(err));
|
|
803
818
|
}
|
|
804
819
|
};
|
|
805
820
|
|
|
@@ -862,7 +877,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
862
877
|
} catch (err) {
|
|
863
878
|
this.logger.error(err);
|
|
864
879
|
await this.runErrorEvent(appUser, 'errorUpdate', data, err);
|
|
865
|
-
throw new InternalServerErrorException(err
|
|
880
|
+
throw new InternalServerErrorException(getErrorMessage(err));
|
|
866
881
|
}
|
|
867
882
|
};
|
|
868
883
|
|
|
@@ -903,7 +918,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
903
918
|
// existingdata['_id']=''
|
|
904
919
|
await this.validateData(appUser, data, id);
|
|
905
920
|
|
|
906
|
-
const isolationFilter = {};
|
|
921
|
+
const isolationFilter: FilterQuery<T> = {};
|
|
907
922
|
// this.polishIsolationFilter(isolationFilter);
|
|
908
923
|
isolationFilter['_id'] = id;
|
|
909
924
|
this.applyNestedDateTime(appUser, data, 'update');
|
|
@@ -923,7 +938,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
923
938
|
return result; // await this.findById(appUser, id);
|
|
924
939
|
} catch (err) {
|
|
925
940
|
this.logger.error(err);
|
|
926
|
-
throw new InternalServerErrorException(err
|
|
941
|
+
throw new InternalServerErrorException(getErrorMessage(err));
|
|
927
942
|
}
|
|
928
943
|
};
|
|
929
944
|
|
|
@@ -961,7 +976,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
961
976
|
// existingdata['_id']=''
|
|
962
977
|
// console.log("newdata",data)
|
|
963
978
|
//path record no validation
|
|
964
|
-
//
|
|
979
|
+
// this.validateData(appUser, data);
|
|
965
980
|
|
|
966
981
|
const isolationFilter = { ...this.getIsolationFilter(appUser) };
|
|
967
982
|
this.polishIsolationFilter(isolationFilter);
|
|
@@ -983,9 +998,9 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
983
998
|
await this.runEvent(appUser, 'afterPatch', { id: id, patchData: data, prevData: existingdata }, false);
|
|
984
999
|
return result; //await this.findById(appUser, id);
|
|
985
1000
|
} catch (err) {
|
|
986
|
-
this.logger.error(err
|
|
1001
|
+
this.logger.error(getErrorMessage(err), 'findIdThenPath error');
|
|
987
1002
|
console.error(err);
|
|
988
|
-
throw new InternalServerErrorException(err
|
|
1003
|
+
throw new InternalServerErrorException(getErrorMessage(err));
|
|
989
1004
|
}
|
|
990
1005
|
};
|
|
991
1006
|
async deleteMany(appUser: UserContext, filter: FilterQuery<T>) {
|
|
@@ -1016,9 +1031,9 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1016
1031
|
// };
|
|
1017
1032
|
|
|
1018
1033
|
const filter = data.filter;
|
|
1019
|
-
const patch = data.data as
|
|
1034
|
+
const patch = data.data as DynamicRecord;
|
|
1020
1035
|
|
|
1021
|
-
const isolationFilter = { ...this.getIsolationFilter(appUser), ...(filter || {}) }
|
|
1036
|
+
const isolationFilter = { ...this.getIsolationFilter(appUser), ...(filter || {}) } as FilterQuery<T>;
|
|
1022
1037
|
this.polishIsolationFilter(isolationFilter);
|
|
1023
1038
|
this.applyNestedDateTime(appUser, patch, 'update');
|
|
1024
1039
|
|
|
@@ -1047,7 +1062,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1047
1062
|
throw new InternalServerErrorException('foreignkeys object undetected');
|
|
1048
1063
|
}
|
|
1049
1064
|
|
|
1050
|
-
const foreignkeysettings = foreignkeys[this.documentName];
|
|
1065
|
+
const foreignkeysettings = (foreignkeys as GlobalForeignKeyDictionary)[this.documentName];
|
|
1051
1066
|
if (!foreignkeysettings) {
|
|
1052
1067
|
return null;
|
|
1053
1068
|
}
|
|
@@ -1064,14 +1079,14 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1064
1079
|
for (let j = 0; j < fobjs.length; j++) {
|
|
1065
1080
|
const fkey = fobjs[j];
|
|
1066
1081
|
//not deleted in current session, check from database
|
|
1067
|
-
const filter = {};
|
|
1082
|
+
const filter: Record<string, string> = {};
|
|
1068
1083
|
filter[fkey] = id;
|
|
1069
|
-
const result
|
|
1084
|
+
const result = (await collection.findOne(filter, {
|
|
1070
1085
|
session: appUser.getDBSession(),
|
|
1071
|
-
});
|
|
1086
|
+
})) as unknown as RelatedRecord | null;
|
|
1072
1087
|
if (result) {
|
|
1073
1088
|
//record deleted but not yet commit, safely ignore
|
|
1074
|
-
if (appUser.searchDeletedRecordId(collectionname, result._id)) continue;
|
|
1089
|
+
if (appUser.searchDeletedRecordId(collectionname, String(result._id ?? ''))) continue;
|
|
1075
1090
|
|
|
1076
1091
|
this.logger.error(result, `related result found in ${collectionname} ${fkey} = ${id}`);
|
|
1077
1092
|
return result;
|
|
@@ -1085,7 +1100,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1085
1100
|
/**
|
|
1086
1101
|
* dummy ping
|
|
1087
1102
|
*/
|
|
1088
|
-
ping(...data) {
|
|
1103
|
+
ping(...data: unknown[]) {
|
|
1089
1104
|
return `hello ${JSON.stringify(data)}`;
|
|
1090
1105
|
}
|
|
1091
1106
|
|
|
@@ -1133,6 +1148,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1133
1148
|
}
|
|
1134
1149
|
} catch (e) {
|
|
1135
1150
|
await this.runErrorEvent(appUser, 'errorSetStatus', data, e);
|
|
1151
|
+
throw e;
|
|
1136
1152
|
}
|
|
1137
1153
|
}
|
|
1138
1154
|
|
|
@@ -1145,7 +1161,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1145
1161
|
* @param {string} eventName The event name
|
|
1146
1162
|
* @param {any} data The data
|
|
1147
1163
|
*/
|
|
1148
|
-
runBackgroundWorker(appUser: UserContext, eventName: string, payloads:
|
|
1164
|
+
runBackgroundWorker(appUser: UserContext, eventName: string, payloads: unknown) {
|
|
1149
1165
|
this.eventEmitter.emit(eventName, appUser, payloads);
|
|
1150
1166
|
}
|
|
1151
1167
|
|
|
@@ -1165,7 +1181,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1165
1181
|
* @param {any} data The data
|
|
1166
1182
|
* @return {Promise} { description_of_the_return_value }
|
|
1167
1183
|
*/
|
|
1168
|
-
async runEvent(appUser: UserContext, shortEventName: string, payloads:
|
|
1184
|
+
async runEvent(appUser: UserContext, shortEventName: string, payloads: unknown, enforce: boolean = true): Promise<unknown> {
|
|
1169
1185
|
const eventName = this.setHookName(shortEventName);
|
|
1170
1186
|
try {
|
|
1171
1187
|
if (enforce && !this.eventEmitter.hasListeners(eventName)) {
|
|
@@ -1178,8 +1194,9 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1178
1194
|
throw new InternalServerErrorException(`${eventName} is invalid worker`);
|
|
1179
1195
|
}
|
|
1180
1196
|
|
|
1181
|
-
const result = res[0];
|
|
1182
|
-
if (result
|
|
1197
|
+
const result: unknown = res[0];
|
|
1198
|
+
if (result instanceof Error) throw result;
|
|
1199
|
+
if (isEventResult(result) && result.name?.includes('Exception')) throw new InternalServerErrorException(result.name);
|
|
1183
1200
|
return result;
|
|
1184
1201
|
}
|
|
1185
1202
|
} catch (e) {
|
|
@@ -1188,12 +1205,12 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1188
1205
|
}
|
|
1189
1206
|
}
|
|
1190
1207
|
|
|
1191
|
-
async runErrorEvent(appUser: UserContext, shortEventName: string, payloads:
|
|
1208
|
+
async runErrorEvent(appUser: UserContext, shortEventName: string, payloads: unknown, error: unknown) {
|
|
1192
1209
|
const eventName = this.setHookName(shortEventName);
|
|
1193
1210
|
try {
|
|
1194
1211
|
if (this.eventEmitter.hasListeners(eventName)) {
|
|
1195
1212
|
console.log('run eventName', eventName);
|
|
1196
|
-
|
|
1213
|
+
await this.eventEmitter.emitAsync(eventName, appUser, payloads, error);
|
|
1197
1214
|
}
|
|
1198
1215
|
} catch (e) {
|
|
1199
1216
|
//runErrorEvent wont handle more error
|
|
@@ -1207,10 +1224,10 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1207
1224
|
this.logger.debug(result, 'genNewDocNo');
|
|
1208
1225
|
|
|
1209
1226
|
// been for to convert become object
|
|
1210
|
-
(data as
|
|
1227
|
+
(data as DynamicRecord)[this.documentIdentityCode] = result;
|
|
1211
1228
|
}
|
|
1212
|
-
async runDefault(appUser: UserContext): Promise<
|
|
1213
|
-
return 'Hello this is ' + this.getDocumentType() + ': ' + this.getDocumentName();
|
|
1229
|
+
async runDefault(appUser: UserContext): Promise<string> {
|
|
1230
|
+
return await Promise.resolve('Hello this is ' + this.getDocumentType() + ': ' + this.getDocumentName());
|
|
1214
1231
|
}
|
|
1215
1232
|
async identifyForeignKeys(appUser: UserContext, data: Partial<T>) {
|
|
1216
1233
|
/**
|
|
@@ -1218,21 +1235,19 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1218
1235
|
* 2. loop through record obtain all foreign key value
|
|
1219
1236
|
* 3. get all unique key value in array {product:['xxxx','yyyy'],customer:['aaa']}
|
|
1220
1237
|
*/
|
|
1221
|
-
const schema = this.jsonschema;
|
|
1222
|
-
|
|
1223
1238
|
//get all foreign keys catalogue
|
|
1224
1239
|
const collections = Object.getOwnPropertyNames(this.foreignkeys);
|
|
1225
1240
|
|
|
1226
1241
|
//obtain exists data in according foreign key
|
|
1227
1242
|
const pipelines: PipelineStage[] = [{ $match: { _id: false } }]; //exclude data from current collection
|
|
1228
1243
|
const vdata = data; //['_doc']
|
|
1229
|
-
const keystore = {};
|
|
1244
|
+
const keystore: Record<string, string[]> = {};
|
|
1230
1245
|
collections.forEach((collectionname) => {
|
|
1231
1246
|
const fks: string[] = this.foreignkeys[collectionname];
|
|
1232
1247
|
let results: string[] = [];
|
|
1233
1248
|
fks.forEach((fieldpath) => {
|
|
1234
1249
|
//console.log("fieldpath:",fieldpath,"vdata",data,vdata)
|
|
1235
|
-
const tmp = jsonpath.query(vdata, fieldpath).filter((item: string
|
|
1250
|
+
const tmp = (jsonpath.query(vdata, fieldpath) as unknown[]).filter((item): item is string => typeof item === 'string' && item != '');
|
|
1236
1251
|
// console.log("tmp",tmp)
|
|
1237
1252
|
|
|
1238
1253
|
results = results.concat(tmp);
|
|
@@ -1262,10 +1277,10 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1262
1277
|
|
|
1263
1278
|
if (!unionresult) {
|
|
1264
1279
|
this.logger.error('foreign key control failed ', 'identifyForeignKeys');
|
|
1265
|
-
throw new InternalServerErrorException(pipelines
|
|
1280
|
+
throw new InternalServerErrorException(pipelines, 'Foreignkey check execution error');
|
|
1266
1281
|
} else {
|
|
1267
|
-
const searchresult:
|
|
1268
|
-
unionresult.forEach((item) => {
|
|
1282
|
+
const searchresult: Record<string, string[]> = {};
|
|
1283
|
+
(unionresult as ForeignKeyAggregateRow[]).forEach((item) => {
|
|
1269
1284
|
if (searchresult[item.collection]) {
|
|
1270
1285
|
searchresult[item.collection].push(item._id);
|
|
1271
1286
|
} else {
|
|
@@ -1308,7 +1323,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1308
1323
|
async checkUniqueKeyExist(appUser: UserContext, data: string[]): Promise<UniqueKeyExistResponse[]> {
|
|
1309
1324
|
const response: UniqueKeyExistResponse[] = [];
|
|
1310
1325
|
const unionKey = this.getDocumentIdentityCode();
|
|
1311
|
-
const searchQuery
|
|
1326
|
+
const searchQuery = { [unionKey]: { $in: data } } as FilterQuery<T>;
|
|
1312
1327
|
// search for multiple union exist
|
|
1313
1328
|
const result = await this.search(appUser, searchQuery);
|
|
1314
1329
|
for (const item of data) {
|
|
@@ -1321,7 +1336,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1321
1336
|
|
|
1322
1337
|
searchToAggregate(appUser: UserContext, filter: FilterQuery<T>, columns: string[], sort: string[][], lookup: { [key: string]: string }, pagination?: { pageSize?: number; pageNo?: number }) {
|
|
1323
1338
|
const pipelines: PipelineStage[] = [];
|
|
1324
|
-
const projection = {};
|
|
1339
|
+
const projection: Record<string, 1> = {};
|
|
1325
1340
|
// console.log('sortsort', sort);
|
|
1326
1341
|
|
|
1327
1342
|
pipelines.push({ $match: filter });
|
|
@@ -1356,7 +1371,7 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1356
1371
|
if (Object.keys(projection).length > 0) pipelines.push({ $project: projection });
|
|
1357
1372
|
|
|
1358
1373
|
if (Array.isArray(sort) && sort.length > 0) {
|
|
1359
|
-
const sortobj = {};
|
|
1374
|
+
const sortobj: Record<string, 1 | -1> = {};
|
|
1360
1375
|
sort.forEach((item) => {
|
|
1361
1376
|
sortobj[item[0]] = item[1].toLowerCase() == 'asc' ? 1 : -1;
|
|
1362
1377
|
});
|
|
@@ -1374,12 +1389,19 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1374
1389
|
return pipelines;
|
|
1375
1390
|
}
|
|
1376
1391
|
|
|
1377
|
-
|
|
1378
|
-
|
|
1392
|
+
private toAggregateSort(sort: SortOption): string[][] {
|
|
1393
|
+
if (Array.isArray(sort)) return sort.map(([field, direction]) => [field, String(direction)]);
|
|
1394
|
+
if (typeof sort === 'string') return [];
|
|
1395
|
+
if (sort && typeof sort === 'object') return Object.entries(sort).map(([field, direction]) => [field, String(direction)]);
|
|
1396
|
+
return [];
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
setHookName(hookName: string) {
|
|
1400
|
+
const resourceName = this.jsonschema['x-simpleapp-config'].resourceName ?? '';
|
|
1379
1401
|
return camelToKebab(resourceName) + '.' + camelToKebab(hookName);
|
|
1380
1402
|
}
|
|
1381
1403
|
//only realtime webhook supported at this moment
|
|
1382
|
-
async callWebhook(appUser: UserContext, actionName: string, data:
|
|
1404
|
+
async callWebhook(appUser: UserContext, actionName: string, data: unknown) {
|
|
1383
1405
|
try {
|
|
1384
1406
|
await this.runWebHook.run(appUser, this.documentName, actionName, data);
|
|
1385
1407
|
} catch (e) {
|
|
@@ -1387,10 +1409,10 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1387
1409
|
}
|
|
1388
1410
|
}
|
|
1389
1411
|
|
|
1390
|
-
async addAuditEvent(appUser: UserContext, documentName: string, id: string, eventType: string, data:
|
|
1412
|
+
async addAuditEvent(appUser: UserContext, documentName: string, id: string, eventType: string, data: unknown) {
|
|
1391
1413
|
await this.logService.addEvent(appUser, documentName, id, eventType, data);
|
|
1392
1414
|
}
|
|
1393
|
-
async addManyAuditEvents(appUser: UserContext, documentName: string, eventType: string, datas:
|
|
1415
|
+
async addManyAuditEvents(appUser: UserContext, documentName: string, eventType: string, datas: unknown[]) {
|
|
1394
1416
|
await this.logService.addManyEvents(appUser, documentName, eventType, datas);
|
|
1395
1417
|
}
|
|
1396
1418
|
|
|
@@ -1462,6 +1484,6 @@ export class SimpleAppService<T extends SchemaFields> {
|
|
|
1462
1484
|
});
|
|
1463
1485
|
}
|
|
1464
1486
|
|
|
1465
|
-
return await this.aggregate(appUser, pipeline);
|
|
1487
|
+
return await this.aggregate<T>(appUser, pipeline);
|
|
1466
1488
|
}
|
|
1467
1489
|
}
|
|
@@ -4,153 +4,158 @@
|
|
|
4
4
|
* last change 2023-09-10
|
|
5
5
|
* author: Ks Tan
|
|
6
6
|
*/
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
export const getPublicResource = (xorg:string) : MenuData[] => {
|
|
7
|
+
import { MenuData } from "~/types";
|
|
8
|
+
import { getAllDocuments } from "../simpleapp/generate/commons/documents";
|
|
9
|
+
export const getDocTypes = () => {
|
|
10
|
+
return getAllDocuments().filter((item) => item.page != "");
|
|
11
|
+
};
|
|
12
|
+
export const getAllDocFormats = () => {
|
|
13
|
+
return getAllDocuments().filter((item) => item.docNumber);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const getPublicResource = (xorg: string): MenuData[] => {
|
|
18
17
|
// const xorg = getUserStore().getCurrentXorg()
|
|
19
|
-
const menus:MenuData[] = [
|
|
20
|
-
{label:
|
|
21
|
-
{label: "Profile", url
|
|
22
|
-
{label: "Profile", url
|
|
23
|
-
{label:
|
|
24
|
-
{label:
|
|
25
|
-
]
|
|
26
|
-
return menus
|
|
27
|
-
}
|
|
28
|
-
export const getMenus =()
|
|
29
|
-
// const logout = async () => {
|
|
30
|
-
// const { signOut } = useAuth();
|
|
31
|
-
// const { data } = await <any>useFetch('/api/auth/logout');
|
|
32
|
-
// const addPath = encodeURIComponent("/login");
|
|
33
|
-
// signOut({ redirect: false });
|
|
34
|
-
// window.location.href = data?.value?.path + addPath;
|
|
35
|
-
// };
|
|
36
|
-
|
|
37
|
-
// const route = useRoute();
|
|
38
|
-
const xorg = getUserStore().getCurrentXorg()
|
|
39
|
-
// let data:MenuData[] =[];
|
|
40
|
-
// //sss
|
|
41
|
-
let allmenus = getAllDocuments().filter(item=>item.page!=
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
let allowmenus=[]
|
|
45
|
-
for(let i=0; i< allmenus.length;i++){
|
|
46
|
-
const keyword = allmenus[i].docName
|
|
18
|
+
const menus: MenuData[] = [
|
|
19
|
+
{ label: "Home", icon: "pi pi-fw pi-home", url: `/${xorg}` },
|
|
20
|
+
{ label: "Profile", url: `/profile`, isolationType: "none", icon: "" },
|
|
21
|
+
{ label: "Profile", url: `/${xorg}/profile`, isolationType: "none", icon: "" },
|
|
22
|
+
{ label: "Signout", icon: "pi pi-fw pi-home", command: () => logout() },
|
|
23
|
+
{ label: "Signin", icon: "pi pi-fw pi-home", url: "/login" },
|
|
24
|
+
];
|
|
25
|
+
return menus;
|
|
26
|
+
};
|
|
27
|
+
export const getMenus = (): MenuData[] => {
|
|
28
|
+
// const logout = async () => {
|
|
29
|
+
// const { signOut } = useAuth();
|
|
30
|
+
// const { data } = await <any>useFetch('/api/auth/logout');
|
|
31
|
+
// const addPath = encodeURIComponent("/login");
|
|
32
|
+
// signOut({ redirect: false });
|
|
33
|
+
// window.location.href = data?.value?.path + addPath;
|
|
34
|
+
// };
|
|
35
|
+
|
|
36
|
+
// const route = useRoute();
|
|
37
|
+
const xorg = getUserStore().getCurrentXorg();
|
|
38
|
+
// let data:MenuData[] =[];
|
|
39
|
+
// //sss
|
|
40
|
+
let allmenus = getAllDocuments().filter((item) => item.page != "");
|
|
41
|
+
|
|
42
|
+
const roles = getUserProfile().roles;
|
|
43
|
+
let allowmenus = [];
|
|
44
|
+
for (let i = 0; i < allmenus.length; i++) {
|
|
45
|
+
const keyword = allmenus[i].docName;
|
|
47
46
|
// if(m.label)
|
|
48
|
-
|
|
49
|
-
if(getUserStore().haveAccess(keyword)){
|
|
50
|
-
const m:MenuData = {
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
|
|
48
|
+
if (getUserStore().haveAccess(keyword)) {
|
|
49
|
+
const m: MenuData = {
|
|
50
|
+
label: t(keyword),
|
|
51
|
+
url: `/${xorg}/${keyword}`,
|
|
52
|
+
isolationType: allmenus[i].isolationType,
|
|
53
|
+
};
|
|
54
|
+
allowmenus.push(m);
|
|
55
|
+
}
|
|
53
56
|
}
|
|
54
|
-
// data = publicMenus()
|
|
55
|
-
// if(xorg){
|
|
56
|
-
// data.push({label: 'Cruds',icon: 'pi pi-fw pi-pencil',items:allowmenus})
|
|
57
|
-
// }
|
|
58
|
-
// console.log('data',data)
|
|
59
|
-
return allowmenus
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return allmenus.sort((one:string, two:string) => (one > two ? -1 : 1))
|
|
69
|
-
|
|
70
|
-
}
|
|
57
|
+
// data = publicMenus()
|
|
58
|
+
// if(xorg){
|
|
59
|
+
// data.push({label: 'Cruds',icon: 'pi pi-fw pi-pencil',items:allowmenus})
|
|
60
|
+
// }
|
|
61
|
+
// console.log('data',data)
|
|
62
|
+
return allowmenus;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const getMenustFromPageMeta = () => {
|
|
66
|
+
const allmenus = useRouter()
|
|
67
|
+
.getRoutes()
|
|
68
|
+
.filter((item) => item.meta && item.meta.menuPath)
|
|
69
|
+
.map((item) => item.meta.menuPath) as string[];
|
|
70
|
+
|
|
71
|
+
return allmenus.sort((one: string, two: string) => (one > two ? -1 : 1));
|
|
72
|
+
};
|
|
71
73
|
export const hasAccessByPageMeta = (pageName: string): boolean => {
|
|
72
|
-
const userGroups = getUserStore().groups || []
|
|
73
|
-
const userRoles = getUserStore().roles || []
|
|
74
|
-
|
|
74
|
+
const userGroups = getUserStore().groups || [];
|
|
75
|
+
const userRoles = getUserStore().roles || [];
|
|
76
|
+
|
|
75
77
|
if (
|
|
76
|
-
userRoles.includes(
|
|
77
|
-
userRoles.includes(
|
|
78
|
-
userRoles.includes(
|
|
78
|
+
userRoles.includes("superadmin") ||
|
|
79
|
+
userRoles.includes("tenantowner") ||
|
|
80
|
+
userRoles.includes("superuser")
|
|
79
81
|
) {
|
|
80
|
-
return true
|
|
82
|
+
return true;
|
|
81
83
|
}
|
|
82
|
-
|
|
83
|
-
const routes = useRouter().getRoutes()
|
|
84
|
+
|
|
85
|
+
const routes = useRouter().getRoutes();
|
|
84
86
|
const route = routes.find((r) => {
|
|
85
|
-
const menuPath = r.meta?.menuPath as string | undefined
|
|
86
|
-
return menuPath && menuPath.endsWith(`/${pageName}`)
|
|
87
|
-
})
|
|
88
|
-
|
|
87
|
+
const menuPath = r.meta?.menuPath as string | undefined;
|
|
88
|
+
return menuPath && menuPath.endsWith(`/${pageName}`);
|
|
89
|
+
});
|
|
90
|
+
|
|
89
91
|
if (!route || !route.meta) {
|
|
90
|
-
return false
|
|
92
|
+
return false;
|
|
91
93
|
}
|
|
92
|
-
|
|
93
|
-
const requiredGroups = route.meta.requiredGroups as string[] | undefined
|
|
94
|
-
|
|
94
|
+
|
|
95
|
+
const requiredGroups = route.meta.requiredGroups as string[] | undefined;
|
|
96
|
+
|
|
95
97
|
if (!requiredGroups || requiredGroups.length === 0) {
|
|
96
|
-
return false
|
|
98
|
+
return false;
|
|
97
99
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
|
|
101
|
+
const hasRole = requiredGroups.some((group) => userRoles.includes(group));
|
|
102
|
+
if (hasRole) return hasRole;
|
|
103
|
+
|
|
104
|
+
return requiredGroups.some((group) => userGroups.includes(group));
|
|
105
|
+
};
|
|
101
106
|
|
|
102
107
|
export const getMenusWithPageMetaAccess = (xorg: string): MenuData[] => {
|
|
103
|
-
const routes = useRouter().getRoutes()
|
|
104
|
-
const allowedMenus: MenuData[] = []
|
|
105
|
-
const { getDescription, getIcon, getIconColor } = useSettingsMenu()
|
|
106
|
-
|
|
107
|
-
const menuRoutes = routes.filter((route) => route.meta && route.meta.menuPath)
|
|
108
|
-
|
|
108
|
+
const routes = useRouter().getRoutes();
|
|
109
|
+
const allowedMenus: MenuData[] = [];
|
|
110
|
+
const { getDescription, getIcon, getIconColor } = useSettingsMenu();
|
|
111
|
+
|
|
112
|
+
const menuRoutes = routes.filter((route) => route.meta && route.meta.menuPath);
|
|
113
|
+
|
|
109
114
|
for (const route of menuRoutes) {
|
|
110
|
-
const menuPath = route.meta.menuPath as string
|
|
111
|
-
const requiredGroups = route.meta.requiredGroups as string[] | undefined
|
|
112
|
-
|
|
113
|
-
const pathParts = menuPath.split(
|
|
114
|
-
const pageName = pathParts[pathParts.length - 1]
|
|
115
|
-
|
|
115
|
+
const menuPath = route.meta.menuPath as string;
|
|
116
|
+
const requiredGroups = route.meta.requiredGroups as string[] | undefined;
|
|
117
|
+
|
|
118
|
+
const pathParts = menuPath.split("/");
|
|
119
|
+
const pageName = pathParts[pathParts.length - 1];
|
|
120
|
+
|
|
116
121
|
if (hasAccessByPageMeta(pageName)) {
|
|
117
|
-
const icon = getIcon(pageName)
|
|
118
|
-
const iconClass = getIconColor(pageName)
|
|
119
|
-
|
|
122
|
+
const icon = getIcon(pageName);
|
|
123
|
+
const iconClass = getIconColor(pageName);
|
|
124
|
+
|
|
120
125
|
const menuItem: MenuData = {
|
|
121
126
|
label: t(pageName),
|
|
122
127
|
url: `/${xorg}/${pageName}`,
|
|
123
|
-
isolationType:
|
|
124
|
-
icon: typeof icon ===
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
allowedMenus.push(menuItem)
|
|
128
|
+
isolationType: "none",
|
|
129
|
+
icon: typeof icon === "string" ? icon : "",
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
allowedMenus.push(menuItem);
|
|
128
133
|
}
|
|
129
134
|
}
|
|
130
|
-
|
|
131
|
-
return allowedMenus
|
|
132
|
-
}
|
|
135
|
+
|
|
136
|
+
return allowedMenus;
|
|
137
|
+
};
|
|
133
138
|
|
|
134
139
|
export const getMenuMetadata = (pageName: string) => {
|
|
135
|
-
const routes = useRouter().getRoutes()
|
|
140
|
+
const routes = useRouter().getRoutes();
|
|
136
141
|
const route = routes.find((r) => {
|
|
137
|
-
const menuPath = r.meta?.menuPath as string | undefined
|
|
138
|
-
return menuPath && menuPath.endsWith(`/${pageName}`)
|
|
139
|
-
})
|
|
140
|
-
|
|
142
|
+
const menuPath = r.meta?.menuPath as string | undefined;
|
|
143
|
+
return menuPath && menuPath.endsWith(`/${pageName}`);
|
|
144
|
+
});
|
|
145
|
+
|
|
141
146
|
if (route?.meta) {
|
|
142
147
|
return {
|
|
143
148
|
description: route.meta.description ? t(route.meta.description as string) : t(pageName),
|
|
144
|
-
icon: route.meta.icon ||
|
|
145
|
-
colorClass: route.meta.colorClass ||
|
|
146
|
-
}
|
|
149
|
+
icon: route.meta.icon || "",
|
|
150
|
+
colorClass: route.meta.colorClass || "",
|
|
151
|
+
};
|
|
147
152
|
}
|
|
148
|
-
|
|
149
|
-
const { getDescription, getIcon, getIconColor } = useSettingsMenu()
|
|
150
|
-
|
|
153
|
+
|
|
154
|
+
const { getDescription, getIcon, getIconColor } = useSettingsMenu();
|
|
155
|
+
|
|
151
156
|
return {
|
|
152
157
|
description: t(getDescription(pageName)),
|
|
153
158
|
icon: getIcon(pageName),
|
|
154
159
|
colorClass: getIconColor(pageName),
|
|
155
|
-
}
|
|
156
|
-
}
|
|
160
|
+
};
|
|
161
|
+
};
|
|
@@ -38,7 +38,10 @@
|
|
|
38
38
|
{{ permission }}
|
|
39
39
|
</span>
|
|
40
40
|
</div>
|
|
41
|
-
<div
|
|
41
|
+
<div
|
|
42
|
+
v-else
|
|
43
|
+
class="flex items-center gap-2"
|
|
44
|
+
>
|
|
42
45
|
<span>|</span>
|
|
43
46
|
<span
|
|
44
47
|
v-for="permission in ['lite', 'pro', 'enterprise']"
|
|
@@ -51,13 +54,9 @@
|
|
|
51
54
|
<p class="text-gray-600">{{ miniApp.intro.description }}</p>
|
|
52
55
|
|
|
53
56
|
<div class="flex items-center gap-1">
|
|
54
|
-
<template
|
|
55
|
-
v-for="[feature, enabled] in Object.entries(
|
|
56
|
-
miniApp.integration.enabled,
|
|
57
|
-
)"
|
|
58
|
-
>
|
|
57
|
+
<template v-for="[feature, enabled] in Object.entries(miniApp.integration.enabled)">
|
|
59
58
|
<span
|
|
60
|
-
v-if="enabled"
|
|
59
|
+
v-if="enabled && $te(`miniAppLang.integrationFeature.${feature}`)"
|
|
61
60
|
class="px-3 py-0.5 bg-primary-50 text-primary-500 font-semibold text-xs rounded uppercase cursor-pointer"
|
|
62
61
|
@click="() => handleOpenFeatureDialog(feature, enabled)"
|
|
63
62
|
>
|
|
@@ -238,14 +237,14 @@
|
|
|
238
237
|
</template>
|
|
239
238
|
|
|
240
239
|
<script setup lang="ts">
|
|
241
|
-
import _
|
|
242
|
-
import { MiniAppDetail } from "~/simpleapp/generate/openapi";
|
|
243
|
-
import MiniAppPermissionWrapper from "../MiniAppPermissionWrapper.vue";
|
|
240
|
+
import _ from "lodash";
|
|
244
241
|
import UndrawNodata from "~/components/icon/UndrawNodata.vue";
|
|
245
|
-
import
|
|
246
|
-
import MiniAppFeatureCustomPage from "../MiniAppFeatureCustomPage.vue";
|
|
242
|
+
import { MiniAppDetail } from "~/simpleapp/generate/openapi";
|
|
247
243
|
import MiniAppFeatureCustomField from "../MiniAppFeatureCustomField.vue";
|
|
244
|
+
import MiniAppFeatureCustomPage from "../MiniAppFeatureCustomPage.vue";
|
|
248
245
|
import MiniAppFeatureScope from "../MiniAppFeatureScope.vue";
|
|
246
|
+
import MiniAppIcon from "../MiniAppIcon.vue";
|
|
247
|
+
import MiniAppPermissionWrapper from "../MiniAppPermissionWrapper.vue";
|
|
249
248
|
|
|
250
249
|
const props = defineProps<{
|
|
251
250
|
miniApp?: MiniAppDetail;
|