waibu-mpa 2.20.2 → 2.21.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/extend/bajoTemplate/template/403.html +14 -0
- package/extend/waibuMpa/route/wmpa.js +1 -1
- package/index.js +72 -0
- package/lib/decorate.js +1 -70
- package/lib/error-handler.js +3 -5
- package/lib/not-found-handler.js +7 -13
- package/package.json +1 -1
- package/wiki/CHANGES.md +12 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
5
|
+
<link rel="stylesheet" href="<%= _routePath('waibuMpa.virtual:/purecss/pure-min.css') %>" />
|
|
6
|
+
<link rel="stylesheet" href="<%= _routePath('waibuMpa.asset:/css/default.css') %>" />
|
|
7
|
+
<title><%= _t('accessDenied') %></title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="container-center">
|
|
11
|
+
<p><%= _t('accessDenied') %></p>
|
|
12
|
+
</div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -22,7 +22,7 @@ const wmpa = {
|
|
|
22
22
|
api = {
|
|
23
23
|
prefix: this.app.waibuRestApi ? this.app.waibuRestApi.config.waibu.prefix : '',
|
|
24
24
|
ext: this.app.waibuRestApi ? (this.app.waibuRestApi.config.format.asExt ? '.json' : '') : '',
|
|
25
|
-
headerKey: this.app.waibuRestApi ? this.app.sumba.
|
|
25
|
+
headerKey: this.app.waibuRestApi ? this.app.sumba._getAuthSetting('jwt', 'waibuRestApi').headerKey : '',
|
|
26
26
|
dataKey: this.app.waibuRestApi ? this.app.waibuRestApi.config.responseKey.data : '',
|
|
27
27
|
rateLimitDelay: 2000,
|
|
28
28
|
rateLimitRetry: 2
|
package/index.js
CHANGED
|
@@ -629,6 +629,78 @@ async function factory (pkgName) {
|
|
|
629
629
|
if (!iconset) return iconset
|
|
630
630
|
return nameOnly ? iconset.name : iconset
|
|
631
631
|
}
|
|
632
|
+
|
|
633
|
+
renderView = async ({ tpl, params = {}, opts = {}, reply } = {}) => {
|
|
634
|
+
const { importModule } = this.app.bajo
|
|
635
|
+
const buildLocals = await importModule('waibu:/lib/build-locals.js')
|
|
636
|
+
const { importPkg } = this.app.bajo
|
|
637
|
+
const { isString, cloneDeep, isEmpty, get } = this.app.lib._
|
|
638
|
+
const { get: getCache, set: setCache } = this.app.bajoCache ?? {}
|
|
639
|
+
const { outmatch } = this.app.lib
|
|
640
|
+
const { routePath } = this.app.waibu
|
|
641
|
+
|
|
642
|
+
async function isCacheable (req, cachedUrls) {
|
|
643
|
+
const { hash } = this.app.bajoExtra
|
|
644
|
+
const { get, omit, isFunction } = this.app.lib._
|
|
645
|
+
const ns = get(req, 'routeOptions.config.ns')
|
|
646
|
+
let cache = get(req, 'routeOptions.config.cache', omit(this.config.page.cache, ['urls']))
|
|
647
|
+
cache.methods = cache.methods ?? ['GET']
|
|
648
|
+
if (!ns || (!this.app.bajoCache) || (!req.site) || !cache.methods.includes(req.method)) return { ttl: 0 }
|
|
649
|
+
if (isFunction(cache)) {
|
|
650
|
+
cache = await cache.call(this.app[ns], req, cachedUrls)
|
|
651
|
+
}
|
|
652
|
+
const url = req.url.split('?')[0].split('#')[0]
|
|
653
|
+
for (const item of cachedUrls) {
|
|
654
|
+
if (item.isMatch(url)) cache.ttlDur = item.ttlDur ?? cache.ttlDur
|
|
655
|
+
}
|
|
656
|
+
const key = `waibu-mpa-page-${req.site.id}-${cache.key ?? (await hash(req.url))}`
|
|
657
|
+
return { key, ttl: cache.ttlDur }
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
const mime = await importPkg('waibu:mime')
|
|
661
|
+
const cfg = this.config
|
|
662
|
+
const cachedUrls = cloneDeep(this.config.page.cache.urls).map(item => {
|
|
663
|
+
if (isString(item)) item = { url: item }
|
|
664
|
+
item.url = routePath(item.url)
|
|
665
|
+
item.isMatch = outmatch(item.url)
|
|
666
|
+
return item
|
|
667
|
+
})
|
|
668
|
+
let ext = path.extname(tpl)
|
|
669
|
+
if (ext === '.md') ext = '.html'
|
|
670
|
+
let mimeType = isEmpty(ext) ? 'text/html' : mime.getType(ext)
|
|
671
|
+
mimeType += `; charset=${cfg.page.charset}`
|
|
672
|
+
reply.header('Content-Type', mimeType)
|
|
673
|
+
reply.header('Content-Language', reply.request.lang)
|
|
674
|
+
reply.header('X-Req-Id', reply.request.id)
|
|
675
|
+
opts.req = reply.request
|
|
676
|
+
opts.reply = reply
|
|
677
|
+
for (const item of ['theme', 'iconset']) {
|
|
678
|
+
if (!reply.request[item]) reply.request[item] = this[item + 's'][0].name
|
|
679
|
+
if (this[item + 's'].length === 1) reply.request[item] = this[item + 's'][0].name
|
|
680
|
+
}
|
|
681
|
+
const { key, ttl } = await isCacheable.call(this, reply.request, cachedUrls)
|
|
682
|
+
if (ttl > 0) {
|
|
683
|
+
const cached = await getCache({ key })
|
|
684
|
+
if (cached) {
|
|
685
|
+
reply.header('X-Wmpa-Cached', true)
|
|
686
|
+
return cached
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
const locals = await buildLocals.call(this, { tpl, params, opts })
|
|
690
|
+
if (!get(reply.request, 'routeOptions.config.noCacheReq')) {
|
|
691
|
+
await this.app.cache.save(`${this.ns}.req:/${opts.req.id}-locals.json`, locals, this.config.reqTtlDur)
|
|
692
|
+
}
|
|
693
|
+
const result = await this.render(tpl, locals, opts)
|
|
694
|
+
if (ttl > 0) await setCache({ key, value: result, ttl })
|
|
695
|
+
if (reply.request.session) {
|
|
696
|
+
ext = path.extname(reply.request.url)
|
|
697
|
+
if (isEmpty(ext) || ['.html'].includes(ext)) {
|
|
698
|
+
if (reply.request.session.prevUrl !== reply.request.url) reply.request.session.prevUrl = reply.request.url
|
|
699
|
+
else reply.request.session.prevUrl = ''
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
return result
|
|
703
|
+
}
|
|
632
704
|
}
|
|
633
705
|
|
|
634
706
|
return WaibuMpa
|
package/lib/decorate.js
CHANGED
|
@@ -1,82 +1,13 @@
|
|
|
1
|
-
import path from 'path'
|
|
2
|
-
|
|
3
|
-
async function isCacheable (req, cachedUrls) {
|
|
4
|
-
const { hash } = this.app.bajoExtra
|
|
5
|
-
const { get, omit, isFunction } = this.app.lib._
|
|
6
|
-
const ns = get(req, 'routeOptions.config.ns')
|
|
7
|
-
let cache = get(req, 'routeOptions.config.cache', omit(this.config.page.cache, ['urls']))
|
|
8
|
-
cache.methods = cache.methods ?? ['GET']
|
|
9
|
-
if (!ns || (!this.app.bajoCache) || (!req.site) || !cache.methods.includes(req.method)) return { ttl: 0 }
|
|
10
|
-
if (isFunction(cache)) {
|
|
11
|
-
cache = await cache.call(this.app[ns], req, cachedUrls)
|
|
12
|
-
}
|
|
13
|
-
const url = req.url.split('?')[0].split('#')[0]
|
|
14
|
-
for (const item of cachedUrls) {
|
|
15
|
-
if (item.isMatch(url)) cache.ttlDur = item.ttlDur ?? cache.ttlDur
|
|
16
|
-
}
|
|
17
|
-
const key = `waibu-mpa-page-${req.site.id}-${cache.key ?? (await hash(req.url))}`
|
|
18
|
-
return { key, ttl: cache.ttlDur }
|
|
19
|
-
}
|
|
20
|
-
|
|
21
1
|
async function decorate () {
|
|
22
|
-
const { importModule } = this.app.bajo
|
|
23
|
-
const buildLocals = await importModule('waibu:/lib/build-locals.js')
|
|
24
|
-
const { importPkg } = this.app.bajo
|
|
25
|
-
const { isString, cloneDeep, isEmpty, get } = this.app.lib._
|
|
26
|
-
const { get: getCache, set: setCache } = this.app.bajoCache ?? {}
|
|
27
|
-
const { outmatch } = this.app.lib
|
|
28
|
-
const { routePath } = this.app.waibu
|
|
29
|
-
const mime = await importPkg('waibu:mime')
|
|
30
2
|
const cfg = this.config
|
|
31
3
|
const me = this
|
|
32
|
-
const cachedUrls = cloneDeep(this.config.page.cache.urls).map(item => {
|
|
33
|
-
if (isString(item)) item = { url: item }
|
|
34
|
-
item.url = routePath(item.url)
|
|
35
|
-
item.isMatch = outmatch(item.url)
|
|
36
|
-
return item
|
|
37
|
-
})
|
|
38
4
|
this.webAppCtx.decorateRequest('theme', cfg.theme.set)
|
|
39
5
|
this.webAppCtx.decorateRequest('iconset', cfg.iconset.set)
|
|
40
6
|
this.webAppCtx.decorateRequest('darkMode', cfg.darkMode.set)
|
|
41
7
|
this.webAppCtx.decorateRequest('referer', '')
|
|
42
8
|
this.webAppCtx.decorateReply('ctags', null)
|
|
43
9
|
this.webAppCtx.decorateReply('view', async function (tpl, params = {}, opts = {}) {
|
|
44
|
-
|
|
45
|
-
let ext = path.extname(tpl)
|
|
46
|
-
if (ext === '.md') ext = '.html'
|
|
47
|
-
let mimeType = isEmpty(ext) ? 'text/html' : mime.getType(ext)
|
|
48
|
-
mimeType += `; charset=${cfg.page.charset}`
|
|
49
|
-
this.header('Content-Type', mimeType)
|
|
50
|
-
this.header('Content-Language', this.request.lang)
|
|
51
|
-
this.header('X-Req-Id', this.request.id)
|
|
52
|
-
opts.req = this.request
|
|
53
|
-
opts.reply = this
|
|
54
|
-
for (const item of ['theme', 'iconset']) {
|
|
55
|
-
if (!this.request[item]) this.request[item] = me[item + 's'][0].name
|
|
56
|
-
if (me[item + 's'].length === 1) this.request[item] = me[item + 's'][0].name
|
|
57
|
-
}
|
|
58
|
-
const { key, ttl } = await isCacheable.call(me, this.request, cachedUrls)
|
|
59
|
-
if (ttl > 0) {
|
|
60
|
-
const cached = await getCache({ key })
|
|
61
|
-
if (cached) {
|
|
62
|
-
this.header('X-Wmpa-Cached', true)
|
|
63
|
-
return cached
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
const locals = await buildLocals.call(me, { tpl, params, opts })
|
|
67
|
-
if (!get(this.request, 'routeOptions.config.noCacheReq')) {
|
|
68
|
-
await me.app.cache.save(`${me.ns}.req:/${opts.req.id}-locals.json`, locals, me.config.reqTtlDur)
|
|
69
|
-
}
|
|
70
|
-
const result = await me.render(tpl, locals, opts)
|
|
71
|
-
if (ttl > 0) await setCache({ key, value: result, ttl })
|
|
72
|
-
if (this.request.session) {
|
|
73
|
-
ext = path.extname(this.request.url)
|
|
74
|
-
if (isEmpty(ext) || ['.html'].includes(ext)) {
|
|
75
|
-
if (this.request.session.prevUrl !== this.request.url) this.request.session.prevUrl = this.request.url
|
|
76
|
-
else this.request.session.prevUrl = ''
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return result
|
|
10
|
+
return await me.renderView({ reply: this, tpl, params, opts })
|
|
80
11
|
})
|
|
81
12
|
}
|
|
82
13
|
|
package/lib/error-handler.js
CHANGED
|
@@ -7,21 +7,19 @@ async function errorHandler (err, req, reply) {
|
|
|
7
7
|
reply.code(err.statusCode)
|
|
8
8
|
reply.header('Content-Type', `text/html; charset=${this.config.page.charset}`)
|
|
9
9
|
reply.header('Content-Language', req.lang)
|
|
10
|
-
|
|
11
10
|
if (err.message === '_notFound' || err.statusCode === 404) {
|
|
12
11
|
return await notFoundHandler.call(this, err, req, reply)
|
|
13
12
|
}
|
|
14
13
|
if (err.noContent) return ''
|
|
15
|
-
|
|
16
|
-
// let result
|
|
17
|
-
let tpl = `${ns}.template:/${err.statusCode ?? 500}.html`
|
|
14
|
+
let tpl = `${this.ns}.template:/${err.statusCode}.html`
|
|
18
15
|
try {
|
|
19
16
|
resolveTemplate(tpl)
|
|
20
17
|
} catch (err) {
|
|
21
18
|
tpl = `${this.ns}.template:/500.html`
|
|
22
19
|
}
|
|
23
20
|
try {
|
|
24
|
-
|
|
21
|
+
req.webApp = this.ns
|
|
22
|
+
return await this.renderView({ reply, tpl, params: { error: err }, opts: { noFlash: true } })
|
|
25
23
|
} catch (err) {
|
|
26
24
|
// only going here if something happened in reply.view
|
|
27
25
|
const content = fs.readFileSync(resolveTemplate(tpl).file, 'utf8')
|
package/lib/not-found-handler.js
CHANGED
|
@@ -1,24 +1,18 @@
|
|
|
1
1
|
async function notFoundHandler (err, req, reply) {
|
|
2
2
|
const welcome = req.url.split('?')[0] === '/'
|
|
3
|
-
const { fs } = this.app.lib
|
|
4
|
-
const { resolveTemplate, compile } = this.app.bajoTemplate
|
|
5
3
|
const msg = req.t('routeNotFound%s%s', req.url, req.method)
|
|
6
4
|
const error = err ?? this.error(msg)
|
|
7
5
|
if (err) error.message = msg
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
if (!welcome) {
|
|
7
|
+
error.statusCode = 404
|
|
8
|
+
reply.code(404)
|
|
9
|
+
}
|
|
10
10
|
reply.header('Content-Type', `text/html; charset=${this.config.page.charset}`)
|
|
11
11
|
reply.header('Content-Language', req.lang)
|
|
12
12
|
if (error.noContent) return ''
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
tpl = resolveTemplate(tpl)
|
|
17
|
-
} catch (err) {
|
|
18
|
-
tpl = resolveTemplate(`${this.ns}.template:/404.html`)
|
|
19
|
-
}
|
|
20
|
-
const content = fs.readFileSync(tpl.file, 'utf8')
|
|
21
|
-
return await compile(content, { error: err })
|
|
13
|
+
const tpl = welcome ? `${this.ns}.template:/welcome.html` : `${this.ns}.template:/404.html`
|
|
14
|
+
req.webApp = this.ns
|
|
15
|
+
return await this.renderView({ reply, tpl, params: { error } })
|
|
22
16
|
}
|
|
23
17
|
|
|
24
18
|
export default notFoundHandler
|
package/package.json
CHANGED
package/wiki/CHANGES.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-06-30
|
|
4
|
+
|
|
5
|
+
- [2.21.1] Bug fix in ```wmpa.js```
|
|
6
|
+
|
|
7
|
+
## 2026-06-20
|
|
8
|
+
|
|
9
|
+
- [2.21.0] Add ```renderView()```
|
|
10
|
+
- [2.21.0] Change ```reply.view()``` to use ```renderView()``` above
|
|
11
|
+
- [2.21.0] Add ```403.html```
|
|
12
|
+
- [2.21.0] Bug fix in ```error-handler.js```
|
|
13
|
+
- [2.21.0] Bug fix in ```not-found-handler.js```
|
|
14
|
+
|
|
3
15
|
## 2026-06-19
|
|
4
16
|
|
|
5
17
|
- [2.20.2] Bug fix in ```not-found-handler.js```
|