waibu 1.1.1 → 1.1.2
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/bajo/hook/on-request.js +3 -2
- package/bajo/hook/on-response.js +3 -1
- package/package.json +3 -3
- package/plugin/factory.js +306 -0
- package/plugin/.alias +0 -1
- package/plugin/.dependencies +0 -1
- package/plugin/config.json +0 -46
- package/plugin/exit.js +0 -3
- package/plugin/init.js +0 -8
- package/plugin/method/escape-chars.js +0 -6
- package/plugin/method/escape.js +0 -11
- package/plugin/method/fetch.js +0 -19
- package/plugin/method/get-ip.js +0 -7
- package/plugin/method/get-plugin-by-prefix.js +0 -13
- package/plugin/method/get-plugin-prefix.js +0 -12
- package/plugin/method/get-routes.js +0 -13
- package/plugin/method/get-uploaded-files.js +0 -11
- package/plugin/method/hook-types.js +0 -4
- package/plugin/method/isIntlPath.js +0 -6
- package/plugin/method/not-found.js +0 -5
- package/plugin/method/parse-filter.js +0 -10
- package/plugin/method/qs.js +0 -17
- package/plugin/method/route-dir.js +0 -14
- package/plugin/method/route-path.js +0 -31
- package/plugin/method/send-mail.js +0 -19
- package/plugin/method/unescape-block.js +0 -12
- package/plugin/method/unescape.js +0 -12
- package/plugin/start.js +0 -45
package/bajo/hook/on-request.js
CHANGED
|
@@ -4,10 +4,11 @@ const onRequest = {
|
|
|
4
4
|
const { get } = this.app.bajo.lib._
|
|
5
5
|
|
|
6
6
|
req.site = this.config.siteInfo
|
|
7
|
-
req.ns = get(reply.request, 'routeOptions.config.ns')
|
|
7
|
+
req.ns = get(reply.request, 'routeOptions.config.ns') ?? this.name
|
|
8
|
+
const ns = get(reply.request, 'routeOptions.config.webApp') ?? this.name
|
|
8
9
|
let msg = '< %s:%s from IP %s'
|
|
9
10
|
if (req.headers['content-length']) msg += ', content length: %s'
|
|
10
|
-
this.log.info(msg, req.method, req.url, this.getIp(req), req.headers['content-length'])
|
|
11
|
+
this.app[ns].log.info(msg, req.method, req.url, this.getIp(req), req.headers['content-length'])
|
|
11
12
|
if (Object.keys(this.config.paramsCharMap).length === 0) return
|
|
12
13
|
for (const key in req.params) {
|
|
13
14
|
let val = req.params[key]
|
package/bajo/hook/on-response.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
const onResponse = {
|
|
2
2
|
level: 5,
|
|
3
3
|
handler: async function onResponse (req, reply) {
|
|
4
|
+
const { get } = this.app.bajo.lib._
|
|
4
5
|
let method = 'info'
|
|
5
6
|
if (reply.statusCode >= 300 && reply.statusCode < 400) method = 'warn'
|
|
6
7
|
else if (reply.statusCode >= 400) method = 'error'
|
|
7
|
-
|
|
8
|
+
const ns = get(reply.request, 'routeOptions.config.webApp') ?? this.name
|
|
9
|
+
this.app[ns].log[method]('> %s:%s with a %d-status took %dms', req.method, req.url, reply.statusCode,
|
|
8
10
|
(reply.elapsedTime ?? 0).toFixed(3))
|
|
9
11
|
}
|
|
10
12
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "waibu",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Web Framework for Bajo",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"@fastify/reply-from": "^12.0.1",
|
|
40
40
|
"@fastify/sensible": "^6.0.2",
|
|
41
41
|
"@fastify/session": "^11.1.0",
|
|
42
|
-
"@fastify/static": "^8.0.4",
|
|
43
42
|
"@fastify/under-pressure": "^9.0.3",
|
|
44
43
|
"fastify": "^5.2.1",
|
|
45
44
|
"fastify-no-icon": "^7.0.0",
|
|
46
|
-
"query-string": "^9.1.1"
|
|
45
|
+
"query-string": "^9.1.1",
|
|
46
|
+
"waibu-fastify-static": "^1.1.0"
|
|
47
47
|
}
|
|
48
48
|
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import collectRoutePathHandlers from '../lib/collect-route-path-handlers.js'
|
|
2
|
+
import fastify from 'fastify'
|
|
3
|
+
import appHook from '../lib/app-hook.js'
|
|
4
|
+
import routeHook from '../lib/webapp-scope/route-hook.js'
|
|
5
|
+
import logRoutes from '../lib/log-routes.js'
|
|
6
|
+
import { boot } from '../lib/app.js'
|
|
7
|
+
import sensible from '@fastify/sensible'
|
|
8
|
+
import noIcon from 'fastify-no-icon'
|
|
9
|
+
import underPressure from '@fastify/under-pressure'
|
|
10
|
+
import handleForward from '../lib/handle-forward.js'
|
|
11
|
+
import handleRedirect from '../lib/handle-redirect.js'
|
|
12
|
+
import buildLocals from '../lib/build-locals.js'
|
|
13
|
+
import queryString from 'query-string'
|
|
14
|
+
|
|
15
|
+
async function factory (pkgName) {
|
|
16
|
+
const me = this
|
|
17
|
+
|
|
18
|
+
return class Waibu extends this.lib.BajoPlugin {
|
|
19
|
+
constructor () {
|
|
20
|
+
super(pkgName, me.app)
|
|
21
|
+
this.alias = 'w'
|
|
22
|
+
this.dependencies = ['bajo-logger', 'bajo-extra']
|
|
23
|
+
this.config = {
|
|
24
|
+
server: {
|
|
25
|
+
host: 'localhost',
|
|
26
|
+
port: 7771
|
|
27
|
+
},
|
|
28
|
+
factory: {
|
|
29
|
+
trustProxy: true,
|
|
30
|
+
bodyLimit: 10485760
|
|
31
|
+
},
|
|
32
|
+
prefixVirtual: '~',
|
|
33
|
+
qsKey: {
|
|
34
|
+
bbox: 'bbox',
|
|
35
|
+
bboxLatField: 'bboxLatField',
|
|
36
|
+
bboxLngField: 'bboxLngField',
|
|
37
|
+
query: 'query',
|
|
38
|
+
match: 'match',
|
|
39
|
+
skip: 'skip',
|
|
40
|
+
page: 'page',
|
|
41
|
+
limit: 'limit',
|
|
42
|
+
sort: 'sort',
|
|
43
|
+
fields: 'fields',
|
|
44
|
+
lang: 'lang'
|
|
45
|
+
},
|
|
46
|
+
paramsCharMap: {},
|
|
47
|
+
logRoutes: false,
|
|
48
|
+
siteInfo: {
|
|
49
|
+
title: 'My Website',
|
|
50
|
+
orgName: 'My Organization'
|
|
51
|
+
},
|
|
52
|
+
cors: {},
|
|
53
|
+
compress: {},
|
|
54
|
+
helmet: {},
|
|
55
|
+
rateLimit: {},
|
|
56
|
+
multipart: {
|
|
57
|
+
attachFieldsToBody: true,
|
|
58
|
+
limits: {
|
|
59
|
+
parts: 100,
|
|
60
|
+
fileSize: 10485760
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
noIcon: true,
|
|
64
|
+
underPressure: false,
|
|
65
|
+
forwardOpts: {
|
|
66
|
+
disableRequestLogging: true
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
this.escapeChars = {
|
|
70
|
+
'<': '<',
|
|
71
|
+
'>': '>',
|
|
72
|
+
'"': '"',
|
|
73
|
+
"'": '''
|
|
74
|
+
}
|
|
75
|
+
this.qs = {
|
|
76
|
+
parse: (item) => {
|
|
77
|
+
return queryString.parse(item, {
|
|
78
|
+
parseBooleans: true,
|
|
79
|
+
parseNumbers: true
|
|
80
|
+
})
|
|
81
|
+
},
|
|
82
|
+
parseUrl: queryString.parseUrl,
|
|
83
|
+
stringify: queryString.stringify,
|
|
84
|
+
stringifyUrl: queryString.stringifyUrl
|
|
85
|
+
}
|
|
86
|
+
this.hookTypes = ['onRequest', 'onResponse', 'preParsing', 'preValidation', 'preHandler',
|
|
87
|
+
'preSerialization', 'onSend', 'onTimeout', 'onError']
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
init = async () => {
|
|
91
|
+
if (this.config.home === '/') this.config.home = false
|
|
92
|
+
await collectRoutePathHandlers.call(this)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
start = async () => {
|
|
96
|
+
const { generateId, runHook } = this.app.bajo
|
|
97
|
+
const cfg = this.getConfig()
|
|
98
|
+
cfg.factory.loggerInstance = this.app.bajoLogger.instance.child(
|
|
99
|
+
{},
|
|
100
|
+
{ msgPrefix: '[waibu] ' }
|
|
101
|
+
)
|
|
102
|
+
cfg.factory.genReqId = req => generateId()
|
|
103
|
+
cfg.factory.disableRequestLogging = true
|
|
104
|
+
cfg.factory.querystringParser = str => this.qs.parse(str)
|
|
105
|
+
|
|
106
|
+
const instance = fastify(cfg.factory)
|
|
107
|
+
instance.decorateRequest('lang', null)
|
|
108
|
+
instance.decorateRequest('t', () => {})
|
|
109
|
+
instance.decorateRequest('format', () => {})
|
|
110
|
+
instance.decorateRequest('langDetector', null)
|
|
111
|
+
instance.decorateRequest('site', null)
|
|
112
|
+
instance.decorateRequest('ns', null)
|
|
113
|
+
this.instance = instance
|
|
114
|
+
this.routes = this.routes || []
|
|
115
|
+
await runHook('waibu:afterCreateContext', instance)
|
|
116
|
+
await instance.register(sensible)
|
|
117
|
+
if (cfg.underPressure) await instance.register(underPressure)
|
|
118
|
+
if (cfg.noIcon) await instance.register(noIcon)
|
|
119
|
+
await handleRedirect.call(this, instance)
|
|
120
|
+
await handleForward.call(this, instance)
|
|
121
|
+
await appHook.call(this)
|
|
122
|
+
await routeHook.call(this, this.name)
|
|
123
|
+
await boot.call(this)
|
|
124
|
+
await instance.listen(cfg.server)
|
|
125
|
+
if (cfg.logRoutes) logRoutes.call(this)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
exit = async () => {
|
|
129
|
+
this.instance.close()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
escape = (text) => {
|
|
133
|
+
const { forOwn } = this.app.bajo.lib._
|
|
134
|
+
forOwn(this.escapeChars, (v, k) => {
|
|
135
|
+
text = text.replaceAll(k, v)
|
|
136
|
+
})
|
|
137
|
+
return text
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
fetch = async (url, opts = {}, extra = {}) => {
|
|
141
|
+
const { fetch } = this.app.bajoExtra
|
|
142
|
+
extra.rawResponse = true
|
|
143
|
+
|
|
144
|
+
url = this.routePath(url, { guessHost: true })
|
|
145
|
+
const resp = await fetch(url, opts, extra)
|
|
146
|
+
const result = await resp.json()
|
|
147
|
+
if (!resp.ok) {
|
|
148
|
+
throw this.error(result.message, {
|
|
149
|
+
statusCode: resp.status,
|
|
150
|
+
success: false
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
return result
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
getIp = (req) => {
|
|
157
|
+
let fwd = req.headers['x-forwarded-for'] ?? ''
|
|
158
|
+
if (!Array.isArray(fwd)) fwd = fwd.split(',').map(ip => ip.trim())
|
|
159
|
+
return fwd[0] ?? req.ip
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
getPluginByPrefix = (prefix) => {
|
|
163
|
+
const { get } = this.app.bajo.lib._
|
|
164
|
+
let plugin
|
|
165
|
+
for (const p of this.app.bajo.pluginNames) {
|
|
166
|
+
if (get(this, `app.${p}.config.waibu.prefix`) === prefix) {
|
|
167
|
+
plugin = this.app[p]
|
|
168
|
+
break
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return plugin
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
getPluginPrefix = (base, webApp = 'waibuMpa') => {
|
|
175
|
+
const { get, trim } = this.app.bajo.lib._
|
|
176
|
+
let prefix = get(this, `app.${base}.config.waibu.prefix`, this.app[base].alias)
|
|
177
|
+
if (base === 'main') {
|
|
178
|
+
const cfg = this.app[webApp].config
|
|
179
|
+
if (cfg.mountMainAsRoot) prefix = ''
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return trim(prefix, '/')
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
getRoutes = (grouped, lite) => {
|
|
186
|
+
const { groupBy, orderBy, mapValues, map, pick } = this.app.bajo.lib._
|
|
187
|
+
const all = this.routes
|
|
188
|
+
let routes
|
|
189
|
+
if (grouped) {
|
|
190
|
+
const group = groupBy(orderBy(all, ['url', 'method']), 'url')
|
|
191
|
+
routes = lite ? mapValues(group, (v, k) => map(v, 'method')) : group
|
|
192
|
+
} else if (lite) routes = map(all, a => pick(a, ['url', 'method']))
|
|
193
|
+
else routes = all
|
|
194
|
+
return routes
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
getUploadedFiles = async (reqId, fileUrl, returnDir) => {
|
|
198
|
+
const { getPluginDataDir, resolvePath } = this.app.bajo
|
|
199
|
+
const { fastGlob } = this.app.bajo.lib
|
|
200
|
+
const dir = `${getPluginDataDir(this.name)}/upload/${reqId}`
|
|
201
|
+
const result = await fastGlob(`${dir}/*`)
|
|
202
|
+
if (!fileUrl) return returnDir ? { dir, files: result } : result
|
|
203
|
+
const files = result.map(f => resolvePath(f, true))
|
|
204
|
+
return returnDir ? { dir, files } : files
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
isIntlPath = (ns) => {
|
|
208
|
+
const { get } = this.app.bajo.lib._
|
|
209
|
+
return get(this.app[ns], 'config.intl.detectors', []).includes('path')
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
notFound = (name, options) => {
|
|
213
|
+
throw this.error('_notFound', { path: name })
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
parseFilter = (req) => {
|
|
217
|
+
const result = {}
|
|
218
|
+
const items = Object.keys(this.config.qsKey)
|
|
219
|
+
for (const item of items) {
|
|
220
|
+
result[item] = req.query[this.config.qsKey[item]]
|
|
221
|
+
}
|
|
222
|
+
return result
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
routeDir = (ns, base) => {
|
|
226
|
+
const { get } = this.app.bajo.lib._
|
|
227
|
+
if (!base) base = ns
|
|
228
|
+
const cfg = this.app[base].config
|
|
229
|
+
const prefix = get(cfg, 'waibu.prefix', this.app[base].alias)
|
|
230
|
+
const dir = prefix === '' ? '' : `/${prefix}`
|
|
231
|
+
if (!ns) return dir
|
|
232
|
+
const cfgMpa = get(this, 'app.waibuMpa.config')
|
|
233
|
+
if (ns === this.app.bajo.mainNs && cfgMpa.mountMainAsRoot) return ''
|
|
234
|
+
if (ns === base) return dir
|
|
235
|
+
return dir + `/${get(this.app[ns].config, 'waibu.prefix', this.app[ns].alias)}`
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
routePath = (name = '', { query = {}, base = 'waibuMpa', params = {}, guessHost } = {}) => {
|
|
239
|
+
const { defaultsDeep, getPlugin } = this.app.bajo
|
|
240
|
+
const { isEmpty, get, trimEnd, trimStart } = this.app.bajo.lib._
|
|
241
|
+
const { breakNsPath } = this.app.bajo
|
|
242
|
+
|
|
243
|
+
const plugin = getPlugin(base)
|
|
244
|
+
const cfg = plugin.config ?? {}
|
|
245
|
+
let info = {}
|
|
246
|
+
if (name.startsWith('mailto:') || name.startsWith('tel:')) return name
|
|
247
|
+
if (['%', '.', '/', '?', '#'].includes(name[0]) || name.slice(1, 2) === ':') info.path = name
|
|
248
|
+
else if (['~'].includes(name[0])) info.path = name.slice(1)
|
|
249
|
+
else {
|
|
250
|
+
info = breakNsPath(name)
|
|
251
|
+
}
|
|
252
|
+
if (info.path.slice(0, 2) === './') info.path = info.path.slice(2)
|
|
253
|
+
if (this.routePathHandlers[info.subNs]) return this.routePathHandlers[info.subNs](name)
|
|
254
|
+
if (info.path.includes('//')) return info.path
|
|
255
|
+
|
|
256
|
+
info.path = info.path.split('/').map(p => {
|
|
257
|
+
return p[0] === ':' && params[p.slice(1)] ? params[p.slice(1)] : p
|
|
258
|
+
}).join('/')
|
|
259
|
+
let url = info.path
|
|
260
|
+
const langDetector = get(cfg, 'intl.detectors', [])
|
|
261
|
+
if (info.ns) url = trimEnd(langDetector.includes('path') ? `/${params.lang ?? ''}${this.routeDir(info.ns)}${info.path}` : `${this.routeDir(info.ns)}${info.path}`, '/')
|
|
262
|
+
info.qs = defaultsDeep({}, query, info.qs)
|
|
263
|
+
if (!isEmpty(info.qs)) url += '?' + this.qs.stringify(info.qs)
|
|
264
|
+
if (!url.startsWith('http') && guessHost) url = `http://${this.config.server.host}:${this.config.server.port}/${trimStart(url, '/')}`
|
|
265
|
+
return url
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
sendMail = async (tpl, { to, cc, bcc, from, subject, data = {}, conn, options = {} }) => {
|
|
269
|
+
if (!this.app.masohiMail) return
|
|
270
|
+
const { get, isString } = this.app.bajo.lib._
|
|
271
|
+
const { generateId } = this.app.bajo
|
|
272
|
+
const { render } = this.app.bajoTemplate
|
|
273
|
+
if (isString(tpl)) tpl = [tpl]
|
|
274
|
+
const locals = await buildLocals.call(this, { tpl, params: data, opts: options })
|
|
275
|
+
const opts = {
|
|
276
|
+
lang: get(options, 'req.lang'),
|
|
277
|
+
groupId: get(options, 'req.id', generateId())
|
|
278
|
+
}
|
|
279
|
+
const message = await render(tpl[0], locals, opts)
|
|
280
|
+
if (tpl[1]) opts.messageText = await render(tpl[1], locals, opts)
|
|
281
|
+
await this.app.masohi.send({ to, cc, bcc, from, subject, message, conn, options: opts })
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
unescapeBlock = (content, start, end, startReplacer, endReplacer) => {
|
|
285
|
+
const { extractText } = this.app.bajo
|
|
286
|
+
const { result } = extractText(content, start, end)
|
|
287
|
+
if (result.length === 0) return content
|
|
288
|
+
const unescaped = this.unescape(result)
|
|
289
|
+
const token = `${start}${result}${end}`
|
|
290
|
+
const replacer = `${startReplacer}${unescaped}${endReplacer}`
|
|
291
|
+
const block = content.replaceAll(token, replacer)
|
|
292
|
+
return this.unescapeBlock(block, start, end, startReplacer, endReplacer)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
unescape = (text) => {
|
|
296
|
+
const { forOwn, invert } = this.app.bajo.lib._
|
|
297
|
+
const mapping = invert(this.escapeChars)
|
|
298
|
+
forOwn(mapping, (v, k) => {
|
|
299
|
+
text = text.replaceAll(k, v)
|
|
300
|
+
})
|
|
301
|
+
return text
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export default factory
|
package/plugin/.alias
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
w
|
package/plugin/.dependencies
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
bajo-logger
|
package/plugin/config.json
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"server": {
|
|
3
|
-
"host": "localhost",
|
|
4
|
-
"port": 7771
|
|
5
|
-
},
|
|
6
|
-
"factory": {
|
|
7
|
-
"trustProxy": true,
|
|
8
|
-
"bodyLimit": 10485760
|
|
9
|
-
},
|
|
10
|
-
"prefixVirtual": "~",
|
|
11
|
-
"qsKey": {
|
|
12
|
-
"bbox": "bbox",
|
|
13
|
-
"bboxLatField": "bboxLatField",
|
|
14
|
-
"bboxLngField": "bboxLngField",
|
|
15
|
-
"query": "query",
|
|
16
|
-
"match": "match",
|
|
17
|
-
"skip": "skip",
|
|
18
|
-
"page": "page",
|
|
19
|
-
"limit": "limit",
|
|
20
|
-
"sort": "sort",
|
|
21
|
-
"fields": "fields",
|
|
22
|
-
"lang": "lang"
|
|
23
|
-
},
|
|
24
|
-
"paramsCharMap": {},
|
|
25
|
-
"logRoutes": false,
|
|
26
|
-
"siteInfo": {
|
|
27
|
-
"title": "My Website",
|
|
28
|
-
"orgName": "My Organization"
|
|
29
|
-
},
|
|
30
|
-
"cors": {},
|
|
31
|
-
"compress": {},
|
|
32
|
-
"helmet": {},
|
|
33
|
-
"rateLimit": {},
|
|
34
|
-
"multipart": {
|
|
35
|
-
"attachFieldsToBody": true,
|
|
36
|
-
"limits": {
|
|
37
|
-
"parts": 100,
|
|
38
|
-
"fileSize": 10485760
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
"noIcon": true,
|
|
42
|
-
"underPressure": false,
|
|
43
|
-
"forwardOpts": {
|
|
44
|
-
"disableRequestLogging": true
|
|
45
|
-
}
|
|
46
|
-
}
|
package/plugin/exit.js
DELETED
package/plugin/init.js
DELETED
package/plugin/method/escape.js
DELETED
package/plugin/method/fetch.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
async function fetch (url, opts = {}, extra = {}) {
|
|
2
|
-
const { getPlugin } = this.app.bajo
|
|
3
|
-
getPlugin('bajoExtra')
|
|
4
|
-
const { fetch } = this.app.bajoExtra
|
|
5
|
-
extra.rawResponse = true
|
|
6
|
-
|
|
7
|
-
url = this.routePath(url, { guessHost: true })
|
|
8
|
-
const resp = await fetch(url, opts, extra)
|
|
9
|
-
const result = await resp.json()
|
|
10
|
-
if (!resp.ok) {
|
|
11
|
-
throw this.error(result.message, {
|
|
12
|
-
statusCode: resp.status,
|
|
13
|
-
success: false
|
|
14
|
-
})
|
|
15
|
-
}
|
|
16
|
-
return result
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export default fetch
|
package/plugin/method/get-ip.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
function getPluginByPrefix (prefix) {
|
|
2
|
-
const { get } = this.app.bajo.lib._
|
|
3
|
-
let plugin
|
|
4
|
-
for (const p of this.app.bajo.pluginNames) {
|
|
5
|
-
if (get(this, `app.${p}.config.waibu.prefix`) === prefix) {
|
|
6
|
-
plugin = this.app[p]
|
|
7
|
-
break
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
return plugin
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export default getPluginByPrefix
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
function getPluginPrefix (base, webApp = 'waibuMpa') {
|
|
2
|
-
const { get, trim } = this.app.bajo.lib._
|
|
3
|
-
let prefix = get(this, `app.${base}.config.waibu.prefix`, this.app[base].alias)
|
|
4
|
-
if (base === 'main') {
|
|
5
|
-
const cfg = this.app[webApp].config
|
|
6
|
-
if (cfg.mountMainAsRoot) prefix = ''
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
return trim(prefix, '/')
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export default getPluginPrefix
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
function getRoutes (grouped, lite) {
|
|
2
|
-
const { groupBy, orderBy, mapValues, map, pick } = this.app.bajo.lib._
|
|
3
|
-
const all = this.routes
|
|
4
|
-
let routes
|
|
5
|
-
if (grouped) {
|
|
6
|
-
const group = groupBy(orderBy(all, ['url', 'method']), 'url')
|
|
7
|
-
routes = lite ? mapValues(group, (v, k) => map(v, 'method')) : group
|
|
8
|
-
} else if (lite) routes = map(all, a => pick(a, ['url', 'method']))
|
|
9
|
-
else routes = all
|
|
10
|
-
return routes
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export default getRoutes
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
async function getUploadedFiles (reqId, fileUrl, returnDir) {
|
|
2
|
-
const { getPluginDataDir, resolvePath } = this.app.bajo
|
|
3
|
-
const { fastGlob } = this.app.bajo.lib
|
|
4
|
-
const dir = `${getPluginDataDir(this.name)}/upload/${reqId}`
|
|
5
|
-
const result = await fastGlob(`${dir}/*`)
|
|
6
|
-
if (!fileUrl) return returnDir ? { dir, files: result } : result
|
|
7
|
-
const files = result.map(f => resolvePath(f, true))
|
|
8
|
-
return returnDir ? { dir, files } : files
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export default getUploadedFiles
|
package/plugin/method/qs.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import queryString from 'query-string'
|
|
2
|
-
|
|
3
|
-
function parse (item) {
|
|
4
|
-
return queryString.parse(item, {
|
|
5
|
-
parseBooleans: true,
|
|
6
|
-
parseNumbers: true
|
|
7
|
-
})
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const qs = {
|
|
11
|
-
parse,
|
|
12
|
-
parseUrl: queryString.parseUrl,
|
|
13
|
-
stringify: queryString.stringify,
|
|
14
|
-
stringifyUrl: queryString.stringifyUrl
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export default qs
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
function routeDir (ns, base) {
|
|
2
|
-
const { get } = this.app.bajo.lib._
|
|
3
|
-
if (!base) base = ns
|
|
4
|
-
const cfg = this.app[base].config
|
|
5
|
-
const prefix = get(cfg, 'waibu.prefix', this.app[base].alias)
|
|
6
|
-
const dir = prefix === '' ? '' : `/${prefix}`
|
|
7
|
-
if (!ns) return dir
|
|
8
|
-
const cfgMpa = get(this, 'app.waibuMpa.config')
|
|
9
|
-
if (ns === this.app.bajo.mainNs && cfgMpa.mountMainAsRoot) return ''
|
|
10
|
-
if (ns === base) return dir
|
|
11
|
-
return dir + `/${get(this.app[ns].config, 'waibu.prefix', this.app[ns].alias)}`
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export default routeDir
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
function routePath (name = '', { query = {}, base = 'waibuMpa', params = {}, guessHost } = {}) {
|
|
2
|
-
const { defaultsDeep, getPlugin } = this.app.bajo
|
|
3
|
-
const { isEmpty, get, trimEnd, trimStart } = this.app.bajo.lib._
|
|
4
|
-
const { breakNsPath } = this.app.bajo
|
|
5
|
-
|
|
6
|
-
const plugin = getPlugin(base)
|
|
7
|
-
const cfg = plugin.config ?? {}
|
|
8
|
-
let info = {}
|
|
9
|
-
if (name.startsWith('mailto:') || name.startsWith('tel:')) return name
|
|
10
|
-
if (['%', '.', '/', '?', '#'].includes(name[0]) || name.slice(1, 2) === ':') info.path = name
|
|
11
|
-
else if (['~'].includes(name[0])) info.path = name.slice(1)
|
|
12
|
-
else {
|
|
13
|
-
info = breakNsPath(name)
|
|
14
|
-
}
|
|
15
|
-
if (info.path.slice(0, 2) === './') info.path = info.path.slice(2)
|
|
16
|
-
if (this.routePathHandlers[info.subNs]) return this.routePathHandlers[info.subNs](name)
|
|
17
|
-
if (info.path.includes('//')) return info.path
|
|
18
|
-
|
|
19
|
-
info.path = info.path.split('/').map(p => {
|
|
20
|
-
return p[0] === ':' && params[p.slice(1)] ? params[p.slice(1)] : p
|
|
21
|
-
}).join('/')
|
|
22
|
-
let url = info.path
|
|
23
|
-
const langDetector = get(cfg, 'intl.detectors', [])
|
|
24
|
-
if (info.ns) url = trimEnd(langDetector.includes('path') ? `/${params.lang ?? ''}${this.routeDir(info.ns)}${info.path}` : `${this.routeDir(info.ns)}${info.path}`, '/')
|
|
25
|
-
info.qs = defaultsDeep({}, query, info.qs)
|
|
26
|
-
if (!isEmpty(info.qs)) url += '?' + this.qs.stringify(info.qs)
|
|
27
|
-
if (!url.startsWith('http') && guessHost) url = `http://${this.config.server.host}:${this.config.server.port}/${trimStart(url, '/')}`
|
|
28
|
-
return url
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export default routePath
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import buildLocals from '../../lib/build-locals.js'
|
|
2
|
-
|
|
3
|
-
async function sendMail (tpl, { to, cc, bcc, from, subject, data = {}, conn, options = {} }) {
|
|
4
|
-
if (!this.app.masohiMail) return
|
|
5
|
-
const { get, isString } = this.app.bajo.lib._
|
|
6
|
-
const { generateId } = this.app.bajo
|
|
7
|
-
const { render } = this.app.bajoTemplate
|
|
8
|
-
if (isString(tpl)) tpl = [tpl]
|
|
9
|
-
const locals = await buildLocals.call(this, { tpl, params: data, opts: options })
|
|
10
|
-
const opts = {
|
|
11
|
-
lang: get(options, 'req.lang'),
|
|
12
|
-
groupId: get(options, 'req.id', generateId())
|
|
13
|
-
}
|
|
14
|
-
const message = await render(tpl[0], locals, opts)
|
|
15
|
-
if (tpl[1]) opts.messageText = await render(tpl[1], locals, opts)
|
|
16
|
-
await this.app.masohi.send({ to, cc, bcc, from, subject, message, conn, options: opts })
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export default sendMail
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
function unescapeBlock (content, start, end, startReplacer, endReplacer) {
|
|
2
|
-
const { extractText } = this.app.bajo
|
|
3
|
-
const { result } = extractText(content, start, end)
|
|
4
|
-
if (result.length === 0) return content
|
|
5
|
-
const unescaped = this.unescape(result)
|
|
6
|
-
const token = `${start}${result}${end}`
|
|
7
|
-
const replacer = `${startReplacer}${unescaped}${endReplacer}`
|
|
8
|
-
const block = content.replaceAll(token, replacer)
|
|
9
|
-
return unescapeBlock.call(this, block, start, end, startReplacer, endReplacer)
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export default unescapeBlock
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import escapeChars from './escape-chars.js'
|
|
2
|
-
|
|
3
|
-
function unescape (text) {
|
|
4
|
-
const { forOwn, invert } = this.app.bajo.lib._
|
|
5
|
-
const mapping = invert(escapeChars)
|
|
6
|
-
forOwn(mapping, (v, k) => {
|
|
7
|
-
text = text.replaceAll(k, v)
|
|
8
|
-
})
|
|
9
|
-
return text
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export default unescape
|
package/plugin/start.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import fastify from 'fastify'
|
|
2
|
-
import appHook from '../lib/app-hook.js'
|
|
3
|
-
import routeHook from '../lib/webapp-scope/route-hook.js'
|
|
4
|
-
import logRoutes from '../lib/log-routes.js'
|
|
5
|
-
import { boot } from '../lib/app.js'
|
|
6
|
-
import sensible from '@fastify/sensible'
|
|
7
|
-
import noIcon from 'fastify-no-icon'
|
|
8
|
-
import underPressure from '@fastify/under-pressure'
|
|
9
|
-
import handleForward from '../lib/handle-forward.js'
|
|
10
|
-
import handleRedirect from '../lib/handle-redirect.js'
|
|
11
|
-
|
|
12
|
-
async function start () {
|
|
13
|
-
const { generateId, runHook } = this.app.bajo
|
|
14
|
-
const cfg = this.getConfig()
|
|
15
|
-
cfg.factory.loggerInstance = this.app.bajoLogger.instance.child(
|
|
16
|
-
{},
|
|
17
|
-
{ msgPrefix: '[waibu] ' }
|
|
18
|
-
)
|
|
19
|
-
cfg.factory.genReqId = req => generateId()
|
|
20
|
-
cfg.factory.disableRequestLogging = true
|
|
21
|
-
cfg.factory.querystringParser = str => this.qs.parse(str)
|
|
22
|
-
|
|
23
|
-
const instance = fastify(cfg.factory)
|
|
24
|
-
instance.decorateRequest('lang', null)
|
|
25
|
-
instance.decorateRequest('t', () => {})
|
|
26
|
-
instance.decorateRequest('format', () => {})
|
|
27
|
-
instance.decorateRequest('langDetector', null)
|
|
28
|
-
instance.decorateRequest('site', null)
|
|
29
|
-
instance.decorateRequest('ns', null)
|
|
30
|
-
this.instance = instance
|
|
31
|
-
this.routes = this.routes || []
|
|
32
|
-
await runHook('waibu:afterCreateContext', instance)
|
|
33
|
-
await instance.register(sensible)
|
|
34
|
-
if (cfg.underPressure) await instance.register(underPressure)
|
|
35
|
-
if (cfg.noIcon) await instance.register(noIcon)
|
|
36
|
-
await handleRedirect.call(this, instance)
|
|
37
|
-
await handleForward.call(this, instance)
|
|
38
|
-
await appHook.call(this)
|
|
39
|
-
await routeHook.call(this, this.name)
|
|
40
|
-
await boot.call(this)
|
|
41
|
-
await instance.listen(cfg.server)
|
|
42
|
-
if (cfg.logRoutes) logRoutes.call(this)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export default start
|