express-ext 0.4.0 → 0.4.1
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/lib/GenericSearchController.js +38 -41
- package/lib/LoadSearchController.js +73 -80
- package/lib/LowCodeController.js +61 -68
- package/lib/SearchController.js +26 -29
- package/lib/resources.js +58 -56
- package/lib/search.js +348 -477
- package/package.json +1 -1
- package/src/GenericController.ts +101 -101
- package/src/GenericSearchController.ts +25 -30
- package/src/HealthController.ts +8 -8
- package/src/LoadController.ts +55 -48
- package/src/LoadSearchController.ts +63 -72
- package/src/LogController.ts +86 -84
- package/src/LowCodeController.ts +44 -54
- package/src/SearchController.ts +19 -23
- package/src/client.ts +46 -46
- package/src/edit.ts +45 -45
- package/src/health.ts +26 -26
- package/src/http.ts +139 -139
- package/src/index.ts +197 -197
- package/src/log.ts +155 -151
- package/src/metadata.ts +44 -27
- package/src/resources.ts +71 -69
- package/src/search.ts +342 -502
- package/src/view.ts +33 -33
package/src/log.ts
CHANGED
|
@@ -1,251 +1,255 @@
|
|
|
1
|
-
import { NextFunction } from
|
|
2
|
-
import { ParamsDictionary, Request, Response } from
|
|
3
|
-
import { ParsedQs } from
|
|
4
|
-
import { PassThrough } from
|
|
5
|
-
import { resources } from
|
|
1
|
+
import { NextFunction } from "express"
|
|
2
|
+
import { ParamsDictionary, Request, Response } from "express-serve-static-core"
|
|
3
|
+
import { ParsedQs } from "qs"
|
|
4
|
+
import { PassThrough } from "stream"
|
|
5
|
+
import { resources } from "./resources"
|
|
6
6
|
|
|
7
7
|
export interface LogConf {
|
|
8
|
-
log?: boolean
|
|
9
|
-
separate?: boolean
|
|
10
|
-
skips?: string
|
|
11
|
-
request?: string
|
|
12
|
-
response?: string
|
|
13
|
-
duration?: string
|
|
14
|
-
status?: string
|
|
15
|
-
size?: string
|
|
8
|
+
log?: boolean
|
|
9
|
+
separate?: boolean
|
|
10
|
+
skips?: string
|
|
11
|
+
request?: string
|
|
12
|
+
response?: string
|
|
13
|
+
duration?: string
|
|
14
|
+
status?: string
|
|
15
|
+
size?: string
|
|
16
16
|
}
|
|
17
17
|
export interface MiddleLog {
|
|
18
|
-
log?: boolean
|
|
19
|
-
separate?: boolean
|
|
20
|
-
skips: string[]
|
|
21
|
-
duration: string
|
|
22
|
-
request: string
|
|
23
|
-
response: string
|
|
24
|
-
status: string
|
|
25
|
-
size: string
|
|
18
|
+
log?: boolean
|
|
19
|
+
separate?: boolean
|
|
20
|
+
skips: string[]
|
|
21
|
+
duration: string
|
|
22
|
+
request: string
|
|
23
|
+
response: string
|
|
24
|
+
status: string
|
|
25
|
+
size: string
|
|
26
26
|
}
|
|
27
27
|
export interface SimpleMap {
|
|
28
|
-
[key: string]: string|number|boolean|Date
|
|
28
|
+
[key: string]: string | number | boolean | Date
|
|
29
29
|
}
|
|
30
30
|
export function createConfig(c?: LogConf): MiddleLog {
|
|
31
31
|
if (!c) {
|
|
32
|
-
return {skips: [], duration:
|
|
32
|
+
return { skips: [], duration: "duration", request: "", response: "", status: "", size: "" }
|
|
33
33
|
}
|
|
34
34
|
const l: MiddleLog = {
|
|
35
35
|
log: c.log,
|
|
36
36
|
separate: c.separate,
|
|
37
|
-
skips: c.skips ? c.skips.split(
|
|
38
|
-
duration: c.duration ? c.duration :
|
|
39
|
-
request: c.request ? c.request :
|
|
40
|
-
response: c.response ? c.response :
|
|
41
|
-
status: c.status ? c.status :
|
|
42
|
-
size: c.size ? c.size :
|
|
43
|
-
}
|
|
44
|
-
return l
|
|
37
|
+
skips: c.skips ? c.skips.split(",") : [],
|
|
38
|
+
duration: c.duration ? c.duration : "duration",
|
|
39
|
+
request: c.request ? c.request : "",
|
|
40
|
+
response: c.response ? c.response : "",
|
|
41
|
+
status: c.status ? c.status : "",
|
|
42
|
+
size: c.size ? c.size : "",
|
|
43
|
+
}
|
|
44
|
+
return l
|
|
45
45
|
}
|
|
46
46
|
export function skip(skips: string[], url: string): boolean {
|
|
47
47
|
if (skips.length === 0) {
|
|
48
|
-
return false
|
|
48
|
+
return false
|
|
49
49
|
}
|
|
50
|
-
const u = removeUrlParams(url)
|
|
50
|
+
const u = removeUrlParams(url)
|
|
51
51
|
for (const s of skips) {
|
|
52
52
|
if (u.endsWith(s)) {
|
|
53
|
-
return true
|
|
53
|
+
return true
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
return false
|
|
56
|
+
return false
|
|
57
57
|
}
|
|
58
58
|
export function removeUrlParams(url: string): string {
|
|
59
|
-
const startParams = url.indexOf(
|
|
60
|
-
return startParams !== -1 ? url.substring(0, startParams) : url
|
|
59
|
+
const startParams = url.indexOf("?")
|
|
60
|
+
return startParams !== -1 ? url.substring(0, startParams) : url
|
|
61
61
|
}
|
|
62
62
|
export interface Middleware {
|
|
63
|
-
conf: MiddleLog
|
|
63
|
+
conf: MiddleLog
|
|
64
64
|
}
|
|
65
|
-
const o =
|
|
65
|
+
const o = "OPTIONS"
|
|
66
66
|
export class MiddlewareLogger {
|
|
67
|
-
constructor(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
constructor(
|
|
68
|
+
public write: (msg: string, m?: SimpleMap) => void,
|
|
69
|
+
conf?: LogConf,
|
|
70
|
+
public build?: (req: Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>, m: SimpleMap) => SimpleMap,
|
|
71
|
+
) {
|
|
72
|
+
this.log = this.log.bind(this)
|
|
73
|
+
this.conf = createConfig(conf)
|
|
74
|
+
}
|
|
75
|
+
conf: MiddleLog
|
|
72
76
|
log(req: Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>, number>, next: NextFunction) {
|
|
73
|
-
const m = req.method
|
|
77
|
+
const m = req.method
|
|
74
78
|
if (m !== o && this.conf.log && !skip(this.conf.skips, req.originalUrl)) {
|
|
75
|
-
const start = process.hrtime()
|
|
76
|
-
const x = this.conf.request
|
|
77
|
-
let r = false
|
|
78
|
-
if (m !==
|
|
79
|
-
r = true
|
|
79
|
+
const start = process.hrtime()
|
|
80
|
+
const x = this.conf.request
|
|
81
|
+
let r = false
|
|
82
|
+
if (m !== "GET" && m !== "DELETE") {
|
|
83
|
+
r = true
|
|
80
84
|
}
|
|
81
|
-
const msg = `${m} ${req.originalUrl}
|
|
85
|
+
const msg = `${m} ${req.originalUrl}`
|
|
82
86
|
if (this.conf.separate && r) {
|
|
83
87
|
if (this.conf.request.length > 0) {
|
|
84
|
-
const op: SimpleMap = {}
|
|
85
|
-
op[x] = JSON.stringify(req.body)
|
|
88
|
+
const op: SimpleMap = {}
|
|
89
|
+
op[x] = JSON.stringify(req.body)
|
|
86
90
|
if (this.build) {
|
|
87
|
-
const op2 = this.build(req, op)
|
|
88
|
-
this.write(msg, op2)
|
|
91
|
+
const op2 = this.build(req, op)
|
|
92
|
+
this.write(msg, op2)
|
|
89
93
|
} else {
|
|
90
|
-
this.write(msg, op)
|
|
94
|
+
this.write(msg, op)
|
|
91
95
|
}
|
|
92
96
|
}
|
|
93
97
|
}
|
|
94
|
-
const chunks: Uint8Array[] = []
|
|
95
|
-
mapResponseBody(res, chunks)
|
|
96
|
-
res.on(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
98
|
+
const chunks: Uint8Array[] = []
|
|
99
|
+
mapResponseBody(res, chunks)
|
|
100
|
+
res.on("finish", () => {
|
|
101
|
+
const duration = getDurationInMilliseconds(start)
|
|
102
|
+
const op: SimpleMap = {}
|
|
103
|
+
if (r && !this.conf.separate && this.conf.request.length > 0) {
|
|
104
|
+
op[x] = JSON.stringify(req.body)
|
|
105
|
+
}
|
|
106
|
+
if (this.conf.response.length > 0) {
|
|
107
|
+
const rsBody = Buffer.concat(chunks).toString(resources.encoding)
|
|
108
|
+
op[this.conf.response] = rsBody
|
|
109
|
+
}
|
|
110
|
+
if (this.conf.status.length > 0) {
|
|
111
|
+
op[this.conf.status] = res.statusCode
|
|
112
|
+
}
|
|
113
|
+
if (this.conf.size.length > 0) {
|
|
114
|
+
if ("_contentLength" in res) {
|
|
115
|
+
op[this.conf.size] = (res as any)["_contentLength"]
|
|
116
|
+
} else if (res.hasHeader("content-length")) {
|
|
117
|
+
const l = res.getHeader("content-length")
|
|
118
|
+
if (typeof l === "number" || typeof l === "string") {
|
|
119
|
+
op[this.conf.size] = l
|
|
117
120
|
}
|
|
118
121
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
122
|
+
}
|
|
123
|
+
op[this.conf.duration] = duration
|
|
124
|
+
if (this.build) {
|
|
125
|
+
const op2 = this.build(req, op)
|
|
126
|
+
this.write(msg, op2)
|
|
127
|
+
} else {
|
|
128
|
+
this.write(msg, op)
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
next()
|
|
128
132
|
} else {
|
|
129
|
-
next()
|
|
133
|
+
next()
|
|
130
134
|
}
|
|
131
135
|
}
|
|
132
136
|
}
|
|
133
137
|
const mapResponseBody = (res: Response<any, Record<string, any>, number>, chunks: Uint8Array[]) => {
|
|
134
|
-
const defaultWrite = res.write.bind(res)
|
|
135
|
-
const defaultEnd = res.end.bind(res)
|
|
136
|
-
const ps = new PassThrough()
|
|
138
|
+
const defaultWrite = res.write.bind(res)
|
|
139
|
+
const defaultEnd = res.end.bind(res)
|
|
140
|
+
const ps = new PassThrough()
|
|
137
141
|
|
|
138
|
-
ps.on(
|
|
142
|
+
ps.on("data", (data: any) => chunks.push(data))
|
|
139
143
|
|
|
140
|
-
(res as any).write = (...args: any) => {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
+
;(res as any).write = (...args: any) => {
|
|
145
|
+
;(ps as any).write(...args)
|
|
146
|
+
;(defaultWrite as any)(...args)
|
|
147
|
+
}
|
|
144
148
|
|
|
145
|
-
(res as any).end = (...args: any) => {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
const NS_PER_SEC = 1e9
|
|
151
|
-
const NS_TO_MS = 1e6
|
|
149
|
+
;(res as any).end = (...args: any) => {
|
|
150
|
+
ps.end(...args)
|
|
151
|
+
defaultEnd(...args)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const NS_PER_SEC = 1e9
|
|
155
|
+
const NS_TO_MS = 1e6
|
|
152
156
|
const getDurationInMilliseconds = (start: [number, number] | undefined) => {
|
|
153
|
-
const diff = process.hrtime(start)
|
|
154
|
-
return (diff[0] * NS_PER_SEC + diff[1]) / NS_TO_MS
|
|
155
|
-
}
|
|
157
|
+
const diff = process.hrtime(start)
|
|
158
|
+
return (diff[0] * NS_PER_SEC + diff[1]) / NS_TO_MS
|
|
159
|
+
}
|
|
156
160
|
|
|
157
161
|
// tslint:disable-next-line:max-classes-per-file
|
|
158
162
|
export class MiddlewareController {
|
|
159
163
|
constructor(public logger: Middleware) {
|
|
160
|
-
this.config = this.config.bind(this)
|
|
164
|
+
this.config = this.config.bind(this)
|
|
161
165
|
}
|
|
162
166
|
config(req: Request, res: Response) {
|
|
163
|
-
const obj: MiddleLog = req.body
|
|
164
|
-
if (!obj || (obj as any) ===
|
|
165
|
-
return res.status(400).end(
|
|
167
|
+
const obj: MiddleLog = req.body
|
|
168
|
+
if (!obj || (obj as any) === "") {
|
|
169
|
+
return res.status(400).end("The request body cannot be empty")
|
|
166
170
|
}
|
|
167
171
|
if (!this.logger) {
|
|
168
|
-
return res.status(503).end(
|
|
172
|
+
return res.status(503).end("Logger is not available")
|
|
169
173
|
}
|
|
170
|
-
let changed = false
|
|
174
|
+
let changed = false
|
|
171
175
|
if (obj.log !== undefined) {
|
|
172
|
-
this.logger.conf.log = obj.log
|
|
173
|
-
changed = true
|
|
176
|
+
this.logger.conf.log = obj.log
|
|
177
|
+
changed = true
|
|
174
178
|
}
|
|
175
179
|
if (obj.separate !== undefined) {
|
|
176
|
-
this.logger.conf.separate = obj.separate
|
|
177
|
-
changed = true
|
|
180
|
+
this.logger.conf.separate = obj.separate
|
|
181
|
+
changed = true
|
|
178
182
|
}
|
|
179
183
|
if (Array.isArray(obj.skips)) {
|
|
180
184
|
if (isValidSkips(obj.skips)) {
|
|
181
|
-
this.logger.conf.skips = obj.skips
|
|
182
|
-
changed = true
|
|
185
|
+
this.logger.conf.skips = obj.skips
|
|
186
|
+
changed = true
|
|
183
187
|
}
|
|
184
188
|
}
|
|
185
|
-
if (typeof obj.duration ===
|
|
186
|
-
this.logger.conf.duration = obj.duration
|
|
187
|
-
changed = true
|
|
189
|
+
if (typeof obj.duration === "string" && obj.duration.length > 0) {
|
|
190
|
+
this.logger.conf.duration = obj.duration
|
|
191
|
+
changed = true
|
|
188
192
|
}
|
|
189
|
-
if (typeof obj.request ===
|
|
190
|
-
this.logger.conf.request = obj.request
|
|
191
|
-
changed = true
|
|
193
|
+
if (typeof obj.request === "string") {
|
|
194
|
+
this.logger.conf.request = obj.request
|
|
195
|
+
changed = true
|
|
192
196
|
}
|
|
193
|
-
if (typeof obj.response ===
|
|
194
|
-
this.logger.conf.response = obj.response
|
|
195
|
-
changed = true
|
|
197
|
+
if (typeof obj.response === "string") {
|
|
198
|
+
this.logger.conf.response = obj.response
|
|
199
|
+
changed = true
|
|
196
200
|
}
|
|
197
|
-
if (typeof obj.status ===
|
|
198
|
-
this.logger.conf.status = obj.status
|
|
199
|
-
changed = true
|
|
201
|
+
if (typeof obj.status === "string") {
|
|
202
|
+
this.logger.conf.status = obj.status
|
|
203
|
+
changed = true
|
|
200
204
|
}
|
|
201
|
-
if (typeof obj.size ===
|
|
202
|
-
this.logger.conf.size = obj.size
|
|
203
|
-
changed = true
|
|
205
|
+
if (typeof obj.size === "string") {
|
|
206
|
+
this.logger.conf.size = obj.size
|
|
207
|
+
changed = true
|
|
204
208
|
}
|
|
205
209
|
if (changed) {
|
|
206
|
-
return res.status(200).json(true).end()
|
|
210
|
+
return res.status(200).json(true).end()
|
|
207
211
|
} else {
|
|
208
|
-
return res.status(204).json(false).end()
|
|
212
|
+
return res.status(204).json(false).end()
|
|
209
213
|
}
|
|
210
214
|
}
|
|
211
215
|
}
|
|
212
216
|
export function isValidSkips(s: string[]): boolean {
|
|
213
217
|
for (const x of s) {
|
|
214
|
-
if (!(typeof x ===
|
|
215
|
-
return false
|
|
218
|
+
if (!(typeof x === "string")) {
|
|
219
|
+
return false
|
|
216
220
|
}
|
|
217
221
|
}
|
|
218
|
-
return true
|
|
222
|
+
return true
|
|
219
223
|
}
|
|
220
224
|
export function mask(s: string, start: number, end: number, replace: string): string {
|
|
221
225
|
if (start < 0) {
|
|
222
|
-
start = 0
|
|
226
|
+
start = 0
|
|
223
227
|
}
|
|
224
228
|
if (end < 0) {
|
|
225
|
-
end = 0
|
|
229
|
+
end = 0
|
|
226
230
|
}
|
|
227
|
-
const t = start + end
|
|
231
|
+
const t = start + end
|
|
228
232
|
if (t >= s.length) {
|
|
229
|
-
return replace.repeat(s.length)
|
|
233
|
+
return replace.repeat(s.length)
|
|
230
234
|
}
|
|
231
|
-
return s.substr(0, start) + replace.repeat(s.length - t) + s.substr(s.length - end)
|
|
235
|
+
return s.substr(0, start) + replace.repeat(s.length - t) + s.substr(s.length - end)
|
|
232
236
|
}
|
|
233
237
|
export function margin(s: string, start: number, end: number, replace: string): string {
|
|
234
238
|
if (start >= end) {
|
|
235
|
-
return
|
|
239
|
+
return ""
|
|
236
240
|
}
|
|
237
241
|
if (start < 0) {
|
|
238
|
-
start = 0
|
|
242
|
+
start = 0
|
|
239
243
|
}
|
|
240
244
|
if (end < 0) {
|
|
241
|
-
end = 0
|
|
245
|
+
end = 0
|
|
242
246
|
}
|
|
243
247
|
if (start >= s.length) {
|
|
244
|
-
return replace.repeat(s.length)
|
|
248
|
+
return replace.repeat(s.length)
|
|
245
249
|
}
|
|
246
250
|
if (end >= s.length) {
|
|
247
|
-
return replace.repeat(start) + s.substr(start)
|
|
251
|
+
return replace.repeat(start) + s.substr(start)
|
|
248
252
|
}
|
|
249
|
-
return replace.repeat(start) + s.substr(start, end - start) + replace.repeat(s.length - end)
|
|
253
|
+
return replace.repeat(start) + s.substr(start, end - start) + replace.repeat(s.length - end)
|
|
250
254
|
}
|
|
251
|
-
export const maskMargin = margin
|
|
255
|
+
export const maskMargin = margin
|
package/src/metadata.ts
CHANGED
|
@@ -1,38 +1,55 @@
|
|
|
1
|
-
export type Type =
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
export type Type =
|
|
2
|
+
| "ObjectId"
|
|
3
|
+
| "date"
|
|
4
|
+
| "datetime"
|
|
5
|
+
| "time"
|
|
6
|
+
| "boolean"
|
|
7
|
+
| "number"
|
|
8
|
+
| "integer"
|
|
9
|
+
| "string"
|
|
10
|
+
| "text"
|
|
11
|
+
| "object"
|
|
12
|
+
| "array"
|
|
13
|
+
| "binary"
|
|
14
|
+
| "primitives"
|
|
15
|
+
| "booleans"
|
|
16
|
+
| "numbers"
|
|
17
|
+
| "integers"
|
|
18
|
+
| "strings"
|
|
19
|
+
| "dates"
|
|
20
|
+
| "datetimes"
|
|
21
|
+
| "times"
|
|
22
|
+
export type Format = "currency" | "percentage" | "email" | "url" | "phone" | "fax" | "ipv4" | "ipv6"
|
|
6
23
|
|
|
7
|
-
export type DataType = Type
|
|
8
|
-
export type FormatType = Format
|
|
24
|
+
export type DataType = Type
|
|
25
|
+
export type FormatType = Format
|
|
9
26
|
export interface ErrorMessage {
|
|
10
|
-
field: string
|
|
11
|
-
code: string
|
|
12
|
-
message?: string
|
|
13
|
-
param?: string|number|Date
|
|
14
|
-
invalid?: string
|
|
27
|
+
field: string
|
|
28
|
+
code: string
|
|
29
|
+
message?: string
|
|
30
|
+
param?: string | number | Date
|
|
31
|
+
invalid?: string
|
|
15
32
|
}
|
|
16
33
|
|
|
17
34
|
export interface Model {
|
|
18
|
-
attributes: Attributes
|
|
35
|
+
attributes: Attributes
|
|
19
36
|
}
|
|
20
37
|
|
|
21
38
|
export interface Attribute {
|
|
22
|
-
name?: string
|
|
23
|
-
type?: Type
|
|
24
|
-
format?: Format
|
|
25
|
-
required?: boolean
|
|
26
|
-
key?: boolean
|
|
27
|
-
length?: number
|
|
28
|
-
min?: number
|
|
29
|
-
max?: number
|
|
30
|
-
gt?: number
|
|
31
|
-
lt?: number
|
|
32
|
-
exp?: RegExp|string
|
|
33
|
-
code?: string
|
|
34
|
-
typeof?: Attributes
|
|
39
|
+
name?: string
|
|
40
|
+
type?: Type
|
|
41
|
+
format?: Format
|
|
42
|
+
required?: boolean
|
|
43
|
+
key?: boolean
|
|
44
|
+
length?: number
|
|
45
|
+
min?: number
|
|
46
|
+
max?: number
|
|
47
|
+
gt?: number
|
|
48
|
+
lt?: number
|
|
49
|
+
exp?: RegExp | string
|
|
50
|
+
code?: string
|
|
51
|
+
typeof?: Attributes
|
|
35
52
|
}
|
|
36
53
|
export interface Attributes {
|
|
37
|
-
[key: string]: Attribute
|
|
54
|
+
[key: string]: Attribute
|
|
38
55
|
}
|