sumba 2.9.0 → 2.10.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,12 +1,16 @@
1
1
  {
2
2
  "properties": [
3
3
  "ns,,50,true,true",
4
- "key,,50,true,true",
4
+ "key,,255,true,true",
5
5
  "value,text"
6
6
  ],
7
7
  "indexes": [{
8
8
  "fields": ["ns", "key", "siteId"],
9
9
  "type": "unique"
10
10
  }],
11
- "features": ["sumba:siteId", "dobo:updatedAt"]
11
+ "features": [
12
+ "sumba:siteId",
13
+ "dobo:immutable",
14
+ "dobo:updatedAt"
15
+ ]
12
16
  }
@@ -4,6 +4,7 @@ const profile = {
4
4
  if (!this.app.masohiMail) return await reply.view('sumba.template:/user/forgot-password.html')
5
5
  const { defaultsDeep } = this.app.lib.aneka
6
6
  const { dayjs } = this.app.lib
7
+ const { get } = this.app.lib._
7
8
  const model = this.app.dobo.getModel('SumbaUser')
8
9
  const form = defaultsDeep(req.body, {})
9
10
  let error
@@ -16,7 +17,7 @@ const profile = {
16
17
  const to = `${data.firstName} ${data.lastName} <${data.email}>`
17
18
  const subject = req.t('forgotPasswordLink')
18
19
  const options = { req, reply }
19
- const exp = req.site.setting.sumba.forgotPasswordExpDur
20
+ const exp = get(req, 'site.setting.sumba.forgotPasswordExpDur')
20
21
  data.fpToken = Buffer.from(`${data.token}:${dayjs().add(exp, 'ms').unix()}`).toString('base64')
21
22
  data._meta = { hostHeader: req.headers.host }
22
23
  const payload = { to, subject, data }
package/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import path from 'path'
2
2
  import createNewSite from './lib/create-new-site.js'
3
3
  import removeSite from './lib/remove-site.js'
4
+ import getSite from './lib/get-site.js'
4
5
 
5
6
  /**
6
7
  * Plugin factory
@@ -132,7 +133,7 @@ async function factory (pkgName) {
132
133
  }
133
134
  }
134
135
  this.unsafeUserFields = ['password']
135
- this.selfBind(['createNewSite', 'removeSite'])
136
+ this.selfBind(['createNewSite', 'removeSite', 'getSite'])
136
137
  }
137
138
 
138
139
  init = async () => {
@@ -405,73 +406,6 @@ async function factory (pkgName) {
405
406
  return guarded
406
407
  }
407
408
 
408
- getSite = async (hostname, useId) => {
409
- const { omit } = this.app.lib._
410
- const omitted = ['status']
411
-
412
- const mergeSetting = async (site) => {
413
- const { defaultsDeep } = this.app.lib.aneka
414
- const { parseObject, dayjs } = this.app.lib
415
- const { trim, get, filter } = this.app.lib._
416
- const defSetting = {}
417
- const nsSetting = {}
418
- const names = this.app.getAllNs()
419
- const query = {
420
- ns: { $in: names },
421
- siteId: site.id
422
- }
423
- const all = await this.app.dobo.getModel('SumbaSiteSetting').findAllRecord({ query })
424
- for (const ns of names) {
425
- nsSetting[ns] = {}
426
- defSetting[ns] = get(this, `app.${ns}.config.siteSetting`, {})
427
- const items = filter(all, { ns })
428
- for (const item of items) {
429
- let value = trim([item.value] ?? '')
430
- if (['[', '{'].includes(value[0])) {
431
- try {
432
- value = parseObject(JSON.parse(value))
433
- } catch (err) {}
434
- } else if (Number(value)) value = Number(value)
435
- else if (['true', 'false'].includes(value)) value = value === 'true'
436
- else {
437
- const dt = dayjs(value)
438
- if (dt.isValid()) value = dt.toDate()
439
- }
440
- nsSetting[ns][item.key] = value
441
- }
442
- }
443
- site.setting = defaultsDeep({}, nsSetting, defSetting)
444
- // additional fields
445
- const country = await this.app.dobo.getModel('CdbCountry').getRecord(site.country, { noHook: true })
446
- site.countryName = (country ?? {}).name ?? site.country
447
- }
448
-
449
- let site = {}
450
-
451
- const multiSite = this.config.multiSite === true ? { enabled: true, catchAll: 'default' } : this.config.multiSite
452
- if (!multiSite.enabled) {
453
- const resp = await this.app.dobo.getModel('SumbaSite').findOneRecord({ query: { alias: 'default' } }, { noHook: true })
454
- site = omit(resp, omitted)
455
- await mergeSetting(site)
456
- return site
457
- }
458
- let query
459
- if (useId) query = { id: hostname }
460
- else query = { hostname }
461
- let row = await this.app.dobo.getModel('SumbaSite').findOneRecord({ query }, { noHook: true })
462
- if (!row) {
463
- if (multiSite.catchAll) {
464
- query = { alias: multiSite.catchAll === true ? 'default' : multiSite.catchAll }
465
- row = await this.app.dobo.getModel('SumbaSite').findOneRecord({ query }, { noHook: true })
466
- }
467
- if (!row) throw this.error('unknownSite')
468
- }
469
- if (row.status !== 'ACTIVE') throw this.error('siteInactiveInfo')
470
- site = omit(row, omitted)
471
- await mergeSetting(site)
472
- return site
473
- }
474
-
475
409
  signout = async ({ req, reply, reason }) => {
476
410
  const { runHook } = this.app.bajo
477
411
  const { getSessionId } = this.app.waibuMpa
@@ -502,9 +436,10 @@ async function factory (pkgName) {
502
436
  return reply.redirectTo(url, { query, params })
503
437
  }
504
438
 
505
- generatePassword = (req) => {
439
+ generatePassword = (req = {}) => {
440
+ const { get } = this.app.lib._
506
441
  const { generateId } = this.app.lib.aneka
507
- const cfg = req ? req.site.setting.sumba.userPassword : this.config.siteSetting.userPassword
442
+ const cfg = get(req, 'site.setting.sumba.userPassword', this.config.siteSetting.userPassword)
508
443
  let passwd = generateId()
509
444
  if (cfg.minLowercase) passwd += generateId({ pattern: 'abcdefghijklmnopqrstuvwxyz', length: cfg.minLowercase })
510
445
  if (cfg.minUppercase) passwd += generateId({ pattern: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', length: cfg.minUppercase })
@@ -583,6 +518,7 @@ async function factory (pkgName) {
583
518
 
584
519
  createNewSite = createNewSite
585
520
  removeSite = removeSite
521
+ getSite = getSite
586
522
  }
587
523
 
588
524
  return Sumba
@@ -0,0 +1,70 @@
1
+ async function getSite (hostname, useId) {
2
+ const { omit } = this.app.lib._
3
+ const omitted = ['status']
4
+
5
+ const mergeSetting = async (site) => {
6
+ const { defaultsDeep, isSet } = this.app.lib.aneka
7
+ const { parseObject, dayjs } = this.app.lib
8
+ const { isEmpty, trim, get, filter, set, isPlainObject, isArray } = this.app.lib._
9
+ const defSetting = {}
10
+ const nsSetting = {}
11
+ const names = this.app.getAllNs()
12
+ const query = {
13
+ ns: { $in: names },
14
+ siteId: site.id
15
+ }
16
+ const all = await this.app.dobo.getModel('SumbaSiteSetting').findAllRecord({ query })
17
+ for (const ns of names) {
18
+ const item = get(this, `app.${ns}.config.siteSetting`)
19
+ if (isSet(item)) defSetting[ns] = item
20
+ const items = filter(all, { ns })
21
+ for (const item of items) {
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
+ }
37
+ }
38
+ site.setting = defaultsDeep({}, nsSetting, defSetting)
39
+ // additional fields
40
+ const country = await this.app.dobo.getModel('CdbCountry').getRecord(site.country, { noHook: true })
41
+ site.countryName = (country ?? {}).name ?? site.country
42
+ }
43
+
44
+ let site = {}
45
+
46
+ const multiSite = this.config.multiSite === true ? { enabled: true, catchAll: 'default' } : this.config.multiSite
47
+ if (!multiSite.enabled) {
48
+ const resp = await this.app.dobo.getModel('SumbaSite').findOneRecord({ query: { alias: 'default' } }, { noHook: true })
49
+ site = omit(resp, omitted)
50
+ await mergeSetting(site)
51
+ return site
52
+ }
53
+ let query
54
+ if (useId) query = { id: hostname }
55
+ else query = { hostname }
56
+ let row = await this.app.dobo.getModel('SumbaSite').findOneRecord({ query }, { noHook: true })
57
+ if (!row) {
58
+ if (multiSite.catchAll) {
59
+ query = { alias: multiSite.catchAll === true ? 'default' : multiSite.catchAll }
60
+ row = await this.app.dobo.getModel('SumbaSite').findOneRecord({ query }, { noHook: true })
61
+ }
62
+ if (!row) throw this.error('unknownSite')
63
+ }
64
+ if (row.status !== 'ACTIVE') throw this.error('siteInactiveInfo')
65
+ site = omit(row, omitted)
66
+ await mergeSetting(site)
67
+ return site
68
+ }
69
+
70
+ export default getSite
@@ -1,6 +1,7 @@
1
1
  import { joiPasswordExtendCore } from 'joi-password'
2
2
 
3
- async function passwordRule (req) {
3
+ async function passwordRule (req = {}) {
4
+ const { get } = this.app.lib._
4
5
  const { importPkg } = this.app.bajo
5
6
  const joi = await importPkg('dobo:joi')
6
7
  const joiPassword = joi.extend(joiPasswordExtendCore)
@@ -9,7 +10,7 @@ async function passwordRule (req) {
9
10
  .min(8)
10
11
  .max(100)
11
12
  .required()
12
- const cfg = req ? req.site.setting.sumba.userPassword : this.config.siteSetting.userPassword
13
+ const cfg = get(req, 'site.setting.sumba.userPassword', this.config.siteSetting.userPassword)
13
14
  if (cfg.minUppercase) password = password.minOfUppercase(cfg.minUppercase)
14
15
  if (cfg.minLowercase) password = password.minOfLowercase(cfg.minLowercase)
15
16
  if (cfg.minSpecialChar) password = password.minOfSpecialCharacters(cfg.minSpecialChar)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sumba",
3
- "version": "2.9.0",
3
+ "version": "2.10.0",
4
4
  "description": "Biz Suite for Bajo Framework",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/wiki/CHANGES.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-03-13
4
+
5
+ - [2.10.0] ```getSite()``` now accept object & array based on their keys to
6
+
3
7
  ## 2026-03-12
4
8
 
5
9
  - [2.9.0] Add ability to restrict/filter dobo's records through site setting