waibu-db 2.26.4 → 2.27.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.
@@ -1,5 +1,10 @@
1
1
  async function wdbBase () {
2
2
  return class WdbBase extends this.app.baseClass.MpaWidget {
3
+ isActionAllowed = async (action) => {
4
+ const { req } = this.component
5
+ if (!this.app.sumba || !this.model) return true
6
+ return await this.app.sumba.isActionAllowed(action, this.model, req)
7
+ }
3
8
  }
4
9
  }
5
10
 
@@ -5,11 +5,11 @@ async function btnAdd () {
5
5
 
6
6
  return class WdbBtnAdd extends WdbBase {
7
7
  build = async () => {
8
- const { isEmpty, get } = this.app.lib._
8
+ const { isEmpty } = this.app.lib._
9
9
  const { req } = this.component
10
10
  this.params.noTag = true
11
- const schema = get(this, 'component.locals.schema', {})
12
- if (schema.view.disabled.includes('create')) {
11
+ const allowed = await this.isActionAllowed('CREATE')
12
+ if (!allowed || this.schema.view.disabled.includes('create')) {
13
13
  this.params.html = ''
14
14
  return
15
15
  }
@@ -6,10 +6,10 @@ async function btnClone () {
6
6
  return class WdbBtnClone extends WdbBase {
7
7
  build = async () => {
8
8
  const { req } = this.component
9
- const { isEmpty, get } = this.app.lib._
9
+ const { isEmpty } = this.app.lib._
10
10
  this.params.noTag = true
11
- const schema = get(this, 'component.locals.schema', {})
12
- if (schema.view.disabled.includes('create')) {
11
+ const allowed = await this.isActionAllowed('CREATE')
12
+ if (!allowed || this.schema.view.disabled.includes('create')) {
13
13
  this.params.html = ''
14
14
  return
15
15
  }
@@ -7,10 +7,10 @@ async function btnDelete () {
7
7
  build = async () => {
8
8
  const { req } = this.component
9
9
  const { generateId } = this.app.lib.aneka
10
- const { isEmpty, get } = this.app.lib._
10
+ const { isEmpty } = this.app.lib._
11
11
  this.params.noTag = true
12
- const schema = get(this, 'component.locals.schema', {})
13
- if (schema.view.disabled.includes('remove')) {
12
+ const allowed = await this.isActionAllowed('REMOVE')
13
+ if (!allowed || this.schema.view.disabled.includes('remove')) {
14
14
  this.params.html = ''
15
15
  return
16
16
  }
@@ -7,10 +7,10 @@ async function btnDetails () {
7
7
  build = async () => {
8
8
  const { req } = this.component
9
9
  const { generateId } = this.app.lib.aneka
10
- const { isEmpty, get } = this.app.lib._
10
+ const { isEmpty } = this.app.lib._
11
11
  this.params.noTag = true
12
- const schema = get(this, 'component.locals.schema', {})
13
- if (schema.view.disabled.includes('get')) {
12
+ const allowed = await this.isActionAllowed('READ')
13
+ if (!allowed || this.schema.view.disabled.includes('get')) {
14
14
  this.params.html = ''
15
15
  return
16
16
  }
@@ -7,10 +7,10 @@ async function btnEdit () {
7
7
  build = async () => {
8
8
  const { req } = this.component
9
9
  const { generateId } = this.app.lib.aneka
10
- const { isEmpty, get } = this.app.lib._
10
+ const { isEmpty } = this.app.lib._
11
11
  this.params.noTag = true
12
- const schema = get(this, 'component.locals.schema', {})
13
- if (schema.view.disabled.includes('update')) {
12
+ const allowed = await this.isActionAllowed('UPDATE')
13
+ if (!allowed || this.schema.view.disabled.includes('update')) {
14
14
  this.params.html = ''
15
15
  return
16
16
  }
@@ -4,19 +4,19 @@ async function table () {
4
4
  const WdbBase = await wdbBase.call(this)
5
5
 
6
6
  return class WdbDataTable extends WdbBase {
7
- isRightAligned = (field, schema) => {
7
+ isRightAligned = (field) => {
8
8
  const { get, find } = this.app.lib._
9
- const prop = find(schema.properties, { name: field })
9
+ const prop = find(this.schema.properties, { name: field })
10
10
  if (!prop) return false
11
- let value = get(schema, 'view.alignEnd', []).includes(field)
11
+ let value = get(this.schema, 'view.alignEnd', []).includes(field)
12
12
  if (!value) value = ['smallint', 'integer', 'float', 'double'].includes(prop.type)
13
13
  return value
14
14
  }
15
15
 
16
- isNoWrap = (field, schema, bodyNowrap) => {
16
+ isNoWrap = (field, bodyNowrap) => {
17
17
  if (bodyNowrap) return true
18
18
  const { get } = this.app.lib._
19
- return get(schema, 'view.noWrap', []).includes(field)
19
+ return get(this.schema, 'view.noWrap', []).includes(field)
20
20
  }
21
21
 
22
22
  build = async () => {
@@ -32,24 +32,30 @@ async function table () {
32
32
  const prettyUrl = this.params.attr.prettyUrl
33
33
 
34
34
  const hasXSite = get(req, 'routeOptions.config.xSite')
35
- const schema = get(locals, 'schema', {})
36
35
  const data = get(locals, 'list.data', [])
37
36
  const filter = get(locals, 'list.filter', {})
38
37
  const count = get(locals, 'list.count', 0)
38
+ const allowed = await this.isActionAllowed('READ')
39
+ if (!allowed) {
40
+ const alert = '<c:alert color="error" t:content="accessDenied" margin="top-4"/>'
41
+ this.params.noTag = true
42
+ this.params.html = await buildSentence(alert)
43
+ return
44
+ }
39
45
  if (count === 0 || data.length === 0) {
40
46
  const alert = '<c:alert color="warning" t:content="noRecordFound" margin="top-4"/>'
41
47
  this.params.noTag = true
42
48
  this.params.html = await buildSentence(alert)
43
49
  return
44
50
  }
45
- const disableds = get(schema, 'view.disabled', [])
51
+ const disableds = get(this.schema, 'view.disabled', [])
46
52
  if (disableds.includes('find')) {
47
53
  this.params.html = ''
48
54
  return
49
55
  }
50
56
  const qsKey = this.app.waibu.config.qsKey
51
57
  let fields = without(get(locals, `_meta.query.${qsKey.fields}`, '').split(','), '')
52
- if (isEmpty(fields)) fields = without(schema.view.fields, 'id')
58
+ if (isEmpty(fields)) fields = without(this.schema.view.fields, 'id')
53
59
  if (data.length > 0) {
54
60
  fields = intersection(fields, Object.keys(data[0]))
55
61
  }
@@ -64,9 +70,9 @@ async function table () {
64
70
 
65
71
  let selection = this.params.attr.selection
66
72
  if (!isSet(selection)) {
67
- const canDelete = !disableds.includes('remove')
68
- const canEdit = !disableds.includes('update')
69
- const canDetails = !disableds.includes('get')
73
+ const canDelete = !disableds.includes('remove') && (await this.isActionAllowed('REMOVE'))
74
+ const canEdit = !disableds.includes('update') && (await this.isActionAllowed('UPDATE'))
75
+ const canDetails = !disableds.includes('get') && (await this.isActionAllowed('READ'))
70
76
  if (canEdit || canDetails) selection = 'single'
71
77
  if (canDelete) selection = 'multi'
72
78
  if (selection) this.params.attr.hover = true
@@ -76,13 +82,13 @@ async function table () {
76
82
  const html = []
77
83
  let items = []
78
84
  // head
79
- for (const f of schema.view.fields) {
85
+ for (const f of this.schema.view.fields) {
80
86
  if (!fields.includes(f)) continue
81
- let prop = find(schema.properties, { name: f })
82
- if (!prop) prop = find(schema.view.calcFields, { name: f })
87
+ let prop = find(this.schema.properties, { name: f })
88
+ if (!prop) prop = find(this.schema.view.calcFields, { name: f })
83
89
  if (!prop) continue
84
- let head = req.t(get(schema, `view.label.${f}`, `field.${f}`))
85
- if (!this.params.attr.noSort && (schema.sortables ?? []).includes(f)) {
90
+ let head = req.t(get(this.schema, `view.label.${f}`, `field.${f}`))
91
+ if (!this.params.attr.noSort && (this.schema.sortables ?? []).includes(f)) {
86
92
  let sortItem = `${f}:-1`
87
93
  let icon = this.params.attr.sortUpIcon ?? 'caretUp'
88
94
  if (f === sortCol) {
@@ -91,7 +97,7 @@ async function table () {
91
97
  }
92
98
  const item = set({ page: 1 }, qsKey.sort, sortItem)
93
99
  const href = buildUrl({ params: item })
94
- const attr = this.isRightAligned(f, schema) ? { text: 'align:end' } : {}
100
+ const attr = this.isRightAligned(f, this.schema) ? { text: 'align:end' } : {}
95
101
  const content = [
96
102
  await buildTag({ tag: 'div', attr, html: head }),
97
103
  await buildTag({ tag: 'a', attr: { icon, href, noIconLink: true }, prepend: '<div class="ms-1">', append: '</div>' })
@@ -99,8 +105,8 @@ async function table () {
99
105
  head = await buildTag({ tag: 'div', attr: { flex: 'justify-content:between align-items:end' }, html: content.join('\n') })
100
106
  }
101
107
  let text = this.params.attr.headerNowrap ? '' : 'nowrap'
102
- if (text === '' && this.isNoWrap(f, schema, group.body.nowrap)) text = 'nowrap'
103
- if (this.isRightAligned(f, schema)) text += ' align:end'
108
+ if (text === '' && this.isNoWrap(f, group.body.nowrap)) text = 'nowrap'
109
+ if (this.isRightAligned(f)) text += ' align:end'
104
110
  const attr = { dataKey: f, dataType: prop.type, text }
105
111
  items.push(await buildTag({ tag: 'th', attr, html: head }))
106
112
  }
@@ -126,13 +132,13 @@ async function table () {
126
132
  if (selection) {
127
133
  const tag = selection === 'single' ? 'formRadio' : 'formCheck'
128
134
  const attr = { 'x-model': 'selected', name: '_rt', value: d.id, noLabel: true, noWrapper: true }
129
- const type = find(schema.properties, { name: 'id' }).type
135
+ const type = find(this.schema.properties, { name: 'id' }).type
130
136
  const prepend = `<td data-value="${d.id}" data-key="id" data-type="${type}">`
131
137
  lines.push(await buildTag({ tag, attr, prepend, append: '</td>' }))
132
138
  }
133
- for (const f of schema.view.fields) {
139
+ for (const f of this.schema.view.fields) {
134
140
  if (!fields.includes(f)) continue
135
- const prop = find(schema.properties, { name: f })
141
+ const prop = find(this.schema.properties, { name: f })
136
142
  if (!prop) continue
137
143
  let dataValue = d[f]
138
144
  if (['datetime'].includes(prop.type) && dataValue instanceof Date && !isNaN(dataValue)) dataValue = escape(dataValue.toISOString())
@@ -140,16 +146,16 @@ async function table () {
140
146
  let value = this.getRefValue({ field: f, data: d, refName: this.getRefName(f) }) ?? get(d, `_fmt.${f}`, d[f])
141
147
  const attr = { dataValue, dataKey: prop.name, dataType: prop.type }
142
148
  if (!disableds.includes('get')) attr.style = { cursor: 'pointer' }
143
- const formatCell = get(schema, `view.formatCell.${f}`)
149
+ const formatCell = get(this.schema, `view.formatCell.${f}`)
144
150
  if (formatCell) merge(attr, await formatCell.call(this, value, d, { params: this.params, req }))
145
- const noWrap = this.isNoWrap(f, schema, group.body.nowrap) ? 'nowrap' : ''
146
- if (this.isRightAligned(f, schema)) attr.text = `align:end ${noWrap}`
151
+ const noWrap = this.isNoWrap(f, group.body.nowrap) ? 'nowrap' : ''
152
+ if (this.isRightAligned(f)) attr.text = `align:end ${noWrap}`
147
153
  else attr.text = `${noWrap}`
148
154
  if (!hasXSite && d._immutable && d._immutable.length > 0) {
149
155
  if (d._immutable[0] === '*' || d._immutable.includes(f)) attr.text += ' color:body-tertiary'
150
156
  }
151
157
  if (f === 'id') attr.text += ' color:body-tertiary'
152
- const format = get(schema, `view.format.${f}`)
158
+ const format = get(this.schema, `view.format.${f}`)
153
159
  if (format) {
154
160
  const formatted = await format.call(this, value, d, { params: this.params, req })
155
161
  if (isPlainObject(formatted) && formatted.href) {
@@ -163,7 +169,7 @@ async function table () {
163
169
  value = formatted
164
170
  }
165
171
  if (['object', 'array'].includes(prop.type) && !isHtmlLink(value)) value = getTruncated(value, 20) // TODO: should be handle by css instead
166
- if (!get(schema, 'view.noEscape', []).includes(f) && !isHtmlLink(value)) value = escape(value)
172
+ if (!get(this.schema, 'view.noEscape', []).includes(f) && !isHtmlLink(value)) value = escape(value)
167
173
  const line = await buildTag({ tag: 'td', attr, html: value })
168
174
  lines.push(line)
169
175
  }
@@ -183,10 +189,10 @@ async function table () {
183
189
  window.location.href = url
184
190
  }
185
191
  `
186
- const xDataView = get(schema, 'view.x.data', '')
192
+ const xDataView = get(this.schema, 'view.x.data', '')
187
193
  if (!isEmpty(xDataView)) xData += `, ${xDataView}`
188
194
  let xInit = ''
189
- const xInitView = get(schema, 'view.x.init', '')
195
+ const xInitView = get(this.schema, 'view.x.init', '')
190
196
  if (!isEmpty(xInitView)) xInit += `${xInitView}\n`
191
197
 
192
198
  if (selection === 'multi') {
@@ -35,6 +35,7 @@ async function addHandler ({ req, reply, model, params = {}, template, addOnsHan
35
35
  if (!form._addmore) return reply.redirectTo(buildUrl({ url: req.url, base: 'list', params: { page: 1 }, exclude: ['id', 'mode'] }))
36
36
  if (!form._cloneprev) form = pick(form, ['_addmore', '_cloneprev'])
37
37
  } catch (err) {
38
+ if (err.orgMessage === 'accessDenied') throw err
38
39
  error = err
39
40
  }
40
41
  } else {
@@ -20,6 +20,7 @@ async function deleteHandler ({ req, reply, model, params = {}, templateDisabled
20
20
  await removeRecord({ model, id, req, reply, options: opts })
21
21
  result.push(true)
22
22
  } catch (err) {
23
+ if (err.orgMessage === 'accessDenied') throw err
23
24
  console.log(err)
24
25
  result.push(err.message)
25
26
  }
@@ -43,6 +43,7 @@ async function editHandler ({ req, reply, model, id, params = {}, template, addO
43
43
  form = resp.data
44
44
  return reply.redirectTo(buildUrl({ url: req.url, base: req.params.base ?? req.query.base ?? 'list', params: { page: 1 }, exclude: ['id'] }))
45
45
  } catch (err) {
46
+ if (err.orgMessage === 'accessDenied') throw err
46
47
  error = err
47
48
  }
48
49
  }
package/lib/util.js CHANGED
@@ -7,7 +7,7 @@ export async function prepCrud ({ model, id, req, reply, transaction, options =
7
7
  const { pascalCase } = this.app.lib.aneka
8
8
  const { cloneDeep, has, isString, omit } = this.app.lib._
9
9
  const { parseObject } = this.app.lib
10
- const { buildFilterQuery, buildFilterSearch } = await importModule('dobo:/lib/factory/model/_util.js', { asDefaultImport: false })
10
+ const { buildFilterQuery, buildFilterSearch } = await importModule('dobo:/lib/factory/model/helper.js', { asDefaultImport: false })
11
11
 
12
12
  const cfgWeb = this.app.waibu.getConfig()
13
13
  const opts = cloneDeep(omit(options, ['trx']))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waibu-db",
3
- "version": "2.26.4",
3
+ "version": "2.27.1",
4
4
  "description": "DB Helper",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/wiki/CHANGES.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-07-12
4
+
5
+ - [2.27.1] Bug fix in `util.js`
6
+
7
+ ## 2026-06-29
8
+
9
+ - [2.27.0] Add ```isActionAllowed()``` in ```wdb-base```
10
+ - [2.27.0] Add all related action permission on all widgets & page handlers
11
+
3
12
  ## 2026-06-24
4
13
 
5
14
  - [2.26.4] Bug fix in ```wdb-btn-columns``` widget