waibu-mpa 2.5.1 → 2.6.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/404.html +14 -0
- package/extend/bajoTemplate/template/500.html +14 -0
- package/extend/dobo/model/session.json +1 -2
- package/extend/waibuStatic/asset/css/default.css +7 -0
- package/index.js +2 -1
- package/lib/class/component.js +19 -9
- package/lib/class/widget/datalist.js +4 -1
- package/lib/error-handler.js +8 -17
- package/lib/not-found-handler.js +8 -4
- package/lib/session/setup.js +20 -0
- package/package.json +1 -1
- package/wiki/CHANGES.md +13 -0
- package/extend/bajoTemplate/partial/404.html +0 -4
- package/extend/bajoTemplate/partial/500.html +0 -8
- package/extend/bajoTemplate/template/_500.html +0 -8
|
@@ -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('pageNotFound') %></title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="container-center">
|
|
11
|
+
<p><%= _t('pageNotFound') %></p>
|
|
12
|
+
</div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -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.virtual:/purecss/pure-min.css') %>" />
|
|
7
|
+
<title><%= _t('internalServerError') %></title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="container-center">
|
|
11
|
+
<%= error.message %>
|
|
12
|
+
</div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
package/index.js
CHANGED
package/lib/class/component.js
CHANGED
|
@@ -206,9 +206,13 @@ async function componentFactory () {
|
|
|
206
206
|
* @param {Object} params - The parameters containing options and values.
|
|
207
207
|
* @returns {string} The generated `<option>` elements.
|
|
208
208
|
*/
|
|
209
|
-
buildOptions = (params) => {
|
|
210
|
-
const { isString, has, omit, find, isPlainObject, isArray, pick } = this.app.lib._
|
|
209
|
+
buildOptions = async (params) => {
|
|
210
|
+
const { isString, has, omit, find, isPlainObject, isArray, pick, camelCase, get } = this.app.lib._
|
|
211
211
|
const { attrToArray } = this.app.waibu
|
|
212
|
+
const { getMethod, callHandler } = this.app.bajo
|
|
213
|
+
let prop = {}
|
|
214
|
+
const schema = get(this, 'locals.schema')
|
|
215
|
+
if (schema) prop = find(schema.properties, { name: params.attr.name }) ?? {}
|
|
212
216
|
if (!has(params.attr, 'options')) return
|
|
213
217
|
const items = []
|
|
214
218
|
let input = params.attr.options
|
|
@@ -216,10 +220,14 @@ async function componentFactory () {
|
|
|
216
220
|
if (!has(params.attr, 'multiple') && values.length > 0) values = [values[0]]
|
|
217
221
|
let options = []
|
|
218
222
|
if (isString(input)) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
+
if (getMethod(input, false)) {
|
|
224
|
+
input = await callHandler(input)
|
|
225
|
+
} else {
|
|
226
|
+
input = attrToArray(input, ';').map(item => {
|
|
227
|
+
const [value, text] = item.split(':')
|
|
228
|
+
return { value, text }
|
|
229
|
+
})
|
|
230
|
+
}
|
|
223
231
|
}
|
|
224
232
|
if (isPlainObject(input)) {
|
|
225
233
|
for (const key in input) {
|
|
@@ -233,7 +241,8 @@ async function componentFactory () {
|
|
|
233
241
|
}
|
|
234
242
|
for (const opt of options) {
|
|
235
243
|
const sel = find(values, v => opt.value === v)
|
|
236
|
-
|
|
244
|
+
const ttext = camelCase(`${prop.name} ${opt.text}`)
|
|
245
|
+
items.push(`<option value="${opt.value}"${sel ? ' selected' : ''}>${this.req.te(ttext) ? this.req.t(ttext) : (opt.text ?? opt.value)}</option>`)
|
|
237
246
|
}
|
|
238
247
|
params.attr = omit(params.attr, ['options'])
|
|
239
248
|
return items.join('\n')
|
|
@@ -266,6 +275,7 @@ async function componentFactory () {
|
|
|
266
275
|
* @returns {Promise<string>} The built sentence.
|
|
267
276
|
*/
|
|
268
277
|
buildSentence = async (sentence, params = {}, extra = {}) => {
|
|
278
|
+
const { get } = this.app.lib._
|
|
269
279
|
if (Array.isArray(sentence)) sentence = sentence.join(' ')
|
|
270
280
|
const { minify, renderString } = this.app.waibuMpa
|
|
271
281
|
if (extra.wrapped) sentence = '<w>' + sentence + '</w>'
|
|
@@ -274,8 +284,8 @@ async function componentFactory () {
|
|
|
274
284
|
ext: params.ext ?? '.html',
|
|
275
285
|
req: this.req,
|
|
276
286
|
reply: this.reply,
|
|
277
|
-
theme: this
|
|
278
|
-
iconset: this
|
|
287
|
+
theme: get(this, 'theme.name', 'default'),
|
|
288
|
+
iconset: get(this, 'iconset.name', 'default')
|
|
279
289
|
}
|
|
280
290
|
let html = await renderString(sentence, params, opts)
|
|
281
291
|
if (extra.wrapped) html = html.slice(3, html.length - 4)
|
|
@@ -4,7 +4,10 @@ async function datalistFactory () {
|
|
|
4
4
|
super(options)
|
|
5
5
|
this.component.normalizeAttr(this.params)
|
|
6
6
|
this.params.tag = 'datalist'
|
|
7
|
-
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
build = async () => {
|
|
10
|
+
if (this.params.attr.options) this.params.html = await this.component.buildOptions(this.params)
|
|
8
11
|
}
|
|
9
12
|
}
|
|
10
13
|
|
package/lib/error-handler.js
CHANGED
|
@@ -1,36 +1,27 @@
|
|
|
1
1
|
import notFoundHandler from './not-found-handler.js'
|
|
2
2
|
|
|
3
3
|
async function errorHandler (err, req, reply) {
|
|
4
|
-
const { getPluginFile } = this.app.bajo
|
|
5
4
|
const { fs } = this.app.lib
|
|
6
|
-
const { resolveTemplate } = this.app.bajoTemplate
|
|
7
|
-
const { template } = this.app.lib._
|
|
5
|
+
const { resolveTemplate, compile } = this.app.bajoTemplate
|
|
8
6
|
err.statusCode = err.statusCode ?? 500
|
|
9
7
|
reply.code(err.statusCode)
|
|
8
|
+
reply.header('Content-Type', `text/html; charset=${this.config.page.charset}`)
|
|
9
|
+
reply.header('Content-Language', req.lang)
|
|
10
10
|
|
|
11
11
|
if (err.message === '_notFound' || err.statusCode === 404) {
|
|
12
12
|
return await notFoundHandler.call(this, err, req, reply)
|
|
13
13
|
}
|
|
14
14
|
if (err.noContent) return ''
|
|
15
15
|
const ns = err.ns ?? this.ns
|
|
16
|
-
let result
|
|
16
|
+
// let result
|
|
17
17
|
let tpl = `${ns}.template:/${err.statusCode ?? 500}.html`
|
|
18
18
|
try {
|
|
19
|
-
|
|
19
|
+
tpl = resolveTemplate(tpl)
|
|
20
20
|
} catch (err) {
|
|
21
|
-
tpl = `${this.ns}.template:/500.html`
|
|
21
|
+
tpl = resolveTemplate(`${this.ns}.template:/500.html`)
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
} catch (err) {
|
|
26
|
-
// only getting here when there is error on view rendering
|
|
27
|
-
console.error(err)
|
|
28
|
-
const file = getPluginFile(`${this.ns}:/extend/bajoTemplate/template/_500.html`)
|
|
29
|
-
const content = fs.readFileSync(file)
|
|
30
|
-
const compiled = template(content)
|
|
31
|
-
result = compiled({ error: err })
|
|
32
|
-
}
|
|
33
|
-
return result
|
|
23
|
+
const content = fs.readFileSync(tpl.file, 'utf8')
|
|
24
|
+
return await compile(content, { error: err })
|
|
34
25
|
}
|
|
35
26
|
|
|
36
27
|
export default errorHandler
|
package/lib/not-found-handler.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
async function notFoundHandler (err, req, reply) {
|
|
2
2
|
const welcome = req.url.split('?')[0] === '/'
|
|
3
|
-
const {
|
|
3
|
+
const { fs } = this.app.lib
|
|
4
|
+
const { resolveTemplate, compile } = this.app.bajoTemplate
|
|
4
5
|
const msg = req.t('routeNotFound%s%s', req.url, req.method)
|
|
5
6
|
const error = err ?? this.error(msg)
|
|
6
7
|
if (err) error.message = msg
|
|
7
8
|
error.statusCode = 404
|
|
8
9
|
reply.code(404)
|
|
10
|
+
reply.header('Content-Type', `text/html; charset=${this.config.page.charset}`)
|
|
11
|
+
reply.header('Content-Language', req.lang)
|
|
9
12
|
if (error.noContent) return ''
|
|
10
13
|
const ns = error.ns ?? this.ns
|
|
11
14
|
let tpl = welcome ? `${this.ns}.template:/welcome.html` : `${ns}.template:/404.html`
|
|
12
15
|
try {
|
|
13
|
-
|
|
16
|
+
tpl = resolveTemplate(tpl)
|
|
14
17
|
} catch (err) {
|
|
15
|
-
tpl = `${this.ns}.template:/404.html`
|
|
18
|
+
tpl = resolveTemplate(`${this.ns}.template:/404.html`)
|
|
16
19
|
}
|
|
17
|
-
|
|
20
|
+
const content = fs.readFileSync(tpl.file, 'utf8')
|
|
21
|
+
return await compile(content, { error: err })
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
export default notFoundHandler
|
package/lib/session/setup.js
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
import Store from './store.js'
|
|
2
2
|
|
|
3
|
+
async function trashOld () {
|
|
4
|
+
if (!this.app.dobo) return
|
|
5
|
+
const { get } = this.app.lib._
|
|
6
|
+
const { dayjs } = this.app.lib
|
|
7
|
+
const model = this.app.dobo.getModel('WmpaSession')
|
|
8
|
+
const recs = await model.findAllRecord({ sort: { createdAt: 1 } }, { noHook: true, noModelHook: true })
|
|
9
|
+
for (const rec of recs) {
|
|
10
|
+
let expires = get(rec, 'session.cookie.expires')
|
|
11
|
+
if (!expires) expires = dayjs(rec.createdAt).add(this.config.session.cookie.maxAge, 'ms').toISOString()
|
|
12
|
+
const diff = dayjs(expires).diff(dayjs())
|
|
13
|
+
if (diff < 0) await model.removeRecord(rec.id, { noReturn: true })
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
3
17
|
async function sessionSetup () {
|
|
4
18
|
const { importPkg, runHook } = this.app.bajo
|
|
5
19
|
const [cookie, session, flash] = await importPkg('waibu:@fastify/cookie',
|
|
6
20
|
'waibu:@fastify/session', 'waibu:@fastify/flash')
|
|
7
21
|
const cfg = this.getConfig('session')
|
|
8
22
|
if (!cfg) return
|
|
23
|
+
delete cfg.trashOldDur
|
|
9
24
|
await runHook(`${this.ns}:beforeSessionSetup`)
|
|
10
25
|
if (this.app.dobo) {
|
|
11
26
|
this.sessionStore = new Store(this)
|
|
@@ -15,6 +30,11 @@ async function sessionSetup () {
|
|
|
15
30
|
this.webAppCtx.register(session, cfg)
|
|
16
31
|
this.webAppCtx.register(flash)
|
|
17
32
|
await runHook(`${this.ns}:afterSessionSetup`, this.webAppCtx)
|
|
33
|
+
|
|
34
|
+
trashOld.call(this)
|
|
35
|
+
setInterval(() => {
|
|
36
|
+
trashOld.call(this)
|
|
37
|
+
}, this.config.session.trashOldDur)
|
|
18
38
|
}
|
|
19
39
|
|
|
20
40
|
export default sessionSetup
|
package/package.json
CHANGED
package/wiki/CHANGES.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-02-21
|
|
4
|
+
|
|
5
|
+
- [2.6.1] Bug fix on ```errorHandler```
|
|
6
|
+
- [2.6.1] Bug fix on ```notFoundHandler```
|
|
7
|
+
- [2.6.1] Add fallback template for both handlers above
|
|
8
|
+
|
|
9
|
+
## 2026-02-18
|
|
10
|
+
|
|
11
|
+
- [2.6.0] Add auto trashing old session
|
|
12
|
+
- [2.6.0] Remove unecessary ```expires``` field in ```WmpaSession```
|
|
13
|
+
- [2.6.0] Change component's ```buildOptions()``` to async method to accomodate ```prop.values``` as a handler
|
|
14
|
+
- [2.6.0] Bug fix on theme and iconset resolver
|
|
15
|
+
|
|
3
16
|
## 2026-02-18
|
|
4
17
|
|
|
5
18
|
- [2.5.0] Move ```attrTo*()``` and ```base64Json*()``` to ```waibu``` because they are sometimes needed outside the ```waibu-mpa```
|