sumba 2.22.1 → 2.24.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.
Files changed (46) hide show
  1. package/extend/bajo/hook/bajo.extend@after-read-config.js +13 -0
  2. package/extend/bajo/hook/dobo.sumba-route-guard@after-transaction.js +6 -0
  3. package/extend/bajo/hook/dobo@before-count-record.js +3 -3
  4. package/extend/bajo/hook/dobo@before-driver-create-record.js +17 -0
  5. package/extend/bajo/hook/dobo@before-driver-find-all-record.js +13 -0
  6. package/extend/bajo/hook/dobo@before-driver-find-record.js +96 -0
  7. package/extend/bajo/hook/dobo@before-driver-get-record.js +22 -0
  8. package/extend/bajo/hook/dobo@before-driver-remove-record.js +10 -0
  9. package/extend/bajo/hook/dobo@before-driver-update-record.js +10 -0
  10. package/extend/bajo/hook/waibu-mpa@pre-parsing.js +5 -4
  11. package/extend/bajo/hook/waibu-rest-api@pre-parsing.js +5 -4
  12. package/extend/bajo/hook/waibu-static@pre-parsing.js +5 -4
  13. package/extend/bajo/hook/waibu@after-app-boot.js +5 -23
  14. package/extend/bajo/hook/waibu@pre-parsing.js +0 -5
  15. package/extend/bajo/intl/en-US.json +13 -6
  16. package/extend/bajo/intl/id.json +14 -2
  17. package/extend/dobo/feature/team-ids.js +20 -0
  18. package/extend/dobo/fixture/route-guard.js +26 -0
  19. package/extend/dobo/model/attrib-guard.js +36 -0
  20. package/extend/dobo/model/model-guard.js +37 -0
  21. package/extend/dobo/model/route-guard.js +45 -0
  22. package/extend/waibuDb/schema/attrib-guard.js +15 -0
  23. package/extend/waibuDb/schema/model-guard.js +15 -0
  24. package/extend/waibuDb/schema/route-guard.js +15 -0
  25. package/extend/waibuDb/schema/user.js +19 -26
  26. package/extend/waibuMpa/extend/waibuAdmin/route/all-sites/@action.js +1 -1
  27. package/extend/waibuMpa/extend/waibuAdmin/route/attrib-guard/@action.js +11 -0
  28. package/extend/waibuMpa/extend/waibuAdmin/route/cache/@action.js +1 -1
  29. package/extend/waibuMpa/extend/waibuAdmin/route/model-guard/@action.js +11 -0
  30. package/extend/waibuMpa/extend/waibuAdmin/route/reset-user-password.js +2 -1
  31. package/extend/waibuMpa/extend/waibuAdmin/route/route-guard/@action.js +11 -0
  32. package/extend/waibuMpa/extend/waibuAdmin/route/session/@action.js +1 -1
  33. package/extend/waibuRestApi/route/manage/route-guard/model-builder.json +4 -0
  34. package/index.js +37 -47
  35. package/lib/get-user.js +4 -4
  36. package/lib/util.js +53 -74
  37. package/package.json +1 -1
  38. package/wiki/CHANGES.md +11 -0
  39. package/extend/bajo/hook/dobo@before-create-record.js +0 -17
  40. package/extend/bajo/hook/dobo@before-find-record.js +0 -63
  41. package/extend/bajo/hook/dobo@before-get-record.js +0 -23
  42. package/extend/bajo/hook/dobo@before-remove-record.js +0 -10
  43. package/extend/bajo/hook/dobo@before-update-record.js +0 -10
  44. package/extend/sumba/route-guard/anonymous.json +0 -10
  45. package/extend/sumba/route-guard/secure.json +0 -8
  46. package/lib/collect.js +0 -52
