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,,
|
|
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": [
|
|
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
|
|
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
|
|
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
|
package/lib/get-site.js
ADDED
|
@@ -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
|
package/lib/password-rule.js
CHANGED
|
@@ -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
|
|
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