waibu-mpa 2.20.1 → 2.21.0

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.
@@ -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>
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
- // this = fastify context!
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
 
@@ -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
- const ns = err.ns ?? this.ns
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
- return await reply.view(tpl, { error: err }, { noFlash: true })
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')
@@ -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
- error.statusCode = 404
9
- reply.code(404)
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 ns = error.ns ?? this.ns
14
- let tpl = welcome ? `${this.ns}.template:/welcome.html` : `${ns}.template:/404.html`
15
- try {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waibu-mpa",
3
- "version": "2.20.1",
3
+ "version": "2.21.0",
4
4
  "description": "MPA support for Waibu Framework",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/wiki/CHANGES.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-06-20
4
+
5
+ - [2.21.0] Add ```renderView()```
6
+ - [2.21.0] Change ```reply.view()``` to use ```renderView()``` above
7
+ - [2.21.0] Add ```403.html```
8
+ - [2.21.0] Bug fix in ```error-handler.js```
9
+ - [2.21.0] Bug fix in ```not-found-handler.js```
10
+
11
+ ## 2026-06-19
12
+
13
+ - [2.20.2] Bug fix in ```not-found-handler.js```
14
+
3
15
  ## 2026-06-18
4
16
 
5
17
  - [2.20.1] Bug fix in ```build-route.js```