sumba 2.10.0 → 2.11.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.
- package/extend/bajo/hook/dobo@before-find-record.js +18 -10
- package/extend/bajo/hook/dobo@before-get-record.js +2 -1
- package/extend/bajo/hook/waibu@pre-parsing.js +1 -2
- package/extend/bajo/intl/en-US.json +1 -0
- package/extend/bajo/intl/id.json +1 -0
- package/extend/dobo/feature/team-id.js +9 -1
- package/extend/dobo/model/site.json +2 -2
- package/extend/dobo/model/team-setting.json +17 -0
- package/extend/dobo/model/team-user.json +2 -14
- package/extend/masohiSocketIo/middleware/server/auth.js +0 -1
- package/extend/waibuDb/schema/team-setting.js +51 -0
- package/extend/waibuDb/schema/team-user.js +2 -2
- package/extend/waibuMpa/extend/waibuAdmin/route/team-setting/@action.js +11 -0
- package/index.js +9 -33
- package/lib/check-team.js +0 -1
- package/lib/get-site.js +12 -22
- package/lib/get-user.js +50 -0
- package/lib/util.js +27 -0
- package/package.json +1 -1
- package/wiki/CHANGES.md +8 -0
|
@@ -4,11 +4,22 @@ export async function rebuildFilter (modelName, filter, req) {
|
|
|
4
4
|
const { isEmpty, isPlainObject, map, find, get } = this.app.lib._
|
|
5
5
|
filter.query = filter.query ?? {}
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
|
|
7
|
+
const queryBySiteSetting = (query) => {
|
|
8
|
+
if (!req.site) return
|
|
9
9
|
const setting = get(req, `site.setting.dobo.query.${modelName}`)
|
|
10
10
|
if (isPlainObject(setting) && !isEmpty(setting)) query.$and.push(setting)
|
|
11
|
-
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const queryByTeamSetting = (query) => {
|
|
14
|
+
if (!req.user) return
|
|
15
|
+
const q = []
|
|
16
|
+
for (const team of req.user.teams) {
|
|
17
|
+
const item = get(team, `setting.dobo.query.${modelName}`)
|
|
18
|
+
if (item) q.push(item)
|
|
19
|
+
}
|
|
20
|
+
if (isEmpty(q)) return
|
|
21
|
+
if (q.length === 1) query.$and.push(q[0])
|
|
22
|
+
else query.$and.push({ $or: q })
|
|
12
23
|
}
|
|
13
24
|
|
|
14
25
|
const model = this.app.dobo.getModel(modelName)
|
|
@@ -17,14 +28,12 @@ export async function rebuildFilter (modelName, filter, req) {
|
|
|
17
28
|
const hasTeamId = model.hasProperty('teamId')
|
|
18
29
|
const isAdmin = find(get(req, 'user.teams', []), { alias: 'administrator' }) && useAdmin.includes(get(req, 'routeOptions.config.ns'))
|
|
19
30
|
const q = { $and: [] }
|
|
31
|
+
queryBySiteSetting(q)
|
|
32
|
+
queryByTeamSetting(q)
|
|
20
33
|
if (!isEmpty(filter.query)) {
|
|
21
34
|
if (filter.query.$and) q.$and.push(...filter.query.$and)
|
|
22
35
|
else q.$and.push(filter.query)
|
|
23
36
|
}
|
|
24
|
-
if (!(hasSiteId || hasUserId || hasTeamId)) {
|
|
25
|
-
filter.query = queryByModel(q)
|
|
26
|
-
return filter
|
|
27
|
-
}
|
|
28
37
|
if (hasSiteId) q.$and.push({ siteId: req.site.id })
|
|
29
38
|
if (hasTeamId && !isAdmin) {
|
|
30
39
|
const teamIds = map(req.user.teams, 'id')
|
|
@@ -33,14 +42,13 @@ export async function rebuildFilter (modelName, filter, req) {
|
|
|
33
42
|
} else if (!isAdmin) {
|
|
34
43
|
if (hasUserId) q.$and.push({ userId: req.user.id })
|
|
35
44
|
}
|
|
36
|
-
filter.query =
|
|
37
|
-
return filter
|
|
45
|
+
filter.query = q
|
|
38
46
|
}
|
|
39
47
|
|
|
40
48
|
export async function handler (modelName, filter, options = {}) {
|
|
41
49
|
const { req } = options
|
|
42
50
|
if (options.noAutoFilter || !req) return
|
|
43
|
-
|
|
51
|
+
await rebuildFilter.call(this, modelName, filter, req)
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
const doboBeforeFindRecord = {
|
|
@@ -5,7 +5,8 @@ export async function checker (modelName, id, options = {}) {
|
|
|
5
5
|
|
|
6
6
|
const model = this.app.dobo.getModel(modelName)
|
|
7
7
|
if (options.noAutoFilter || !req) return
|
|
8
|
-
const filter =
|
|
8
|
+
const filter = {}
|
|
9
|
+
await rebuildFilter.call(this, modelName, filter, req)
|
|
9
10
|
if (filter.query.$and) filter.query.$and.push({ id })
|
|
10
11
|
else filter.query.id = id
|
|
11
12
|
filter.limit = 1
|
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"manageUser": "Manage User",
|
|
101
101
|
"manageTeam": "Manage Team",
|
|
102
102
|
"manageTeamUser": "Manage Team Member",
|
|
103
|
+
"manageTeamSetting": "Manage Team Setting",
|
|
103
104
|
"resetUserPassword": "Reset User Password",
|
|
104
105
|
"unknownUser": "User Unknown/Invalid",
|
|
105
106
|
"socialMedia": "Social Media",
|
package/extend/bajo/intl/id.json
CHANGED
|
@@ -101,6 +101,7 @@
|
|
|
101
101
|
"manageUser": "Kelola Pengguna",
|
|
102
102
|
"manageTeam": "Kelola Tim",
|
|
103
103
|
"manageTeamUser": "Kelola Anggota Tim",
|
|
104
|
+
"manageTeamSetting": "Kelola Setelan Tim",
|
|
104
105
|
"resetUserPassword": "Reset Kata Sandi Pengguna",
|
|
105
106
|
"unknownUser": "Pengguna Tidak Dikenal/Tidak Valid",
|
|
106
107
|
"socialMedia": "Media Sosial",
|
|
@@ -4,11 +4,19 @@ async function teamId (opts = {}) {
|
|
|
4
4
|
name: 'teamId',
|
|
5
5
|
type: 'string',
|
|
6
6
|
maxLength: 50,
|
|
7
|
+
required: true,
|
|
7
8
|
ref: {
|
|
8
9
|
site: {
|
|
9
10
|
model: 'SumbaSite',
|
|
10
11
|
propName: 'id',
|
|
11
|
-
type: '1:1'
|
|
12
|
+
type: '1:1',
|
|
13
|
+
fields: ['id', 'alias', 'hostname', 'title']
|
|
14
|
+
},
|
|
15
|
+
team: {
|
|
16
|
+
model: 'SumbaTeam',
|
|
17
|
+
propName: 'id',
|
|
18
|
+
type: '1:1',
|
|
19
|
+
fields: ['id', 'name']
|
|
12
20
|
}
|
|
13
21
|
},
|
|
14
22
|
index: true
|
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
"properties": [{
|
|
4
4
|
"name": "hostname",
|
|
5
5
|
"type": "string",
|
|
6
|
-
"minLength": 5,
|
|
7
6
|
"maxLength": 100,
|
|
7
|
+
"required": true,
|
|
8
8
|
"index": "unique"
|
|
9
9
|
}, {
|
|
10
10
|
"name": "alias",
|
|
11
11
|
"type": "string",
|
|
12
12
|
"maxLength": 100,
|
|
13
13
|
"index": "unique",
|
|
14
|
+
"required": true,
|
|
14
15
|
"immutable": true
|
|
15
16
|
}, {
|
|
16
17
|
"name": "title",
|
|
@@ -20,7 +21,6 @@
|
|
|
20
21
|
}, {
|
|
21
22
|
"name": "orgName",
|
|
22
23
|
"type": "string",
|
|
23
|
-
"minLength": 5,
|
|
24
24
|
"maxLength": 100,
|
|
25
25
|
"index": true
|
|
26
26
|
}, {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"buildLevel": 5,
|
|
3
|
+
"properties": [
|
|
4
|
+
"ns,,50,true,true",
|
|
5
|
+
"key,,255,true,true",
|
|
6
|
+
"value,text"
|
|
7
|
+
],
|
|
8
|
+
"indexes": [{
|
|
9
|
+
"fields": ["ns", "key", "siteId", "teamId"],
|
|
10
|
+
"type": "unique"
|
|
11
|
+
}],
|
|
12
|
+
"features": [
|
|
13
|
+
"sumba:siteId",
|
|
14
|
+
"dobo:updatedAt",
|
|
15
|
+
"sumba:teamId"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"buildLevel": 4,
|
|
3
|
-
"properties": [
|
|
4
|
-
"name": "teamId",
|
|
5
|
-
"type": "string",
|
|
6
|
-
"maxLength": 50,
|
|
7
|
-
"required": true,
|
|
8
|
-
"ref": {
|
|
9
|
-
"team": {
|
|
10
|
-
"model": "SumbaTeam",
|
|
11
|
-
"propName": "id",
|
|
12
|
-
"type": "1:1",
|
|
13
|
-
"fields": ["id", "name"]
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}],
|
|
3
|
+
"properties": [],
|
|
17
4
|
"indexes": [{
|
|
18
5
|
"fields": ["userId", "siteId", "teamId"],
|
|
19
6
|
"type": "unique"
|
|
@@ -21,6 +8,7 @@
|
|
|
21
8
|
"features": [
|
|
22
9
|
"dobo:createdAt",
|
|
23
10
|
"dobo:updatedAt",
|
|
11
|
+
"sumba:teamId",
|
|
24
12
|
"sumba:siteId",
|
|
25
13
|
"sumba:userId",
|
|
26
14
|
"dobo:immutable"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
async function teamUser () {
|
|
2
|
+
return {
|
|
3
|
+
common: {
|
|
4
|
+
layout: [
|
|
5
|
+
{ name: 'meta', fields: ['id', 'teamId', 'createdAt', 'updatedAt'] },
|
|
6
|
+
{ name: 'general', fields: ['ns', 'key', 'value'] }
|
|
7
|
+
],
|
|
8
|
+
calcFields: [
|
|
9
|
+
{ name: 'team', type: 'string' }
|
|
10
|
+
],
|
|
11
|
+
valueFormatter: {
|
|
12
|
+
team: (val, rec) => {
|
|
13
|
+
return rec._ref.team.name
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
widget: {
|
|
17
|
+
teamId: {
|
|
18
|
+
component: 'form-select-ext',
|
|
19
|
+
attr: {
|
|
20
|
+
remoteUrl: 'sumba.restapi:/manage/team',
|
|
21
|
+
remoteSearchField: 'name',
|
|
22
|
+
remoteLabelField: 'name',
|
|
23
|
+
remoteApiKey: true,
|
|
24
|
+
ref: 'team:name'
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
view: {
|
|
30
|
+
list: {
|
|
31
|
+
fields: ['team', 'ns', 'key', 'value', 'createdAt', 'updatedAt'],
|
|
32
|
+
stat: {
|
|
33
|
+
aggregate: [
|
|
34
|
+
{ fields: ['userId'], group: 'userId', aggregate: ['count'] },
|
|
35
|
+
{ fields: ['teamId'], group: 'teamId', aggregate: ['count'] }
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
details: {
|
|
40
|
+
},
|
|
41
|
+
add: {
|
|
42
|
+
hidden: ['id', 'createdAt', 'updatedAt']
|
|
43
|
+
},
|
|
44
|
+
edit: {
|
|
45
|
+
readonly: ['id', 'createdAt', 'updatedAt']
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default teamUser
|
|
@@ -25,7 +25,7 @@ async function teamUser () {
|
|
|
25
25
|
remoteSearchField: 'username',
|
|
26
26
|
remoteLabelField: 'username',
|
|
27
27
|
remoteApiKey: true,
|
|
28
|
-
|
|
28
|
+
ref: 'user:username'
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
teamId: {
|
|
@@ -35,7 +35,7 @@ async function teamUser () {
|
|
|
35
35
|
remoteSearchField: 'name',
|
|
36
36
|
remoteLabelField: 'name',
|
|
37
37
|
remoteApiKey: true,
|
|
38
|
-
|
|
38
|
+
ref: 'team:name'
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const action = {
|
|
2
|
+
method: ['GET', 'POST'],
|
|
3
|
+
title: 'manageTeamSetting',
|
|
4
|
+
handler: async function (req, reply) {
|
|
5
|
+
const { importModule } = this.app.bajo
|
|
6
|
+
const crudSkel = await importModule('waibuAdmin:/lib/crud-skel.js')
|
|
7
|
+
return await crudSkel.call(this, 'SumbaTeamSetting', req, reply)
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default action
|
package/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import path from 'path'
|
|
|
2
2
|
import createNewSite from './lib/create-new-site.js'
|
|
3
3
|
import removeSite from './lib/remove-site.js'
|
|
4
4
|
import getSite from './lib/get-site.js'
|
|
5
|
+
import getUser from './lib/get-user.js'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Plugin factory
|
|
@@ -11,6 +12,7 @@ import getSite from './lib/get-site.js'
|
|
|
11
12
|
*/
|
|
12
13
|
async function factory (pkgName) {
|
|
13
14
|
const me = this
|
|
15
|
+
const { getModel } = this.app.dobo
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Sumba class
|
|
@@ -133,7 +135,7 @@ async function factory (pkgName) {
|
|
|
133
135
|
}
|
|
134
136
|
}
|
|
135
137
|
this.unsafeUserFields = ['password']
|
|
136
|
-
this.selfBind(['createNewSite', 'removeSite', 'getSite'])
|
|
138
|
+
this.selfBind(['createNewSite', 'removeSite', 'getSite', 'getUser'])
|
|
137
139
|
}
|
|
138
140
|
|
|
139
141
|
init = async () => {
|
|
@@ -188,6 +190,7 @@ async function factory (pkgName) {
|
|
|
188
190
|
{ title: 'manageUser', href: `waibuAdmin:/${prefix}/user/list` },
|
|
189
191
|
{ title: 'manageTeam', href: `waibuAdmin:/${prefix}/team/list` },
|
|
190
192
|
{ title: 'manageTeamUser', href: `waibuAdmin:/${prefix}/team-user/list` },
|
|
193
|
+
{ title: 'manageTeamSetting', href: `waibuAdmin:/${prefix}/team-setting/list` },
|
|
191
194
|
{ title: 'manageDownload', href: `waibuAdmin:/${prefix}/download/list` },
|
|
192
195
|
{ title: '-' },
|
|
193
196
|
{ title: 'siteSetting', href: `waibuAdmin:/${prefix}/site-setting/list` },
|
|
@@ -201,37 +204,9 @@ async function factory (pkgName) {
|
|
|
201
204
|
}]
|
|
202
205
|
}
|
|
203
206
|
|
|
204
|
-
getUser = async (rec, safe = true) => {
|
|
205
|
-
const { omit, isPlainObject } = this.app.lib._
|
|
206
|
-
let user
|
|
207
|
-
if (isPlainObject(rec)) user = rec
|
|
208
|
-
else {
|
|
209
|
-
const mdl = this.app.dobo.getModel('SumbaUser')
|
|
210
|
-
user = await mdl.getRecord(rec, { noHook: true, throwNotFound: false })
|
|
211
|
-
}
|
|
212
|
-
if (!user) return null
|
|
213
|
-
return safe ? omit(user, this.unsafeUserFields) : user
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
mergeTeam = async (user, site) => {
|
|
217
|
-
if (!user) return
|
|
218
|
-
const { map, pick } = this.app.lib._
|
|
219
|
-
user.teams = []
|
|
220
|
-
const query = { userId: user.id, siteId: site.id }
|
|
221
|
-
let mdl = this.app.dobo.getModel('SumbaTeamUser')
|
|
222
|
-
const userTeam = await mdl.findAllRecord({ query })
|
|
223
|
-
if (userTeam.length === 0) return
|
|
224
|
-
delete query.userId
|
|
225
|
-
query.id = { $in: map(userTeam, 'teamId') }
|
|
226
|
-
query.status = 'ENABLED'
|
|
227
|
-
mdl = this.app.dobo.getModel('SumbaTeam')
|
|
228
|
-
const team = await mdl.findAllRecord({ query })
|
|
229
|
-
if (team.length > 0) user.teams.push(...map(team, t => pick(t, ['id', 'alias'])))
|
|
230
|
-
}
|
|
231
|
-
|
|
232
207
|
getUserFromUsernamePassword = async (username = '', password = '', req) => {
|
|
233
208
|
const { importPkg } = this.app.bajo
|
|
234
|
-
const model =
|
|
209
|
+
const model = getModel('SumbaUser')
|
|
235
210
|
await model.validate({ username, password }, null, { partial: true, ns: ['sumba', 'dobo'], fields: ['username', 'password'] })
|
|
236
211
|
const bcrypt = await importPkg('bajoExtra:bcrypt')
|
|
237
212
|
|
|
@@ -287,7 +262,7 @@ async function factory (pkgName) {
|
|
|
287
262
|
if (!isMd5(token)) return false
|
|
288
263
|
token = await hash(token)
|
|
289
264
|
const query = { token }
|
|
290
|
-
const rows = await
|
|
265
|
+
const rows = await getModel('SumbaUser').findRecord({ query }, { req, noHook: true })
|
|
291
266
|
if (rows.length === 0) throw this.error('invalidKey', merge({ statusCode: 401 }, payload))
|
|
292
267
|
if (rows[0].status !== 'ACTIVE') throw this.error('userInactive', merge({ details: [{ field: 'status', error: 'inactive' }], statusCode: 401 }, payload))
|
|
293
268
|
req.user = await getUser(rows[0])
|
|
@@ -353,7 +328,7 @@ async function factory (pkgName) {
|
|
|
353
328
|
const decoded = await verifier(token)
|
|
354
329
|
const id = decoded.payload.uid
|
|
355
330
|
try {
|
|
356
|
-
const rec = await
|
|
331
|
+
const rec = await getModel('SumbaUser').getRecord(id, { req, noHook: true })
|
|
357
332
|
if (!rec) throw this.error('invalidToken', { statusCode: 401 })
|
|
358
333
|
if (rec.status !== 'ACTIVE') throw this.error('userInactive', { details: [{ field: 'status', error: 'inactive' }], statusCode: 401 })
|
|
359
334
|
req.user = await getUser(rec)
|
|
@@ -472,7 +447,7 @@ async function factory (pkgName) {
|
|
|
472
447
|
getApiKeyFromUserId = async id => {
|
|
473
448
|
const { hash } = this.app.bajoExtra
|
|
474
449
|
const options = { forceNoHidden: true, noHook: true, noCache: true, attachment: true, mimeType: true }
|
|
475
|
-
const resp = await
|
|
450
|
+
const resp = await getModel('SumbaUser').getRecord(id, options)
|
|
476
451
|
return await hash(resp.salt)
|
|
477
452
|
}
|
|
478
453
|
|
|
@@ -519,6 +494,7 @@ async function factory (pkgName) {
|
|
|
519
494
|
createNewSite = createNewSite
|
|
520
495
|
removeSite = removeSite
|
|
521
496
|
getSite = getSite
|
|
497
|
+
getUser = getUser
|
|
522
498
|
}
|
|
523
499
|
|
|
524
500
|
return Sumba
|
package/lib/check-team.js
CHANGED
|
@@ -3,7 +3,6 @@ import { pathsToCheck } from './check-user-id.js'
|
|
|
3
3
|
async function checkTeam (req, reply, source) {
|
|
4
4
|
if (!req.user) return
|
|
5
5
|
const { map } = this.app.lib._
|
|
6
|
-
await this.mergeTeam(req.user, req.site)
|
|
7
6
|
const paths = pathsToCheck.call(this, req, true)
|
|
8
7
|
const teams = map(req.user.teams, 'alias')
|
|
9
8
|
const match = this.checkPathsByTeam({ paths, method: req.method, teams, guards: this.teamRoutes })
|
package/lib/get-site.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { parseNsSettings } from './util.js'
|
|
2
|
+
|
|
3
|
+
async function getSite (req) {
|
|
4
|
+
const { runHook } = this.app.bajo
|
|
5
|
+
const { omit, isPlainObject } = this.app.lib._
|
|
6
|
+
const { getHostname } = this.app.waibu
|
|
3
7
|
const omitted = ['status']
|
|
4
8
|
|
|
9
|
+
await runHook(`${this.ns}:beforeGetSite`, req)
|
|
5
10
|
const mergeSetting = async (site) => {
|
|
6
11
|
const { defaultsDeep, isSet } = this.app.lib.aneka
|
|
7
|
-
const {
|
|
8
|
-
const { isEmpty, trim, get, filter, set, isPlainObject, isArray } = this.app.lib._
|
|
12
|
+
const { get, filter } = this.app.lib._
|
|
9
13
|
const defSetting = {}
|
|
10
14
|
const nsSetting = {}
|
|
11
15
|
const names = this.app.getAllNs()
|
|
@@ -18,27 +22,13 @@ async function getSite (hostname, useId) {
|
|
|
18
22
|
const item = get(this, `app.${ns}.config.siteSetting`)
|
|
19
23
|
if (isSet(item)) defSetting[ns] = item
|
|
20
24
|
const items = filter(all, { ns })
|
|
21
|
-
|
|
22
|
-
let value = trim([item.value] ?? '')
|
|
23
|
-
if (['[', '{'].includes(value[0])) {
|
|
24
|
-
try {
|
|
25
|
-
value = parseObject(JSON.parse(value))
|
|
26
|
-
} catch (err) {}
|
|
27
|
-
} else if (Number(value)) value = Number(value)
|
|
28
|
-
else if (['true', 'false'].includes(value)) value = value === 'true'
|
|
29
|
-
else if (item.key.endsWith('$in')) value = value.split(',').map(v => v.trim())
|
|
30
|
-
else {
|
|
31
|
-
const dt = dayjs(value)
|
|
32
|
-
if (dt.isValid()) value = dt.toDate()
|
|
33
|
-
}
|
|
34
|
-
if ((isPlainObject(value) || isArray(value)) && isEmpty(value)) continue
|
|
35
|
-
set(nsSetting, `${ns}.${item.key}`, value)
|
|
36
|
-
}
|
|
25
|
+
parseNsSettings.call(this, ns, nsSetting, items)
|
|
37
26
|
}
|
|
38
27
|
site.setting = defaultsDeep({}, nsSetting, defSetting)
|
|
39
28
|
// additional fields
|
|
40
29
|
const country = await this.app.dobo.getModel('CdbCountry').getRecord(site.country, { noHook: true })
|
|
41
30
|
site.countryName = (country ?? {}).name ?? site.country
|
|
31
|
+
await runHook(`${this.ns}:afterGetSite`, req, site)
|
|
42
32
|
}
|
|
43
33
|
|
|
44
34
|
let site = {}
|
|
@@ -51,8 +41,8 @@ async function getSite (hostname, useId) {
|
|
|
51
41
|
return site
|
|
52
42
|
}
|
|
53
43
|
let query
|
|
54
|
-
if (
|
|
55
|
-
else query = { hostname }
|
|
44
|
+
if (!isPlainObject(req)) query = { id: req }
|
|
45
|
+
else query = { hostname: getHostname(req) }
|
|
56
46
|
let row = await this.app.dobo.getModel('SumbaSite').findOneRecord({ query }, { noHook: true })
|
|
57
47
|
if (!row) {
|
|
58
48
|
if (multiSite.catchAll) {
|
package/lib/get-user.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { parseNsSettings } from './util.js'
|
|
2
|
+
|
|
3
|
+
async function getUser (rec, safe = true) {
|
|
4
|
+
const { runHook } = this.app.bajo
|
|
5
|
+
const { map, pick, omit, isPlainObject } = this.app.lib._
|
|
6
|
+
const { getModel } = this.app.dobo
|
|
7
|
+
await runHook(`${this.ns}:beforeGetUser`, rec, safe)
|
|
8
|
+
let user
|
|
9
|
+
if (!isPlainObject(rec)) {
|
|
10
|
+
const mdl = getModel('SumbaUser')
|
|
11
|
+
user = await mdl.getRecord(rec, { noHook: true, throwNotFound: false })
|
|
12
|
+
} else {
|
|
13
|
+
user = rec
|
|
14
|
+
}
|
|
15
|
+
if (!user) return null
|
|
16
|
+
// merge teams
|
|
17
|
+
user.teams = []
|
|
18
|
+
const query = { userId: user.id, siteId: user.siteId }
|
|
19
|
+
let mdl = getModel('SumbaTeamUser')
|
|
20
|
+
const userTeam = await mdl.findAllRecord({ query })
|
|
21
|
+
if (userTeam.length === 0) return
|
|
22
|
+
delete query.userId
|
|
23
|
+
query.id = { $in: map(userTeam, 'teamId') }
|
|
24
|
+
query.status = 'ENABLED'
|
|
25
|
+
mdl = getModel('SumbaTeam')
|
|
26
|
+
const teams = await mdl.findAllRecord({ query })
|
|
27
|
+
if (teams.length > 0) {
|
|
28
|
+
// setting
|
|
29
|
+
delete query.id
|
|
30
|
+
delete query.status
|
|
31
|
+
query.siteId = user.siteId
|
|
32
|
+
query.teamId = { $in: teams.map(t => t.id + '') }
|
|
33
|
+
mdl = getModel('SumbaTeamSetting')
|
|
34
|
+
const items = await mdl.findAllRecord({ query })
|
|
35
|
+
for (const team of teams) {
|
|
36
|
+
const names = map(items, 'ns')
|
|
37
|
+
const item = pick(team, ['id', 'alias'])
|
|
38
|
+
item.setting = {}
|
|
39
|
+
for (const ns of names) {
|
|
40
|
+
parseNsSettings.call(this, ns, item.setting, items.filter(s => s.teamId === (team.id + '')))
|
|
41
|
+
}
|
|
42
|
+
user.teams.push(item)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
user = safe ? omit(user, this.unsafeUserFields) : user
|
|
46
|
+
await runHook(`${this.ns}:afterGetUser`, rec, safe, user)
|
|
47
|
+
return user
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default getUser
|
package/lib/util.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function parseNsSettings (ns, setting, items) {
|
|
2
|
+
const { trim, set, isPlainObject, isArray, isEmpty, find } = this.app.lib._
|
|
3
|
+
const { parseObject, dayjs } = this.app.lib
|
|
4
|
+
|
|
5
|
+
for (const item of items) {
|
|
6
|
+
if (item.ns === '_var' || ns === '_var') continue
|
|
7
|
+
let value = trim([item.value] ?? '')
|
|
8
|
+
if (value[0] === '#' && value[value.length - 1] === '#') {
|
|
9
|
+
const val = value.slice(1, -1)
|
|
10
|
+
const newValue = find(items, { ns: '_var', key: val })
|
|
11
|
+
if (newValue) value = newValue.value
|
|
12
|
+
}
|
|
13
|
+
if (['[', '{'].includes(value[0]) && [']', '}'].includes(value[value.length - 1])) {
|
|
14
|
+
try {
|
|
15
|
+
value = parseObject(JSON.parse(value))
|
|
16
|
+
} catch (err) {}
|
|
17
|
+
} else if (Number(value)) value = Number(value)
|
|
18
|
+
else if (['true', 'false'].includes(value)) value = value === 'true'
|
|
19
|
+
else if (item.key.endsWith('$in')) value = value.split(',').map(v => v.trim())
|
|
20
|
+
else {
|
|
21
|
+
const dt = dayjs(value)
|
|
22
|
+
if (dt.isValid()) value = dt.toDate()
|
|
23
|
+
}
|
|
24
|
+
if ((isPlainObject(value) || isArray(value)) && isEmpty(value)) continue
|
|
25
|
+
set(setting, `${ns}.${item.key}`, value)
|
|
26
|
+
}
|
|
27
|
+
}
|
package/package.json
CHANGED
package/wiki/CHANGES.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-03-22
|
|
4
|
+
|
|
5
|
+
- [2.11.0] Add ```Team Setting``` feature
|
|
6
|
+
- [2.11.0] Rewrite ```getUser()```
|
|
7
|
+
- [2.11.0] Rewrite ```getSite()```
|
|
8
|
+
- [2.11.0] Bug fix in model reference not displayed correctly on ```Details View```
|
|
9
|
+
- [2.11.1] Bug fix in ```parseNsSetting()```
|
|
10
|
+
|
|
3
11
|
## 2026-03-13
|
|
4
12
|
|
|
5
13
|
- [2.10.0] ```getSite()``` now accept object & array based on their keys to
|