waibu-mpa 2.10.1 → 2.12.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.
- package/extend/dobo/model/session.json +2 -1
- package/index.js +9 -10
- package/lib/build-page/concat-resources.js +7 -11
- package/lib/build-routes.js +5 -1
- package/lib/class/component.js +2 -2
- package/lib/decorate.js +38 -1
- package/lib/load-resource.js +1 -1
- package/package.json +1 -1
- package/wiki/CHANGES.md +9 -0
package/index.js
CHANGED
|
@@ -33,12 +33,16 @@ async function factory (pkgName) {
|
|
|
33
33
|
prefix: ''
|
|
34
34
|
},
|
|
35
35
|
waibuAdmin: {
|
|
36
|
-
|
|
36
|
+
menuHandler: false,
|
|
37
|
+
modelDisabled: '*'
|
|
37
38
|
},
|
|
38
39
|
mountMainAsRoot: true,
|
|
39
40
|
page: {
|
|
40
41
|
charset: 'utf-8',
|
|
41
|
-
|
|
42
|
+
cache: {
|
|
43
|
+
ttlDur: 0,
|
|
44
|
+
urls: []
|
|
45
|
+
},
|
|
42
46
|
insertWarning: false,
|
|
43
47
|
usePluginTitle: false,
|
|
44
48
|
scriptsAtEndOfBody: true
|
|
@@ -63,7 +67,6 @@ async function factory (pkgName) {
|
|
|
63
67
|
},
|
|
64
68
|
emoji: true,
|
|
65
69
|
viewEngine: {
|
|
66
|
-
cacheMaxAge: 0,
|
|
67
70
|
layout: {
|
|
68
71
|
default: 'waibuMpa:/default.html',
|
|
69
72
|
fallback: true
|
|
@@ -81,7 +84,7 @@ async function factory (pkgName) {
|
|
|
81
84
|
},
|
|
82
85
|
component: {
|
|
83
86
|
unknownTag: 'replaceWithDiv',
|
|
84
|
-
|
|
87
|
+
ttlDur: '1m'
|
|
85
88
|
}
|
|
86
89
|
},
|
|
87
90
|
iconset: {
|
|
@@ -95,7 +98,7 @@ async function factory (pkgName) {
|
|
|
95
98
|
}
|
|
96
99
|
},
|
|
97
100
|
concatResource: {
|
|
98
|
-
|
|
101
|
+
ttlDur: 0,
|
|
99
102
|
excluded: [],
|
|
100
103
|
css: false,
|
|
101
104
|
scripts: false,
|
|
@@ -144,13 +147,10 @@ async function factory (pkgName) {
|
|
|
144
147
|
}
|
|
145
148
|
|
|
146
149
|
this.configProd = {
|
|
147
|
-
viewEngine: {
|
|
148
|
-
cacheMaxAge: 300
|
|
149
|
-
},
|
|
150
150
|
theme: {
|
|
151
151
|
component: {
|
|
152
152
|
unknownTag: 'remove',
|
|
153
|
-
|
|
153
|
+
ttlDur: '5m'
|
|
154
154
|
}
|
|
155
155
|
},
|
|
156
156
|
prettier: false,
|
|
@@ -509,7 +509,6 @@ async function factory (pkgName) {
|
|
|
509
509
|
const ext = path.extname(tpl)
|
|
510
510
|
if (['.json', '.js', '.css'].includes(ext)) opts.partial = true
|
|
511
511
|
opts.ext = ext
|
|
512
|
-
opts.cacheMaxAge = this.config.page.cacheMaxAgeDur
|
|
513
512
|
const viewEngine = this.getViewEngine(ext)
|
|
514
513
|
return await viewEngine.render(tpl, locals, opts)
|
|
515
514
|
}
|
|
@@ -7,22 +7,19 @@ async function apply ({ $, req, tag, attr, type }) {
|
|
|
7
7
|
const { routePath } = this.app.waibu
|
|
8
8
|
const { hash, fetch } = this.app.bajoExtra
|
|
9
9
|
const { isEmpty, map, without } = this.app.lib._
|
|
10
|
-
const
|
|
10
|
+
const { get: getCache, set: setCache } = this.app.bajoCache ?? {}
|
|
11
|
+
const prefix = this.app.bajoCache.config.exportPrefix
|
|
11
12
|
const excluded = map(this.config.concatResource.excluded, item => routePath(item))
|
|
12
|
-
let cachePrefix = this.app.bajoCache.config.externalPrefix
|
|
13
|
-
if (!cachePrefix) return
|
|
14
|
-
cachePrefix += ':'
|
|
15
|
-
|
|
16
13
|
const baseUrl = `${req.protocol}://${req.hostname}${req.port ? `:${req.port}` : ''}` // TODO: auth, if any
|
|
17
14
|
const items = []
|
|
18
15
|
$(`${tag}[${attr}]`).each(function () {
|
|
19
16
|
items.push(this.attribs[attr])
|
|
20
17
|
})
|
|
21
18
|
if (items.length === 0) return
|
|
22
|
-
const key = cachePrefix + await hash(items)
|
|
23
|
-
const cached = await cache.get({ key })
|
|
24
19
|
let keys = []
|
|
25
20
|
const notKeys = []
|
|
21
|
+
const key = `${prefix}-waibu-mpa-concat-resource-${await hash(items)}`
|
|
22
|
+
const cached = await getCache({ key })
|
|
26
23
|
if (!cached) {
|
|
27
24
|
const contents = []
|
|
28
25
|
for (const item of items) {
|
|
@@ -46,14 +43,13 @@ async function apply ({ $, req, tag, attr, type }) {
|
|
|
46
43
|
}
|
|
47
44
|
if (isEmpty(contents)) return
|
|
48
45
|
$(map(keys, k => `${tag}[${attr}="${k}"]`).join(',')).remove()
|
|
49
|
-
// await cache.set({ key, value: contents.join('\n'), ttl: this.config.page.cacheMaxAge * 1000 })
|
|
50
46
|
const value = { contents, notKeys, type, tag, attr }
|
|
51
|
-
await
|
|
47
|
+
await setCache({ key, value, ttl: this.config.concatResource.ttlDur })
|
|
52
48
|
} else {
|
|
53
49
|
keys = without(items, ...cached.notKeys)
|
|
54
50
|
$(map(keys, k => `${tag}[${attr}="${k}"]`).join(',')).remove()
|
|
55
51
|
}
|
|
56
|
-
const rsc = `bajoCache:/external/${key.
|
|
52
|
+
const rsc = `bajoCache:/external/${key.slice(prefix.length + 1)}.${type}`
|
|
57
53
|
if (tag === 'link') $('head').prepend(printLink.call(this, rsc))
|
|
58
54
|
else if (tag === 'script') $('body').append(printScript.call(this, rsc))
|
|
59
55
|
}
|
|
@@ -61,7 +57,7 @@ async function apply ({ $, req, tag, attr, type }) {
|
|
|
61
57
|
async function concatResources (options) {
|
|
62
58
|
const { $, req } = options ?? {}
|
|
63
59
|
if (!(this.app.bajoExtra && this.app.bajoCache)) return
|
|
64
|
-
if (this.config.concatResource.
|
|
60
|
+
if (this.config.concatResource.ttlDur === 0) return
|
|
65
61
|
if (this.config.concatResource.css) await apply.call(this, { $, req, tag: 'link', attr: 'href', type: 'css' })
|
|
66
62
|
if (this.config.concatResource.links) await apply.call(this, { $, req, tag: 'link', attr: 'href', type: 'css' })
|
|
67
63
|
if (this.config.concatResource.scripts) await apply.call(this, { $, req, tag: 'script', attr: 'src', type: 'js' })
|
package/lib/build-routes.js
CHANGED
|
@@ -3,7 +3,7 @@ import path from 'path'
|
|
|
3
3
|
export async function build ({ files, pathPrefix, dir, ns, cfg, parent, urlPrefix, subRoute }) {
|
|
4
4
|
const { defaultsDeep } = this.app.lib.aneka
|
|
5
5
|
const { importModule, readJson } = this.app.bajo
|
|
6
|
-
const { isFunction, isPlainObject, pick, last, camelCase } = this.app.lib._
|
|
6
|
+
const { isFunction, isPlainObject, pick, last, camelCase, omit } = this.app.lib._
|
|
7
7
|
const { titleize } = this.app.lib.aneka
|
|
8
8
|
const { getPluginPrefix } = this.app.waibu
|
|
9
9
|
const mergeRouteHooks = await importModule('waibu:/lib/webapp-scope/merge-route-hooks.js')
|
|
@@ -45,10 +45,14 @@ export async function build ({ files, pathPrefix, dir, ns, cfg, parent, urlPrefi
|
|
|
45
45
|
m.config.prefix = getPluginPrefix(ns)
|
|
46
46
|
m.config.pathSrc = m.url
|
|
47
47
|
m.config.webApp = parent ?? ns
|
|
48
|
+
m.config.interSite = m.interSite
|
|
48
49
|
m.config.ns = ns
|
|
49
50
|
m.config.subNs = ''
|
|
50
51
|
m.config.title = m.title ?? camelCase(last(m.url.split('/')))
|
|
51
52
|
m.config.subRoute = subRoute
|
|
53
|
+
if (m.cache === true) m.cache = omit(me.config.page.cache, ['urls'])
|
|
54
|
+
m.config.cache = defaultsDeep(m.cache ?? {}, { ttlDur: 0 })
|
|
55
|
+
|
|
52
56
|
delete m.title
|
|
53
57
|
m = defaultsDeep(pick(cfg, ['exposeHeadRoute', 'bodyLimit']), m)
|
|
54
58
|
mods.push(m)
|
package/lib/class/component.js
CHANGED
|
@@ -11,7 +11,7 @@ async function componentFactory () {
|
|
|
11
11
|
this.locals = locals
|
|
12
12
|
this.reply = reply
|
|
13
13
|
this.req = req
|
|
14
|
-
this.
|
|
14
|
+
this.ttlDur = get(plugin, 'app.waibuMpa.config.theme.component.ttlDur', 0)
|
|
15
15
|
this.namespace = 'c:'
|
|
16
16
|
this.noTags = []
|
|
17
17
|
this.scriptBlock = scriptBlock
|
|
@@ -118,7 +118,7 @@ async function componentFactory () {
|
|
|
118
118
|
if (isEmpty(params.html)) return await this.render(params)
|
|
119
119
|
|
|
120
120
|
const merged = merge({}, params.locals, { attr: params.attr })
|
|
121
|
-
const result = await compile(params.html, merged, { ttl: this.
|
|
121
|
+
const result = await compile(params.html, merged, { ttl: this.ttlDur })
|
|
122
122
|
params.html = result
|
|
123
123
|
return await this.render(params)
|
|
124
124
|
}
|
package/lib/decorate.js
CHANGED
|
@@ -1,17 +1,45 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
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
|
+
|
|
3
21
|
async function decorate () {
|
|
4
22
|
const { importPkg } = this.app.bajo
|
|
5
|
-
const { isEmpty } = this.app.lib._
|
|
23
|
+
const { isString, cloneDeep, isEmpty } = this.app.lib._
|
|
24
|
+
const { get: getCache, set: setCache } = this.app.bajoCache ?? {}
|
|
25
|
+
const { outmatch } = this.app.lib
|
|
26
|
+
const { routePath } = this.app.waibu
|
|
6
27
|
const mime = await importPkg('waibu:mime')
|
|
7
28
|
const cfg = this.config
|
|
8
29
|
const me = this
|
|
30
|
+
const cachedUrls = cloneDeep(this.config.page.cache.urls).map(item => {
|
|
31
|
+
if (isString(item)) item = { url: item }
|
|
32
|
+
item.url = routePath(item.url)
|
|
33
|
+
item.isMatch = outmatch(item.url)
|
|
34
|
+
return item
|
|
35
|
+
})
|
|
9
36
|
this.webAppCtx.decorateRequest('theme', cfg.theme.set)
|
|
10
37
|
this.webAppCtx.decorateRequest('iconset', cfg.iconset.set)
|
|
11
38
|
this.webAppCtx.decorateRequest('darkMode', cfg.darkMode.set)
|
|
12
39
|
this.webAppCtx.decorateRequest('referer', '')
|
|
13
40
|
this.webAppCtx.decorateReply('ctags', null)
|
|
14
41
|
this.webAppCtx.decorateReply('view', async function (tpl, params = {}, opts = {}) {
|
|
42
|
+
// this = fastify context!
|
|
15
43
|
let ext = path.extname(tpl)
|
|
16
44
|
if (ext === '.md') ext = '.html'
|
|
17
45
|
let mimeType = isEmpty(ext) ? 'text/html' : mime.getType(ext)
|
|
@@ -24,7 +52,16 @@ async function decorate () {
|
|
|
24
52
|
if (!this.request[item]) this.request[item] = me[item + 's'][0].name
|
|
25
53
|
if (me[item + 's'].length === 1) this.request[item] = me[item + 's'][0].name
|
|
26
54
|
}
|
|
55
|
+
const { key, ttl } = await isCacheable.call(me, this.request, cachedUrls)
|
|
56
|
+
if (ttl > 0) {
|
|
57
|
+
const cached = await getCache({ key })
|
|
58
|
+
if (cached) {
|
|
59
|
+
this.header('X-Wmpa-Cached', true)
|
|
60
|
+
return cached
|
|
61
|
+
}
|
|
62
|
+
}
|
|
27
63
|
const result = await me.render(tpl, params, opts)
|
|
64
|
+
if (ttl > 0) await setCache({ key, value: result, ttl })
|
|
28
65
|
if (this.request.session) {
|
|
29
66
|
ext = path.extname(this.request.url)
|
|
30
67
|
if (isEmpty(ext) || ['.html'].includes(ext)) {
|
package/lib/load-resource.js
CHANGED
|
@@ -19,7 +19,7 @@ async function loadResource (mod = [], item) {
|
|
|
19
19
|
} else items.push(mod[item][i])
|
|
20
20
|
}
|
|
21
21
|
for (const c of extItems) {
|
|
22
|
-
let emod = await readConfig(`${c.ns}:${c.path}`, { ns: c.ns })
|
|
22
|
+
let emod = await readConfig(`${c.ns}:${c.path}`, { ns: c.ns, ignoreError: false })
|
|
23
23
|
if (!isArray(emod)) emod = [emod]
|
|
24
24
|
for (const m of emod) {
|
|
25
25
|
items.push(m)
|
package/package.json
CHANGED
package/wiki/CHANGES.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-03-30
|
|
4
|
+
|
|
5
|
+
- [2.12.0] Add inter site module support
|
|
6
|
+
|
|
7
|
+
## 2026-03-27
|
|
8
|
+
|
|
9
|
+
- [2.11.0] Add options to enable cache with Bajo Cache module
|
|
10
|
+
- [2.11.0] Change all ```cacheMaxAge``` keys to ```ttlDur``` to align with above mentioned cache engine
|
|
11
|
+
|
|
3
12
|
## 2026-03-22
|
|
4
13
|
|
|
5
14
|
- [2.10.1] Bug fix in applying routes to ```webCtx```
|