sumba 2.20.0 → 2.22.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.
- package/extend/bajo/hook/dobo.sumba-site@after-update-record.js +1 -1
- package/extend/bajo/hook/dobo.sumba-user@after-record-validation.js +0 -1
- package/extend/bajo/hook/dobo.sumba-user@after-update-record.js +1 -1
- package/extend/bajo/intl/en-US.json +2 -1
- package/extend/bajo/intl/id.json +2 -1
- package/extend/bajoTemplate/partial/api-key-modal.html +9 -11
- package/extend/dobo/feature/country.js +4 -2
- package/extend/dobo/feature/notes.js +11 -0
- package/extend/dobo/model/user.js +83 -0
- package/extend/waibuDb/schema/team-setting.js +0 -5
- package/extend/waibuDb/schema/user-setting.js +0 -8
- package/extend/waibuDb/schema/user.js +2 -8
- package/extend/waibuMpa/extend/waibuAdmin/route/site.js +1 -2
- package/extend/waibuMpa/route/your-stuff/profile/edit.js +1 -3
- package/extend/waibuMpa/route/your-stuff/profile.js +1 -3
- package/extend/waibuMpa/route/your-stuff/reset-api-key.js +2 -3
- package/extend/waibuRestApi/route/user/access-token/@type/create.js +1 -3
- package/extend/waibuRestApi/route/user/api-key/get.js +1 -3
- package/extend/waibuRestApi/route/your-stuff/api-key/get.js +1 -2
- package/extend/waibuRestApi/route/your-stuff/api-key/update.js +1 -2
- package/index.js +16 -13
- package/package.json +1 -1
- package/wiki/CHANGES.md +8 -0
- package/extend/dobo/model/user.json +0 -70
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export async function clearCache (id, result) {
|
|
2
2
|
if (!this.app.bajoCache) return
|
|
3
|
-
const { clear } = this.app.bajoCache
|
|
3
|
+
const { clear } = this.app.bajoCache ?? {}
|
|
4
4
|
const { get } = this.app.lib._
|
|
5
5
|
await clear({ key: 'dobo|SumbaSite|getSite|default' })
|
|
6
6
|
await clear({ key: `dobo|SumbaSite|getSite|multiSite|${id}` })
|
|
@@ -3,7 +3,6 @@ async function afterRecordValidation (body, options) {
|
|
|
3
3
|
const { has } = this.app.lib._
|
|
4
4
|
|
|
5
5
|
if (has(body, 'password') && !isBcrypt(body.password)) body.password = await hash(body.password, 'bcrypt')
|
|
6
|
-
// if (has(body, 'token') && !isMd5(body.token)) body.token = await hash(body.token)
|
|
7
6
|
}
|
|
8
7
|
|
|
9
8
|
export default afterRecordValidation
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export async function clearCache (id, result) {
|
|
2
2
|
if (!this.app.bajoCache) return
|
|
3
3
|
const { hash } = this.app.bajoExtra
|
|
4
|
-
const { clear } = this.app.bajoCache
|
|
4
|
+
const { clear } = this.app.bajoCache ?? {}
|
|
5
5
|
const { get, isEmpty } = this.app.lib._
|
|
6
6
|
let token = get(result, 'data.token', get(result, 'oldData.token', ''))
|
|
7
7
|
if (!isEmpty(token)) token = await hash(token)
|
package/extend/bajo/intl/id.json
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
<c:modal id="api-key-modal" t:title="apiKey">
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
<c:
|
|
5
|
-
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
</c:div>
|
|
11
|
-
</c:modal-body>
|
|
1
|
+
<c:modal id="api-key-modal" t:title="apiKey" size="lg">
|
|
2
|
+
<pre><code id="api-key"><%= form.apiKey %></code></pre>
|
|
3
|
+
<c:div flex="justify-content:between align-items:center" margin="top-3">
|
|
4
|
+
<c:a icon="arrowsRepeat" href="sumba:/your-stuff/reset-api-key" t:content="reset" />
|
|
5
|
+
<div>
|
|
6
|
+
<c:btn color="primary" x-data @click="await wbs.copyToClipboard('#api-key', true)" t:content="copyClipboard" />
|
|
7
|
+
<c:btn margin="start-1" color="secondary" dismiss t:content="close" />
|
|
8
|
+
</div>
|
|
9
|
+
</c:div>
|
|
12
10
|
</c:modal>
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
async function country (opts = {}) {
|
|
2
2
|
opts.field = opts.field ?? 'country'
|
|
3
|
+
opts.enforceRule = true
|
|
3
4
|
return {
|
|
4
5
|
properties: [{
|
|
5
6
|
name: opts.field,
|
|
6
7
|
type: 'string',
|
|
7
8
|
maxLength: 2,
|
|
8
9
|
index: opts.index ?? true,
|
|
10
|
+
required: opts.required,
|
|
9
11
|
values: 'sumba:getCountriesValues',
|
|
10
|
-
rules: ['uppercase', { rule: 'length', params: 2 }],
|
|
11
|
-
rulesMsg: { 'any.only': 'validCountryCodeRequired' }
|
|
12
|
+
rules: opts.enforceRule ? ['uppercase', { rule: 'length', params: 2 }] : [],
|
|
13
|
+
rulesMsg: opts.enforceRule ? { 'any.only': 'validCountryCodeRequired' } : undefined
|
|
12
14
|
}],
|
|
13
15
|
rules: [{ rule: 'trim', fields: [opts.field] }]
|
|
14
16
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
async function user () {
|
|
2
|
+
return {
|
|
3
|
+
buildLevel: 2,
|
|
4
|
+
properties: [{
|
|
5
|
+
name: 'username',
|
|
6
|
+
type: 'string',
|
|
7
|
+
minLength: 5,
|
|
8
|
+
maxLength: 50,
|
|
9
|
+
rules: ['alphanum']
|
|
10
|
+
}, {
|
|
11
|
+
name: 'password',
|
|
12
|
+
type: 'string',
|
|
13
|
+
minLength: 8,
|
|
14
|
+
maxLength: 100
|
|
15
|
+
}, {
|
|
16
|
+
name: 'token',
|
|
17
|
+
type: 'string',
|
|
18
|
+
maxLength: 100,
|
|
19
|
+
index: true
|
|
20
|
+
}, {
|
|
21
|
+
name: 'salt',
|
|
22
|
+
type: 'string',
|
|
23
|
+
maxLength: 100,
|
|
24
|
+
required: true
|
|
25
|
+
}, {
|
|
26
|
+
name: 'apiKey',
|
|
27
|
+
type: 'string',
|
|
28
|
+
maxLength: 100,
|
|
29
|
+
virtual: true,
|
|
30
|
+
getValue: async function (val, rec) {
|
|
31
|
+
return await this.plugin.hash(rec.salt)
|
|
32
|
+
}
|
|
33
|
+
}, {
|
|
34
|
+
name: 'provider',
|
|
35
|
+
type: 'string',
|
|
36
|
+
maxLength: 50,
|
|
37
|
+
index: true,
|
|
38
|
+
default: 'local'
|
|
39
|
+
}, {
|
|
40
|
+
name: 'email',
|
|
41
|
+
type: 'string',
|
|
42
|
+
maxLength: 100,
|
|
43
|
+
required: true,
|
|
44
|
+
rules: ['email']
|
|
45
|
+
}, {
|
|
46
|
+
name: 'firstName',
|
|
47
|
+
type: 'string',
|
|
48
|
+
maxLength: 50,
|
|
49
|
+
required: true,
|
|
50
|
+
index: true
|
|
51
|
+
}, {
|
|
52
|
+
name: 'lastName',
|
|
53
|
+
type: 'string',
|
|
54
|
+
maxLength: 50,
|
|
55
|
+
required: true,
|
|
56
|
+
index: true
|
|
57
|
+
}],
|
|
58
|
+
rules: [{ rule: 'trim', fields: ['username', 'firstName', 'lastName'] }],
|
|
59
|
+
indexes: [{
|
|
60
|
+
fields: ['username', 'siteId'],
|
|
61
|
+
type: 'unique'
|
|
62
|
+
}, {
|
|
63
|
+
fields: ['email', 'siteId'],
|
|
64
|
+
type: 'unique'
|
|
65
|
+
}],
|
|
66
|
+
hidden: ['password', 'token'],
|
|
67
|
+
features: [
|
|
68
|
+
'sumba:address',
|
|
69
|
+
'sumba:social',
|
|
70
|
+
{
|
|
71
|
+
name: 'sumba:status',
|
|
72
|
+
default: 'UNVERIFIED',
|
|
73
|
+
values: ['UNVERIFIED', 'ACTIVE', 'INACTIVE']
|
|
74
|
+
},
|
|
75
|
+
'sumba:siteId',
|
|
76
|
+
'dobo:createdAt',
|
|
77
|
+
'dobo:updatedAt',
|
|
78
|
+
'dobo:immutable'
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default user
|
|
@@ -5,14 +5,6 @@ async function teamUser ({ req } = {}) {
|
|
|
5
5
|
{ name: 'meta', fields: ['id', 'userId', 'createdAt', 'updatedAt'] },
|
|
6
6
|
{ name: 'general', fields: ['ns', 'key', 'value'] }
|
|
7
7
|
],
|
|
8
|
-
calcFields: [
|
|
9
|
-
{ name: 'user', type: 'string' }
|
|
10
|
-
],
|
|
11
|
-
formatValue: {
|
|
12
|
-
user: (val, rec) => {
|
|
13
|
-
return rec._ref.user.name
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
8
|
widget: {
|
|
17
9
|
userId: {
|
|
18
10
|
component: 'form-select-ext',
|
|
@@ -3,16 +3,10 @@ async function user ({ req } = {}) {
|
|
|
3
3
|
common: {
|
|
4
4
|
layout: [
|
|
5
5
|
{ name: 'meta', fields: ['id:3', 'createdAt:3', 'updatedAt:3', 'status:3'] },
|
|
6
|
-
{ name: 'account', fields: ['username:3', 'email:3', 'provider:3', 'password:3', 'firstName:3', 'lastName:3', '
|
|
6
|
+
{ name: 'account', fields: ['username:3', 'email:3', 'provider:3', 'password:3', 'firstName:3', 'lastName:3', 'apiKey:6'] },
|
|
7
7
|
{ name: 'address', fields: ['address1:12', 'address2:12', 'city:6-md 8-sm', 'zipCode:2-md 4-sm', 'provinceState:4-md', 'country:6-md', 'phone:6-md', 'website:12'] },
|
|
8
8
|
{ name: 'socialMedia', fields: ['socX:3-md 6-sm', 'socInstagram:3-md 6-sm', 'socFacebook:3-md 6-sm', 'socLinkedIn:3-md 6-sm'] }
|
|
9
9
|
],
|
|
10
|
-
formatValue: {
|
|
11
|
-
token: async function (val, rec) {
|
|
12
|
-
const { hash } = this.app.bajoExtra
|
|
13
|
-
return await hash(rec.salt)
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
10
|
widget: {
|
|
17
11
|
token: {
|
|
18
12
|
component: 'form-plaintext'
|
|
@@ -25,7 +19,7 @@ async function user ({ req } = {}) {
|
|
|
25
19
|
sort: 'username:1',
|
|
26
20
|
limit: 10
|
|
27
21
|
},
|
|
28
|
-
fields: ['createdAt', 'status', 'username', 'provider', 'email', 'firstName', 'lastName', 'city', 'zipCode', 'provinceState', 'country', 'phone'],
|
|
22
|
+
fields: ['createdAt', 'status', 'username', 'provider', 'email', 'firstName', 'lastName', 'city', 'zipCode', 'provinceState', 'country', 'phone', 'apiKey'],
|
|
29
23
|
stat: {
|
|
30
24
|
aggregate: [
|
|
31
25
|
{ fields: ['status'], group: 'status', aggregate: ['count'] },
|
|
@@ -8,16 +8,14 @@ const profile = {
|
|
|
8
8
|
const { updateRecord, getRecord } = this.app.waibuDb
|
|
9
9
|
const { getSchemaExt } = this.app.waibuDb
|
|
10
10
|
const { omit, pick } = this.app.lib._
|
|
11
|
-
const { hash } = this.app.bajoExtra
|
|
12
11
|
|
|
13
|
-
const options = { forceNoHidden: ['token'], noHook: true, noCache: true,
|
|
12
|
+
const options = { forceNoHidden: ['token'], noHook: true, noCache: true, fmt: true }
|
|
14
13
|
const mdl = this.app.dobo.getModel(model)
|
|
15
14
|
|
|
16
15
|
const { schema } = await getSchemaExt(model, 'edit', { ...options, args: [{ req, model: mdl }] })
|
|
17
16
|
|
|
18
17
|
const resp = await getRecord({ model, req, id: req.user.id, options })
|
|
19
18
|
let form = defaultsDeep(req.body, omit(resp.data, ['password', 'salt']))
|
|
20
|
-
form.token = await hash(form.token)
|
|
21
19
|
let error
|
|
22
20
|
if (req.method === 'POST') {
|
|
23
21
|
try {
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
const profile = {
|
|
2
2
|
method: ['GET'],
|
|
3
3
|
handler: async function (req, reply) {
|
|
4
|
-
const { hash } = this.app.bajoExtra
|
|
5
4
|
const { getRecord } = this.app.waibuDb
|
|
6
|
-
const options = { forceNoHidden: ['token'], noHook: true, noCache: true, attachment: true, mimeType: true,
|
|
5
|
+
const options = { forceNoHidden: ['token'], noHook: true, noCache: true, attachment: true, mimeType: true, fmt: true }
|
|
7
6
|
const resp = await getRecord({ model: 'SumbaUser', req, id: req.user.id, options })
|
|
8
7
|
const form = resp.data
|
|
9
|
-
form.token = await hash(form.salt)
|
|
10
8
|
return await reply.view('sumba.template:/your-stuff/profile/view.html', { form })
|
|
11
9
|
}
|
|
12
10
|
}
|
|
@@ -4,11 +4,10 @@ const resetApiKey = {
|
|
|
4
4
|
const { defaultsDeep } = this.app.lib.aneka
|
|
5
5
|
const { importPkg } = this.app.bajo
|
|
6
6
|
const { generateId } = this.app.lib.aneka
|
|
7
|
-
const { hash } = this.app.bajoExtra
|
|
8
7
|
const delay = await importPkg('bajo:delay')
|
|
9
8
|
const bcrypt = await importPkg('bajoExtra:bcrypt')
|
|
10
9
|
const Joi = await importPkg('dobo:joi')
|
|
11
|
-
const form = defaultsDeep(req.body, { apiKey:
|
|
10
|
+
const form = defaultsDeep(req.body, { apiKey: req.user.apiKey })
|
|
12
11
|
const model = this.app.dobo.getModel('SumbaUser')
|
|
13
12
|
let error
|
|
14
13
|
if (req.method === 'POST') {
|
|
@@ -21,7 +20,7 @@ const resetApiKey = {
|
|
|
21
20
|
} catch (err) {
|
|
22
21
|
throw this.error('validationError', { details: err.details, values: err.values, ns: this.ns, statusCode: 422, code: 'DB_VALIDATION' })
|
|
23
22
|
}
|
|
24
|
-
const rec = await model.getRecord(req.user.id, { forceNoHidden: true })
|
|
23
|
+
const rec = await model.getRecord(req.user.id, { forceNoHidden: true, noMagic: true })
|
|
25
24
|
const verified = await bcrypt.compare(form.password, rec.password)
|
|
26
25
|
if (!verified) throw this.error('validationError', { details: [{ field: 'password', error: 'invalidPassword' }], statusCode: 400 })
|
|
27
26
|
await model.transaction(async (trx) => {
|
|
@@ -13,15 +13,13 @@ async function create () {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const handler = async function (req, reply) {
|
|
16
|
-
const { hash } = this.app.bajoExtra
|
|
17
|
-
|
|
18
16
|
if (!['api-key', 'jwt', 'apiKey'].includes(req.params.type)) throw this.error('invalidTokenType')
|
|
19
17
|
const rec = await this.getUserByUsernamePassword(req.body.username, req.body.password, req)
|
|
20
18
|
if (req.params.type === 'jwt') {
|
|
21
19
|
const jwt = await this.createJwtFromUserRecord(rec)
|
|
22
20
|
return { data: jwt }
|
|
23
21
|
}
|
|
24
|
-
return { data: { token:
|
|
22
|
+
return { data: { token: rec.apiKey } }
|
|
25
23
|
}
|
|
26
24
|
return { schema, handler }
|
|
27
25
|
}
|
|
@@ -12,10 +12,8 @@ async function get () {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const handler = async function (req, reply, options) {
|
|
15
|
-
const { hash } = this.app.bajoExtra
|
|
16
|
-
|
|
17
15
|
const profile = await this.app.dobo.getModel('SumbaUser').getRecord(req.user.id)
|
|
18
|
-
return { data: { token:
|
|
16
|
+
return { data: { token: profile.apiKey } }
|
|
19
17
|
}
|
|
20
18
|
return { schema, handler }
|
|
21
19
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { response } from './update.js'
|
|
2
2
|
|
|
3
3
|
async function get () {
|
|
4
|
-
const { hash } = this.app.bajoExtra
|
|
5
4
|
const schema = { response: await response.call(this) }
|
|
6
5
|
const handler = async function get (req, reply) {
|
|
7
6
|
const rec = await this.app.dobo.getModel('SumbaUser').getRecord(req.user.id, { forceNoHidden: true })
|
|
8
|
-
return { data: { token:
|
|
7
|
+
return { data: { token: rec.apiKey } }
|
|
9
8
|
}
|
|
10
9
|
return { schema, handler }
|
|
11
10
|
}
|
|
@@ -22,7 +22,6 @@ export const body = {
|
|
|
22
22
|
async function update () {
|
|
23
23
|
const { importPkg } = this.app.bajo
|
|
24
24
|
const { generateId } = this.app.lib.aneka
|
|
25
|
-
const { hash } = this.app.bajoExtra
|
|
26
25
|
const bcrypt = await importPkg('bajoExtra:bcrypt')
|
|
27
26
|
const model = this.app.dobo.getModel('SumbaUser')
|
|
28
27
|
|
|
@@ -34,7 +33,7 @@ async function update () {
|
|
|
34
33
|
if (!verified) throw this.error('invalidPassword', { details: [{ field: 'password', error: 'invalidPassword' }], statusCode: 400 })
|
|
35
34
|
const input = { salt: generateId() }
|
|
36
35
|
const resp = await model.updateRecord(req.user.id, input, { forceNoHidden: true })
|
|
37
|
-
return { data: { token:
|
|
36
|
+
return { data: { token: resp.apiKey } }
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
return { schema, handler }
|
package/index.js
CHANGED
|
@@ -91,7 +91,8 @@ async function factory (pkgName) {
|
|
|
91
91
|
apiKey: {
|
|
92
92
|
type: 'Bearer',
|
|
93
93
|
qsKey: 'apiKey',
|
|
94
|
-
headerKey: 'X-Auth-ApiKey'
|
|
94
|
+
headerKey: 'X-Auth-ApiKey',
|
|
95
|
+
algo: 'sha256' // changing this require each and every user to reset their apiKey
|
|
95
96
|
},
|
|
96
97
|
basic: {
|
|
97
98
|
},
|
|
@@ -251,7 +252,6 @@ async function factory (pkgName) {
|
|
|
251
252
|
createJwtFromUserRecord = async (rec) => {
|
|
252
253
|
const { importPkg } = this.app.bajo
|
|
253
254
|
const { dayjs } = this.app.lib
|
|
254
|
-
const { hash } = this.app.bajoExtra
|
|
255
255
|
const { get, pick } = this.app.lib._
|
|
256
256
|
|
|
257
257
|
const fastJwt = await importPkg('bajoExtra:fast-jwt')
|
|
@@ -260,7 +260,7 @@ async function factory (pkgName) {
|
|
|
260
260
|
const opts = pick(this.config.auth.common.jwt, ['expiresInDur'])
|
|
261
261
|
opts.key = get(this.config, 'auth.common.jwt.secret')
|
|
262
262
|
const sign = createSigner(opts)
|
|
263
|
-
const apiKey = await hash(rec.
|
|
263
|
+
const apiKey = await this.hash(rec.token)
|
|
264
264
|
const payload = { uid: rec.id, apiKey }
|
|
265
265
|
const token = await sign(payload)
|
|
266
266
|
const expiresAt = dayjs().add(opts.expiresInDur).toDate()
|
|
@@ -281,12 +281,12 @@ async function factory (pkgName) {
|
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
verifyApiKey = async (req, reply, source, payload) => {
|
|
284
|
-
const { merge } = this.app.lib._
|
|
285
|
-
const
|
|
284
|
+
const { merge, camelCase } = this.app.lib._
|
|
285
|
+
const checker = this.app.bajoExtra[camelCase(`is ${this.config.auth.common.apiKey.algo}`)]
|
|
286
286
|
|
|
287
287
|
let token = await this._getToken('apiKey', req, source)
|
|
288
|
-
if (!
|
|
289
|
-
token = await hash(token)
|
|
288
|
+
if (!checker(token)) return false
|
|
289
|
+
token = await this.hash(token)
|
|
290
290
|
const user = await this.getUserByToken(token, req)
|
|
291
291
|
if (!user) throw this.error('invalidKey', merge({ statusCode: 401 }, payload))
|
|
292
292
|
if (user.status !== 'ACTIVE') throw this.error('userInactive', merge({ details: [{ field: 'status', error: 'inactive' }], statusCode: 401 }, payload))
|
|
@@ -463,10 +463,9 @@ async function factory (pkgName) {
|
|
|
463
463
|
}
|
|
464
464
|
|
|
465
465
|
getApiKeyFromUserId = async id => {
|
|
466
|
-
const { hash } = this.app.bajoExtra
|
|
467
466
|
const options = { forceNoHidden: true, noHook: true, noCache: true, attachment: true, mimeType: true }
|
|
468
467
|
const resp = await getModel('SumbaUser').getRecord(id, options)
|
|
469
|
-
return await hash(resp.salt)
|
|
468
|
+
return await this.hash(resp.salt)
|
|
470
469
|
}
|
|
471
470
|
|
|
472
471
|
getCountriesValues = async () => {
|
|
@@ -509,11 +508,10 @@ async function factory (pkgName) {
|
|
|
509
508
|
await this.app.masohiMail.send({ payload, source: source ?? this.ns, conn })
|
|
510
509
|
}
|
|
511
510
|
|
|
512
|
-
resetToken = async (
|
|
511
|
+
resetToken = async (text) => {
|
|
513
512
|
const { generateId } = this.app.lib.aneka
|
|
514
|
-
const
|
|
515
|
-
|
|
516
|
-
const token = await hash(await hash(salt))
|
|
513
|
+
const salt = text ?? generateId()
|
|
514
|
+
const token = await this.hash(await this.hash(salt))
|
|
517
515
|
return { salt, token }
|
|
518
516
|
}
|
|
519
517
|
|
|
@@ -557,6 +555,11 @@ async function factory (pkgName) {
|
|
|
557
555
|
return item
|
|
558
556
|
}
|
|
559
557
|
|
|
558
|
+
hash = async (item) => {
|
|
559
|
+
const { hash } = this.app.bajoExtra
|
|
560
|
+
return await hash(item, this.config.auth.common.apiKey.algo)
|
|
561
|
+
}
|
|
562
|
+
|
|
560
563
|
createNewSite = createNewSite
|
|
561
564
|
removeSite = removeSite
|
|
562
565
|
getSite = getSite
|
package/package.json
CHANGED
package/wiki/CHANGES.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-04-25
|
|
4
|
+
|
|
5
|
+
- [2.21.0] Change options to format value using the new key set by dobo
|
|
6
|
+
- [2.21.0] Remove ```options.retainOriginalValue``` since it is not needed anymore
|
|
7
|
+
- [2.21.0] Remove ```property.formatValue``` from all properties
|
|
8
|
+
- [2.21.0] Remove ```schema.formatValue``` from all schemas
|
|
9
|
+
- [2.21.0] Remove ```schema.calcFields``` from all schemas
|
|
10
|
+
|
|
3
11
|
## 2026-04-23
|
|
4
12
|
|
|
5
13
|
- [2.20.0] Add ```parseRouteGuard()```
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"buildLevel": 2,
|
|
3
|
-
"properties": [{
|
|
4
|
-
"name": "username",
|
|
5
|
-
"type": "string",
|
|
6
|
-
"minLength": 5,
|
|
7
|
-
"maxLength": 50,
|
|
8
|
-
"rules": ["alphanum"]
|
|
9
|
-
}, {
|
|
10
|
-
"name": "password",
|
|
11
|
-
"type": "string",
|
|
12
|
-
"minLength": 8,
|
|
13
|
-
"maxLength": 100
|
|
14
|
-
}, {
|
|
15
|
-
"name": "token",
|
|
16
|
-
"type": "string",
|
|
17
|
-
"maxLength": 50,
|
|
18
|
-
"index": true
|
|
19
|
-
}, {
|
|
20
|
-
"name": "salt",
|
|
21
|
-
"type": "string",
|
|
22
|
-
"maxLength": 50,
|
|
23
|
-
"required": true
|
|
24
|
-
}, {
|
|
25
|
-
"name": "provider",
|
|
26
|
-
"type": "string",
|
|
27
|
-
"maxLength": 50,
|
|
28
|
-
"index": true,
|
|
29
|
-
"default": "local"
|
|
30
|
-
}, {
|
|
31
|
-
"name": "email",
|
|
32
|
-
"type": "string",
|
|
33
|
-
"maxLength": 100,
|
|
34
|
-
"required": true,
|
|
35
|
-
"rules": ["email"]
|
|
36
|
-
}, {
|
|
37
|
-
"name": "firstName",
|
|
38
|
-
"type": "string",
|
|
39
|
-
"maxLength": 50,
|
|
40
|
-
"required": true,
|
|
41
|
-
"index": true
|
|
42
|
-
}, {
|
|
43
|
-
"name": "lastName",
|
|
44
|
-
"type": "string",
|
|
45
|
-
"maxLength": 50,
|
|
46
|
-
"required": true,
|
|
47
|
-
"index": true
|
|
48
|
-
}],
|
|
49
|
-
"rules": [{ "rule": "trim", "fields": ["username", "firstName", "lastName"] }],
|
|
50
|
-
"indexes": [{
|
|
51
|
-
"fields": ["username", "siteId"],
|
|
52
|
-
"type": "unique"
|
|
53
|
-
}, {
|
|
54
|
-
"fields": ["email", "siteId"],
|
|
55
|
-
"type": "unique"
|
|
56
|
-
}],
|
|
57
|
-
"hidden": ["password", "token"],
|
|
58
|
-
"features": [
|
|
59
|
-
"sumba:address",
|
|
60
|
-
"sumba:social",
|
|
61
|
-
{
|
|
62
|
-
"name": "sumba:status",
|
|
63
|
-
"default": "UNVERIFIED"
|
|
64
|
-
},
|
|
65
|
-
"sumba:siteId",
|
|
66
|
-
"dobo:createdAt",
|
|
67
|
-
"dobo:updatedAt",
|
|
68
|
-
"dobo:immutable"
|
|
69
|
-
]
|
|
70
|
-
}
|