sumba 1.0.17 → 1.0.19
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/hook/dobo@before-record-get.js +1 -1
- package/bajo/hook/waibu-mpa.sumba@after-build-locals.js +6 -6
- package/bajo/hook/waibu-mpa@pre-parsing.js +6 -6
- package/bajo/hook/waibu@after-app-boot.js +5 -5
- package/bajo/intl/en-US.json +89 -0
- package/bajo/intl/id.json +90 -0
- package/bajo/method/get-user-from-username-password.js +3 -3
- package/bajo/method/verify/api-key.js +2 -2
- package/bajo/method/verify/jwt.js +2 -2
- package/bajo/method/verify/session.js +1 -1
- package/bajoAdmin/coll/user.json +2 -2
- package/lib/check-site-id.js +2 -2
- package/lib/check-user-id.js +4 -4
- package/package.json +1 -1
- package/waibuDb/schema/user.json +4 -4
- package/waibuMpa/partial/api-key-modal.html +4 -4
- package/waibuMpa/partial/help/contact-form/success.html +4 -4
- package/waibuMpa/partial/help/trouble-tickets/list.html +0 -1
- package/waibuMpa/partial/list-item/member-links.html +6 -6
- package/waibuMpa/partial/list-item/your-profile.html +1 -1
- package/waibuMpa/partial/my-stuff/change-password.html +12 -0
- package/waibuMpa/partial/my-stuff/profile/edit.html +50 -0
- package/waibuMpa/partial/my-stuff/profile/view.html +34 -0
- package/waibuMpa/partial/my-stuff/reset-api-key.html +12 -0
- package/waibuMpa/partial/signout.html +2 -2
- package/waibuMpa/partial/user/activation.html +10 -0
- package/waibuMpa/partial/user/forgot-password.html +10 -0
- package/waibuMpa/partial/user/signup/form.html +16 -0
- package/waibuMpa/partial/user/signup/success.html +8 -0
- package/waibuMpa/route/my-stuff/change-password.js +3 -3
- package/waibuMpa/route/my-stuff/profile/edit.js +1 -1
- package/waibuMpa/route/my-stuff/reset-api-key.js +3 -3
- package/waibuMpa/route/signin.js +1 -1
- package/waibuMpa/route/signout.js +2 -2
- package/waibuMpa/route/user/activation.js +2 -2
- package/waibuMpa/route/user/signup.js +1 -1
- package/waibuMpa/template/help/contact-form/form.html +1 -2
- package/waibuMpa/template/help/contact-form/success.html +1 -2
- package/waibuMpa/template/help/trouble-tickets/list.html +1 -2
- package/waibuMpa/template/info/about-us.id.md +1 -2
- package/waibuMpa/template/info/about-us.md +1 -1
- package/waibuMpa/template/info/cookie-policy.md +1 -1
- package/waibuMpa/template/info/privacy.md +1 -1
- package/waibuMpa/template/info/terms-conditions.md +1 -1
- package/waibuMpa/template/my-stuff/change-password.html +1 -13
- package/waibuMpa/template/my-stuff/profile/edit.html +1 -51
- package/waibuMpa/template/my-stuff/profile/view.html +1 -35
- package/waibuMpa/template/my-stuff/reset-api-key.html +1 -13
- package/waibuMpa/template/signin.html +1 -1
- package/waibuMpa/template/signout.html +1 -1
- package/waibuMpa/template/user/activation.html +1 -11
- package/waibuMpa/template/user/forgot-password.html +1 -11
- package/waibuMpa/template/user/signup/form.html +1 -17
- package/waibuMpa/template/user/signup/success.html +1 -9
- package/waibuRestApi/route/my-stuff/change-password/update.js +1 -1
- package/waibuRestApi/route/user/access-token/@type/create.js +1 -1
- package/bajoI18N/resource/en-US.json +0 -11
- package/bajoI18N/resource/id.json +0 -78
|
@@ -11,7 +11,7 @@ export async function checker (model, id, options = {}) {
|
|
|
11
11
|
const filter = { query: { id }, limit: 1 }
|
|
12
12
|
filter.query[i] = rec
|
|
13
13
|
const rows = await recordFind(model, filter)
|
|
14
|
-
if (rows.length === 0) throw this.error('
|
|
14
|
+
if (rows.length === 0) throw this.error('recordNotFound%s%s', id, model, { statusCode: 404 })
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -2,13 +2,13 @@ async function afterBuildLocals (locals, req) {
|
|
|
2
2
|
const { routePath } = this.app.waibu
|
|
3
3
|
const items = []
|
|
4
4
|
if (req.user) {
|
|
5
|
-
items.push({ icon: 'person', tooltip: '
|
|
6
|
-
items.push({ icon: 'key', tooltip: '
|
|
7
|
-
items.push({ component: 'navItemSignout', tooltip: '
|
|
5
|
+
items.push({ icon: 'person', tooltip: 'yourProfile', href: routePath('sumba:/my-stuff/profile') })
|
|
6
|
+
items.push({ icon: 'key', tooltip: 'changePassword', href: routePath('sumba:/my-stuff/change-password') })
|
|
7
|
+
items.push({ component: 'navItemSignout', tooltip: 'signout', bottom: true })
|
|
8
8
|
} else {
|
|
9
|
-
items.push({ icon: 'signin', tooltip: '
|
|
10
|
-
items.push({ icon: 'key', tooltip: '
|
|
11
|
-
items.push({ icon: 'personAdd', tooltip: '
|
|
9
|
+
items.push({ icon: 'signin', tooltip: 'signin', href: routePath('sumba:/signin') })
|
|
10
|
+
items.push({ icon: 'key', tooltip: 'forgotPassword', href: routePath('sumba:/user/forgot-password') })
|
|
11
|
+
items.push({ icon: 'personAdd', tooltip: 'newUserSignup', href: routePath('sumba:/user/signup') })
|
|
12
12
|
}
|
|
13
13
|
for (const item of items) {
|
|
14
14
|
if (locals._meta.url.startsWith(item.href)) item.active = true
|
|
@@ -13,17 +13,17 @@ const preParsing = {
|
|
|
13
13
|
req.menu = req.menu ?? {}
|
|
14
14
|
if (req.user) {
|
|
15
15
|
req.menu.user = [
|
|
16
|
-
{ value: routePath('sumba:/change-password', req), text: req.
|
|
17
|
-
{ value: routePath('sumba:/profile', req), text: req.
|
|
16
|
+
{ value: routePath('sumba:/change-password', req), text: req.t('Change Password') },
|
|
17
|
+
{ value: routePath('sumba:/profile', req), text: req.t('yourProfile') },
|
|
18
18
|
'-',
|
|
19
|
-
{ value: routePath('sumba:/signout', req), text: req.
|
|
19
|
+
{ value: routePath('sumba:/signout', req), text: req.t('signout') }
|
|
20
20
|
]
|
|
21
21
|
} else {
|
|
22
22
|
req.menu.user = [
|
|
23
|
-
{ value: routePath('sumba:/signin', req), text: req.
|
|
23
|
+
{ value: routePath('sumba:/signin', req), text: req.t('signin') },
|
|
24
24
|
'-',
|
|
25
|
-
{ value: routePath('sumba:/signup', req), text: req.
|
|
26
|
-
{ value: routePath('sumba:/forgot-password', req), text: req.
|
|
25
|
+
{ value: routePath('sumba:/signup', req), text: req.t('signup') },
|
|
26
|
+
{ value: routePath('sumba:/forgot-password', req), text: req.t('forgotPassword') }
|
|
27
27
|
]
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import collectRoutes from '../../lib/collect-routes.js'
|
|
2
2
|
|
|
3
3
|
async function afterAppBoot () {
|
|
4
|
-
this.log.trace('
|
|
4
|
+
this.log.trace('collectingRouteGuards')
|
|
5
5
|
await collectRoutes.call(this, 'secure')
|
|
6
6
|
await collectRoutes.call(this, 'anonymous')
|
|
7
|
-
this.log.trace('
|
|
8
|
-
this.log.trace('
|
|
9
|
-
this.log.trace('
|
|
10
|
-
this.log.trace('
|
|
7
|
+
this.log.trace('secureRoutes%d', this.secureRoutes.length)
|
|
8
|
+
this.log.trace('secureInvRoutes%d', this.secureInvRoutes.length)
|
|
9
|
+
this.log.trace('anonRoutes%d', this.anonymousRoutes.length)
|
|
10
|
+
this.log.trace('anonInvRoutes%d', this.anonymousInvRoutes.length)
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export default afterAppBoot
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"invalidKey": "Invalid api key provided",
|
|
3
|
+
"userInactive": "User is inactive or temporarily disabled",
|
|
4
|
+
"invalidToken": "Invalid token or token is expired",
|
|
5
|
+
"unknownUsername": "Unknown username",
|
|
6
|
+
"invalidPassword": "Invalid password",
|
|
7
|
+
"unknownSite": "Unknown site or site is not configured yet",
|
|
8
|
+
"siteInactiveInfo": "This site is currently inactive or disabled. Please contact your admin, thanks!",
|
|
9
|
+
"noAuthMethod": "No authentication method found. Please contact your admin immediately!",
|
|
10
|
+
"invalidAuthMethod%s": "Invalid authentication method '%s'",
|
|
11
|
+
"accessDenied": "Access denied. Can't authenticate you through all available methods",
|
|
12
|
+
"invalidTokenType": "Invalid token type",
|
|
13
|
+
"invalidCurrentPassword": "Invalid current password",
|
|
14
|
+
"themeGloballyDisabled": "Theme support is globally disabled!",
|
|
15
|
+
"themeNotInstalled%s": "Theme '%s' is not installed",
|
|
16
|
+
"profile": "Profile",
|
|
17
|
+
"yourProfile": "Your Profile",
|
|
18
|
+
"editYourProfile": "Edit Your Profile",
|
|
19
|
+
"account": "Account",
|
|
20
|
+
"address": "Address",
|
|
21
|
+
"phoneEmail": "Phone/Email",
|
|
22
|
+
"changePassword": "Change Password",
|
|
23
|
+
"signout": "Signout",
|
|
24
|
+
"signin": "Signin",
|
|
25
|
+
"yourStuff": "Your Stuff",
|
|
26
|
+
"forgotPassword": "Forgot Password",
|
|
27
|
+
"userActivation": "User Activation",
|
|
28
|
+
"signup": "Signup",
|
|
29
|
+
"newUserSignup": "New User Signup",
|
|
30
|
+
"updateProfile": "Update Profile",
|
|
31
|
+
"backToProfile": "Back To Profile",
|
|
32
|
+
"displayApiKey": "Display API Key",
|
|
33
|
+
"apiKey": "API Key",
|
|
34
|
+
"aboutUs": "About Us",
|
|
35
|
+
"invalidActivationKey": "Invalid activation key",
|
|
36
|
+
"signoutNow": "Yes, sign me out now",
|
|
37
|
+
"signoutWarning": "You\\'re about to sign out. This means your session will be cleared and you\\'ll be asked for your credential again in case you want to access any protected pages and resources.<br /><br />Are your sure?",
|
|
38
|
+
"clearInvalidKey": "Clear Invalid Fields",
|
|
39
|
+
"linkPasswordWillbeSent": "Link to reset password will be sent to your email",
|
|
40
|
+
"signinSuccessfully": "You've successfully signed in",
|
|
41
|
+
"signoutSuccessfully": "You've successfully signed out",
|
|
42
|
+
"profileUpdated": "Your profile has been successfully updated",
|
|
43
|
+
"passwordChangedReSignin": "You've successfully changed your password. Now please re-signin with your new password",
|
|
44
|
+
"agreeToTerm%s%s": "I agree to the <a href=\"%s\">%s</a>",
|
|
45
|
+
"userActivated": "User account successfully activated",
|
|
46
|
+
"userCreated": "User account successfully created",
|
|
47
|
+
"resetApiKey": "Reset API Key",
|
|
48
|
+
"resetApiKeySuccessfull": "You've successfully reset your API key",
|
|
49
|
+
"personInCharge": "Person In Charge",
|
|
50
|
+
"contactForm": "Contact Form",
|
|
51
|
+
"terms": "Terms",
|
|
52
|
+
"cookie": "Cookie Policy",
|
|
53
|
+
"cookiePolicy": "Cookie Policy",
|
|
54
|
+
"privacy": "Privacy Statement",
|
|
55
|
+
"termsConditions": "Terms and Conditions",
|
|
56
|
+
"privacyStatement": "Privacy Statement",
|
|
57
|
+
"legalInfo": "Legal Info",
|
|
58
|
+
"help": "Help",
|
|
59
|
+
"troubleTickets": "Trouble Tickets",
|
|
60
|
+
"guest": "Guest",
|
|
61
|
+
"formSubmitted": "Form submitted successfully",
|
|
62
|
+
"formSubmittedInfo": "Please allow us to read your submission thoroughly and we'll get back to you rightaway",
|
|
63
|
+
"thankYou": "Thank you!",
|
|
64
|
+
"warningMemberOnly%s": "Please authenticate yourself first, because the <a href=\"%s\">page<a> your're trying to access is a member only page",
|
|
65
|
+
"collectingRouteGuards": "Collecting route guards:",
|
|
66
|
+
"secureRoutes%d": "- Secure routes: %d",
|
|
67
|
+
"secureInvRoutes%d": "- Secure, inverted routes: %d",
|
|
68
|
+
"anonRoutes%d": "- Anonymous routes: %d",
|
|
69
|
+
"anonInvRoutes%d": "- Anonymous, inverted routes: %d",
|
|
70
|
+
"field": {
|
|
71
|
+
"currentPassword": "Current Password",
|
|
72
|
+
"newPassword": "New Password",
|
|
73
|
+
"verifyNewPassword": "Verify New Password",
|
|
74
|
+
"usernameEmail": "Username/Email",
|
|
75
|
+
"apiKey": "API Key",
|
|
76
|
+
"hostname": "Hostname",
|
|
77
|
+
"iconset": "Iconset",
|
|
78
|
+
"subject": "Subject",
|
|
79
|
+
"message": "Message"
|
|
80
|
+
},
|
|
81
|
+
"validation": {
|
|
82
|
+
"password.minOfUppercase": "Contains at leaset {{min}} uppercase characters",
|
|
83
|
+
"password.minOfSpecialCharacters": "Contains at least {{min}} special characters",
|
|
84
|
+
"password.minOfLowercase": "Contains at least {{min}} special characters",
|
|
85
|
+
"password.minOfNumeric": "Contains at least {{min}} numerical characters",
|
|
86
|
+
"password.noWhiteSpaces": "No space allowed",
|
|
87
|
+
"password.onlyLatinCharacters": "Only latin characters allowed"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"invalidKey": "Kunci API yang diberikan tidak valud",
|
|
3
|
+
"userInactive": "Pengguna sedang tidak aktif atau sedang ditangguhkan secara temporer",
|
|
4
|
+
"invalidToken": "Token tidak valid atau token telah kadaluarsa",
|
|
5
|
+
"unknownUsername": "Nama pengguna tidak dikenal",
|
|
6
|
+
"invalidPassword": "Kata kunci tidak valid",
|
|
7
|
+
"unknownSite": "Situs tidak dikenal atau situs ini belum dikonfigurasikan dengan benar",
|
|
8
|
+
"siteInactiveInfo": "Situs sedang tidak aktif atau sedang ditangguhkan. Silahkan hubungi admin Anda segera!",
|
|
9
|
+
"noAuthMethod": "Tidak ditemukan metode otentikasi satupun. Silahkan hubungi admin Anda segera!",
|
|
10
|
+
"invalidAuthMethod%s": "Metode otentikasi '%s' tidak valid",
|
|
11
|
+
"accessDenied": "Akses ditolak. Tidak bisa mengotentikasi Anda dengan seluruh metode yang tersedia",
|
|
12
|
+
"invalidTokenType": "Tipe token tidak valid",
|
|
13
|
+
"invalidCurrentPassword": "Kata sandi saat ini tidak valid",
|
|
14
|
+
"themeGloballyDisabled": "Dukungan tema secara global tidak aktif!",
|
|
15
|
+
"themeNotInstalled%s": "Tema '%s' tidak terinstall",
|
|
16
|
+
"profile": "Profil",
|
|
17
|
+
"yourProfile": "Profil Anda",
|
|
18
|
+
"editYourProfile": "Edit Profil Anda",
|
|
19
|
+
"account": "Akun",
|
|
20
|
+
"address": "Alamat",
|
|
21
|
+
"phoneEmail": "Telpon/Surel",
|
|
22
|
+
"changePassword": "Ganti Kata Sandi",
|
|
23
|
+
"signout": "Keluar",
|
|
24
|
+
"signin": "Masuk",
|
|
25
|
+
"yourStuff": "Perkakas Anda",
|
|
26
|
+
"forgotPassword": "Lupa Kata Sandi",
|
|
27
|
+
"userActivation": "Aktivasi Pengguna",
|
|
28
|
+
"signup": "Pendaftaran",
|
|
29
|
+
"newUserSignup": "Pendaftaran Pengguna Baru",
|
|
30
|
+
"updateProfile": "Perbarui Profil",
|
|
31
|
+
"backToProfile": "Kembali ke Profil",
|
|
32
|
+
"displayApiKey": "Tampilkan Kunci API",
|
|
33
|
+
"apiKey": "Kunci API",
|
|
34
|
+
"aboutUs": "Tentang Kami",
|
|
35
|
+
"invalidActivationKey": "Kunci otentikasi tidak valid",
|
|
36
|
+
"signoutNow": "Ya, keluarkan saya sekarang juga",
|
|
37
|
+
"signoutWarning": "Anda akan keluar. Ini berarti sesi Anda akan dihapus dan Anda akan ditanyakan kembali kredensial Anda jika Anda akan mengakses halaman-halaman yang diproteksi.<br /><br />Anda yakin?",
|
|
38
|
+
"clearInvalidKey": "Bersihkan Kolom Tidak Valid",
|
|
39
|
+
"linkPasswordWillbeSent": "Tautan untuk mengubah kata sandi akan dikirim ke surel Anda",
|
|
40
|
+
"signinSuccessfully": "Anda sukses melakukan login",
|
|
41
|
+
"signoutSuccessfully": "Anda sukses melakukan logout",
|
|
42
|
+
"profileUpdated": "Profil Anda sukses diperbaru",
|
|
43
|
+
"passwordChangedReSignin": "Anda sukses melakukan perbaruan kata kunci. Sekarang silahkan melakukan login ulang dengan kata kunci baru Anda",
|
|
44
|
+
"agreeToTerm%s%s": "Saya setuju dengan semua <a href=\"%s\">%s</a> yang berlaku",
|
|
45
|
+
"userActivated": "Akun pengguna sukses diaktivasi",
|
|
46
|
+
"userCreated": "Akun pengguna sukses dibuat",
|
|
47
|
+
"resetApiKey": "Reset Kunci API",
|
|
48
|
+
"resetApiKeySuccessfull": "Anda sukses mereset kunci API anda",
|
|
49
|
+
"personInCharge": "Penanggung Jawab",
|
|
50
|
+
"contactForm": "Form Kontak",
|
|
51
|
+
"cookie": "Cookie",
|
|
52
|
+
"cookiePolicy": "Kebijakan Cookie",
|
|
53
|
+
"privacy": "Privasi",
|
|
54
|
+
"privacyStatement": "Pernyataan Privasi",
|
|
55
|
+
"terms": "Terms",
|
|
56
|
+
"termsConditions": "Term dan Kondisi",
|
|
57
|
+
"legalInfo": "Info Legal",
|
|
58
|
+
"help": "Bantuan",
|
|
59
|
+
"troubleTickets": "Tiket Masalah",
|
|
60
|
+
"guest": "Tamu",
|
|
61
|
+
"formSubmitted": "Formulir sukses dikirim",
|
|
62
|
+
"formSubmittedInfo": "Kami akan mereview sebentar kiriman Anda dan akan menghubungi Anda kembali secepatnya",
|
|
63
|
+
"thankYou": "Thank you!",
|
|
64
|
+
"warningMemberOnly%s": "Silahkan melalukan otentikasi terlebih dahulu karena <a href=\"%s\">halaman<a> yang akan Anda akses adalah salah satu halaman khusus anggota saja",
|
|
65
|
+
"collectingRouteGuards": "Mengoleksi pertahanan jalur:",
|
|
66
|
+
"secureRoutes%d": "- Jalur aman: %d",
|
|
67
|
+
"secureInvRoutes%d": "- Jalur aman, terbalik: %d",
|
|
68
|
+
"anonRoutes%d": "- Jalur anonim: %d",
|
|
69
|
+
"anonInvRoutes%d": "- Jalur anonim, terbalik: %d",
|
|
70
|
+
"field": {
|
|
71
|
+
"currentPassword": "Kata Sandi Saat Ini",
|
|
72
|
+
"newPassword": "Kata Sandi Baru",
|
|
73
|
+
"verifyPassword": "Verifikasi Kata Sandi",
|
|
74
|
+
"verifyNewPassword": "Verifikasi Sandi Baru",
|
|
75
|
+
"usernameEmail": "Nama Pengguna/Surel",
|
|
76
|
+
"apiKey": "Kunci API",
|
|
77
|
+
"hostname": "Nama Host",
|
|
78
|
+
"iconset": "Iconset",
|
|
79
|
+
"subject": "Judul",
|
|
80
|
+
"message": "Pesan"
|
|
81
|
+
},
|
|
82
|
+
"validation": {
|
|
83
|
+
"password.minOfUppercase": "Mengandung setidaknya {{min}} huruf besar",
|
|
84
|
+
"password.minOfSpecialCharacters": "Mengandung setidaknya {{min}} karakter spesial",
|
|
85
|
+
"password.minOfLowercase": "Mengandung setidaknya {{min}} huruf kecil",
|
|
86
|
+
"password.minOfNumeric": "Mengandung setidaknya {{min}} numerik/angka",
|
|
87
|
+
"password.noWhiteSpaces": "Tidak boleh ada spasi",
|
|
88
|
+
"password.onlyLatinCharacters": "Harus berupa karakter latin saja"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -8,11 +8,11 @@ async function getUserByUsernamePassword (username = '', password = '', req) {
|
|
|
8
8
|
|
|
9
9
|
const query = { username }
|
|
10
10
|
const rows = await recordFind(model, { query }, { req, forceNoHidden: true, noHook: true })
|
|
11
|
-
if (rows.length === 0) throw this.error('
|
|
11
|
+
if (rows.length === 0) throw this.error('validationError', { details: [{ field: 'username', error: 'Unknown username' }], statusCode: 401 })
|
|
12
12
|
const rec = rows[0]
|
|
13
|
-
if (rec.status !== 'ACTIVE') throw this.error('
|
|
13
|
+
if (rec.status !== 'ACTIVE') throw this.error('validationError', { details: ['User is inactive or temporarily disabled'], statusCode: 401 })
|
|
14
14
|
const verified = await bcrypt.compare(password, rec.password)
|
|
15
|
-
if (!verified) throw this.error('
|
|
15
|
+
if (!verified) throw this.error('validationError', { details: [{ field: 'password', error: 'invalidPassword' }], statusCode: 401 })
|
|
16
16
|
return rec
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -31,8 +31,8 @@ async function verifyApiKey (req, reply, source) {
|
|
|
31
31
|
token = await hash(token)
|
|
32
32
|
const query = { token }
|
|
33
33
|
const rows = await recordFind('SumbaUser', { query }, { req, noHook: true })
|
|
34
|
-
if (rows.length === 0) throw this.error('
|
|
35
|
-
if (rows[0].status !== 'ACTIVE') throw this.error('
|
|
34
|
+
if (rows.length === 0) throw this.error('invalidKey', { statusCode: 401 })
|
|
35
|
+
if (rows[0].status !== 'ACTIVE') throw this.error('userInactive', { details: [{ field: 'status', error: 'inactive' }], statusCode: 401 })
|
|
36
36
|
req.user = await getUser(rows[0])
|
|
37
37
|
return true
|
|
38
38
|
}
|
|
@@ -19,8 +19,8 @@ async function verifyJwt (req, reply, source) {
|
|
|
19
19
|
const id = decoded.payload.uid
|
|
20
20
|
try {
|
|
21
21
|
const rec = await recordGet('SumbaUser', id, { req, noHook: true })
|
|
22
|
-
if (!rec) throw this.error('
|
|
23
|
-
if (rec.status !== 'ACTIVE') throw this.error('
|
|
22
|
+
if (!rec) throw this.error('invalidToken', { statusCode: 401 })
|
|
23
|
+
if (rec.status !== 'ACTIVE') throw this.error('userInactive', { details: [{ field: 'status', error: 'inactive' }], statusCode: 401 })
|
|
24
24
|
req.user = await getUser(rec)
|
|
25
25
|
return true
|
|
26
26
|
} catch (err) {
|
|
@@ -9,7 +9,7 @@ async function verifySession (req, reply, source) {
|
|
|
9
9
|
}
|
|
10
10
|
const redir = routePath(this.config.redirect.signin, req)
|
|
11
11
|
req.session.ref = req.url
|
|
12
|
-
throw this.error('
|
|
12
|
+
throw this.error('_redirect', { redirect: redir })
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export default verifySession
|
package/bajoAdmin/coll/user.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"status;small:12,medium:4"
|
|
18
18
|
]
|
|
19
19
|
}, {
|
|
20
|
-
"title": "
|
|
20
|
+
"title": "account",
|
|
21
21
|
"fields": [
|
|
22
22
|
"username;small:6,medium:6",
|
|
23
23
|
"email;small:6,medium:6",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"status;small:12,medium:4"
|
|
63
63
|
]
|
|
64
64
|
}, {
|
|
65
|
-
"title": "
|
|
65
|
+
"title": "account",
|
|
66
66
|
"fields": [
|
|
67
67
|
"username;small:6,medium:6",
|
|
68
68
|
"email;small:6,medium:6",
|
package/lib/check-site-id.js
CHANGED
|
@@ -44,9 +44,9 @@ async function checkSiteId (req, reply) {
|
|
|
44
44
|
}
|
|
45
45
|
const filter = { query }
|
|
46
46
|
const rows = await recordFind('SumbaSite', filter, { noHook: true })
|
|
47
|
-
if (rows.length === 0) throw this.error('
|
|
47
|
+
if (rows.length === 0) throw this.error('unknownSite')
|
|
48
48
|
const row = omit(rows[0], omitted)
|
|
49
|
-
if (row.status !== 'ACTIVE') throw this.error('
|
|
49
|
+
if (row.status !== 'ACTIVE') throw this.error('siteInactiveInfo')
|
|
50
50
|
req.site = row
|
|
51
51
|
await mergeSetting.call(this, req)
|
|
52
52
|
}
|
package/lib/check-user-id.js
CHANGED
|
@@ -34,7 +34,7 @@ async function anonymous (req) {
|
|
|
34
34
|
if (match) {
|
|
35
35
|
const redir = routePath(this.config.redirect.signout, req)
|
|
36
36
|
req.session.ref = req.url
|
|
37
|
-
throw this.error('
|
|
37
|
+
throw this.error('_redirect', { redirect: redir })
|
|
38
38
|
}
|
|
39
39
|
req.user = omit(await getUser(req.session.user.id), ['password', 'token'])
|
|
40
40
|
}
|
|
@@ -73,18 +73,18 @@ async function checkUserId (req, reply, source) {
|
|
|
73
73
|
return
|
|
74
74
|
}
|
|
75
75
|
const authMethods = this.config.auth[match.source].methods ?? []
|
|
76
|
-
if (isEmpty(authMethods)) throw this.error('
|
|
76
|
+
if (isEmpty(authMethods)) throw this.error('noAuthMethod', { statusCode: 500 })
|
|
77
77
|
let success
|
|
78
78
|
for (const m of authMethods) {
|
|
79
79
|
const handler = this[camelCase(`verify ${m}`)]
|
|
80
|
-
if (!handler) throw this.error('
|
|
80
|
+
if (!handler) throw this.error('invalidAuthMethod%s', m, { statusCode: 500 })
|
|
81
81
|
const check = await handler(req, reply, source, ctx)
|
|
82
82
|
if (check) {
|
|
83
83
|
success = check
|
|
84
84
|
break
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
|
-
if (!success) throw this.error('
|
|
87
|
+
if (!success) throw this.error('accessDenied', { statusCode: 401 })
|
|
88
88
|
await mergeSetting.call(this, req)
|
|
89
89
|
return success
|
|
90
90
|
}
|
package/package.json
CHANGED
package/waibuDb/schema/user.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"layout": [
|
|
4
|
-
{ "name": "
|
|
5
|
-
{ "name": "
|
|
6
|
-
{ "name": "
|
|
7
|
-
{ "name": "
|
|
4
|
+
{ "name": "meta", "fields": ["id", "createdAt", "updatedAt"] },
|
|
5
|
+
{ "name": "account", "fields": ["username", "email", "status:4-md 6-sm", "siteId:4-md 6-sm", "firstName", "lastName"] },
|
|
6
|
+
{ "name": "address", "fields": ["address1:12", "address2:12", "city:6-md 8-sm", "zipCode:2-md 4-sm", "provinceState:4-md", { "name": "country", "col": "6-md", "component": "form-select-country" }, "phone:6-md", "website:12"] },
|
|
7
|
+
{ "name": "socialMedia", "fields": ["twitter:3-md 6-sm", "instagram:3-md 6-sm", "facebook:3-md 6-sm", "linkedIn:3-md 6-sm"] }
|
|
8
8
|
],
|
|
9
9
|
"stat": {
|
|
10
10
|
"aggregate": [
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
<c:modal id="api-key-modal" t:title="
|
|
1
|
+
<c:modal id="api-key-modal" t:title="apiKey">
|
|
2
2
|
<c:modal-body>
|
|
3
3
|
<pre><code id="api-key"><%= form.token %></code></pre>
|
|
4
4
|
<c:div flex="justify-content:between align-items:center" margin="top-3">
|
|
5
|
-
<c:a icon="arrowsRepeat" href="sumba:/my-stuff/reset-api-key" t:content="
|
|
5
|
+
<c:a icon="arrowsRepeat" href="sumba:/my-stuff/reset-api-key" t:content="reset" />
|
|
6
6
|
<div>
|
|
7
|
-
<c:btn color="primary" x-data @click="await wbs.copyToClipboard('#api-key', true)" t:content="
|
|
8
|
-
<c:btn margin="start-1" color="secondary" dismiss t:content="
|
|
7
|
+
<c:btn color="primary" x-data @click="await wbs.copyToClipboard('#api-key', true)" t:content="copyClipboard" />
|
|
8
|
+
<c:btn margin="start-1" color="secondary" dismiss t:content="close" />
|
|
9
9
|
</div>
|
|
10
10
|
</c:div>
|
|
11
11
|
</c:modal-body>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
<c:p t:content="
|
|
2
|
-
<c:p t:content="
|
|
3
|
-
<c:p t:content="
|
|
1
|
+
<c:p t:content="formSubmitted" />
|
|
2
|
+
<c:p t:content="formSubmittedInfo" />
|
|
3
|
+
<c:p t:content="thankYou" />
|
|
4
4
|
<% if (!page.noLinks) { %>
|
|
5
5
|
<c:list type="unstyled" margin="start-3">
|
|
6
|
-
<c:list-item t:content="
|
|
6
|
+
<c:list-item t:content="gotoHome" href="/" icon="house"/>
|
|
7
7
|
</c:list>
|
|
8
8
|
<% } %>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
list
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<% if (_meta.user) { %>
|
|
2
|
-
<c:list-item href="sumba:/my-stuff/profile" t:content="
|
|
3
|
-
<c:list-item href="sumba:/my-stuff/change-password" t:content="
|
|
2
|
+
<c:list-item href="sumba:/my-stuff/profile" t:content="yourProfile" />
|
|
3
|
+
<c:list-item href="sumba:/my-stuff/change-password" t:content="changePassword" />
|
|
4
4
|
<% if (attr.withSignout) { %>
|
|
5
|
-
<c:list-item href="sumba:/signout" t:content="
|
|
5
|
+
<c:list-item href="sumba:/signout" t:content="signout" />
|
|
6
6
|
<% } %>
|
|
7
7
|
<% } else { %>
|
|
8
|
-
<c:list-item href="sumba:/signin" t:content="
|
|
9
|
-
<c:list-item href="sumba:/user/signup" t:content="
|
|
10
|
-
<c:list-item href="sumba:/user/forgot-password" t:content="
|
|
8
|
+
<c:list-item href="sumba:/signin" t:content="signin" />
|
|
9
|
+
<c:list-item href="sumba:/user/signup" t:content="signup" />
|
|
10
|
+
<c:list-item href="sumba:/user/forgot-password" t:content="forgotPassword" />
|
|
11
11
|
<% } %>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<c:list-item t:content="
|
|
1
|
+
<c:list-item t:content="yourProfile" href="sumba:/my-stuff/profile" icon="person"/>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<c:grid-row>
|
|
2
|
+
<c:grid-col col="6-md">
|
|
3
|
+
<c:form button reset-validation referer>
|
|
4
|
+
<c:form-password name="currentPassword" label-floating wrapper-margin="bottom-2" />
|
|
5
|
+
<c:form-password name="newPassword" label-floating wrapper-margin="bottom-2" />
|
|
6
|
+
<c:form-password name="verifyNewPassword" label-floating wrapper-margin="bottom-3" />
|
|
7
|
+
</c:form>
|
|
8
|
+
</c:grid-col>
|
|
9
|
+
<c:grid-col col="6-md">
|
|
10
|
+
|
|
11
|
+
</c:grid-col>
|
|
12
|
+
</c:grid-row>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<c:form referer>
|
|
2
|
+
<c:grid-row>
|
|
3
|
+
<c:grid-col col="8-md">
|
|
4
|
+
<c:fieldset t:legend="meta" grid-gutter="2" margin="bottom-3">
|
|
5
|
+
<c:form-plaintext name="createdAt" label-floating col="6-md"/>
|
|
6
|
+
<c:form-plaintext name="updatedAt" label-floating col="6-md"/>
|
|
7
|
+
</c:fieldset>
|
|
8
|
+
<c:fieldset t:legend="account" grid-gutter="2" margin="bottom-3">
|
|
9
|
+
<c:form-plaintext name="username" label-floating col="6-lg"/>
|
|
10
|
+
<c:form-input name="email" label-floating col="6-lg"/>
|
|
11
|
+
<c:form-input name="firstName" label-floating col="6-lg"/>
|
|
12
|
+
<c:form-input name="lastName" label-floating col="6-lg"/>
|
|
13
|
+
</c:fieldset>
|
|
14
|
+
<c:fieldset t:legend="address" grid-gutter="2">
|
|
15
|
+
<c:form-input name="address1" label-floating col="12"/>
|
|
16
|
+
<c:form-input name="address2" label-floating col="12"/>
|
|
17
|
+
<c:form-input name="city" label-floating col="8-lg 7-md"/>
|
|
18
|
+
<c:form-input name="zipCode" label-floating col="4-lg 5-md"/>
|
|
19
|
+
<c:form-input name="provinceState" label-floating col="6-lg"/>
|
|
20
|
+
<c:form-select-country name="country" label-floating col="6-lg"/>
|
|
21
|
+
<c:form-input name="phone" label-floating col="6-lg"/>
|
|
22
|
+
<c:form-input name="website" label-floating col="6-lg"/>
|
|
23
|
+
</c:fieldset>
|
|
24
|
+
</c:grid-col>
|
|
25
|
+
<c:grid-col col="4-md">
|
|
26
|
+
<c:heading type="5" t:content="image" />
|
|
27
|
+
<c:img holder="400x200" responsive rounded />
|
|
28
|
+
<c:form-file name="image" no-label wrapper-margin="top-2"/>
|
|
29
|
+
<c:btn-group margin="top-3" flex="full">
|
|
30
|
+
<c:btn type="reset" t:content="reset" color="secondary" />
|
|
31
|
+
<c:btn type="submit" t:content="submit" color="primary" />
|
|
32
|
+
</c:btn-group>
|
|
33
|
+
<% if (arguments[0].error) { %>
|
|
34
|
+
<c:btn x-data @click="wbs.invalidateForm" href="#" icon="remove" color="link" size="sm" margin="top-2">
|
|
35
|
+
<c:t>clearInvalidFields</c:t>
|
|
36
|
+
</c:btn>
|
|
37
|
+
<% } %>
|
|
38
|
+
<hr />
|
|
39
|
+
<c:heading type="5" t:content="more" margin="top-3" />
|
|
40
|
+
<c:list type="group" hover>
|
|
41
|
+
<c:list-item href="sumba:/my-stuff/change-password" t:content="changePassword" />
|
|
42
|
+
<c:list-item t:content="displayApiKey" target="api-key-modal"/>
|
|
43
|
+
</c:list>
|
|
44
|
+
<c:list type="unstyled" margin="top-3">
|
|
45
|
+
<c:list-item href="sumba:/my-stuff/profile" t:content="backToProfile" icon="arrowStart" />
|
|
46
|
+
</c:list>
|
|
47
|
+
<c:include resource="sumba.partial:/api-key-modal.html" />
|
|
48
|
+
</c:grid-col>
|
|
49
|
+
</c:grid-row>
|
|
50
|
+
</c:form>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<c:grid-row gutter="4">
|
|
2
|
+
<c:grid-col col="8-md">
|
|
3
|
+
<c:fieldset t:legend="meta" grid-gutter="2" margin="bottom-3">
|
|
4
|
+
<c:form-plaintext name="createdAt" label-floating col="6-md"/>
|
|
5
|
+
<c:form-plaintext name="updatedAt" label-floating col="6-md"/>
|
|
6
|
+
</c:fieldset>
|
|
7
|
+
<c:fieldset t:legend="account" grid-gutter="2" margin="bottom-3">
|
|
8
|
+
<c:form-plaintext name="username" label-floating col="6-lg"/>
|
|
9
|
+
<c:form-plaintext name="email" label-floating col="6-lg"/>
|
|
10
|
+
<c:form-plaintext name="firstName" label-floating col="6-lg"/>
|
|
11
|
+
<c:form-plaintext name="lastName" label-floating col="6-lg"/>
|
|
12
|
+
</c:fieldset>
|
|
13
|
+
<c:fieldset t:legend="address" grid-gutter="2">
|
|
14
|
+
<c:form-plaintext name="address1" label-floating col="12"/>
|
|
15
|
+
<c:form-plaintext name="address2" label-floating col="12"/>
|
|
16
|
+
<c:form-plaintext name="city" label-floating col="8-lg 7-md"/>
|
|
17
|
+
<c:form-plaintext name="zipCode" label-floating col="4-lg 5-md"/>
|
|
18
|
+
<c:form-plaintext name="provinceState" label-floating col="6-lg"/>
|
|
19
|
+
<c:form-plaintext name="country" label-floating col="6-lg"/>
|
|
20
|
+
<c:form-plaintext name="phone" label-floating col="6-lg"/>
|
|
21
|
+
<c:form-plaintext name="website" label-floating col="6-lg"/>
|
|
22
|
+
</c:fieldset>
|
|
23
|
+
</c:grid-col>
|
|
24
|
+
<c:grid-col col="4-md">
|
|
25
|
+
<c:heading type="5" t:content="image" />
|
|
26
|
+
<c:img holder="400x200" responsive rounded />
|
|
27
|
+
<c:heading type="5" t:content="more" margin="top-3" />
|
|
28
|
+
<c:list type="group" hover>
|
|
29
|
+
<c:list-item href="sumba:/my-stuff/profile/edit" t:content="updateProfile" />
|
|
30
|
+
<c:list-item t:content="displayApiKey" target="api-key-modal"/>
|
|
31
|
+
</c:list>
|
|
32
|
+
<c:include resource="sumba.partial:/api-key-modal.html" />
|
|
33
|
+
</c:grid-col>
|
|
34
|
+
</c:grid-row>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<c:form button reset-validation>
|
|
2
|
+
<c:form-plaintext name="apiKey" label-floating wrapper-margin="bottom-2" />
|
|
3
|
+
<c:form-password name="password" label-floating wrapper-margin="bottom-3" />
|
|
4
|
+
</c:form>
|
|
5
|
+
<% if (!page.noLinks) { %>
|
|
6
|
+
<c:list type="unstyled" margin="start-3">
|
|
7
|
+
<c:include resource="sumba.partial:/list-item/your-profile.html" />
|
|
8
|
+
<c:include resource="sumba.partial:/list-item/change-password.html" />
|
|
9
|
+
<c:include resource="sumba.partial:/list-item/signout.html" />
|
|
10
|
+
<c:include resource="sumba.partial:/list-item/goto-home.html" />
|
|
11
|
+
</c:list>
|
|
12
|
+
<% } %>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<c:p t:content="signoutWarning" />
|
|
2
2
|
<c:form method="POST" action="sumba:/signout" referer>
|
|
3
|
-
<c:btn color="primary" t:content="
|
|
3
|
+
<c:btn color="primary" t:content="signoutNow" type="submit" />
|
|
4
4
|
<% if (!page.noLinks) { %>
|
|
5
|
-
<c:btn color="link" icon="house" t:content="
|
|
5
|
+
<c:btn color="link" icon="house" t:content="gotoHome" href="/" />
|
|
6
6
|
<% } %>
|
|
7
7
|
</c:form>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<c:form button reset-validation referer>
|
|
2
|
+
<c:form-input name="key" label-floating />
|
|
3
|
+
</c:form>
|
|
4
|
+
<% if (!page.noLinks) { %>
|
|
5
|
+
<c:list type="unstyled" margin="start-3">
|
|
6
|
+
<c:include resource="sumba.partial:/list-item/signin.html" />
|
|
7
|
+
<c:include resource="sumba.partial:/list-item/forgot-password.html" />
|
|
8
|
+
<c:include resource="sumba.partial:/list-item/goto-home.html" />
|
|
9
|
+
</c:list>
|
|
10
|
+
<% } %>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<c:form button reset-validation>
|
|
2
|
+
<c:form-input name="usernameEmail" label-floating wrapper-margin="bottom-2" t:hint="linkPasswordWillbeSent"/>
|
|
3
|
+
</c:form>
|
|
4
|
+
<% if (!page.noLinks) { %>
|
|
5
|
+
<c:list type="unstyled" margin="start-3">
|
|
6
|
+
<c:include resource="sumba.partial:/list-item/signin.html" />
|
|
7
|
+
<c:include resource="sumba.partial:/list-item/user-signup.html" />
|
|
8
|
+
<c:include resource="sumba.partial:/list-item/goto-home.html" />
|
|
9
|
+
</c:list>
|
|
10
|
+
<% } %>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<c:form button reset-validation referer grid-gutter="2">
|
|
2
|
+
<c:form-input name="username" label-floating />
|
|
3
|
+
<c:form-password name="password" col="6-lg" label-floating />
|
|
4
|
+
<c:form-password name="verifyPassword" col="6-lg" label-floating />
|
|
5
|
+
<c:form-input type="email" name="email" label-floating />
|
|
6
|
+
<c:form-input name="firstName" col="6-lg" label-floating />
|
|
7
|
+
<c:form-input name="lastName" col="6-lg" label-floating />
|
|
8
|
+
<c:form-check name="agree" col="12" value="true" t:label="agreeToTerm%s%s|<%= _routePath('sumba:/info/terms-conditions') %>|termsConditions" />
|
|
9
|
+
</c:form>
|
|
10
|
+
<% if (!page.noLinks) { %>
|
|
11
|
+
<c:list type="unstyled" margin="start-3">
|
|
12
|
+
<c:include resource="sumba.partial:/list-item/forgot-password.html" />
|
|
13
|
+
<c:include resource="sumba.partial:/list-item/signin.html" />
|
|
14
|
+
<c:include resource="sumba.partial:/list-item/goto-home.html" />
|
|
15
|
+
</c:list>
|
|
16
|
+
<% } %>
|
|
@@ -21,15 +21,15 @@ const profile = {
|
|
|
21
21
|
try {
|
|
22
22
|
await schema.validateAsync(req.body, this.app.dobo.config.validationParams)
|
|
23
23
|
} catch (err) {
|
|
24
|
-
throw this.error('
|
|
24
|
+
throw this.error('validationError', { details: err.details, values: err.values, ns: this.name, statusCode: 422, code: 'DB_VALIDATION' })
|
|
25
25
|
}
|
|
26
26
|
const rec = await recordGet(model, req.user.id)
|
|
27
27
|
const verified = await bcrypt.compare(req.body.currentPassword, rec.password)
|
|
28
|
-
if (!verified) throw this.error('
|
|
28
|
+
if (!verified) throw this.error('invalidCurrentPassword', { details: [{ field: 'currentPassword', error: 'invalidPassword' }], statusCode: 400 })
|
|
29
29
|
await recordUpdate(model, req.user.id, { password: req.body.newPassword })
|
|
30
30
|
// signout and redirect to signin
|
|
31
31
|
req.session.user = null
|
|
32
|
-
req.flash('notify', req.t('
|
|
32
|
+
req.flash('notify', req.t('passwordChangedReSignin'))
|
|
33
33
|
return reply.redirectTo('sumba:/signin')
|
|
34
34
|
} catch (err) {
|
|
35
35
|
error = err
|
|
@@ -17,7 +17,7 @@ const profile = {
|
|
|
17
17
|
} else {
|
|
18
18
|
const resp = await recordUpdate({ req, model: 'SumbaUser', id: req.user.id, body: form, options: { noFlash: true, hidden: [] } })
|
|
19
19
|
form = resp.data
|
|
20
|
-
req.flash('notify', req.t('
|
|
20
|
+
req.flash('notify', req.t('profileUpdated'))
|
|
21
21
|
}
|
|
22
22
|
} catch (err) {
|
|
23
23
|
console.log(err)
|
|
@@ -18,14 +18,14 @@ const resetApiKey = {
|
|
|
18
18
|
try {
|
|
19
19
|
await schema.validateAsync(req.body, this.app.dobo.config.validationParams)
|
|
20
20
|
} catch (err) {
|
|
21
|
-
throw this.error('
|
|
21
|
+
throw this.error('validationError', { details: err.details, values: err.values, ns: this.name, statusCode: 422, code: 'DB_VALIDATION' })
|
|
22
22
|
}
|
|
23
23
|
const rec = await recordGet(model, req.user.id)
|
|
24
24
|
const verified = await bcrypt.compare(req.body.password, rec.password)
|
|
25
|
-
if (!verified) throw this.error('
|
|
25
|
+
if (!verified) throw this.error('validationError', { details: [{ field: 'password', error: 'invalidPassword' }], statusCode: 400 })
|
|
26
26
|
await recordUpdate(model, req.user.id, { token: generateId() })
|
|
27
27
|
await delay(2000) // ensure req.user cache is expired
|
|
28
|
-
req.flash('notify', req.t('
|
|
28
|
+
req.flash('notify', req.t('resetApiKeySuccessfull'))
|
|
29
29
|
return reply.redirectTo('sumba:/profile')
|
|
30
30
|
} catch (err) {
|
|
31
31
|
error = err
|
package/waibuMpa/route/signin.js
CHANGED
|
@@ -19,7 +19,7 @@ const signin = {
|
|
|
19
19
|
await runHook(`${this.name}:afterSignin`, user, sid, req)
|
|
20
20
|
const { query, params } = req
|
|
21
21
|
const url = !isEmpty(referer) ? referer : this.config.redirect.afterSignin
|
|
22
|
-
req.flash('notify', req.t('
|
|
22
|
+
req.flash('notify', req.t('signinSuccessfully'))
|
|
23
23
|
return reply.redirectTo(url, { query, params })
|
|
24
24
|
} catch (err) {
|
|
25
25
|
error = err
|
|
@@ -12,12 +12,12 @@ const signout = {
|
|
|
12
12
|
const sid = await getSessionId(req.headers.cookie)
|
|
13
13
|
const user = req.session.user
|
|
14
14
|
req.session.user = null
|
|
15
|
-
if (this.bajoEmitter) await this.app.bajoEmitter.emit(`${this.name}.
|
|
15
|
+
if (this.bajoEmitter) await this.app.bajoEmitter.emit(`${this.name}.signout`, user, sid)
|
|
16
16
|
await runHook(`${this.name}:afterSignout`, user, sid, req)
|
|
17
17
|
const { query, params } = req
|
|
18
18
|
// const url = !isEmpty(referer) ? referer : this.config.redirect.home
|
|
19
19
|
const url = this.config.redirect.afterSignout
|
|
20
|
-
req.flash('notify', req.t('
|
|
20
|
+
req.flash('notify', req.t('signoutSuccessfully'))
|
|
21
21
|
return reply.redirectTo(url, { query, params })
|
|
22
22
|
}
|
|
23
23
|
return reply.view('sumba.template:/signout.html', { form: { referer } })
|
|
@@ -9,9 +9,9 @@ const userActivation = {
|
|
|
9
9
|
try {
|
|
10
10
|
const query = { status: 'UNVERIFIED', token: req.body.key }
|
|
11
11
|
const result = await recordFind('SumbaUser', { query }, { noHook: true })
|
|
12
|
-
if (result.length === 0) throw this.error('
|
|
12
|
+
if (result.length === 0) throw this.error('validationError', { details: [{ field: 'key', error: 'invalidActivationKey' }] })
|
|
13
13
|
await recordUpdate('SumbaUser', result[0].id, { status: 'ACTIVE' }, { noHook: true, noValidation: true, noFlash: true })
|
|
14
|
-
req.flash('notify', req.t('
|
|
14
|
+
req.flash('notify', req.t('userActivated'))
|
|
15
15
|
return reply.redirectTo(this.config.redirect.signin, req)
|
|
16
16
|
} catch (err) {
|
|
17
17
|
error = err
|
|
@@ -23,7 +23,7 @@ const signup = {
|
|
|
23
23
|
const validation = { ns: ['sumba', 'dobo'], fields, extFields }
|
|
24
24
|
req.body.token = generateId()
|
|
25
25
|
const { data } = await recordCreate({ model: 'SumbaUser', req, reply, options: { validation, noFlash: true } })
|
|
26
|
-
req.flash('notify', '
|
|
26
|
+
req.flash('notify', 'userCreated')
|
|
27
27
|
return reply.view('sumba.template:/user/signup/success.html', { form: req.body, data })
|
|
28
28
|
} catch (err) {
|
|
29
29
|
error = err
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
layout: sumba.layout:/info.html
|
|
3
|
-
t:title:
|
|
3
|
+
t:title: aboutUs
|
|
4
4
|
---
|
|
5
5
|
Dolore aliqua excepteur consequat sint anim eiusmod aliqua ex in. Consequat velit officia ipsum ut proident magna laborum aliqua irure quis tempor dolor. Lorem duis duis sint adipisicing do in mollit cillum proident nisi adipisicing dolor. Excepteur mollit eu cillum aute aliquip laboris incididunt officia dolore qui quis cupidatat anim amet. Voluptate labore consequat esse culpa ea. Aliqua cupidatat excepteur non laborum enim minim.
|
|
6
6
|
|
|
@@ -1,16 +1,4 @@
|
|
|
1
1
|
---
|
|
2
|
-
t:title:
|
|
2
|
+
t:title: changePassword
|
|
3
3
|
layout: sumba.layout:/wide.html
|
|
4
4
|
---
|
|
5
|
-
<c:grid-row>
|
|
6
|
-
<c:grid-col col="6-md">
|
|
7
|
-
<c:form button reset-validation referer>
|
|
8
|
-
<c:form-password name="currentPassword" label-floating wrapper-margin="bottom-2" />
|
|
9
|
-
<c:form-password name="newPassword" label-floating wrapper-margin="bottom-2" />
|
|
10
|
-
<c:form-password name="verifyNewPassword" label-floating wrapper-margin="bottom-3" />
|
|
11
|
-
</c:form>
|
|
12
|
-
</c:grid-col>
|
|
13
|
-
<c:grid-col col="6-md">
|
|
14
|
-
|
|
15
|
-
</c:grid-col>
|
|
16
|
-
</c:grid-row>
|
|
@@ -1,56 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
t:title:
|
|
2
|
+
t:title: editYourProfile
|
|
3
3
|
layout: sumba.layout:/wide.html
|
|
4
4
|
scripts:
|
|
5
5
|
- waibuExtra.virtual:/holderjs/holder.min.js
|
|
6
6
|
---
|
|
7
|
-
<c:form referer>
|
|
8
|
-
<c:grid-row>
|
|
9
|
-
<c:grid-col col="8-md">
|
|
10
|
-
<c:fieldset t:legend="Meta" grid-gutter="2" margin="bottom-3">
|
|
11
|
-
<c:form-plaintext name="createdAt" label-floating col="6-md"/>
|
|
12
|
-
<c:form-plaintext name="updatedAt" label-floating col="6-md"/>
|
|
13
|
-
</c:fieldset>
|
|
14
|
-
<c:fieldset t:legend="Account" grid-gutter="2" margin="bottom-3">
|
|
15
|
-
<c:form-plaintext name="username" label-floating col="6-lg"/>
|
|
16
|
-
<c:form-input name="email" label-floating col="6-lg"/>
|
|
17
|
-
<c:form-input name="firstName" label-floating col="6-lg"/>
|
|
18
|
-
<c:form-input name="lastName" label-floating col="6-lg"/>
|
|
19
|
-
</c:fieldset>
|
|
20
|
-
<c:fieldset t:legend="Address" grid-gutter="2">
|
|
21
|
-
<c:form-input name="address1" label-floating col="12"/>
|
|
22
|
-
<c:form-input name="address2" label-floating col="12"/>
|
|
23
|
-
<c:form-input name="city" label-floating col="8-lg 7-md"/>
|
|
24
|
-
<c:form-input name="zipCode" label-floating col="4-lg 5-md"/>
|
|
25
|
-
<c:form-input name="provinceState" label-floating col="6-lg"/>
|
|
26
|
-
<c:form-select-country name="country" label-floating col="6-lg"/>
|
|
27
|
-
<c:form-input name="phone" label-floating col="6-lg"/>
|
|
28
|
-
<c:form-input name="website" label-floating col="6-lg"/>
|
|
29
|
-
</c:fieldset>
|
|
30
|
-
</c:grid-col>
|
|
31
|
-
<c:grid-col col="4-md">
|
|
32
|
-
<c:heading type="5" t:content="Image" />
|
|
33
|
-
<c:img holder="400x200" responsive rounded />
|
|
34
|
-
<c:form-file name="image" no-label wrapper-margin="top-2"/>
|
|
35
|
-
<c:btn-group margin="top-3" flex="full">
|
|
36
|
-
<c:btn type="reset" t:content="Reset" color="secondary" />
|
|
37
|
-
<c:btn type="submit" t:content="Submit" color="primary" />
|
|
38
|
-
</c:btn-group>
|
|
39
|
-
<% if (arguments[0].error) { %>
|
|
40
|
-
<c:btn x-data @click="wbs.invalidateForm" href="#" icon="remove" color="link" size="sm" margin="top-2">
|
|
41
|
-
<c:t>Clear Invalid Fields</c:t>
|
|
42
|
-
</c:btn>
|
|
43
|
-
<% } %>
|
|
44
|
-
<hr />
|
|
45
|
-
<c:heading type="5" t:content="More" margin="top-3" />
|
|
46
|
-
<c:list type="group" hover>
|
|
47
|
-
<c:list-item href="sumba:/my-stuff/change-password" t:content="Change Password" />
|
|
48
|
-
<c:list-item t:content="Display API Key" target="api-key-modal"/>
|
|
49
|
-
</c:list>
|
|
50
|
-
<c:list type="unstyled" margin="top-3">
|
|
51
|
-
<c:list-item href="sumba:/my-stuff/profile" t:content="Back to Profile" icon="arrowStart" />
|
|
52
|
-
</c:list>
|
|
53
|
-
<c:include resource="sumba.partial:/api-key-modal.html" />
|
|
54
|
-
</c:grid-col>
|
|
55
|
-
</c:grid-row>
|
|
56
|
-
</c:form>
|
|
@@ -1,40 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
t:title:
|
|
2
|
+
t:title: yourProfile
|
|
3
3
|
layout: sumba.layout:/wide.html
|
|
4
4
|
scripts:
|
|
5
5
|
- waibuExtra.virtual:/holderjs/holder.min.js
|
|
6
6
|
---
|
|
7
|
-
<c:grid-row gutter="4">
|
|
8
|
-
<c:grid-col col="8-md">
|
|
9
|
-
<c:fieldset t:legend="Meta" grid-gutter="2" margin="bottom-3">
|
|
10
|
-
<c:form-plaintext name="createdAt" label-floating col="6-md"/>
|
|
11
|
-
<c:form-plaintext name="updatedAt" label-floating col="6-md"/>
|
|
12
|
-
</c:fieldset>
|
|
13
|
-
<c:fieldset t:legend="Account" grid-gutter="2" margin="bottom-3">
|
|
14
|
-
<c:form-plaintext name="username" label-floating col="6-lg"/>
|
|
15
|
-
<c:form-plaintext name="email" label-floating col="6-lg"/>
|
|
16
|
-
<c:form-plaintext name="firstName" label-floating col="6-lg"/>
|
|
17
|
-
<c:form-plaintext name="lastName" label-floating col="6-lg"/>
|
|
18
|
-
</c:fieldset>
|
|
19
|
-
<c:fieldset t:legend="Address" grid-gutter="2">
|
|
20
|
-
<c:form-plaintext name="address1" label-floating col="12"/>
|
|
21
|
-
<c:form-plaintext name="address2" label-floating col="12"/>
|
|
22
|
-
<c:form-plaintext name="city" label-floating col="8-lg 7-md"/>
|
|
23
|
-
<c:form-plaintext name="zipCode" label-floating col="4-lg 5-md"/>
|
|
24
|
-
<c:form-plaintext name="provinceState" label-floating col="6-lg"/>
|
|
25
|
-
<c:form-plaintext name="country" label-floating col="6-lg"/>
|
|
26
|
-
<c:form-plaintext name="phone" label-floating col="6-lg"/>
|
|
27
|
-
<c:form-plaintext name="website" label-floating col="6-lg"/>
|
|
28
|
-
</c:fieldset>
|
|
29
|
-
</c:grid-col>
|
|
30
|
-
<c:grid-col col="4-md">
|
|
31
|
-
<c:heading type="5" t:content="Image" />
|
|
32
|
-
<c:img holder="400x200" responsive rounded />
|
|
33
|
-
<c:heading type="5" t:content="More" margin="top-3" />
|
|
34
|
-
<c:list type="group" hover>
|
|
35
|
-
<c:list-item href="sumba:/my-stuff/profile/edit" t:content="Update Profile" />
|
|
36
|
-
<c:list-item t:content="Display API Key" target="api-key-modal"/>
|
|
37
|
-
</c:list>
|
|
38
|
-
<c:include resource="sumba.partial:/api-key-modal.html" />
|
|
39
|
-
</c:grid-col>
|
|
40
|
-
</c:grid-row>
|
|
@@ -1,16 +1,4 @@
|
|
|
1
1
|
---
|
|
2
|
-
t:title:
|
|
2
|
+
t:title: resetApiKey
|
|
3
3
|
layout: sumba.layout:/wide.html
|
|
4
4
|
---
|
|
5
|
-
<c:form button reset-validation>
|
|
6
|
-
<c:form-plaintext name="apiKey" label-floating wrapper-margin="bottom-2" />
|
|
7
|
-
<c:form-password name="password" label-floating wrapper-margin="bottom-3" />
|
|
8
|
-
</c:form>
|
|
9
|
-
<% if (!page.noLinks) { %>
|
|
10
|
-
<c:list type="unstyled" margin="start-3">
|
|
11
|
-
<c:include resource="sumba.partial:/list-item/your-profile.html" />
|
|
12
|
-
<c:include resource="sumba.partial:/list-item/change-password.html" />
|
|
13
|
-
<c:include resource="sumba.partial:/list-item/signout.html" />
|
|
14
|
-
<c:include resource="sumba.partial:/list-item/goto-home.html" />
|
|
15
|
-
</c:list>
|
|
16
|
-
<% } %>
|
|
@@ -1,13 +1,3 @@
|
|
|
1
1
|
---
|
|
2
|
-
t:title:
|
|
2
|
+
t:title: userActivation
|
|
3
3
|
---
|
|
4
|
-
<c:form button reset-validation referer>
|
|
5
|
-
<c:form-input name="key" label-floating />
|
|
6
|
-
</c:form>
|
|
7
|
-
<% if (!page.noLinks) { %>
|
|
8
|
-
<c:list type="unstyled" margin="start-3">
|
|
9
|
-
<c:include resource="sumba.partial:/list-item/signin.html" />
|
|
10
|
-
<c:include resource="sumba.partial:/list-item/forgot-password.html" />
|
|
11
|
-
<c:include resource="sumba.partial:/list-item/goto-home.html" />
|
|
12
|
-
</c:list>
|
|
13
|
-
<% } %>
|
|
@@ -1,13 +1,3 @@
|
|
|
1
1
|
---
|
|
2
|
-
t:title:
|
|
2
|
+
t:title: forgotPassword
|
|
3
3
|
---
|
|
4
|
-
<c:form button reset-validation>
|
|
5
|
-
<c:form-input name="usernameEmail" label-floating wrapper-margin="bottom-2" t:hint="Link to reset password wil be sent to your email"/>
|
|
6
|
-
</c:form>
|
|
7
|
-
<% if (!page.noLinks) { %>
|
|
8
|
-
<c:list type="unstyled" margin="start-3">
|
|
9
|
-
<c:include resource="sumba.partial:/list-item/signin.html" />
|
|
10
|
-
<c:include resource="sumba.partial:/list-item/user-signup.html" />
|
|
11
|
-
<c:include resource="sumba.partial:/list-item/goto-home.html" />
|
|
12
|
-
</c:list>
|
|
13
|
-
<% } %>
|
|
@@ -1,19 +1,3 @@
|
|
|
1
1
|
---
|
|
2
|
-
t:title:
|
|
2
|
+
t:title: newUserSignup
|
|
3
3
|
---
|
|
4
|
-
<c:form button reset-validation referer grid-gutter="2">
|
|
5
|
-
<c:form-input name="username" label-floating />
|
|
6
|
-
<c:form-password name="password" col="6-lg" label-floating />
|
|
7
|
-
<c:form-password name="verifyPassword" col="6-lg" label-floating />
|
|
8
|
-
<c:form-input type="email" name="email" label-floating />
|
|
9
|
-
<c:form-input name="firstName" col="6-lg" label-floating />
|
|
10
|
-
<c:form-input name="lastName" col="6-lg" label-floating />
|
|
11
|
-
<c:form-check name="agree" col="12" value="true" t:label="I agree to the <a href='../info/terms-conditions'>Terms and Conditions</a>" />
|
|
12
|
-
</c:form>
|
|
13
|
-
<% if (!page.noLinks) { %>
|
|
14
|
-
<c:list type="unstyled" margin="start-3">
|
|
15
|
-
<c:include resource="sumba.partial:/list-item/forgot-password.html" />
|
|
16
|
-
<c:include resource="sumba.partial:/list-item/signin.html" />
|
|
17
|
-
<c:include resource="sumba.partial:/list-item/goto-home.html" />
|
|
18
|
-
</c:list>
|
|
19
|
-
<% } %>
|
|
@@ -1,11 +1,3 @@
|
|
|
1
1
|
---
|
|
2
|
-
t:title:
|
|
2
|
+
t:title: newUserSignup
|
|
3
3
|
---
|
|
4
|
-
<c:p t:content="Form submitted successfully!" />
|
|
5
|
-
<c:p t:content="Please allow us to read your submission thoroughly and we'll get back to you rightaway" />
|
|
6
|
-
<c:p t:content="Thank you!" />
|
|
7
|
-
<% if (!page.noLinks) { %>
|
|
8
|
-
<c:list type="unstyled" margin="start-3">
|
|
9
|
-
<c:list-item t:content="Goto Home" href="/" icon="house"/>
|
|
10
|
-
</c:list>
|
|
11
|
-
<% } %>
|
|
@@ -42,7 +42,7 @@ async function update ({ ctx }) {
|
|
|
42
42
|
const handler = async function get (req, reply, options) {
|
|
43
43
|
const rec = await recordGet(model, req.user.id)
|
|
44
44
|
const verified = await bcrypt.compare(req.body.currentPassword, rec.password)
|
|
45
|
-
if (!verified) throw this.error('
|
|
45
|
+
if (!verified) throw this.error('invalidCurrentPassword', { details: [{ field: 'current', error: 'invalidPassword' }], statusCode: 400 })
|
|
46
46
|
const input = { password: req.body.password }
|
|
47
47
|
await recordUpdate(model, req.user.id, input)
|
|
48
48
|
return {}
|
|
@@ -16,7 +16,7 @@ async function create () {
|
|
|
16
16
|
const { hash } = this.app.bajoExtra
|
|
17
17
|
const { getUserFromUsernamePassword, createJwtFromUserRecord } = this
|
|
18
18
|
|
|
19
|
-
if (!['api-key', 'jwt', 'apiKey'].includes(req.params.type)) throw this.error('
|
|
19
|
+
if (!['api-key', 'jwt', 'apiKey'].includes(req.params.type)) throw this.error('invalidTokenType')
|
|
20
20
|
const rec = await getUserFromUsernamePassword(req.body.username, req.body.password, req)
|
|
21
21
|
if (req.params.type === 'jwt') {
|
|
22
22
|
const jwt = await createJwtFromUserRecord(rec)
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"signoutWarning": "You\\'re about to sign out. This means your session will be cleared and you\\'ll be asked for your credential again in case you want to access any protected pages and resources.<br /><br />Are your sure?",
|
|
3
|
-
"field": {
|
|
4
|
-
"currentPassword": "Current Password",
|
|
5
|
-
"newPassword": "New Password",
|
|
6
|
-
"verifyNewPassword": "Verify New Password",
|
|
7
|
-
"usernameEmail": "Username/Email",
|
|
8
|
-
"subject": "Subject",
|
|
9
|
-
"message": "Message"
|
|
10
|
-
}
|
|
11
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"Invalid api key provided": "",
|
|
3
|
-
"User is inactive or temporarily disabled": "",
|
|
4
|
-
"Invalid token or token is expired": "",
|
|
5
|
-
"Unknown username": "Nama pengguna tidak dikenal",
|
|
6
|
-
"Invalid password": "Kata kunci tidak valid",
|
|
7
|
-
"Unknown site or site is not configured yet": "",
|
|
8
|
-
"This site is currently inactive or disabled. Please contact your admin, thanks!": "",
|
|
9
|
-
"No authentication method found. Please contact your admin immediately!": "",
|
|
10
|
-
"Invalid authentication method '%s'": "",
|
|
11
|
-
"Access denied. Can't authenticate you through all available methods": "",
|
|
12
|
-
"Invalid token type": "",
|
|
13
|
-
"Invalid current password": "",
|
|
14
|
-
"Theme support is globally disabled!": "",
|
|
15
|
-
"Theme '%s' is not installed": "",
|
|
16
|
-
"Profile": "Profil",
|
|
17
|
-
"Your Profile": "Profil Anda",
|
|
18
|
-
"Edit Your Profile": "Ubah Profil Anda",
|
|
19
|
-
"Account": "Akun",
|
|
20
|
-
"Address": "Alamat",
|
|
21
|
-
"Phone/Email": "Telpon/Surel",
|
|
22
|
-
"Change Password": "Ganti Kata Sandi",
|
|
23
|
-
"Signout": "Keluar",
|
|
24
|
-
"Signin": "Masuk",
|
|
25
|
-
"Your Stuff": "Perkakas Anda",
|
|
26
|
-
"Forgot Password": "Lupa Kata Sandi",
|
|
27
|
-
"User Activation": "Aktivasi Pengguna",
|
|
28
|
-
"New User Signup": "Pendaftaran Pengguna",
|
|
29
|
-
"Update Profile": "Perbarui Profil",
|
|
30
|
-
"Back to Profile": "Kembali ke Profil",
|
|
31
|
-
"Display API Key": "Tampilkan Kunci API",
|
|
32
|
-
"API Key": "Kunci API",
|
|
33
|
-
"About Us": "Tentang Kami",
|
|
34
|
-
"Terms": "Ketentuan",
|
|
35
|
-
"Privacy": "Privasi",
|
|
36
|
-
"Invalid activation key": "Kunci aktivasi invalid",
|
|
37
|
-
"Yes, sign me out now!": "Ya, keluarkan saya sekarang!",
|
|
38
|
-
"signoutWarning": "Anda akan keluar. Ini berarti sesi Anda akan dihapus dan Anda akan ditanyakan kembali kredensial Anda jika Anda akan mengakses halaman-halaman yang diproteksi.<br /><br />Anda yakin?",
|
|
39
|
-
"Clear Invalid Fields": "Bersihkan Kolom Invalid",
|
|
40
|
-
"Link to reset password wil be sent to your email": "Tautan untuk mengubah kata sandi akan dikirim ke surel Anda",
|
|
41
|
-
"You've successfully signed in!": "Anda sukses melakukan login",
|
|
42
|
-
"You've successfully signed out!": "Anda sukses melakukan logout",
|
|
43
|
-
"Your profile is successfully updated": "Profil Anda sukses diperbarui",
|
|
44
|
-
"You've successfully changed your password. Now please re-signin with your new password": "Anda sukses melakukan perbaruan kata kunci. Sekarang silahkan melakukan login ulang dengan kata kunci baru Anda",
|
|
45
|
-
"I agree to the <a href='./info/terms-conditions'>Terms and Conditions</a>": "Saya setuju dengan semua <a href='./info/terms-conditions'>Ketentuan dan Persyaratan</a>",
|
|
46
|
-
"User account successfully activated": "Akun pengguna sukses diaktivasi",
|
|
47
|
-
"User account successfully created": "Akun pengguna sukses dibuat",
|
|
48
|
-
"Reset API Key": "Reset Kunci API",
|
|
49
|
-
"Person In Charge": "Penanggung Jawab",
|
|
50
|
-
"Contact Form": "Form Kontak",
|
|
51
|
-
"All rights reserved": "Hak cipta dilindungi Undang-undang",
|
|
52
|
-
"Terms & Conditions": "Term & Kondisi",
|
|
53
|
-
"Cookie Policy": "Kebijakan Cookie",
|
|
54
|
-
"Privacy Statement": "Pernyataan Privasi",
|
|
55
|
-
"Legal Info": "Info Legal",
|
|
56
|
-
"Help": "Bantuan",
|
|
57
|
-
"Trouble Tickets": "Tiket Masalah",
|
|
58
|
-
"field": {
|
|
59
|
-
"currentPassword": "Kata Sandi Saat Ini",
|
|
60
|
-
"newPassword": "Kata Sandi Baru",
|
|
61
|
-
"verifyPassword": "Verifikasi Kata Sandi",
|
|
62
|
-
"verifyNewPassword": "Verifikasi Sandi Baru",
|
|
63
|
-
"usernameEmail": "Nama Pengguna/Surel",
|
|
64
|
-
"apiKey": "Kunci API",
|
|
65
|
-
"hostname": "Nama Host",
|
|
66
|
-
"iconset": "Iconset",
|
|
67
|
-
"subject": "Judul",
|
|
68
|
-
"message": "Pesan"
|
|
69
|
-
},
|
|
70
|
-
"validation": {
|
|
71
|
-
"password.minOfUppercase": "Mengandung setidaknya {{min}} huruf besar",
|
|
72
|
-
"password.minOfSpecialCharacters": "Mengandung setidaknya {{min}} karakter spesial",
|
|
73
|
-
"password.minOfLowercase": "Mengandung setidaknya {{min}} huruf kecil",
|
|
74
|
-
"password.minOfNumeric": "Mengandung setidaknya {{min}} numerik/angka",
|
|
75
|
-
"password.noWhiteSpaces": "Tidak boleh ada spasi",
|
|
76
|
-
"password.onlyLatinCharacters": "Harus berupa karakter latin saja"
|
|
77
|
-
}
|
|
78
|
-
}
|