sumba 2.11.1 → 2.12.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.
@@ -1,7 +1,4 @@
1
- import checkUserId from '../../../lib/check-user-id.js'
2
- import checkTheme from '../../../lib/check-theme.js'
3
- import checkIconset from '../../../lib/check-iconset.js'
4
- import checkTeam from '../../../lib/check-team.js'
1
+ import { checkUserId, checkTeam, checkTheme, checkIconset } from '../../../lib/util.js'
5
2
 
6
3
  const preParsing = {
7
4
  level: 10,
@@ -1,5 +1,4 @@
1
- import checkUserId from '../../../lib/check-user-id.js'
2
- import checkTeam from '../../../lib/check-team.js'
1
+ import { checkUserId, checkTeam } from '../../../lib/util.js'
3
2
 
4
3
  const preParsing = {
5
4
  level: 10,
@@ -1,5 +1,4 @@
1
- import checkUserId from '../../../lib/check-user-id.js'
2
- import checkTeam from '../../../lib/check-team.js'
1
+ import { checkUserId, checkTeam } from '../../../lib/util.js'
3
2
 
4
3
  const preParsing = {
5
4
  level: 10,
@@ -1,5 +1,4 @@
1
- import collectRoutes from '../../../lib/collect-routes.js'
2
- import collectTeam from '../../../lib/collect-team.js'
1
+ import { collectRoutes, collectTeam } from '../../../lib/collect.js'
3
2
 
4
3
  async function afterAppBoot () {
5
4
  const { runHook } = this.app.bajo
@@ -1,7 +1,12 @@
1
+ import { checkNoRouteSetting, checkNoRouteSettingKey } from '../../../lib/util.js'
2
+
1
3
  const preParsing = {
2
4
  level: 10,
3
5
  handler: async function (req, reply) {
6
+ const { get } = this.app.lib._
4
7
  req.site = await this.getSite(req)
8
+ const routes = get(req.site, checkNoRouteSettingKey)
9
+ checkNoRouteSetting.call(this, req, routes)
5
10
  }
6
11
  }
7
12
 
@@ -23,21 +23,38 @@ export async function collect ({ type = '', handler, container, file, ns, dir })
23
23
  }
24
24
  }
25
25
 
26
- function handler (item) {
26
+ function collectHandler (item, keys = []) {
27
27
  const { isString } = this.app.lib._
28
- for (const k of ['methods']) {
28
+ for (const k of keys) {
29
29
  item[k] = item[k] ?? []
30
30
  if (isString(item[k])) item[k] = item[k].split(',')
31
31
  }
32
32
  }
33
33
 
34
- async function collectRoutes (type) {
34
+ export async function collectRoutes (type) {
35
35
  const { eachPlugins } = this.app.bajo
36
36
  const me = this
37
+
38
+ function handler (item) {
39
+ collectHandler.call(this, item, ['methods'])
40
+ }
41
+
37
42
  await eachPlugins(async function ({ file, dir }) {
38
43
  const { ns } = this
39
44
  await collect.call(me, { type, container: 'Routes', handler, file, ns, dir })
40
45
  }, { glob: `route/${type}.*`, prefix: this.ns })
41
46
  }
42
47
 
43
- export default collectRoutes
48
+ export async function collectTeam () {
49
+ const { eachPlugins } = this.app.bajo
50
+ const me = this
51
+
52
+ function handler (item) {
53
+ collectHandler.call(this, item, ['methods', 'teams', 'features'])
54
+ }
55
+
56
+ await eachPlugins(async function ({ file, dir }) {
57
+ const { ns } = this
58
+ await collect.call(me, { type: 'team', container: 'Routes', handler, file, ns, dir })
59
+ }, { glob: 'route/team.*', prefix: this.ns })
60
+ }
package/lib/util.js CHANGED
@@ -1,6 +1,9 @@
1
+ export const checkNoRouteSettingKey = 'setting.waibu.noRoutes.$in'
2
+
1
3
  export function parseNsSettings (ns, setting, items) {
2
- const { trim, set, isPlainObject, isArray, isEmpty, find } = this.app.lib._
4
+ const { trim, set, isPlainObject, isArray, isEmpty, find, get, isString } = this.app.lib._
3
5
  const { parseObject, dayjs } = this.app.lib
6
+ const { routePath } = this.app.waibu
4
7
 
5
8
  for (const item of items) {
6
9
  if (item.ns === '_var' || ns === '_var') continue
@@ -16,7 +19,7 @@ export function parseNsSettings (ns, setting, items) {
16
19
  } catch (err) {}
17
20
  } else if (Number(value)) value = Number(value)
18
21
  else if (['true', 'false'].includes(value)) value = value === 'true'
19
- else if (item.key.endsWith('$in')) value = value.split(',').map(v => v.trim())
22
+ else if (item.key.endsWith('$in')) value = value.split('\n').map(v => v.trim())
20
23
  else {
21
24
  const dt = dayjs(value)
22
25
  if (dt.isValid()) value = dt.toDate()
@@ -24,4 +27,149 @@ export function parseNsSettings (ns, setting, items) {
24
27
  if ((isPlainObject(value) || isArray(value)) && isEmpty(value)) continue
25
28
  set(setting, `${ns}.${item.key}`, value)
26
29
  }
30
+ const key = checkNoRouteSettingKey.slice(checkNoRouteSettingKey.indexOf('.') + 1)
31
+ let noRoutes = get(setting, key, [])
32
+ if (noRoutes.length > 0) {
33
+ noRoutes = noRoutes.map(item => {
34
+ if (!isString(item)) return item
35
+ let [url, methods] = item.split('|')
36
+ if (methods === '*' || !methods) methods = 'GET,POST,PUT,DELETE'
37
+ methods = methods.split(',').map(m => m.trim().toUpperCase())
38
+ return { url: routePath(url), methods }
39
+ })
40
+ set(setting, key, noRoutes)
41
+ }
42
+ }
43
+
44
+ export function checkNoRouteSetting (req, routes) {
45
+ const { isEmpty } = this.app.lib._
46
+ const { outmatch } = this.app.lib
47
+ if (isEmpty(routes)) return
48
+ for (const route of routes) {
49
+ const isMatchUrl = outmatch(route.url)
50
+ if (isMatchUrl(req.url) || isMatchUrl(req.routeOptions.url)) {
51
+ if (route.methods.includes(req.method)) throw this.error('accessDenied', { statusCode: 403 })
52
+ }
53
+ }
54
+ }
55
+
56
+ export function pathsToCheck (req, withHome) {
57
+ const { uniq, without } = this.app.lib._
58
+ return uniq(without([req.routeOptions.url, req.url], undefined, null))
59
+ }
60
+
61
+ export async function checkIconset (req, reply) {
62
+ const { get, isString } = this.app.lib._
63
+ const mpa = this.app.waibuMpa
64
+
65
+ if (!req.site) return
66
+ const siteIconset = get(req, 'site.setting.waibuMpa.iconset')
67
+ req.iconset = siteIconset ?? get(mpa, 'config.iconset.set', 'default')
68
+ const hiconset = req.headers['x-iconset']
69
+ if (isString(hiconset) && mpa.getIconset(hiconset)) req.iconset = hiconset
70
+ req.iconset = req.iconset ?? 'default'
71
+ }
72
+
73
+ export async function checkTheme (req, reply) {
74
+ const { get, isString } = this.app.lib._
75
+ const mpa = this.app.waibuMpa
76
+
77
+ if (!req.site) return
78
+ const siteTheme = get(req, 'site.setting.waibuMpa.theme')
79
+ req.theme = siteTheme ?? get(mpa, 'config.theme.set', 'default')
80
+ const htheme = req.headers['x-theme']
81
+ if (isString(htheme) && mpa.getTheme(htheme)) req.theme = htheme
82
+ req.theme = req.theme ?? 'default'
83
+ }
84
+
85
+ export async function checkTeam (req, reply, source) {
86
+ const { get, isEmpty } = this.app.lib._
87
+ if (!req.user) return
88
+ const { map } = this.app.lib._
89
+ const paths = pathsToCheck.call(this, req, true)
90
+ const teams = map(req.user.teams, 'alias')
91
+ let match = this.checkPathsByTeam({ paths, method: req.method, teams, guards: this.teamRoutes })
92
+ if (!match) match = this.checkPathsByTeam({ paths, method: req.method, teams, guards: this.teamNegRoutes })
93
+ if (!match) {
94
+ const guards = map(this.teamRoutes, 'path')
95
+ match = !this.checkPathsByGuard({ paths, guards })
96
+ }
97
+ if (!match) throw this.error('accessDenied', { statusCode: 403 })
98
+ const routes = []
99
+ for (const team of (req.user.teams ?? [])) {
100
+ const items = get(team, checkNoRouteSettingKey, [])
101
+ if (isEmpty(items)) continue
102
+ routes.push(...items)
103
+ }
104
+ checkNoRouteSetting.call(this, req, routes)
105
+ }
106
+
107
+ export async function checkUserId (req, reply, source) {
108
+ const { merge, isEmpty, camelCase, get } = this.app.lib._
109
+ const { routePath } = this.app.waibu
110
+ const userId = get(req, 'session.userId')
111
+
112
+ const setUser = async () => {
113
+ const id = get(req, 'session.userId')
114
+ if (!id) return
115
+ try {
116
+ const user = await this.getUser(id)
117
+ if (user) req.user = user
118
+ else req.session.userId = null
119
+ } catch (err) {
120
+ console.log(err)
121
+ req.session.userId = null
122
+ }
123
+ }
124
+
125
+ if (req.session) req.session.siteId = req.site.id
126
+
127
+ const webApp = get(req, 'routeOptions.config.webApp', 'waibu')
128
+ if (!req.routeOptions.url) {
129
+ if (!req.session) return
130
+ await setUser()
131
+ return
132
+ }
133
+
134
+ const paths = pathsToCheck.call(this, req)
135
+ let securePath = await this.checkPathsByRoute({ paths, method: req.method, guards: this.secureRoutes })
136
+ if (securePath) {
137
+ const neg = await this.checkPathsByRoute({ paths, method: req.method, guards: this.secureNegRoutes })
138
+ if (neg) securePath = undefined
139
+ }
140
+ let anonymousPath = await this.checkPathsByRoute({ paths, method: req.method, guards: this.anonymousRoutes })
141
+ if (anonymousPath) {
142
+ const neg = await this.checkPathsByRoute({ paths, method: req.method, guards: this.anonymousNegRoutes })
143
+ if (neg) anonymousPath = undefined
144
+ }
145
+ if (!securePath && !anonymousPath) {
146
+ if (userId) await setUser()
147
+ return
148
+ }
149
+ if (anonymousPath) {
150
+ if (!userId) return
151
+ req.session.ref = req.url
152
+ return reply.redirectTo(routePath(this.config.redirect.signout))
153
+ }
154
+ if (securePath) {
155
+ if (userId) {
156
+ await setUser()
157
+ return
158
+ }
159
+ const silentOnError = this.config.auth[webApp].silentOnError ?? this.config.auth.common.silentOnError
160
+ const payload = silentOnError ? { noContent: true } : undefined
161
+ const authMethods = this.config.auth[webApp].methods ?? []
162
+ if (isEmpty(authMethods)) throw this.error('noAuthMethod', merge({ statusCode: 500 }, payload))
163
+ let success
164
+ for (const m of authMethods) {
165
+ const handler = this[camelCase(`verify ${m}`)]
166
+ if (!handler) throw this.error('invalidAuthMethod%s', m, merge({ statusCode: 500 }, payload))
167
+ const check = await handler(req, reply, source, payload)
168
+ if (check) {
169
+ success = check
170
+ break
171
+ }
172
+ }
173
+ if (!success) throw this.error('accessDeniedNoAuth', merge({ statusCode: 403 }, payload))
174
+ }
27
175
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sumba",
3
- "version": "2.11.1",
3
+ "version": "2.12.0",
4
4
  "description": "Biz Suite for Bajo Framework",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/wiki/CHANGES.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## 2026-03-22
4
4
 
5
+ - [2.12.0] Add no route settings sitewise
6
+ - [2.12.0] Add no route settings for teams
7
+ - [2.12.0] Some organizatorial changes in module files
8
+
9
+ ## 2026-03-22
10
+
5
11
  - [2.11.0] Add ```Team Setting``` feature
6
12
  - [2.11.0] Rewrite ```getUser()```
7
13
  - [2.11.0] Rewrite ```getSite()```
@@ -1,13 +0,0 @@
1
- async function checkIconset (req, reply) {
2
- const { get, isString } = this.app.lib._
3
- const mpa = this.app.waibuMpa
4
-
5
- if (!req.site) return
6
- const siteIconset = get(req, 'site.setting.waibuMpa.iconset')
7
- req.iconset = siteIconset ?? get(mpa, 'config.iconset.set', 'default')
8
- const hiconset = req.headers['x-iconset']
9
- if (isString(hiconset) && mpa.getIconset(hiconset)) req.iconset = hiconset
10
- req.iconset = req.iconset ?? 'default'
11
- }
12
-
13
- export default checkIconset
package/lib/check-team.js DELETED
@@ -1,19 +0,0 @@
1
- import { pathsToCheck } from './check-user-id.js'
2
-
3
- async function checkTeam (req, reply, source) {
4
- if (!req.user) return
5
- const { map } = this.app.lib._
6
- const paths = pathsToCheck.call(this, req, true)
7
- const teams = map(req.user.teams, 'alias')
8
- const match = this.checkPathsByTeam({ paths, method: req.method, teams, guards: this.teamRoutes })
9
- if (match) return
10
- const negMatch = this.checkPathsByTeam({ paths, method: req.method, teams, guards: this.teamNegRoutes })
11
- if (negMatch) return
12
- const guards = map(this.teamRoutes, 'path')
13
- // fallback
14
- const guarded = this.checkPathsByGuard({ paths, guards })
15
- if (!guarded) return
16
- throw this.error('accessDenied', { statusCode: 403 })
17
- }
18
-
19
- export default checkTeam
@@ -1,13 +0,0 @@
1
- async function checkTheme (req, reply) {
2
- const { get, isString } = this.app.lib._
3
- const mpa = this.app.waibuMpa
4
-
5
- if (!req.site) return
6
- const siteTheme = get(req, 'site.setting.waibuMpa.theme')
7
- req.theme = siteTheme ?? get(mpa, 'config.theme.set', 'default')
8
- const htheme = req.headers['x-theme']
9
- if (isString(htheme) && mpa.getTheme(htheme)) req.theme = htheme
10
- req.theme = req.theme ?? 'default'
11
- }
12
-
13
- export default checkTheme
@@ -1,76 +0,0 @@
1
- export function pathsToCheck (req, withHome) {
2
- const { uniq, without } = this.app.lib._
3
- return uniq(without([req.routeOptions.url, req.url], undefined, null))
4
- }
5
-
6
- async function setUser (req) {
7
- const { get } = this.app.lib._
8
- const id = get(req, 'session.userId')
9
- if (!id) return
10
- try {
11
- const user = await this.getUser(id)
12
- if (user) req.user = user
13
- else req.session.userId = null
14
- } catch (err) {
15
- console.log(err)
16
- req.session.userId = null
17
- }
18
- }
19
-
20
- async function checkUserId (req, reply, source) {
21
- const { merge, isEmpty, camelCase, get } = this.app.lib._
22
- const { routePath } = this.app.waibu
23
- const userId = get(req, 'session.userId')
24
- if (req.session) req.session.siteId = req.site.id
25
-
26
- const webApp = get(req, 'routeOptions.config.webApp', 'waibu')
27
- if (!req.routeOptions.url) {
28
- if (!req.session) return
29
- await setUser.call(this, req)
30
- return
31
- }
32
-
33
- const paths = pathsToCheck.call(this, req)
34
- let securePath = await this.checkPathsByRoute({ paths, method: req.method, guards: this.secureRoutes })
35
- if (securePath) {
36
- const neg = await this.checkPathsByRoute({ paths, method: req.method, guards: this.secureNegRoutes })
37
- if (neg) securePath = undefined
38
- }
39
- let anonymousPath = await this.checkPathsByRoute({ paths, method: req.method, guards: this.anonymousRoutes })
40
- if (anonymousPath) {
41
- const neg = await this.checkPathsByRoute({ paths, method: req.method, guards: this.anonymousNegRoutes })
42
- if (neg) anonymousPath = undefined
43
- }
44
- if (!securePath && !anonymousPath) {
45
- if (userId) await setUser.call(this, req)
46
- return
47
- }
48
- if (anonymousPath) {
49
- if (!userId) return
50
- req.session.ref = req.url
51
- return reply.redirectTo(routePath(this.config.redirect.signout))
52
- }
53
- if (securePath) {
54
- if (userId) {
55
- await setUser.call(this, req)
56
- return
57
- }
58
- const silentOnError = this.config.auth[webApp].silentOnError ?? this.config.auth.common.silentOnError
59
- const payload = silentOnError ? { noContent: true } : undefined
60
- const authMethods = this.config.auth[webApp].methods ?? []
61
- if (isEmpty(authMethods)) throw this.error('noAuthMethod', merge({ statusCode: 500 }, payload))
62
- let success
63
- for (const m of authMethods) {
64
- const handler = this[camelCase(`verify ${m}`)]
65
- if (!handler) throw this.error('invalidAuthMethod%s', m, merge({ statusCode: 500 }, payload))
66
- const check = await handler(req, reply, source, payload)
67
- if (check) {
68
- success = check
69
- break
70
- }
71
- }
72
- if (!success) throw this.error('accessDeniedNoAuth', merge({ statusCode: 403 }, payload))
73
- }
74
- }
75
-
76
- export default checkUserId
@@ -1,5 +0,0 @@
1
- async function collectRedirects () {
2
- this.redirects = []
3
- }
4
-
5
- export default collectRedirects
@@ -1,20 +0,0 @@
1
- import { collect } from './collect-routes.js'
2
-
3
- function handler (item) {
4
- const { isString } = this.app.lib._
5
- for (const k of ['methods', 'teams', 'features']) {
6
- item[k] = item[k] ?? []
7
- if (isString(item[k])) item[k] = item[k].split(',')
8
- }
9
- }
10
-
11
- async function collectTeam () {
12
- const { eachPlugins } = this.app.bajo
13
- const me = this
14
- await eachPlugins(async function ({ file, dir }) {
15
- const { ns } = this
16
- await collect.call(me, { type: 'team', container: 'Routes', handler, file, ns, dir })
17
- }, { glob: 'route/team.*', prefix: this.ns })
18
- }
19
-
20
- export default collectTeam