@@ -0,0 +1,13 @@
1
+ import path from 'path'
2
+
3
+ async function afterReadConfig (file, result, options) {
4
+ const base = path.basename(file, path.extname(file))
5
+ // rewrite fixtures
6
+ if (!(base === 'route-guard' && Array.isArray(result) && file.includes('/fixture/'))) return
7
+ for (const res of result) {
8
+ if (res.path.slice(0, 2) === ':/') res.path = options.sourceNs + res.path
9
+ res.path = res.path.replaceAll('{ns}', options.sourceNs)
10
+ }
11
+ }
12
+
13
+ export default afterReadConfig
@@ -0,0 +1,6 @@
1
+ async function afterTransaction (action, ...args) {
2
+ if (!['createRecord', 'updateRecord', 'removeRecord'].includes(action)) return
3
+ await this.getRouteGuards(true)
4
+ }
5
+
6
+ export default afterTransaction
@@ -1,8 +1,8 @@
1
- import { handler } from './dobo@before-find-record.js'
1
+ import { handler } from './dobo@before-driver-find-record.js'
2
2
 
3
- const doboBeforeCountRecord = {
3
+ const doboBeforeDriverCountRecord = {
4
4
  level: 1000,
5
5
  handler
6
6
  }
7
7
 
8
- export default doboBeforeCountRecord
8
+ export default doboBeforeDriverCountRecord
@@ -0,0 +1,17 @@
1
+ const doboBeforeDriverCreateRecord = {
2
+ level: 1000,
3
+ handler: async function (model, body, options = {}) {
4
+ const { get } = this.app.lib._
5
+ const { isSet } = this.app.lib.aneka
6
+ const { req } = options
7
+ if (options.noAutoFilter || !req || get(req, 'routeOptions.config.crossSite')) return
8
+ const item = { siteId: 'site.id', userId: 'user.id' }
9
+ for (const i in item) {
10
+ const rec = get(req, item[i])
11
+ const field = model.getProperty(i)
12
+ if (rec && field && !isSet(body[i])) body[i] = field.type === 'string' ? (rec + '') : rec
13
+ }
14
+ }
15
+ }
16
+
17
+ export default doboBeforeDriverCreateRecord
@@ -0,0 +1,13 @@
1
+ import { rebuildFilter } from './dobo@before-driver-find-record.js'
2
+
3
+ const doboBeforeDriverFindAllRecord = {
4
+ level: 1000,
5
+ handler: async function (model, filter, options) {
6
+ const { req } = options
7
+ const { isEmpty } = this.app.lib._
8
+ if (options.noAutoFilter || !req || isEmpty(req.site)) return
9
+ await rebuildFilter.call(this, model, filter, options)
10
+ }
11
+ }
12
+
13
+ export default doboBeforeDriverFindAllRecord
@@ -0,0 +1,96 @@
1
+ async function applyModelGuard ({ model, q, teamIds, options }) {
2
+ const { isEmpty, get, set, uniq } = this.app.lib._
3
+ const { req } = options
4
+ const { getModel } = this.app.dobo
5
+
6
+ const guards = []
7
+ const query = { status: 'ACTIVE', siteId: req.site.id + '' }
8
+ const results = await getModel('SumbaModelGuard').findAllRecord({ query }, { noMagic: true, dataOnly: true, noDriverHook: true })
9
+ const item = {}
10
+
11
+ function add (res) {
12
+ item[res.column].push(...res.value)
13
+ }
14
+
15
+ for (const res of results) {
16
+ res.teamIds = res.teamIds ?? []
17
+ if (!(res.models ?? []).includes(model.name) || isEmpty(res.value)) continue
18
+ item[res.column] = item[res.column] ?? []
19
+ if (teamIds.length > 0) {
20
+ for (const id of res.teamIds) {
21
+ if (teamIds.includes(id)) add(res)
22
+ }
23
+ }
24
+ }
25
+ for (const key in item) {
26
+ item[key] = uniq(item[key])
27
+ if (item[key].length === 0) continue
28
+ guards.push(set({}, key, { $in: item[key] }))
29
+ }
30
+
31
+ const allowEmpty = get(this, `config.dobo.model.${model.name}.allowEmptyQuery`, true)
32
+ if (guards.length === 0 && !allowEmpty) throw this.error('_emptyColumnQuery') // signal driver to about with no result immediately
33
+ q.$and.push(...guards)
34
+ }
35
+
36
+ export async function applyAttribGuard ({ model, teamIds, options }) {
37
+ const { getModel } = this.app.dobo
38
+ const { uniq } = this.app.lib._
39
+ const { req } = options
40
+
41
+ const query = { status: 'ACTIVE', siteId: req.site.id + '' }
42
+ const results = await getModel('SumbaAttribGuard').findAllRecord({ query }, { noMagic: true, dataOnly: true, noDriverHook: true })
43
+ const result = results.find(item => (item.models ?? []).includes(model.name))
44
+ if (!result) return
45
+ options.hidden = options.hidden ?? []
46
+ for (const id of result.teamIds ?? []) {
47
+ if (teamIds.includes(id)) options.hidden.push(...(result.hiddenCols ?? []))
48
+ }
49
+ options.hidden = uniq(options.hidden)
50
+ }
51
+
52
+ export async function rebuildFilter (model, filter = {}, options = {}) {
53
+ const { isEmpty, get } = this.app.lib._
54
+ const { req } = options
55
+ const hasSiteId = model.hasProperty('siteId')
56
+ const hasUserId = model.hasProperty('userId')
57
+ const hasTeamId = model.hasProperty('teamId')
58
+ const teams = get(req, 'user.teams', [])
59
+ const teamIds = teams.map(team => team.id + '')
60
+ const aliases = teams.map(team => team.alias)
61
+ const q = { $and: [] }
62
+
63
+ filter.query = filter.query ?? {}
64
+ if (!isEmpty(filter.query)) {
65
+ if (filter.query.$and) q.$and.push(...filter.query.$and)
66
+ else q.$and.push(filter.query)
67
+ }
68
+ if (req.routeOptions.config.crossSite) return
69
+ if (hasSiteId) q.$and.push({ siteId: req.site.id + '' })
70
+ if (aliases.includes('administrator')) {
71
+ filter.query = q
72
+ return
73
+ }
74
+
75
+ if (hasTeamId) {
76
+ if (hasUserId) q.$and.push({ $or: [{ teamId: { $in: teamIds } }, { userId: req.user.id + '' }] })
77
+ else q.$and.push({ teamId: { $in: teamIds } })
78
+ } else if (hasUserId) q.$and.push({ userId: req.user.id + '' })
79
+
80
+ await applyModelGuard.call(this, { model, q, teamIds, options })
81
+ await applyAttribGuard.call(this, { model, teamIds, options })
82
+ filter.query = q
83
+ }
84
+
85
+ export async function handler (model, filter, options = {}) {
86
+ const { isEmpty } = this.app.lib._
87
+ if (options.noAutoFilter || !options.req || isEmpty((options.req ?? {}).site)) return
88
+ await rebuildFilter.call(this, model, filter, options)
89
+ }
90
+
91
+ const doboBeforeDriverFindRecord = {
92
+ level: 1000,
93
+ handler
94
+ }
95
+
96
+ export default doboBeforeDriverFindRecord
@@ -0,0 +1,22 @@
1
+ import { rebuildFilter } from './dobo@before-driver-find-record.js'
2
+
3
+ export async function checker (model, id, options = {}) {
4
+ const { req } = options
5
+
6
+ if (options.noAutoFilter || !req) return
7
+ const filter = {}
8
+ await rebuildFilter.call(this, model, filter, options)
9
+ if (filter.query.$and) filter.query.$and.push({ id })
10
+ else filter.query.id = id
11
+ const row = await model.findOneRecord(filter, { count: false })
12
+ if (!row) throw this.app.dobo.error('recordNotFound%s%s', id, this.name, { statusCode: 404 })
13
+ }
14
+
15
+ const doboBeforeDriverGetRecord = {
16
+ level: 1000,
17
+ handler: async function (model, id, options) {
18
+ await checker.call(this, model, id, options)
19
+ }
20
+ }
21
+
22
+ export default doboBeforeDriverGetRecord
@@ -0,0 +1,10 @@
1
+ import { checker } from './dobo@before-driver-get-record.js'
2
+
3
+ const doboBeforeDriverRemoveRecord = {
4
+ level: 1000,
5
+ handler: async function (model, id, options = {}) {
6
+ await checker.call(this, model, id, options.req)
7
+ }
8
+ }
9
+
10
+ export default doboBeforeDriverRemoveRecord
@@ -0,0 +1,10 @@
1
+ import { checker } from './dobo@before-driver-get-record.js'
2
+
3
+ const doboBeforeDriverUpdateRecord = {
4
+ level: 1000,
5
+ handler: async function (model, id, body, options = {}) {
6
+ await checker.call(this, model, id, options)
7
+ }
8
+ }
9
+
10
+ export default doboBeforeDriverUpdateRecord
@@ -1,13 +1,14 @@
1
- import { checkUserId, checkTeam, checkTheme, checkIconset, checkinterSite } from '../../../lib/util.js'
1
+ import { checkUserId, checkTeam, checkTheme, checkIconset, checkCrossSite } from '../../../lib/util.js'
2
2
 
3
3
  const preParsing = {
4
4
  level: 10,
5
5
  handler: async function (req, reply) {
6
6
  await checkTheme.call(this, req, reply)
7
7
  await checkIconset.call(this, req, reply)
8
- await checkUserId.call(this, req, reply, 'waibuMpa')
9
- await checkTeam.call(this, req, reply, 'waibuMpa')
10
- await checkinterSite.call(this, req, reply)
8
+ const secure = await checkUserId.call(this, req, reply, 'waibuMpa')
9
+ if (!secure) return
10
+ await checkTeam.call(this, req, reply, secure)
11
+ await checkCrossSite.call(this, req, reply)
11
12
  }
12
13
  }
13
14
 
@@ -1,11 +1,12 @@
1
- import { checkUserId, checkTeam, checkinterSite } from '../../../lib/util.js'
1
+ import { checkUserId, checkTeam, checkCrossSite } from '../../../lib/util.js'
2
2
 
3
3
  const preParsing = {
4
4
  level: 10,
5
5
  handler: async function (req, reply) {
6
- await checkUserId.call(this, req, reply, 'waibuRestApi')
7
- await checkTeam.call(this, req, reply, 'waibuRestApi')
8
- await checkinterSite.call(this, req, reply)
6
+ const secure = await checkUserId.call(this, req, reply, 'waibuRestApi')
7
+ if (!secure) return
8
+ await checkTeam.call(this, req, reply, secure)
9
+ await checkCrossSite.call(this, req, reply)
9
10
  }
10
11
  }
11
12
 
@@ -1,11 +1,12 @@
1
- import { checkUserId, checkTeam, checkinterSite } from '../../../lib/util.js'
1
+ import { checkUserId, checkTeam, checkCrossSite } from '../../../lib/util.js'
2
2
 
3
3
  const preParsing = {
4
4
  level: 10,
5
5
  handler: async function (req, reply) {
6
- await checkUserId.call(this, req, reply, 'waibuStatic')
7
- await checkTeam.call(this, req, reply, 'waibuStatic')
8
- await checkinterSite.call(this, req, reply)
6
+ const secure = await checkUserId.call(this, req, reply, 'waibuStatic')
7
+ if (!secure) return
8
+ await checkTeam.call(this, req, reply, secure)
9
+ await checkCrossSite.call(this, req, reply)
9
10
  }
10
11
  }
11
12
 
@@ -1,23 +1,5 @@
1
- import { collectRoutes, collectTeam } from '../../../lib/collect.js'
2
-
3
- async function afterAppBoot () {
4
- const { runHook } = this.app.bajo
5
- await runHook(`${this.ns}:beforeBoot`)
6
- this.log.trace('collectingRouteGuards')
7
- await collectRoutes.call(this, 'secure')
8
- await runHook(`${this.ns}:afterCollectSecureRoutes`, this.secureRoutes, this.secureNegRoutes)
9
- this.log.trace('secureRoutes%d', this.secureRoutes.length)
10
- this.log.trace('secureNegRoutes%d', this.secureNegRoutes.length)
11
- await collectRoutes.call(this, 'anonymous')
12
- await runHook(`${this.ns}:afterCollectAnonymousRoutes`, this.anonymousRoutes, this.anonymousNegRoutes)
13
- this.log.trace('anonRoutes%d', this.anonymousRoutes.length)
14
- this.log.trace('anonNegRoutes%d', this.anonymousNegRoutes.length)
15
- this.log.trace('collectingTeamGuards')
16
- await collectTeam.call(this)
17
- await runHook(`${this.ns}:afterCollectTeamRoutes`, this.teamRoutes, this.teamNegRoutes)
18
- this.log.trace('teamRoutes%d', this.teamRoutes.length)
19
- this.log.trace('teamNegRoutes%d', this.teamNegRoutes.length)
20
- await runHook(`${this.ns}:afterBoot`)
21
- }
22
-
23
- export default afterAppBoot
1
+ async function afterAppBoot () {
2
+ await this.getRouteGuards(true)
3
+ }
4
+
5
+ export default afterAppBoot
@@ -1,13 +1,8 @@
1
- import { checkNoRouteSetting, checkNoRouteSettingKey } from '../../../lib/util.js'
2
-
3
1
  const preParsing = {
4
2
  level: 10,
5
3
  handler: async function (req, reply) {
6
- const { get } = this.app.lib._
7
4
  const { getHostname } = this.app.waibu
8
5
  req.site = await this.getSite(getHostname(req))
9
- const routes = get(req.site, checkNoRouteSettingKey)
10
- checkNoRouteSetting.call(this, req, routes)
11
6
  }
12
7
  }
13
8
 
@@ -61,11 +61,6 @@
61
61
  "troubleTickets": "Trouble Tickets",
62
62
  "guest": "Guest",
63
63
  "warningMemberOnly%s": "Please authenticate yourself first, because the <a href=\"%s\">page<a> your're trying to access is a member only page",
64
- "collectingRouteGuards": "Collecting route guards:",
65
- "secureRoutes%d": "- Secure routes: %d",
66
- "secureNegRoutes%d": "- Secure, negated routes: %d",
67
- "anonRoutes%d": "- Anonymous routes: %d",
68
- "anonNegRoutes%d": "- Anonymous, negated routes: %d",
69
64
  "replies": "Replies",
70
65
  "compose": "Compose",
71
66
  "yourReply": "Your Reply",
@@ -136,6 +131,10 @@
136
131
  "statusOpen": "Open",
137
132
  "statusClosed": "Closed",
138
133
  "allSites": "All Sites",
134
+ "permission": "Permission",
135
+ "routeGuard": "Route",
136
+ "modelGuard": "Model",
137
+ "attribGuard": "Attribute",
139
138
  "field": {
140
139
  "currentPassword": "Current Password",
141
140
  "newPassword": "New Password",
@@ -159,7 +158,15 @@
159
158
  "team": "Team",
160
159
  "ns": "Module's Namespace",
161
160
  "value": "Value",
162
- "notes": "Notes"
161
+ "notes": "Notes",
162
+ "path": "Path",
163
+ "inverse": "Inverse?",
164
+ "anonymous": "Anonymous?",
165
+ "methods": "Methods",
166
+ "teamIds": "Teams",
167
+ "column": "Column",
168
+ "hiddenCols": "Hidden Columns",
169
+ "models": "Models"
163
170
  },
164
171
  "validation": {
165
172
  "password": {
@@ -137,6 +137,10 @@
137
137
  "statusOpen": "Terbuka",
138
138
  "statusClosed": "Tertutup",
139
139
  "allSites": "Semua Situs",
140
+ "permission": "Permisi",
141
+ "routeGuard": "Rute",
142
+ "modelGuard": "Model",
143
+ "attribGuard": "Atribut",
140
144
  "field": {
141
145
  "currentPassword": "Kata Sandi Saat Ini",
142
146
  "newPassword": "Kata Sandi Baru",
@@ -159,8 +163,16 @@
159
163
  "user": "Pengguna",
160
164
  "team": "Tim",
161
165
  "ns": "Ruang Nama Modul",
162
- "value": "Value",
163
- "notes": "Catatan"
166
+ "value": "Nilai",
167
+ "notes": "Catatan",
168
+ "path": "Jejak",
169
+ "inverse": "Inverse?",
170
+ "anonymous": "Anonim?",
171
+ "methods": "Metode",
172
+ "teamIds": "Tim",
173
+ "column": "Kolom",
174
+ "hideCols": "Sembunyikan Kolom",
175
+ "models": "Model"
164
176
  },
165
177
  "validation": {
166
178
  "password": {
@@ -0,0 +1,20 @@
1
+ async function teamIds (opts = {}) {
2
+ return {
3
+ properties: [{
4
+ name: 'teamIds',
5
+ type: 'array',
6
+ default: [],
7
+ ref: {
8
+ teamIds: {
9
+ model: 'SumbaTeam',
10
+ field: 'id',
11
+ searchField: 'name',
12
+ fields: ['id', 'alias', 'name']
13
+ }
14
+ },
15
+ index: true
16
+ }]
17
+ }
18
+ }
19
+
20
+ export default teamIds
@@ -0,0 +1,26 @@
1
+ const routes = [
2
+ 'sumba:/your-stuff/**/*',
3
+ 'sumba:/signout',
4
+ 'sumba:/help/trouble-tickets/**/*',
5
+ 'sumba.restapi:/user/api-key',
6
+ 'sumba.restapi:/your-stuff/**/*',
7
+ 'sumba.restapi:/manage/**/*',
8
+ '~sumba:/user/**/*',
9
+ '~sumba:/signin',
10
+ '~sumba.restapi:/user/access-token/**/*'
11
+ ]
12
+
13
+ async function routeGuard () {
14
+ return routes.map(r => {
15
+ const anonymous = r[0] === '~'
16
+ return {
17
+ path: anonymous ? r.slice(1) : r,
18
+ anonymous,
19
+ _immutable: true,
20
+ siteId: '?:SumbaSite::alias:default',
21
+ status: 'ACTIVE'
22
+ }
23
+ })
24
+ }
25
+
26
+ export default routeGuard
@@ -0,0 +1,36 @@
1
+ async function routeGuard () {
2
+ return {
3
+ properties: [
4
+ {
5
+ name: 'models',
6
+ type: 'array',
7
+ required: true
8
+ },
9
+ 'hiddenCols,array',
10
+ 'teamIds,sumba:teamIds'
11
+ ],
12
+ indexes: [{
13
+ type: 'unique',
14
+ fields: ['models', 'siteId']
15
+ }],
16
+ features: [
17
+ {
18
+ name: 'sumba:status',
19
+ values: ['ACTIVE', 'INACTIVE'],
20
+ default: 'ACTIVE'
21
+ },
22
+ 'dobo:immutable',
23
+ 'dobo:updatedAt',
24
+ 'sumba:siteId'
25
+ ],
26
+ options: {
27
+ attachment: false
28
+ },
29
+ buildEnd: async function (model) {
30
+ const prop = model.properties.find(prop => prop.name === 'models')
31
+ prop.values = this.app.dobo.models.map(model => model.name).sort().map(item => ({ value: item, text: item }))
32
+ }
33
+ }
34
+ }
35
+
36
+ export default routeGuard
@@ -0,0 +1,37 @@
1
+ async function routeGuard () {
2
+ return {
3
+ properties: [
4
+ {
5
+ name: 'models',
6
+ type: 'array',
7
+ required: true
8
+ },
9
+ 'column,,50,true,true',
10
+ 'value,array',
11
+ 'teamIds,sumba:teamIds'
12
+ ],
13
+ indexes: [{
14
+ type: 'unique',
15
+ fields: ['models', 'column', 'siteId']
16
+ }],
17
+ features: [
18
+ {
19
+ name: 'sumba:status',
20
+ values: ['ACTIVE', 'INACTIVE'],
21
+ default: 'ACTIVE'
22
+ },
23
+ 'dobo:immutable',
24
+ 'dobo:updatedAt',
25
+ 'sumba:siteId'
26
+ ],
27
+ options: {
28
+ attachment: false
29
+ },
30
+ buildEnd: async function (model) {
31
+ const prop = model.properties.find(prop => prop.name === 'models')
32
+ prop.values = this.app.dobo.models.map(model => model.name).sort().map(item => ({ value: item, text: item }))
33
+ }
34
+ }
35
+ }
36
+
37
+ export default routeGuard
@@ -0,0 +1,45 @@
1
+ async function routeGuard () {
2
+ return {
3
+ properties: [
4
+ 'path,,255,true,true',
5
+ {
6
+ name: 'methods',
7
+ type: 'array',
8
+ required: true,
9
+ default: ['GET', 'POST', 'UPDATE', 'DELETE'],
10
+ values: ['GET', 'POST', 'UPDATE', 'DELETE']
11
+ },
12
+ 'teamIds,sumba:teamIds',
13
+ {
14
+ name: 'weight',
15
+ type: 'smallint',
16
+ default: 0
17
+ }, {
18
+ name: 'inverse',
19
+ type: 'boolean',
20
+ required: true,
21
+ default: false
22
+ }, {
23
+ name: 'anonymous',
24
+ type: 'boolean',
25
+ required: true,
26
+ default: false
27
+ }
28
+ ],
29
+ features: [
30
+ {
31
+ name: 'sumba:status',
32
+ values: ['ACTIVE', 'INACTIVE'],
33
+ default: 'ACTIVE'
34
+ },
35
+ 'dobo:immutable',
36
+ 'dobo:updatedAt',
37
+ 'sumba:siteId'
38
+ ],
39
+ options: {
40
+ attachment: false
41
+ }
42
+ }
43
+ }
44
+
45
+ export default routeGuard
@@ -0,0 +1,15 @@
1
+ async function attribGuard () {
2
+ return {
3
+ common: {
4
+ widget: {
5
+ teamIds: {
6
+ attr: {
7
+ refUrl: 'waibuAdmin:/{prefix}/team/details?id={id}'
8
+ }
9
+ }
10
+ }
11
+ }
12
+ }
13
+ }
14
+
15
+ export default attribGuard
@@ -0,0 +1,15 @@
1
+ async function modelGuard () {
2
+ return {
3
+ common: {
4
+ widget: {
5
+ teamIds: {
6
+ attr: {
7
+ refUrl: 'waibuAdmin:/{prefix}/team/details?id={id}'
8
+ }
9
+ }
10
+ }
11
+ }
12
+ }
13
+ }
14
+
15
+ export default modelGuard
@@ -0,0 +1,15 @@
1
+ async function routeGuard () {
2
+ return {
3
+ common: {
4
+ widget: {
5
+ teamIds: {
6
+ attr: {
7
+ refUrl: 'waibuAdmin:/{prefix}/team/details?id={id}'
8
+ }
9
+ }
10
+ }
11
+ }
12
+ }
13
+ }
14
+
15
+ export default routeGuard