waibu-mpa 1.0.13 → 1.0.14
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/bajoI18N/resource/en-US.json +2 -0
- package/bajoI18N/resource/id.json +2 -0
- package/lib/build-locals.js +6 -0
- package/lib/class/factory/page-end.js +5 -6
- package/lib/class/view-engine.js +31 -3
- package/lib/error.js +1 -0
- package/lib/get-cached-result.js +1 -1
- package/lib/not-found.js +3 -2
- package/package.json +1 -1
- package/waibuMpa/partial/404.html +6 -0
- package/waibuMpa/partial/500.html +10 -0
- package/waibuMpa/partial/address.html +8 -0
- package/waibuMpa/template/404.html +0 -1
- package/waibuMpa/template/500.html +0 -4
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
"Success": "Sukses",
|
|
23
23
|
"Warning": "Peringatan",
|
|
24
24
|
"Danger": "Bahaya",
|
|
25
|
+
"404Message": "Maaf, halaman yang Anda inginkan tidak ditemukan. Silahkan kontak Admin Anda segera!",
|
|
26
|
+
"500Message": "Maaf, ada kesalahan internal server. Silahkan kontak Admin Anda segera!",
|
|
25
27
|
"field": {
|
|
26
28
|
"session": "Sesi",
|
|
27
29
|
"expires": "Kadaluarsa"
|
package/lib/build-locals.js
CHANGED
|
@@ -15,6 +15,7 @@ async function buildLocals ({ template, params = {}, opts = {} } = {}) {
|
|
|
15
15
|
const { getAppTitle } = this.app.waibuMpa
|
|
16
16
|
const { set, merge, pick, get, isEmpty, find } = this.app.bajo.lib._
|
|
17
17
|
const { req, reply } = opts
|
|
18
|
+
|
|
18
19
|
params.page = merge(params.page ?? {}, { ns: req.ns, appTitle: getAppTitle(req.ns) })
|
|
19
20
|
set(params, 'menu.homes', buildHomesMenu.call(this))
|
|
20
21
|
|
|
@@ -24,9 +25,14 @@ async function buildLocals ({ template, params = {}, opts = {} } = {}) {
|
|
|
24
25
|
const routeOpts = get(req, 'routeOptions.config', {})
|
|
25
26
|
const _meta = { theme, iconset, site, user, lang, darkMode, routeOpts }
|
|
26
27
|
merge(_meta, pick(req, ['url', 'params', 'query']))
|
|
28
|
+
_meta.env = this.app.bajo.config.env
|
|
27
29
|
_meta.url = _meta.url.split('?')[0].split('#')[0]
|
|
28
30
|
_meta.route = get(req, 'routeOptions.url')
|
|
29
31
|
_meta.template = template
|
|
32
|
+
if (params.error) {
|
|
33
|
+
_meta.statusCode = params.error.statusCode ?? 200
|
|
34
|
+
_meta.errorMessage = params.error.message
|
|
35
|
+
}
|
|
30
36
|
if (req.flash && !opts.partial) _meta.flash = reply.flash()
|
|
31
37
|
const merged = merge({}, params, { _meta })
|
|
32
38
|
await runHook(`${this.name}:afterBuildLocals`, merged, req)
|
|
@@ -20,9 +20,8 @@ class PageEnd extends Factory {
|
|
|
20
20
|
let tc = ''
|
|
21
21
|
if (!this.params.attr.noToastContainer && factory.toastStack && factory.toast) {
|
|
22
22
|
const toasts = []
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const details = get(err, 'details', [])
|
|
23
|
+
if (get(locals, 'error')) {
|
|
24
|
+
const details = get(locals.error, 'details', [])
|
|
26
25
|
if (details.length > 0) {
|
|
27
26
|
const list = [`<ul class="m-0 ${details.length === 1 ? 'list-unstyled' : ''}">`]
|
|
28
27
|
for (const d of details) {
|
|
@@ -34,12 +33,12 @@ class PageEnd extends Factory {
|
|
|
34
33
|
const attr = {
|
|
35
34
|
border: 'side:all width:0',
|
|
36
35
|
text: 'background:danger',
|
|
37
|
-
title: req.t(err.message)
|
|
36
|
+
title: req.t(locals.err.message)
|
|
38
37
|
}
|
|
39
38
|
toasts.push(await this.component.buildTag({ tag: 'toast', attr, html: list.join('\n') }))
|
|
40
|
-
} else {
|
|
39
|
+
} else if (!(locals._meta.statusCode === 404 || locals._meta.statusCode >= 500)) {
|
|
41
40
|
const attr = { border: 'side:all width:0', text: 'background:danger' }
|
|
42
|
-
toasts.push(await this.component.buildTag({ tag: 'toast', attr, html: this.component.req.t(
|
|
41
|
+
toasts.push(await this.component.buildTag({ tag: 'toast', attr, html: this.component.req.t(locals.error.message) }))
|
|
43
42
|
}
|
|
44
43
|
}
|
|
45
44
|
const notifications = get(locals, '_meta.flash.notify', [])
|
package/lib/class/view-engine.js
CHANGED
|
@@ -9,6 +9,7 @@ class ViewEngine {
|
|
|
9
9
|
this.fileExts = typeof fileExts === 'string' ? [fileExts] : fileExts
|
|
10
10
|
this.cacheMaxAge = plugin.app.waibuMpa.config.viewEngine.cacheMaxAge
|
|
11
11
|
this.md = get(plugin, 'app.bajoMarkdown')
|
|
12
|
+
this.history = {}
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
_applySetting (opts = {}) {
|
|
@@ -20,9 +21,23 @@ class ViewEngine {
|
|
|
20
21
|
opts.theme = get(setting, 'theme', req.theme)
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
_clearHistory () {
|
|
25
|
+
const { omit } = this.plugin.app.bajo.lib._
|
|
26
|
+
const maxAge = 60
|
|
27
|
+
const now = Date.now()
|
|
28
|
+
const omitted = []
|
|
29
|
+
for (const reqId in this.history) {
|
|
30
|
+
const history = this.history[reqId]
|
|
31
|
+
if ((history.ts + (maxAge * 1000)) < now) omitted.push(reqId)
|
|
32
|
+
}
|
|
33
|
+
this.history = omit(this.history, omitted)
|
|
34
|
+
}
|
|
35
|
+
|
|
23
36
|
async render (tpl, locals = {}, opts = {}) {
|
|
24
|
-
|
|
37
|
+
this._clearHistory()
|
|
38
|
+
const { trim, isEmpty, last } = this.plugin.app.bajo.lib._
|
|
25
39
|
const { fs } = this.plugin.app.bajo.lib
|
|
40
|
+
const { req } = opts
|
|
26
41
|
const mpa = this.plugin.app.waibuMpa
|
|
27
42
|
const { ns, subSubNs, path, qs } = mpa.getResource(tpl)
|
|
28
43
|
this._applySetting(opts)
|
|
@@ -34,12 +49,23 @@ class ViewEngine {
|
|
|
34
49
|
resp = mpa.resolvePartial(`${ns}.partial${subSubNs ? ('.' + subSubNs) : ''}:${path}`, opts)
|
|
35
50
|
}
|
|
36
51
|
const file = resp.file
|
|
52
|
+
// prevent looping
|
|
53
|
+
if (this.history[req.id]) {
|
|
54
|
+
if (last(this.history[req.id].file) === file) throw this.plugin.error('Template looping detected: %s => %s', tpl, file)
|
|
55
|
+
this.history[req.id].file.push(file)
|
|
56
|
+
} else {
|
|
57
|
+
this.history[req.id] = {
|
|
58
|
+
ts: Date.now(),
|
|
59
|
+
file: [file]
|
|
60
|
+
}
|
|
61
|
+
}
|
|
37
62
|
let content = trim(fs.readFileSync(file, 'utf8'))
|
|
38
63
|
if (isEmpty(content) && tpl.includes('.template')) {
|
|
39
64
|
content = '<c:include resource="' + tpl.replace('.template', '.partial') + '" />'
|
|
40
65
|
}
|
|
41
66
|
opts.ext = _path.extname(file)
|
|
42
67
|
opts.fn = true
|
|
68
|
+
opts.tpl = tpl
|
|
43
69
|
return await this.write(content, locals, opts)
|
|
44
70
|
}
|
|
45
71
|
|
|
@@ -66,9 +92,11 @@ class ViewEngine {
|
|
|
66
92
|
let layout
|
|
67
93
|
if (!opts.partial) {
|
|
68
94
|
locals.page = merge(locals.page, parseObject(parsed.frontMatter, { parseValue: true, i18n: req.i18n, ns: reqNs }))
|
|
69
|
-
layout = locals.page.layout ?? qs.layout ?? opts.layout ?? `${
|
|
95
|
+
layout = locals.page.layout ?? qs.layout ?? opts.layout ?? (locals.page.ns ? `${locals.page.ns}.layout:/default.html` : 'main.layout:/default.html')
|
|
96
|
+
console.log('---', locals.page.layout, qs.layout, opts.layout, reqNs, layout)
|
|
70
97
|
const ext = _path.extname(layout)
|
|
71
98
|
const { file } = mpa.resolveLayout(layout, opts)
|
|
99
|
+
console.log('---', layout, file)
|
|
72
100
|
let layoutContent = fs.readFileSync(file, 'utf8')
|
|
73
101
|
const layoutParsed = await this.md.parse(layoutContent, merge({ readFile: false, parseContent: false, i18n: req.i18n }, mpa.config.markdown))
|
|
74
102
|
layoutContent = ext === '.md' ? this.md.parseContent(layoutParsed.content, mdOpts) : layoutParsed.content
|
|
@@ -89,7 +117,7 @@ class ViewEngine {
|
|
|
89
117
|
locals.page.fullTitle = locals.fullTitle ?? (locals.page.title ? `${locals.page.title} - ${req.t(locals.page.appTitle)}` : req.t(locals.page.appTitle))
|
|
90
118
|
}
|
|
91
119
|
// if (['.js'].includes(opts.ext)) return content // TODO: clash with lodash template
|
|
92
|
-
return await getCachedResult.call(this.plugin, content, locals, { req, ttl: this.cacheMaxAge, fn: opts.fn })
|
|
120
|
+
return await getCachedResult.call(this.plugin, content, locals, { req, ttl: this.cacheMaxAge, fn: opts.fn, tpl: opts.tpl })
|
|
93
121
|
}
|
|
94
122
|
}
|
|
95
123
|
|
package/lib/error.js
CHANGED
|
@@ -4,6 +4,7 @@ const extHandler = async function (err, req, reply) {
|
|
|
4
4
|
const { getPluginFile } = this.app.bajo
|
|
5
5
|
const { fs } = this.app.bajo.lib
|
|
6
6
|
const { template } = this.app.bajo.lib._
|
|
7
|
+
err.statusCode = err.statusCode ?? 500
|
|
7
8
|
|
|
8
9
|
if (err.message.toLowerCase() === 'notfound' || err.statusCode === 404) {
|
|
9
10
|
return await handler.call(this, req, reply)
|
package/lib/get-cached-result.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import crypto from 'crypto'
|
|
2
2
|
|
|
3
|
-
async function getCachedResult (content, params, { req, ttl = 0, fn = false, keyFn } = {}) {
|
|
3
|
+
async function getCachedResult (content, params, { req, ttl = 0, fn = false, keyFn, tpl } = {}) {
|
|
4
4
|
const _ = this.app.bajo.lib._
|
|
5
5
|
const { template, get } = _
|
|
6
6
|
const cache = this.app.bajoCache
|
package/lib/not-found.js
CHANGED
|
@@ -30,9 +30,10 @@ export async function handler (req, reply) {
|
|
|
30
30
|
if (fn) redirectTo = await fn(req)
|
|
31
31
|
if (redirectTo) return reply.redirectTo(redirectTo)
|
|
32
32
|
}
|
|
33
|
-
const
|
|
33
|
+
const error = this.error('Route \'%s (%s)\' not found', req.url, req.method)
|
|
34
|
+
error.statusCode = 404
|
|
34
35
|
reply.code(404)
|
|
35
|
-
if (reply.view) return await reply.view('waibuMpa.template:/404.html', {
|
|
36
|
+
if (reply.view) return await reply.view('waibuMpa.template:/404.html', { error })
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
async function notFound (ctx) {
|
package/package.json
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<c:container margin="y-4">
|
|
2
|
+
<c:div text="align:center">
|
|
3
|
+
<c:heading type="6-display" t:content="Internal Server Error" />
|
|
4
|
+
<% if (_meta.env === 'dev') { %>
|
|
5
|
+
<%= _meta.errorMessage %>
|
|
6
|
+
<% } else { %>
|
|
7
|
+
<c:t>500Message</c:t>
|
|
8
|
+
<% } %>
|
|
9
|
+
</c:div>
|
|
10
|
+
</c:container>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<% const type = attr.type ?? 'site' %>
|
|
2
|
+
<address>
|
|
3
|
+
<c:strong text="nowrap"><%= type === 'user' ? (_meta[type].firstName + ' ' + _meta[type].lastName) : _meta[type].orgName %></c:strong><br />
|
|
4
|
+
<%= _meta[type].address1 %><br/>
|
|
5
|
+
<%= _meta[type].address2 %><br />
|
|
6
|
+
<c:span text="nowrap"><%= _meta[type].city %> <%= _meta[type].zipCode %> - <%= _meta[type].provinceState %></c:span><br />
|
|
7
|
+
<%= _meta[type].country %><br />
|
|
8
|
+
</address>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
404
|