@simitgroup/simpleapp-generator 1.0.23 → 1.0.25
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/README.md +48 -0
- package/dist/createproject.js +1 -1
- package/dist/createproject.js.map +1 -1
- package/dist/framework.js +37 -25
- package/dist/framework.js.map +1 -1
- package/dist/generate.js +38 -16
- package/dist/generate.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/processors/jsonschemabuilder.js +9 -9
- package/dist/processors/jsonschemabuilder.js.map +1 -1
- package/package.json +1 -1
- package/src/framework.ts +50 -36
- package/src/generate.ts +37 -21
- package/src/index.ts +2 -1
- package/src/processors/jsonschemabuilder.ts +9 -9
- package/src/type.ts +1 -0
- package/templates/basic/apischema.eta +8 -4
- package/templates/basic/controller.eta +21 -5
- package/templates/basic/jsonschema.eta +2 -0
- package/templates/basic/model.eta +4 -7
- package/templates/basic/module.eta +2 -0
- package/templates/basic/pageindex.vue.eta +28 -15
- package/templates/basic/pageindexwithid.vue.eta +13 -1
- package/templates/basic/service.eta +11 -9
- package/templates/basic/simpleappclient.eta +9 -8
- package/templates/basic/type.eta +2 -1
- package/templates/nest/SimpleAppController.eta +5 -5
- package/templates/nest/SimpleAppService.eta +87 -31
- package/templates/nest/TenantMiddleware.eta +39 -0
- package/templates/nest/User.eta +115 -0
- package/templates/nest/app.module.eta +24 -4
- package/templates/nest/inputvalidation-exception.eta +6 -0
- package/templates/nest/nest.env.eta +12 -1
- package/templates/nest/nest.main.eta +14 -3
- package/templates/nest/oauth2-redirect.eta +79 -0
- package/templates/nuxt/components.crudsimple.vue.eta +60 -48
- package/templates/nuxt/components.debugdocdata.vue.eta +12 -4
- package/templates/nuxt/components.eventmonitor.vue.eta +17 -11
- package/templates/nuxt/components.menus.vue.eta +14 -12
- package/templates/nuxt/composables.getautocomplete.ts.eta +19 -8
- package/templates/nuxt/composables.getmenus.ts.eta +27 -9
- package/templates/nuxt/env.eta +12 -0
- package/templates/nuxt/layouts.default.vue.eta +10 -3
- package/templates/nuxt/nuxt.config.ts.eta +10 -6
- package/templates/nuxt/pages.[xorg].index.vue.eta +19 -0
- package/templates/nuxt/pages.index.vue.eta +17 -1
- package/templates/nuxt/pages.login.vue.eta +20 -0
- package/templates/nuxt/plugins.simpleapp.ts.eta +12 -4
- package/templates/nuxt/server.api.auth.logout.ts.eta +12 -0
- package/templates/nuxt/server.api.auth[...].ts.eta +144 -0
- package/templates/nuxt/server.api.ts.eta +33 -19
- package/src/createproject.ts +0 -121
- package/src/index2.ts-old +0 -132
- package/src/installdependency.sh +0 -5
- package/src/installnest.ts +0 -0
- package/src/installnuxt.ts +0 -0
- package/templates/basic/backend.config.eta +0 -1
- package/templates/basic/beforesave.eta +0 -7
- package/templates/basic/controller2.eta +0 -86
- package/templates/basic/frontend.config.eta +0 -1
- package/templates/basic/model-converter.etabackup +0 -21
- package/templates/basic/readme.eta +0 -3
- package/templates/basic/service.etabackupe +0 -106
- package/templates/basic/type.etabackup +0 -23
- package/templates/basic/uischema.eta +0 -13
|
@@ -13,6 +13,7 @@ import { ApiTags, ApiBody, ApiResponse, ApiOperation } from '@nestjs/swagger';
|
|
|
13
13
|
const doctype = 'person'.toUpperCase();
|
|
14
14
|
type ServiceType = {
|
|
15
15
|
list: Function;
|
|
16
|
+
search: Function;
|
|
16
17
|
create: Function;
|
|
17
18
|
update: Function;
|
|
18
19
|
delete: Function;
|
|
@@ -34,10 +35,12 @@ export class SimpleAppController<TService extends ServiceType, TApiSchema, T> {
|
|
|
34
35
|
this.service = service;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
38
|
async _list() {
|
|
39
39
|
return this.service.list();
|
|
40
40
|
}
|
|
41
|
+
async _search(searchObject:Object) {
|
|
42
|
+
return this.service.search(searchObject);
|
|
43
|
+
}
|
|
41
44
|
async _autocomplete(keyword: string) {
|
|
42
45
|
return this.service.getAutoComplete(keyword);
|
|
43
46
|
}
|
|
@@ -47,7 +50,6 @@ export class SimpleAppController<TService extends ServiceType, TApiSchema, T> {
|
|
|
47
50
|
return result as Type<TApiSchema>;
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
|
|
51
53
|
async _create(data: TApiSchema) {
|
|
52
54
|
//const newdata: persontype.Person = { ...data };
|
|
53
55
|
const newdata: T = {} as T; //= { ...data };
|
|
@@ -55,14 +57,12 @@ export class SimpleAppController<TService extends ServiceType, TApiSchema, T> {
|
|
|
55
57
|
return this.service.setData(newdata).create() as TApiSchema;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
async _update( id: string,data: TApiSchema) {
|
|
60
|
+
async _update(id: string, data: TApiSchema) {
|
|
60
61
|
const newdata: T = {} as T; //= { ...data };
|
|
61
62
|
Object.assign(newdata, data); //
|
|
62
63
|
return this.service.findIdThenUpdate(id, newdata) as TApiSchema;
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
|
|
66
66
|
async _delete(id: string) {
|
|
67
67
|
return this.service.findIdThenDelete(id);
|
|
68
68
|
}
|
|
@@ -4,10 +4,18 @@ import { Model } from 'mongoose';
|
|
|
4
4
|
import Ajv from 'ajv';
|
|
5
5
|
import addFormats from 'ajv-formats';
|
|
6
6
|
import foreignkeys from '../dicts/foreignkeys.json';
|
|
7
|
+
|
|
7
8
|
import {
|
|
8
9
|
NotFoundException,
|
|
9
10
|
InternalServerErrorException,
|
|
10
11
|
} from '@nestjs/common/exceptions';
|
|
12
|
+
import { User } from './User';
|
|
13
|
+
|
|
14
|
+
export enum IsolationType {
|
|
15
|
+
'org' = 'org',
|
|
16
|
+
'tenant' = 'tenant',
|
|
17
|
+
'branch' = 'branch',
|
|
18
|
+
}
|
|
11
19
|
@Injectable()
|
|
12
20
|
export class SimpleAppService<T extends { _id?: string }> {
|
|
13
21
|
protected jsonschema = { type: 'object', properties: {}, required: [] };
|
|
@@ -16,11 +24,19 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
16
24
|
protected documentName = 'category';
|
|
17
25
|
protected documentType = 'CAT';
|
|
18
26
|
protected LIMITPERPAGE = 20;
|
|
27
|
+
protected isolationtype: IsolationType = IsolationType.org;
|
|
28
|
+
protected isolationFilter: any = {};
|
|
19
29
|
protected data: T = { _id: '' } as T;
|
|
20
|
-
|
|
30
|
+
private doc: Model<T>; //set private to prevent developer break data isolation control
|
|
21
31
|
protected errorlist = [];
|
|
22
|
-
constructor(
|
|
32
|
+
constructor(
|
|
33
|
+
newdoc: Model<T>,
|
|
34
|
+
isolationtype: IsolationType = IsolationType.org,
|
|
35
|
+
) {
|
|
23
36
|
this.doc = newdoc;
|
|
37
|
+
this.isolationtype = isolationtype;
|
|
38
|
+
|
|
39
|
+
// this.tenantdoc = tenantdoc
|
|
24
40
|
}
|
|
25
41
|
getRecordId = (): string => this.data._id;
|
|
26
42
|
setSchema = (newschema) => (this.jsonschema = newschema);
|
|
@@ -34,9 +50,27 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
34
50
|
this.data = { ...newdata };
|
|
35
51
|
return this;
|
|
36
52
|
};
|
|
53
|
+
getIsolationFilter = () => {
|
|
54
|
+
let isolationFilter = {};
|
|
55
|
+
switch (this.isolationtype) {
|
|
56
|
+
case 'branch':
|
|
57
|
+
isolationFilter = User.getInstance().getBranchFilter();
|
|
58
|
+
break;
|
|
59
|
+
case 'tenant':
|
|
60
|
+
isolationFilter = User.getInstance().getTenantFilter();
|
|
61
|
+
break;
|
|
62
|
+
case 'org':
|
|
63
|
+
default:
|
|
64
|
+
isolationFilter = User.getInstance().getOrgFilter();
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
return isolationFilter;
|
|
68
|
+
};
|
|
37
69
|
async list() {
|
|
38
70
|
try {
|
|
39
|
-
|
|
71
|
+
//console.log("this.isolationFilter",this.getIsolationFilter())
|
|
72
|
+
const products = await this.doc.find(this.getIsolationFilter());
|
|
73
|
+
// console.log(products)
|
|
40
74
|
const productlist = products.map((p: T) => {
|
|
41
75
|
return p;
|
|
42
76
|
});
|
|
@@ -53,6 +87,8 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
53
87
|
filter1[this.documentIdentityCode] = { $regex: keyword };
|
|
54
88
|
filter2[this.documentIdentityName] = { $regex: keyword };
|
|
55
89
|
const filterobj = { $or: [filter1, filter2] };
|
|
90
|
+
|
|
91
|
+
Object.assign(filterobj, this.getIsolationFilter());
|
|
56
92
|
const projections = {
|
|
57
93
|
id: `\$_id`,
|
|
58
94
|
label: `\$${this.documentIdentityCode}`,
|
|
@@ -69,31 +105,37 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
69
105
|
throw new InternalServerErrorException(err.message);
|
|
70
106
|
}
|
|
71
107
|
}
|
|
72
|
-
async
|
|
108
|
+
async search(filters: Object) {
|
|
73
109
|
try {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
110
|
+
Object.assign(filters, this.getIsolationFilter());
|
|
111
|
+
const products = await this.doc.find(filters);
|
|
112
|
+
const productlist = products.map((p: T) => {
|
|
113
|
+
return p;
|
|
114
|
+
});
|
|
115
|
+
// console.log(products);
|
|
116
|
+
return productlist;
|
|
77
117
|
} catch (err) {
|
|
78
|
-
//error
|
|
79
118
|
throw new InternalServerErrorException(err.message);
|
|
80
119
|
}
|
|
81
|
-
|
|
82
|
-
if (!this.data) {
|
|
83
|
-
//data not found
|
|
84
|
-
throw new NotFoundException('Document Not found:');
|
|
85
|
-
}
|
|
86
|
-
//console.log('this.data', this.data);
|
|
87
|
-
return this.data;
|
|
88
120
|
// return this;
|
|
89
121
|
}
|
|
122
|
+
async findById(id: string) {
|
|
123
|
+
const data = await this.search({ _id: id });
|
|
124
|
+
if (data.length == 1) {
|
|
125
|
+
// console.log('data0', data[0]);
|
|
126
|
+
return data[0];
|
|
127
|
+
} else {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
90
131
|
|
|
91
132
|
async create() {
|
|
92
133
|
let result;
|
|
93
134
|
|
|
94
135
|
try {
|
|
136
|
+
Object.assign(this.data, User.getInstance().getCreateFilter());
|
|
95
137
|
delete this.data._id;
|
|
96
|
-
this.validateData(this.data);
|
|
138
|
+
this.validateData(this.data);
|
|
97
139
|
const newdoc = new this.doc(this.data);
|
|
98
140
|
result = await newdoc.save();
|
|
99
141
|
} catch (err) {
|
|
@@ -103,14 +145,14 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
103
145
|
}
|
|
104
146
|
async update() {
|
|
105
147
|
const id: string = this.getRecordId();
|
|
148
|
+
Object.assign(this.data, User.getInstance().getUpdateFilter());
|
|
106
149
|
this.findIdThenUpdate(id, this.data);
|
|
107
150
|
}
|
|
108
151
|
async delete() {
|
|
109
|
-
|
|
110
152
|
const id: string = this.getRecordId();
|
|
111
153
|
const dependency = await this.getRelatedRecords(id);
|
|
112
154
|
if (!dependency) {
|
|
113
|
-
|
|
155
|
+
return await this.findIdThenDelete(id);
|
|
114
156
|
} else {
|
|
115
157
|
return dependency;
|
|
116
158
|
}
|
|
@@ -121,10 +163,10 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
121
163
|
validateData(data: T) {
|
|
122
164
|
const ajv = new Ajv({ allErrors: true });
|
|
123
165
|
addFormats(ajv);
|
|
124
|
-
ajv.addFormat('
|
|
125
|
-
ajv.addFormat('
|
|
126
|
-
ajv.addFormat('x-text'
|
|
127
|
-
ajv.addFormat('x-html'
|
|
166
|
+
ajv.addFormat('x-document-no', /.*$/);
|
|
167
|
+
ajv.addFormat('x-document-name', /.*$/);
|
|
168
|
+
ajv.addFormat('x-text', /.*$/);
|
|
169
|
+
ajv.addFormat('x-html', /.*$/);
|
|
128
170
|
ajv.addKeyword({
|
|
129
171
|
keyword: 'x-foreignkey',
|
|
130
172
|
type: 'string',
|
|
@@ -135,28 +177,36 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
135
177
|
for (let i = 0; i < this.errorlist.length; i++) {
|
|
136
178
|
erromsg.push(this.errorlist[i].message);
|
|
137
179
|
}
|
|
180
|
+
console.log("pre-validation:",erromsg)
|
|
138
181
|
throw new InternalServerErrorException(erromsg.join('\n'));
|
|
139
182
|
}
|
|
140
183
|
const validate = ajv.compile(this.jsonschema);
|
|
141
184
|
const valid = validate(data);
|
|
185
|
+
//console.log(validate.errors);
|
|
142
186
|
if (!valid) {
|
|
143
|
-
//console.log(validate.errors);
|
|
187
|
+
// console.log(validate.errors);
|
|
144
188
|
const erromsg: string[] = [];
|
|
145
189
|
for (let i = 0; i < validate.errors.length; i++) {
|
|
146
190
|
erromsg.push(validate.errors[i].message);
|
|
147
191
|
}
|
|
192
|
+
console.log("ajv erromsg: ",erromsg)
|
|
148
193
|
throw new InternalServerErrorException(erromsg.join('\n'));
|
|
149
194
|
}
|
|
150
195
|
this.hook('post-validation', data);
|
|
151
196
|
}
|
|
152
197
|
|
|
153
|
-
async findIdThenDelete(id: string) {
|
|
198
|
+
async findIdThenDelete(id: string): Promise<any> {
|
|
154
199
|
// const data = await this.findById(id);
|
|
155
200
|
try {
|
|
156
201
|
//console.log('deletedeletedeletedelete');
|
|
157
202
|
const dependency = await this.getRelatedRecords(id);
|
|
158
203
|
if (!dependency) {
|
|
159
|
-
|
|
204
|
+
let filter = this.getIsolationFilter();
|
|
205
|
+
filter['_id'] = id;
|
|
206
|
+
const deleteresult = await this.doc.deleteOne(filter);
|
|
207
|
+
// this.doc.findByIdAndDelete(id)
|
|
208
|
+
|
|
209
|
+
//this.doc.findByIdAndDelete(id);
|
|
160
210
|
return deleteresult;
|
|
161
211
|
} else {
|
|
162
212
|
return Promise.reject(dependency);
|
|
@@ -166,20 +216,26 @@ export class SimpleAppService<T extends { _id?: string }> {
|
|
|
166
216
|
}
|
|
167
217
|
}
|
|
168
218
|
|
|
169
|
-
async
|
|
170
|
-
|
|
219
|
+
updateOne = async (data: T) => {
|
|
220
|
+
this.doc.updateOne(data);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
findIdThenUpdate = async (id: string, data: T) => {
|
|
224
|
+
const existingdata = await this.findById(id);
|
|
171
225
|
|
|
172
226
|
try {
|
|
173
|
-
|
|
227
|
+
Object.assign(data, User.getInstance().getUpdateFilter());
|
|
228
|
+
Object.assign(existingdata, data);
|
|
174
229
|
this.validateData(data);
|
|
175
|
-
|
|
176
|
-
|
|
230
|
+
let filter = this.getIsolationFilter();
|
|
231
|
+
filter['_id'] = id;
|
|
232
|
+
const result = await this.doc.findOneAndUpdate(filter, data);
|
|
177
233
|
|
|
178
234
|
return result;
|
|
179
235
|
} catch (err) {
|
|
180
236
|
throw new InternalServerErrorException(err.message);
|
|
181
237
|
}
|
|
182
|
-
}
|
|
238
|
+
};
|
|
183
239
|
|
|
184
240
|
//find what foreign key constraint
|
|
185
241
|
async getRelatedRecords(id: string) {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Injectable, NestMiddleware, Scope } from '@nestjs/common';
|
|
2
|
+
import { Request, Response, NextFunction } from 'express';
|
|
3
|
+
// import * as jwt from 'nestjs-jwt'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import { User } from './User';
|
|
7
|
+
// import {KeycloakConfigService} from "../keycloak/keycloak.service"
|
|
8
|
+
@Injectable({
|
|
9
|
+
scope: Scope.REQUEST,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
export class TenantMiddleware implements NestMiddleware {
|
|
13
|
+
use(req: Request, res: Response, next: NextFunction) {
|
|
14
|
+
if (req.baseUrl == '/oauth2-redirect.html') {
|
|
15
|
+
next();
|
|
16
|
+
return ;
|
|
17
|
+
}
|
|
18
|
+
if(!req.headers['authorization']){
|
|
19
|
+
return res.status(401).send('Undefine bearer token');
|
|
20
|
+
}
|
|
21
|
+
if (!req.headers['x-org']){
|
|
22
|
+
return res.status(401).send('undefine header string x-org');
|
|
23
|
+
}
|
|
24
|
+
const u = User.getInstance();
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
u.setXorg(req.headers['x-org'].toString());
|
|
28
|
+
let tokenstr:string = req.headers['authorization']
|
|
29
|
+
tokenstr = tokenstr.replace("Bearer ",'')
|
|
30
|
+
u.setUserToken(tokenstr);
|
|
31
|
+
next();
|
|
32
|
+
} catch {
|
|
33
|
+
return res.status(401).send('Invalid x-org or user info');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Injectable, Scope } from '@nestjs/common';
|
|
2
|
+
import Base64URL from '@darkwolf/base64url';
|
|
3
|
+
import * as jwt from 'jsonwebtoken'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@Injectable({
|
|
10
|
+
scope: Scope.REQUEST,
|
|
11
|
+
})
|
|
12
|
+
export class User {
|
|
13
|
+
private static instance: User;
|
|
14
|
+
protected uid: string = '';
|
|
15
|
+
protected uname: string = '';
|
|
16
|
+
protected email: string = '';
|
|
17
|
+
protected fullname:string=''
|
|
18
|
+
protected xOrg: string = '';
|
|
19
|
+
protected tenantId: number = 0;
|
|
20
|
+
protected orgId: number = 0;
|
|
21
|
+
protected branchId: number = 0;
|
|
22
|
+
protected accessrights:any = {}
|
|
23
|
+
protected token:string = ''
|
|
24
|
+
protected refreshtoken:string = ''
|
|
25
|
+
constructor() {}
|
|
26
|
+
public static getInstance(): User {
|
|
27
|
+
if (!User.instance) {
|
|
28
|
+
User.instance = new User();
|
|
29
|
+
}
|
|
30
|
+
return User.instance;
|
|
31
|
+
}
|
|
32
|
+
setUserToken = (tokenstr: string) => {
|
|
33
|
+
const tokeninfo = jwt.decode(tokenstr)
|
|
34
|
+
// realm_access: {
|
|
35
|
+
// roles: [
|
|
36
|
+
// 'default-roles-simitdeveloper',
|
|
37
|
+
// 'offline_access',
|
|
38
|
+
// 'uma_authorization'
|
|
39
|
+
// ]
|
|
40
|
+
// },
|
|
41
|
+
// resource_access: { account: { roles: [Array] } },
|
|
42
|
+
// scope: 'openid email profile',
|
|
43
|
+
// sid: '53192f53-d4af-413b-b8d7-1e186419fe53',
|
|
44
|
+
// email_verified: false,
|
|
45
|
+
// name: 'kstan kstan',
|
|
46
|
+
// preferred_username: 'kstan',
|
|
47
|
+
// given_name: 'kstan',
|
|
48
|
+
// family_name: 'kstan',
|
|
49
|
+
// email: 'kstan@simitgroup.com'
|
|
50
|
+
|
|
51
|
+
const u = User.getInstance()
|
|
52
|
+
u.token = tokenstr
|
|
53
|
+
u.uid = tokeninfo.sid;
|
|
54
|
+
u.email = tokeninfo.email
|
|
55
|
+
u.uname = tokeninfo.preferred_username
|
|
56
|
+
u.fullname = tokeninfo.name
|
|
57
|
+
u.accessrights = tokeninfo.resource_access
|
|
58
|
+
};
|
|
59
|
+
getInfo = () => {
|
|
60
|
+
return User.getInstance();
|
|
61
|
+
};
|
|
62
|
+
getBranchFilter = () => {
|
|
63
|
+
return {
|
|
64
|
+
tenantId: User.getInstance().tenantId,
|
|
65
|
+
orgId: User.getInstance().orgId,
|
|
66
|
+
branchId: User.getInstance().branchId,
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
getTenantFilter = () => {
|
|
70
|
+
return { tenantId: User.getInstance().tenantId };
|
|
71
|
+
};
|
|
72
|
+
getOrgFilter = () => {
|
|
73
|
+
return {
|
|
74
|
+
tenantId: User.getInstance().tenantId,
|
|
75
|
+
orgId: User.getInstance().orgId,
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
getCreateFilter = () => {
|
|
80
|
+
const u = User.getInstance();
|
|
81
|
+
return {
|
|
82
|
+
tenantId: u.tenantId,
|
|
83
|
+
orgId: u.orgId,
|
|
84
|
+
branchId: u.branchId,
|
|
85
|
+
createdby: u.uid,
|
|
86
|
+
updatedby: u.uid,
|
|
87
|
+
created: new Date().getTime().toString(),
|
|
88
|
+
updated: new Date().getTime().toString(),
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
getUpdateFilter = () => {
|
|
92
|
+
const u = User.getInstance();
|
|
93
|
+
return {
|
|
94
|
+
updatedby: u.uid,
|
|
95
|
+
updated: new Date().getTime().toString(),
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
setXorg = (xorg) => {
|
|
99
|
+
try {
|
|
100
|
+
const decodedText: string = Base64URL.decodeText(xorg);
|
|
101
|
+
const arrXorg = decodedText.split('-');
|
|
102
|
+
|
|
103
|
+
if (arrXorg.length == 3) {
|
|
104
|
+
const u = User.getInstance();
|
|
105
|
+
u.tenantId = Number(arrXorg[0]);
|
|
106
|
+
u.orgId = Number(arrXorg[1]);
|
|
107
|
+
u.branchId = Number(arrXorg[2]);
|
|
108
|
+
} else {
|
|
109
|
+
throw 'invalid x-org';
|
|
110
|
+
}
|
|
111
|
+
} catch (err) {
|
|
112
|
+
throw err;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
@@ -1,15 +1,35 @@
|
|
|
1
|
-
import { Module } from '@nestjs/common';
|
|
1
|
+
import { Module,MiddlewareConsumer,NestModule } from '@nestjs/common';
|
|
2
2
|
import { MongooseModule } from '@nestjs/mongoose';
|
|
3
3
|
import { ConfigModule } from '@nestjs/config';
|
|
4
|
-
|
|
4
|
+
import { ServeStaticModule } from '@nestjs/serve-static';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import {TenantMiddleware} from './class/TenantMiddleware'
|
|
5
7
|
<% for(let i=0;i<it.length; i++){ %>
|
|
6
8
|
import {<%= it[i].docname %>Module} from './docs/<%= it[i].doctype %>/<%= it[i].doctype %>.module'
|
|
7
9
|
<%}%>
|
|
8
10
|
|
|
9
11
|
@Module({
|
|
10
12
|
//define environment variables: MONGODB_URL='mongodb://<user>:<pass>@<host>:<port>/<db>?authMechanism=DEFAULT'
|
|
11
|
-
imports: [
|
|
13
|
+
imports: [
|
|
14
|
+
ConfigModule.forRoot(),
|
|
15
|
+
MongooseModule.forRoot(process.env.MONGODB_URL),
|
|
16
|
+
ServeStaticModule.forRoot({
|
|
17
|
+
rootPath: join(__dirname, '..', 'public_html'),
|
|
18
|
+
exclude: ['/api/(.*)'],
|
|
19
|
+
}),
|
|
20
|
+
<% for(let i=0;i<it.length; i++){ %><%= it[i].docname %>Module,<%}%>],
|
|
12
21
|
controllers: [],
|
|
13
22
|
providers: [],
|
|
14
23
|
})
|
|
15
|
-
export class AppModule {
|
|
24
|
+
export class AppModule implements NestModule{
|
|
25
|
+
configure(consumer: MiddlewareConsumer) {
|
|
26
|
+
consumer
|
|
27
|
+
.apply(TenantMiddleware)
|
|
28
|
+
// .exclude('/api-yaml')
|
|
29
|
+
// .exclude('/api-json')
|
|
30
|
+
// .exclude('/api')
|
|
31
|
+
.forRoutes('*')
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -4,4 +4,15 @@ HTTP_PORT=<%=it.backendPort%>
|
|
|
4
4
|
|
|
5
5
|
PROJECT_NAME=SimpleApp Demo1
|
|
6
6
|
PROJECT_DESCRIPTION=Try CRUD
|
|
7
|
-
PROJECT_Version=1.0.0
|
|
7
|
+
PROJECT_Version=1.0.0
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
OAUTH2_CONFIGURL=<%=it.keycloaksetting.OAUTH2_CONFIGURL%>
|
|
11
|
+
|
|
12
|
+
OAUTH2_CLIENTID=<%=it.keycloaksetting.OAUTH2_CLIENTID%>
|
|
13
|
+
|
|
14
|
+
OAUTH2_CLIENTSECRET=<%=it.keycloaksetting.OAUTH2_CLIENTSECRET%>
|
|
15
|
+
|
|
16
|
+
AUTH_SECRET_KEY=<%=it.keycloaksetting.AUTH_SECRET_KEY%>
|
|
17
|
+
|
|
18
|
+
AUTH_ORIGIN=http://localhost:8080
|
|
@@ -9,12 +9,23 @@ async function bootstrap() {
|
|
|
9
9
|
.setTitle(process.env.PROJECT_NAME)
|
|
10
10
|
.setDescription(process.env.PROJECT_DESCRIPTION)
|
|
11
11
|
.setVersion(process.env.PROJECT_VERSION)
|
|
12
|
+
.addApiKey({
|
|
13
|
+
in: 'header',name: 'x-org',type: 'apiKey',description: 'base 64 url encode. example: MS0xLTE',
|
|
14
|
+
},'x-org',)
|
|
15
|
+
.addOAuth2({
|
|
16
|
+
name:'oauth2',in:'header',type:'oauth2',flows:{
|
|
17
|
+
implicit:{
|
|
18
|
+
authorizationUrl: `${process.env.OAUTH2_CONFIGURL}/protocol/openid-connect/auth`,
|
|
19
|
+
scopes:[],
|
|
20
|
+
}}},'oauth2')
|
|
21
|
+
.addSecurityRequirements('x-org')
|
|
22
|
+
.addSecurityRequirements('oauth2')
|
|
12
23
|
.build();
|
|
13
24
|
const document = SwaggerModule.createDocument(app, config);
|
|
14
25
|
SwaggerModule.setup('api', app, document, {
|
|
15
|
-
swaggerOptions: { showExtensions: true },
|
|
26
|
+
swaggerOptions: { showExtensions: true, persistAuthorization: true },
|
|
16
27
|
});
|
|
17
28
|
|
|
18
|
-
await app.listen(process.env.HTTP_PORT ??
|
|
29
|
+
await app.listen(process.env.HTTP_PORT ?? 8000); //listen which port
|
|
19
30
|
}
|
|
20
|
-
bootstrap();
|
|
31
|
+
bootstrap();
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en-US">
|
|
3
|
+
<head>
|
|
4
|
+
<title>Swagger UI: OAuth2 Redirect</title>
|
|
5
|
+
</head>
|
|
6
|
+
<body>
|
|
7
|
+
<script>
|
|
8
|
+
'use strict';
|
|
9
|
+
function run () {
|
|
10
|
+
var oauth2 = window.opener.swaggerUIRedirectOauth2;
|
|
11
|
+
var sentState = oauth2.state;
|
|
12
|
+
var redirectUrl = oauth2.redirectUrl;
|
|
13
|
+
var isValid, qp, arr;
|
|
14
|
+
|
|
15
|
+
if (/code|token|error/.test(window.location.hash)) {
|
|
16
|
+
qp = window.location.hash.substring(1).replace('?', '&');
|
|
17
|
+
} else {
|
|
18
|
+
qp = location.search.substring(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
arr = qp.split("&");
|
|
22
|
+
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
|
|
23
|
+
qp = qp ? JSON.parse('{' + arr.join() + '}',
|
|
24
|
+
function (key, value) {
|
|
25
|
+
return key === "" ? value : decodeURIComponent(value);
|
|
26
|
+
}
|
|
27
|
+
) : {};
|
|
28
|
+
|
|
29
|
+
isValid = qp.state === sentState;
|
|
30
|
+
|
|
31
|
+
if ((
|
|
32
|
+
oauth2.auth.schema.get("flow") === "accessCode" ||
|
|
33
|
+
oauth2.auth.schema.get("flow") === "authorizationCode" ||
|
|
34
|
+
oauth2.auth.schema.get("flow") === "authorization_code"
|
|
35
|
+
) && !oauth2.auth.code) {
|
|
36
|
+
if (!isValid) {
|
|
37
|
+
oauth2.errCb({
|
|
38
|
+
authId: oauth2.auth.name,
|
|
39
|
+
source: "auth",
|
|
40
|
+
level: "warning",
|
|
41
|
+
message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (qp.code) {
|
|
46
|
+
delete oauth2.state;
|
|
47
|
+
oauth2.auth.code = qp.code;
|
|
48
|
+
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
|
|
49
|
+
} else {
|
|
50
|
+
let oauthErrorMsg;
|
|
51
|
+
if (qp.error) {
|
|
52
|
+
oauthErrorMsg = "["+qp.error+"]: " +
|
|
53
|
+
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
|
|
54
|
+
(qp.error_uri ? "More info: "+qp.error_uri : "");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
oauth2.errCb({
|
|
58
|
+
authId: oauth2.auth.name,
|
|
59
|
+
source: "auth",
|
|
60
|
+
level: "error",
|
|
61
|
+
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
|
|
66
|
+
}
|
|
67
|
+
window.close();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (document.readyState !== 'loading') {
|
|
71
|
+
run();
|
|
72
|
+
} else {
|
|
73
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
74
|
+
run();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
</script>
|
|
78
|
+
</body>
|
|
79
|
+
</html>
|