sumba 0.0.1 → 0.1.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/bajo/config.json +48 -1
- package/bajo/helper/create-jwt-from-user-record.js +19 -0
- package/bajo/helper/get-user-from-username-password.js +18 -0
- package/bajo/helper/get-user.js +12 -0
- package/bajo/helper/has-column.js +10 -0
- package/bajo/helper/verify/api-key.js +40 -0
- package/bajo/helper/verify/basic.js +45 -0
- package/bajo/helper/verify/jwt.js +30 -0
- package/bajo/helper/verify/session.js +16 -0
- package/bajo/hook/bajo@after-init-bajo-web-mpa.js +5 -0
- package/bajo/hook/bajo@after-init-bajo-web-restapi.js +5 -0
- package/bajo/hook/bajoDb.SumbaUser@on-before-record-create.js +10 -0
- package/bajo/hook/bajoDb.SumbaUser@on-before-record-validation.js +25 -0
- package/bajo/hook/bajoDb@on-before-record-create.js +15 -0
- package/bajo/hook/bajoDb@on-before-record-find.js +23 -0
- package/bajo/hook/bajoDb@on-before-record-get.js +25 -0
- package/bajo/hook/bajoDb@on-before-record-remove.js +10 -0
- package/bajo/hook/bajoDb@on-before-record-update.js +10 -0
- package/bajo/hook/bajoWeb@after-boot-app.js +8 -0
- package/bajo/hook/bajoWeb@after-create-context.js +5 -0
- package/bajo/hook/bajoWeb@on-request.js +10 -0
- package/bajo/hook/bajoWebMpa@pre-parsing.js +33 -0
- package/bajo/hook/bajoWebRestapi@pre-parsing.js +10 -0
- package/bajo/hook/bajoWebStatic@pre-parsing.js +10 -0
- package/bajoAdmin/coll/site-setting.json +3 -0
- package/bajoAdmin/coll/site.json +8 -0
- package/bajoAdmin/coll/user-setting.json +3 -0
- package/bajoAdmin/coll/user.json +98 -0
- package/bajoAdmin/mpa/lib/pre-handler.js +11 -0
- package/bajoAdmin/mpa/route/userman/add.js +16 -0
- package/bajoAdmin/mpa/route/userman/def.json +7 -0
- package/bajoAdmin/mpa/route/userman/delete.js +12 -0
- package/bajoAdmin/mpa/route/userman/detail.js +15 -0
- package/bajoAdmin/mpa/route/userman/edit.js +16 -0
- package/bajoAdmin/mpa/route/userman/list.js +16 -0
- package/bajoDb/feature/address.js +46 -0
- package/bajoDb/feature/country.js +15 -0
- package/bajoDb/feature/lat-lng.js +19 -0
- package/bajoDb/feature/lat.js +13 -0
- package/bajoDb/feature/lng.js +13 -0
- package/bajoDb/feature/person-in-charge.js +25 -0
- package/bajoDb/feature/site-id.js +12 -0
- package/bajoDb/feature/social.js +23 -0
- package/bajoDb/feature/status.js +19 -0
- package/bajoDb/feature/ts.js +13 -0
- package/bajoDb/feature/user-id.js +13 -0
- package/bajoDb/fixture/site-setting.json +4 -0
- package/bajoDb/fixture/site.json +10 -0
- package/bajoDb/fixture/user-setting.json +5 -0
- package/bajoDb/fixture/user.json +11 -0
- package/bajoDb/i18n/en-US.json +6 -0
- package/bajoDb/i18n/id.json +6 -0
- package/bajoDb/schema/site-setting.json +12 -0
- package/bajoDb/schema/site.json +38 -0
- package/bajoDb/schema/user-setting.json +8 -0
- package/bajoDb/schema/user.json +56 -0
- package/bajoI18N/resource/id.json +25 -0
- package/bajoWebMpa/route/activation.js +25 -0
- package/bajoWebMpa/route/change-password.js +24 -0
- package/bajoWebMpa/route/forgot-password.js +8 -0
- package/bajoWebMpa/route/home.js +8 -0
- package/bajoWebMpa/route/profile.js +24 -0
- package/bajoWebMpa/route/signin.js +31 -0
- package/bajoWebMpa/route/signout.js +22 -0
- package/bajoWebMpa/route/signup.js +28 -0
- package/bajoWebMpa/template/default/activation.njk +19 -0
- package/bajoWebMpa/template/default/change-password.njk +24 -0
- package/bajoWebMpa/template/default/forgot-password.njk +20 -0
- package/bajoWebMpa/template/default/home.njk +8 -0
- package/bajoWebMpa/template/default/partial/pp.njk +16 -0
- package/bajoWebMpa/template/default/profile.njk +30 -0
- package/bajoWebMpa/template/default/signin.njk +21 -0
- package/bajoWebMpa/template/default/signout.njk +19 -0
- package/bajoWebMpa/template/default/signup-success.njk +9 -0
- package/bajoWebMpa/template/default/signup.njk +31 -0
- package/bajoWebRestapi/route/account/token/@type/create.js +58 -0
- package/lib/check-dark-mode.js +13 -0
- package/lib/check-lang.js +15 -0
- package/lib/check-site-id.js +36 -0
- package/lib/check-theme.js +9 -0
- package/lib/check-user-id.js +84 -0
- package/lib/collect-redirects.js +5 -0
- package/lib/collect-routes.js +57 -0
- package/package.json +5 -2
- package/sumba/bajoWebMpa@anonymous-routes.json +6 -0
- package/sumba/bajoWebMpa@secure-routes.json +5 -0
package/bajo/config.json
CHANGED
|
@@ -1,3 +1,50 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
2
|
+
"multiSite": true,
|
|
3
|
+
"auth": {
|
|
4
|
+
"common": {
|
|
5
|
+
"apiKey": {
|
|
6
|
+
"type": "Bearer",
|
|
7
|
+
"qsKey": "apiKey",
|
|
8
|
+
"headerKey": "X-Sumba-ApiKey"
|
|
9
|
+
},
|
|
10
|
+
"basic": {
|
|
11
|
+
},
|
|
12
|
+
"jwt": {
|
|
13
|
+
"type": "Bearer",
|
|
14
|
+
"qsKey": "token",
|
|
15
|
+
"headerKey": "X-Sumba-Token",
|
|
16
|
+
"secret": "668de9cf57316c7dbf52f7ff7611c299",
|
|
17
|
+
"expiresIn": 604800000
|
|
18
|
+
},
|
|
19
|
+
"omitUserFields": ["password", "token"]
|
|
20
|
+
},
|
|
21
|
+
"bajoWebRestapi": {
|
|
22
|
+
"methods": ["basic", "apiKey", "jwt"]
|
|
23
|
+
},
|
|
24
|
+
"bajoWebMpa": {
|
|
25
|
+
"methods": ["session"]
|
|
26
|
+
},
|
|
27
|
+
"bajoWebStatic": {
|
|
28
|
+
"methods": ["basic", "apiKey", "jwt"],
|
|
29
|
+
"basic": {
|
|
30
|
+
"useUtf8": true,
|
|
31
|
+
"realm": "Protected Area",
|
|
32
|
+
"warningMessage": "Please authenticate yourself, thank you!"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"redirect": {
|
|
37
|
+
"signin": "sumba:/signin",
|
|
38
|
+
"signout": "sumba:/signout",
|
|
39
|
+
"home": "sumba:/home"
|
|
40
|
+
},
|
|
41
|
+
"userPassword": {
|
|
42
|
+
"minUppercase": 1,
|
|
43
|
+
"minLowercase": 1,
|
|
44
|
+
"minSpecialChar": 1,
|
|
45
|
+
"minNumeric": 1,
|
|
46
|
+
"noWhitespace": false,
|
|
47
|
+
"latinOnlyChars": false
|
|
48
|
+
},
|
|
49
|
+
"dependencies": ["bajo-db", "bajo-extra", "bajo-web", "bajo-common-db", "bajo-emitter"]
|
|
3
50
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
async function createJwtFromUserRecord (rec) {
|
|
2
|
+
const { importPkg, getConfig, dayjs } = this.bajo.helper
|
|
3
|
+
const { hash } = this.bajoExtra.helper
|
|
4
|
+
const { get, pick } = await importPkg('lodash-es')
|
|
5
|
+
const fastJwt = await importPkg('bajo-extra:fast-jwt')
|
|
6
|
+
const { createSigner } = fastJwt
|
|
7
|
+
|
|
8
|
+
const cfg = getConfig('sumba')
|
|
9
|
+
const opts = pick(cfg.auth.common.jwt, ['expiresIn'])
|
|
10
|
+
opts.key = get(cfg, 'auth.common.jwt.secret')
|
|
11
|
+
const sign = createSigner(opts)
|
|
12
|
+
const apiKey = await hash(rec.password)
|
|
13
|
+
const payload = { uid: rec.id, apiKey }
|
|
14
|
+
const token = await sign(payload)
|
|
15
|
+
const expiresAt = dayjs().add(opts.expiresIn).toDate()
|
|
16
|
+
return { token, expiresAt }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default createJwtFromUserRecord
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const coll = 'SumbaUser'
|
|
2
|
+
|
|
3
|
+
async function getUserByUsernamePassword (username = '', password = '', req) {
|
|
4
|
+
const { error, importPkg } = this.bajo.helper
|
|
5
|
+
const { recordFind, validate } = this.bajoDb.helper
|
|
6
|
+
await validate({ username, password }, 'SumbaUser', { ns: ['sumba', 'bajoDb'], fields: ['username', 'password'] })
|
|
7
|
+
const bcrypt = await importPkg('bajo-extra:bcrypt')
|
|
8
|
+
const query = { username }
|
|
9
|
+
const rows = await recordFind(coll, { query }, { req, ignoreHidden: true })
|
|
10
|
+
if (rows.length === 0) throw error('Unknown username', { details: [{ field: 'username', error: 'Unknown username' }], statusCode: 401 })
|
|
11
|
+
const rec = rows[0]
|
|
12
|
+
if (rec.status !== 'ACTIVE') throw error('User is inactive or temporary disabled', { details: ['User is inactive or temporary disabled'], statusCode: 401 })
|
|
13
|
+
const verified = await bcrypt.compare(password, rec.password)
|
|
14
|
+
if (!verified) throw error('Invalid password', { details: [{ field: 'password', error: 'Invalid password' }], statusCode: 401 })
|
|
15
|
+
return rec
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default getUserByUsernamePassword
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
async function getUser (rec, safe = true) {
|
|
2
|
+
const { getConfig, importPkg } = this.bajo.helper
|
|
3
|
+
const { recordGet } = this.bajoDb.helper
|
|
4
|
+
const { omit, isString } = await importPkg('lodash-es')
|
|
5
|
+
const cfg = getConfig('sumba')
|
|
6
|
+
let user
|
|
7
|
+
if (isString(rec)) user = await recordGet('SumbaUser', rec, { skipHook: true })
|
|
8
|
+
else user = rec
|
|
9
|
+
return safe ? omit(user, cfg.auth.common.omitUserFields) : user
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default getUser
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
async function hasColumn (name, coll) {
|
|
2
|
+
const { importPkg } = this.bajo.helper
|
|
3
|
+
const { getInfo } = this.bajoDb.helper
|
|
4
|
+
const { find } = await importPkg('lodash-es')
|
|
5
|
+
const { schema } = await getInfo(coll)
|
|
6
|
+
const result = find(schema.properties, { name })
|
|
7
|
+
return !!result
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default hasColumn
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export async function getSetting (type, source) {
|
|
2
|
+
const { importPkg, getConfig, defaultsDeep } = this.bajo.helper
|
|
3
|
+
const { get } = await importPkg('lodash-es')
|
|
4
|
+
const cfg = getConfig('sumba')
|
|
5
|
+
const setting = defaultsDeep(get(cfg, `auth.${source}.${type}`, {}), get(cfg, `auth.common.${type}`, {}))
|
|
6
|
+
if (type === 'basic') setting.type = 'Basic'
|
|
7
|
+
return setting
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function getToken (type, req, source) {
|
|
11
|
+
const { importPkg } = this.bajo.helper
|
|
12
|
+
const { isEmpty } = await importPkg('lodash-es')
|
|
13
|
+
const setting = await getSetting.call(this, type, source)
|
|
14
|
+
let token = req.headers[setting.headerKey.toLowerCase()]
|
|
15
|
+
if (!['basic'].includes(type) && isEmpty(token)) token = req.query[setting.qsKey]
|
|
16
|
+
if (isEmpty(token)) {
|
|
17
|
+
const parts = (req.headers.authorization || '').split(' ')
|
|
18
|
+
if (parts[0] === setting.type) token = parts[1]
|
|
19
|
+
}
|
|
20
|
+
if (isEmpty(token)) return false
|
|
21
|
+
return token
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function verifyApiKey (ctx, req, reply, source) {
|
|
25
|
+
const { error } = this.bajo.helper
|
|
26
|
+
const { isMd5, hash } = this.bajoExtra.helper
|
|
27
|
+
const { getUser } = this.sumba.helper
|
|
28
|
+
const { recordFind } = this.bajoDb.helper
|
|
29
|
+
let token = await getToken.call(this, 'apiKey', req, source)
|
|
30
|
+
if (!isMd5(token)) return false
|
|
31
|
+
token = await hash(token)
|
|
32
|
+
const query = { token }
|
|
33
|
+
const rows = await recordFind('SumbaUser', { query }, { req })
|
|
34
|
+
if (rows.length === 0) throw error('Invalid api key provided', { statusCode: 401 })
|
|
35
|
+
if (rows[0].status !== 'ACTIVE') throw error('User is inactive or temporary disabled', { details: [{ field: 'status', error: 'inactive' }], statusCode: 401 })
|
|
36
|
+
req.user = await getUser(rows[0])
|
|
37
|
+
return true
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default verifyApiKey
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { getSetting } from './api-key.js'
|
|
2
|
+
|
|
3
|
+
async function setHeader (setting, reply) {
|
|
4
|
+
const { importPkg } = this.bajo.helper
|
|
5
|
+
const { isString } = await importPkg('lodash-es')
|
|
6
|
+
let header = setting.type
|
|
7
|
+
const exts = []
|
|
8
|
+
if (isString(setting.realm)) exts.push(`realm="${setting.realm}"`)
|
|
9
|
+
if (setting.useUtf8) exts.push('charset="UTF-8"')
|
|
10
|
+
if (exts.length > 0) header += ` ${exts.join(', ')}`
|
|
11
|
+
reply.header('WWW-Authenticate', header)
|
|
12
|
+
reply.code(401)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function verifyBasic (ctx, req, reply, source) {
|
|
16
|
+
const { importPkg, print, error } = this.bajo.helper
|
|
17
|
+
const { getUserFromUsernamePassword } = this.sumba.helper
|
|
18
|
+
const { getUser } = this.sumba.helper
|
|
19
|
+
const { isEmpty } = await importPkg('lodash-es')
|
|
20
|
+
const setting = await getSetting.call(this, 'basic', source)
|
|
21
|
+
let authInfo
|
|
22
|
+
const parts = (req.headers.authorization || '').split(' ')
|
|
23
|
+
if (parts[0] === setting.type) authInfo = parts[1]
|
|
24
|
+
if (isEmpty(authInfo)) {
|
|
25
|
+
if (setting.realm) {
|
|
26
|
+
await setHeader.call(this, setting, reply)
|
|
27
|
+
throw error('print', { print: print.__(setting.warningMessage) })
|
|
28
|
+
} else return false
|
|
29
|
+
}
|
|
30
|
+
const decoded = Buffer.from(authInfo, 'base64').toString()
|
|
31
|
+
const [username, password] = decoded.split(':')
|
|
32
|
+
try {
|
|
33
|
+
const user = await getUserFromUsernamePassword(username, password, req)
|
|
34
|
+
req.user = await getUser(user)
|
|
35
|
+
} catch (err) {
|
|
36
|
+
if (err.statusCode === 401 && setting.realm) {
|
|
37
|
+
await setHeader.call(this, setting, reply)
|
|
38
|
+
return err.message
|
|
39
|
+
}
|
|
40
|
+
throw err
|
|
41
|
+
}
|
|
42
|
+
return true
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default verifyBasic
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { getToken, getSetting } from './api-key.js'
|
|
2
|
+
|
|
3
|
+
async function verifyJwt (ctx, req, reply, source) {
|
|
4
|
+
const { importPkg, error } = this.bajo.helper
|
|
5
|
+
const { recordGet } = this.bajoDb.helper
|
|
6
|
+
const { getUser } = this.sumba.helper
|
|
7
|
+
const { isEmpty } = await importPkg('lodash-es')
|
|
8
|
+
const fastJwt = await importPkg('bajo-extra:fast-jwt')
|
|
9
|
+
const { createVerifier } = fastJwt
|
|
10
|
+
const setting = await getSetting.call(this, 'jwt', source)
|
|
11
|
+
const token = await getToken.call(this, 'jwt', req, source)
|
|
12
|
+
if (isEmpty(token)) return false
|
|
13
|
+
const verifier = createVerifier({
|
|
14
|
+
key: setting.secret,
|
|
15
|
+
complete: true
|
|
16
|
+
})
|
|
17
|
+
const decoded = await verifier(token)
|
|
18
|
+
const id = decoded.payload.uid
|
|
19
|
+
try {
|
|
20
|
+
const rec = await recordGet('SumbaUser', id, { req })
|
|
21
|
+
if (!rec) throw error('Invalid token or token is expired', { statusCode: 401 })
|
|
22
|
+
if (rec.status !== 'ACTIVE') throw error('User is inactive or temporary disabled', { details: [{ field: 'status', error: 'inactive' }], statusCode: 401 })
|
|
23
|
+
req.user = await getUser(rec)
|
|
24
|
+
return true
|
|
25
|
+
} catch (err) {
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default verifyJwt
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
async function verifySession (ctx, req, reply, source) {
|
|
2
|
+
const { getConfig, error } = this.bajo.helper
|
|
3
|
+
const { getUser } = this.sumba.helper
|
|
4
|
+
const { routePath } = this.bajoWeb.helper
|
|
5
|
+
const cfg = getConfig('sumba')
|
|
6
|
+
if (!req.session) return false
|
|
7
|
+
if (req.session.user) {
|
|
8
|
+
req.user = await getUser(req.session.user.id)
|
|
9
|
+
return true
|
|
10
|
+
}
|
|
11
|
+
const redir = routePath(cfg.redirect.signin, req)
|
|
12
|
+
req.session.ref = req.url
|
|
13
|
+
throw error('redirect', { redirect: redir })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default verifySession
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
async function bajoDbSiteUserOnBeforeRecordCreate (body, options) {
|
|
2
|
+
const { importPkg } = this.bajo.helper
|
|
3
|
+
const { isBcrypt, hash } = this.bajoExtra.helper
|
|
4
|
+
const { isEmpty } = await importPkg('lodash-es')
|
|
5
|
+
if (isEmpty(body.password)) return
|
|
6
|
+
if (!isBcrypt(body.password)) body.password = await hash(body.password, 'bcrypt')
|
|
7
|
+
body.token = await hash(await hash(body.password))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default bajoDbSiteUserOnBeforeRecordCreate
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { joiPasswordExtendCore } from 'joi-password'
|
|
2
|
+
|
|
3
|
+
async function bajoDbSiteUserOnBeforeRecordValidation (body, options = {}) {
|
|
4
|
+
const { importPkg, getConfig } = this.bajo.helper
|
|
5
|
+
const { set } = await importPkg('lodash-es')
|
|
6
|
+
const joi = await importPkg('bajo-db:joi')
|
|
7
|
+
const cfg = getConfig('sumba')
|
|
8
|
+
const joiPassword = joi.extend(joiPasswordExtendCore)
|
|
9
|
+
let password = joiPassword
|
|
10
|
+
.string()
|
|
11
|
+
.min(8)
|
|
12
|
+
.max(50)
|
|
13
|
+
.required()
|
|
14
|
+
if (cfg.userPassword.minUppercase) password = password.minOfUppercase(cfg.userPassword.minUppercase)
|
|
15
|
+
if (cfg.userPassword.minLowercase) password = password.minOfLowercase(cfg.userPassword.minLowercase)
|
|
16
|
+
if (cfg.userPassword.minSpecialChar) password = password.minOfSpecialCharacters(cfg.userPassword.minSpecialChar)
|
|
17
|
+
if (cfg.userPassword.minNumeric) password = password.minOfNumeric(cfg.userPassword.minNumeric)
|
|
18
|
+
if (cfg.userPassword.noWhitespace) password = password.noWhiteSpaces()
|
|
19
|
+
if (cfg.userPassword.latinOnlyChars) password = password.onlyLatinCharacters()
|
|
20
|
+
|
|
21
|
+
const rule = { password }
|
|
22
|
+
set(options, 'validation.params.rule', rule)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default bajoDbSiteUserOnBeforeRecordValidation
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const bajoDbOnBeforeRecordCreate = {
|
|
2
|
+
level: 1000,
|
|
3
|
+
handler: async function (coll, body, options) {
|
|
4
|
+
const { importPkg } = this.bajo.helper
|
|
5
|
+
const { get } = await importPkg('lodash-es')
|
|
6
|
+
const { hasColumn } = this.sumba.helper
|
|
7
|
+
const item = { siteId: 'req.site.id', userId: 'req.user.id' }
|
|
8
|
+
for (const i in item) {
|
|
9
|
+
const rec = get(options, item[i])
|
|
10
|
+
if (rec && await hasColumn(i, coll)) body[i] = rec
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default bajoDbOnBeforeRecordCreate
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const bajoDbOnBeforeRecordFind = {
|
|
2
|
+
level: 1000,
|
|
3
|
+
handler: async function (coll, filter, options) {
|
|
4
|
+
const { importPkg } = this.bajo.helper
|
|
5
|
+
const { isEmpty, cloneDeep, get, set } = await importPkg('lodash-es')
|
|
6
|
+
const { hasColumn } = this.sumba.helper
|
|
7
|
+
const item = { siteId: 'req.site.id', userId: 'req.user.id' }
|
|
8
|
+
for (const i in item) {
|
|
9
|
+
const rec = get(options, item[i])
|
|
10
|
+
if (rec && await hasColumn(i, coll)) {
|
|
11
|
+
filter.query = filter.query ?? {}
|
|
12
|
+
const old = cloneDeep(filter.query.$or)
|
|
13
|
+
if (old) {
|
|
14
|
+
filter.query = { $and: [old] }
|
|
15
|
+
filter.query.$and.push(set({}, i, rec))
|
|
16
|
+
} else filter.query[i] = rec
|
|
17
|
+
if (isEmpty(filter.query)) filter.query = undefined
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default bajoDbOnBeforeRecordFind
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export async function checker (coll, id, req) {
|
|
2
|
+
const { importPkg, error } = this.bajo.helper
|
|
3
|
+
const { recordFind } = this.bajoDb.helper
|
|
4
|
+
const { get } = await importPkg('lodash-es')
|
|
5
|
+
const { hasColumn } = this.sumba.helper
|
|
6
|
+
const item = { siteId: 'site.id', userId: 'user.id' }
|
|
7
|
+
for (const i in item) {
|
|
8
|
+
const rec = get(req, item[i])
|
|
9
|
+
if (rec && await hasColumn(i, coll)) {
|
|
10
|
+
const filter = { query: { id }, limit: 1 }
|
|
11
|
+
filter.query[i] = rec
|
|
12
|
+
const rows = await recordFind(coll, filter)
|
|
13
|
+
if (rows.length === 0) throw error('Record \'%s@%s\' not found!', id, coll, { statusCode: 404 })
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const bajoDbOnBeforeRecordGet = {
|
|
19
|
+
level: 1000,
|
|
20
|
+
handler: async function (coll, id, options) {
|
|
21
|
+
await checker.call(this, coll, id, options.req)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default bajoDbOnBeforeRecordGet
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { checker } from './bajoDb@on-before-record-get.js'
|
|
2
|
+
|
|
3
|
+
const bajoDbOnBeforeRecordRemove = {
|
|
4
|
+
level: 1000,
|
|
5
|
+
handler: async function (coll, id, options) {
|
|
6
|
+
await checker.call(this, coll, id, options.req)
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default bajoDbOnBeforeRecordRemove
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { checker } from './bajoDb@on-before-record-get.js'
|
|
2
|
+
|
|
3
|
+
const bajoDbOnBeforeRecordUpdate = {
|
|
4
|
+
level: 1000,
|
|
5
|
+
handler: async function (coll, id, body, options) {
|
|
6
|
+
await checker.call(this, coll, id, options.req)
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default bajoDbOnBeforeRecordUpdate
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import checkUserId from '../../lib/check-user-id.js'
|
|
2
|
+
import checkTheme from '../../lib/check-theme.js'
|
|
3
|
+
import checkLang from '../../lib/check-lang.js'
|
|
4
|
+
import checkDarkMode from '../../lib/check-dark-mode.js'
|
|
5
|
+
|
|
6
|
+
const onRequest = {
|
|
7
|
+
level: 10,
|
|
8
|
+
handler: async function (ctx, req, reply) {
|
|
9
|
+
const { routePath } = this.bajoWeb.helper
|
|
10
|
+
await checkDarkMode.call(this, req, reply)
|
|
11
|
+
await checkLang.call(this, req, reply)
|
|
12
|
+
await checkTheme.call(this, req, reply)
|
|
13
|
+
await checkUserId.call(this, req, reply, 'bajoWebMpa')
|
|
14
|
+
req.menu = req.menu ?? {}
|
|
15
|
+
if (req.user) {
|
|
16
|
+
req.menu.user = [
|
|
17
|
+
{ value: routePath('sumba:/change-password', req), text: 'Change Password' },
|
|
18
|
+
{ value: routePath('sumba:/profile', req), text: 'Your Profile' },
|
|
19
|
+
'-',
|
|
20
|
+
{ value: routePath('sumba:/signout', req), text: 'Signout' }
|
|
21
|
+
]
|
|
22
|
+
} else {
|
|
23
|
+
req.menu.user = [
|
|
24
|
+
{ value: routePath('sumba:/signin', req), text: 'Signin' },
|
|
25
|
+
'-',
|
|
26
|
+
{ value: routePath('sumba:/signup', req), text: 'Signup' },
|
|
27
|
+
{ value: routePath('sumba:/forgot-password', req), text: 'Forgot Password' }
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default onRequest
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"onMenu": false,
|
|
3
|
+
"view": {
|
|
4
|
+
"hidden": ["password", "token"],
|
|
5
|
+
"list": {
|
|
6
|
+
"hidden": ["address1", "address2", "zipCode", "provinceState", "country", "website"],
|
|
7
|
+
"defSort": "username:1"
|
|
8
|
+
},
|
|
9
|
+
"detail": {
|
|
10
|
+
"layouts": [{
|
|
11
|
+
"title": "Meta Info",
|
|
12
|
+
"fields": [
|
|
13
|
+
"createdAt;small:6,medium:6",
|
|
14
|
+
"updatedAt;small:6,medium:6",
|
|
15
|
+
"id;small:6,medium:4",
|
|
16
|
+
"siteId;small:6,medium:4",
|
|
17
|
+
"status;small:12,medium:4"
|
|
18
|
+
]
|
|
19
|
+
}, {
|
|
20
|
+
"title": "Account",
|
|
21
|
+
"fields": [
|
|
22
|
+
"username;small:6,medium:6",
|
|
23
|
+
"email;small:6,medium:6",
|
|
24
|
+
"firstName;small:6,medium:6",
|
|
25
|
+
"lastName;small:6,medium:6"
|
|
26
|
+
]
|
|
27
|
+
}, {
|
|
28
|
+
"title": "Address",
|
|
29
|
+
"fields": [
|
|
30
|
+
"address1",
|
|
31
|
+
"address2",
|
|
32
|
+
"city;small:6,medium:8",
|
|
33
|
+
"zipCode;small:6,medium:4",
|
|
34
|
+
"provinceState;small:6,medium:6",
|
|
35
|
+
"country;small:6,medium:6"
|
|
36
|
+
]
|
|
37
|
+
}, {
|
|
38
|
+
"title": "Contact",
|
|
39
|
+
"fields": [
|
|
40
|
+
"phone;small:6,medium:6",
|
|
41
|
+
"website;small:6,medium:6"
|
|
42
|
+
]
|
|
43
|
+
}, {
|
|
44
|
+
"title": "Social Media",
|
|
45
|
+
"fields": [
|
|
46
|
+
"twitter;small:6,medium:3",
|
|
47
|
+
"instagram;small:6,medium:3",
|
|
48
|
+
"facebook;small:6,medium:3",
|
|
49
|
+
"linkedIn;small:6,medium:3"
|
|
50
|
+
]
|
|
51
|
+
}]
|
|
52
|
+
},
|
|
53
|
+
"edit": {
|
|
54
|
+
"layouts": [{
|
|
55
|
+
"title": "Meta Info",
|
|
56
|
+
"fields": [
|
|
57
|
+
"createdAt;small:6,medium:6;formPlaintext",
|
|
58
|
+
"updatedAt;small:6,medium:6;formPlaintext",
|
|
59
|
+
"id;small:6,medium:4;formPlaintext",
|
|
60
|
+
"siteId;small:6,medium:4",
|
|
61
|
+
"status;small:12,medium:4"
|
|
62
|
+
]
|
|
63
|
+
}, {
|
|
64
|
+
"title": "Account",
|
|
65
|
+
"fields": [
|
|
66
|
+
"username;small:6,medium:6",
|
|
67
|
+
"email;small:6,medium:6",
|
|
68
|
+
"firstName;small:6,medium:6",
|
|
69
|
+
"lastName;small:6,medium:6"
|
|
70
|
+
]
|
|
71
|
+
}, {
|
|
72
|
+
"title": "Address",
|
|
73
|
+
"fields": [
|
|
74
|
+
"address1",
|
|
75
|
+
"address2",
|
|
76
|
+
"city;small:6,medium:8",
|
|
77
|
+
"zipCode;small:6,medium:4",
|
|
78
|
+
"provinceState;small:6,medium:6",
|
|
79
|
+
"country;small:6,medium:6"
|
|
80
|
+
]
|
|
81
|
+
}, {
|
|
82
|
+
"title": "Contact",
|
|
83
|
+
"fields": [
|
|
84
|
+
"phone;small:6,medium:6",
|
|
85
|
+
"website;small:6,medium:6"
|
|
86
|
+
]
|
|
87
|
+
}, {
|
|
88
|
+
"title": "Social Media",
|
|
89
|
+
"fields": [
|
|
90
|
+
"twitter;small:6,medium:3",
|
|
91
|
+
"instagram;small:6,medium:3",
|
|
92
|
+
"facebook;small:6,medium:3",
|
|
93
|
+
"linkedIn;small:6,medium:3"
|
|
94
|
+
]
|
|
95
|
+
}]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
async function preHandler (ctx, req, reply) {
|
|
2
|
+
const { importModule, getConfig } = this.bajo.helper
|
|
3
|
+
const cfg = getConfig('bajoAdmin', { full: true })
|
|
4
|
+
const buildCollMenu = await importModule(`${cfg.dir.pkg}/lib//build-coll-menu.js`)
|
|
5
|
+
const buildPagesMenu = await importModule(`${cfg.dir.pkg}/lib//build-pages-menu.js`)
|
|
6
|
+
req.menu = req.menu ?? {}
|
|
7
|
+
req.menu.coll = await buildCollMenu.call(this)
|
|
8
|
+
req.menu.pages = await buildPagesMenu.call(this)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default preHandler
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import preHandler from '../../lib/pre-handler.js'
|
|
2
|
+
|
|
3
|
+
const userman = {
|
|
4
|
+
title: 'User Manager',
|
|
5
|
+
preHandler,
|
|
6
|
+
method: ['GET', 'POST'],
|
|
7
|
+
handler: async function (ctx, req, reply) {
|
|
8
|
+
const { importModule, getConfig, readConfig, currentLoc } = this.bajo.helper
|
|
9
|
+
const { coll, tpl } = await readConfig(currentLoc(import.meta).dir + '/def.json')
|
|
10
|
+
const cfg = getConfig('bajoAdmin', { full: true })
|
|
11
|
+
const addHandler = await importModule(`${cfg.dir.pkg}/lib/crud/add-handler.js`)
|
|
12
|
+
return await addHandler.call(this, { ctx, req, reply, coll, tpl })
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default userman
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const userman = {
|
|
2
|
+
method: 'POST',
|
|
3
|
+
handler: async function (ctx, req, reply) {
|
|
4
|
+
const { importModule, getConfig, readConfig, currentLoc } = this.bajo.helper
|
|
5
|
+
const { coll, tpl } = await readConfig(currentLoc(import.meta).dir + '/def.json')
|
|
6
|
+
const cfg = getConfig('bajoAdmin', { full: true })
|
|
7
|
+
const deleteHandler = await importModule(`${cfg.dir.pkg}/lib/crud/delete-handler.js`)
|
|
8
|
+
return await deleteHandler.call(this, { ctx, req, reply, coll, tpl })
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default userman
|