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.
@@ -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>
@@ -1,8 +1,7 @@
1
1
  {
2
2
  "properties": [
3
3
  "id,,100,primary,true",
4
- "session,object",
5
- "expires,datetime"
4
+ "session,object"
6
5
  ],
7
6
  "features": ["dobo:createdAt", "dobo:updatedAt"],
8
7
  "attachment": false
@@ -0,0 +1,7 @@
1
+ .container-center {
2
+ display: flex;
3
+ justify-content: center;
4
+ align-items: center;
5
+ height: 100vh;
6
+ width: 100%;
7
+ }
package/index.js CHANGED
@@ -57,7 +57,8 @@ async function factory (pkgName) {
57
57
  secure: 'auto',
58
58
  maxAge: 86400 * 7 * 1000
59
59
  },
60
- saveUninitialized: false
60
+ saveUninitialized: false,
61
+ trashOldDur: '5m'
61
62
  },
62
63
  emoji: true,
63
64
  viewEngine: {
@@ -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
- input = attrToArray(input, ';').map(item => {
220
- const [value, text] = item.split(':')
221
- return { value, text }
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
- items.push(`<option value="${opt.value}"${sel ? ' selected' : ''}>${this.req.t(opt.text ?? opt.value)}</option>`)
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.theme.name,
278
- iconset: this.iconset.name
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
- if (this.params.attr.options) this.params.html = this.component.buildOptions(this.params)
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
 
@@ -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
- await resolveTemplate(tpl)
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
- try {
24
- result = await reply.view(tpl, { error: err })
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
@@ -1,20 +1,24 @@
1
1
  async function notFoundHandler (err, req, reply) {
2
2
  const welcome = req.url.split('?')[0] === '/'
3
- const { resolveTemplate } = this.app.bajoTemplate
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
- await resolveTemplate(tpl)
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
- if (reply.view) return await reply.view(tpl, { error })
20
+ const content = fs.readFileSync(tpl.file, 'utf8')
21
+ return await compile(content, { error: err })
18
22
  }
19
23
 
20
24
  export default notFoundHandler
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waibu-mpa",
3
- "version": "2.5.1",
3
+ "version": "2.6.1",
4
4
  "description": "MPA support for Waibu Framework",
5
5
  "main": "index.js",
6
6
  "scripts": {
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```
@@ -1,4 +0,0 @@
1
- <c:div text="align:center">
2
- <c:heading type="6-display" t:content="Not Found" />
3
- <c:p t:content="404Message" />
4
- </c:div>
@@ -1,8 +0,0 @@
1
- <c:div text="align:center">
2
- <c:heading type="6-display" t:content="Internal Server Error" />
3
- <% if (_meta.env === 'dev') { %>
4
- <%= _meta.errorMessage %>
5
- <% } else { %>
6
- <c:t>500Message</c:t>
7
- <% } %>
8
- </c:div>
@@ -1,8 +0,0 @@
1
- <html>
2
- <head>
3
- <title>System Error</title>
4
- </head>
5
- <body>
6
- Error Message: <%= error.message %>
7
- </body>
8
- </html>