waibu 1.1.0 → 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.
@@ -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]
@@ -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
- this.log[method]('> %s:%s with a %d-status took %dms', req.method, req.url, reply.statusCode,
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
  }
@@ -6,7 +6,8 @@ async function errorHandler (ctx, extHandler) {
6
6
  reply.send(err.print)
7
7
  return
8
8
  }
9
- if (me.app.bajo.config.log.level === 'trace' && !['_notFound', '_redirect'].includes(err.message.toLowerCase())) console.error(err)
9
+ if (me.app.bajo.config.env !== 'prod' && me.app.bajo.config.log.level === 'trace' &&
10
+ !['_notFound', '_redirect'].includes(err.message.toLowerCase())) console.error(err)
10
11
  if (extHandler) return await extHandler.call(me, err, req, reply, ctx)
11
12
  if (err.message === '_notFound' || err.statusCode === 404) {
12
13
  reply.code(err.statusCode)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waibu",
3
- "version": "1.1.0",
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
+ '<': '&lt;',
71
+ '>': '&gt;',
72
+ '"': '&quot;',
73
+ "'": '&apos;'
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
@@ -1 +0,0 @@
1
- bajo-logger
@@ -1,46 +0,0 @@
1
- {
2
- "server": {
3
- "host": "localhost",
4
- "port": 7777
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
@@ -1,3 +0,0 @@
1
- export default async function () {
2
- this.instance.close()
3
- }
package/plugin/init.js DELETED
@@ -1,8 +0,0 @@
1
- import collectRoutePathHandlers from '../lib/collect-route-path-handlers.js'
2
-
3
- async function init () {
4
- if (this.config.home === '/') this.config.home = false
5
- await collectRoutePathHandlers.call(this)
6
- }
7
-
8
- export default init
@@ -1,6 +0,0 @@
1
- export default {
2
- '<': '&lt;',
3
- '>': '&gt;',
4
- '"': '&quot;',
5
- "'": '&apos;'
6
- }
@@ -1,11 +0,0 @@
1
- import escapeChars from './escape-chars.js'
2
-
3
- function escape (text) {
4
- const { forOwn } = this.app.bajo.lib._
5
- forOwn(escapeChars, (v, k) => {
6
- text = text.replaceAll(k, v)
7
- })
8
- return text
9
- }
10
-
11
- export default escape
@@ -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
@@ -1,7 +0,0 @@
1
- function getIp (req) {
2
- let fwd = req.headers['x-forwarded-for'] ?? ''
3
- if (!Array.isArray(fwd)) fwd = fwd.split(',').map(ip => ip.trim())
4
- return fwd[0] ?? req.ip
5
- }
6
-
7
- export default getIp
@@ -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
@@ -1,4 +0,0 @@
1
- const hookTypes = ['onRequest', 'onResponse', 'preParsing', 'preValidation', 'preHandler',
2
- 'preSerialization', 'onSend', 'onTimeout', 'onError']
3
-
4
- export default hookTypes
@@ -1,6 +0,0 @@
1
- function isIntlPath (ns) {
2
- const { get } = this.app.bajo.lib._
3
- return get(this.app[ns], 'config.intl.detectors', []).includes('path')
4
- }
5
-
6
- export default isIntlPath
@@ -1,5 +0,0 @@
1
- function notFound (name, options) {
2
- throw this.error('_notFound', { path: name })
3
- }
4
-
5
- export default notFound
@@ -1,10 +0,0 @@
1
- function parseFilter (req) {
2
- const result = {}
3
- const items = Object.keys(this.config.qsKey)
4
- for (const item of items) {
5
- result[item] = req.query[this.config.qsKey[item]]
6
- }
7
- return result
8
- }
9
-
10
- export default parseFilter
@@ -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