sumba 1.0.20 → 1.0.21
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 +10 -7
- package/bajo/hook/dobo.sumba-user@after-record-create.js +12 -0
- package/bajo/hook/dobo.sumba-user@after-record-update.js +13 -0
- package/bajo/hook/dobo.sumba-user@before-record-validation.js +1 -1
- package/bajo/intl/en-US.json +6 -4
- package/bajo/intl/id.json +7 -5
- package/lib/check-site-id.js +2 -2
- package/lib/password-rule.js +8 -7
- package/package.json +1 -1
- package/waibuMpa/partial/_mail/user-activation-success.html +9 -0
- package/waibuMpa/partial/_mail/user-activation-success.id.html +10 -0
- package/waibuMpa/partial/_mail/user-forgot-password-changed.html +4 -0
- package/waibuMpa/partial/_mail/user-forgot-password-changed.id.html +4 -0
- package/waibuMpa/partial/_mail/user-forgot-password-link.html +9 -0
- package/waibuMpa/partial/_mail/user-forgot-password-link.id.html +9 -0
- package/waibuMpa/partial/_mail/user-signup-success.html +16 -0
- package/waibuMpa/partial/_mail/user-signup-success.id.html +17 -0
- package/waibuMpa/partial/list-item/member-links.html +3 -0
- package/waibuMpa/partial/user/forgot-password-nomail.id.md +4 -0
- package/waibuMpa/partial/user/forgot-password.html +4 -0
- package/waibuMpa/partial/user/fpl-invalid.html +1 -0
- package/waibuMpa/partial/user/fpl-invalid.id.md +3 -0
- package/waibuMpa/partial/user/fpl-invalid.md +3 -0
- package/waibuMpa/partial/user/fpl.html +5 -0
- package/waibuMpa/partial/user/signup/success-mail.id.md +5 -0
- package/waibuMpa/partial/user/signup/success-mail.md +5 -0
- package/waibuMpa/partial/user/signup/success-nomail.id.md +5 -0
- package/waibuMpa/partial/user/signup/success-nomail.md +5 -0
- package/waibuMpa/partial/user/signup/success.html +5 -3
- package/waibuMpa/route/my-stuff/change-password.js +3 -3
- package/waibuMpa/route/user/activation.js +5 -3
- package/waibuMpa/route/user/forgot-password/@fpl.js +59 -0
- package/waibuMpa/route/user/forgot-password.js +27 -1
- package/waibuMpa/template/_mail/user-activation-success.html +3 -0
- package/waibuMpa/template/_mail/user-forgot-password-changed.html +3 -0
- package/waibuMpa/template/_mail/user-forgot-password-link.html +3 -0
- package/waibuMpa/template/_mail/user-signup-success.html +3 -0
- package/waibuMpa/template/user/fpl-invalid.html +3 -0
- package/waibuMpa/template/user/fpl.html +3 -0
package/bajo/config.json
CHANGED
|
@@ -58,12 +58,15 @@
|
|
|
58
58
|
"signout": "sumba:/signout",
|
|
59
59
|
"afterSignout": "/"
|
|
60
60
|
},
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
"siteSetting": {
|
|
62
|
+
"forgotPasswordExpDur": "5m",
|
|
63
|
+
"userPassword": {
|
|
64
|
+
"minUppercase": 1,
|
|
65
|
+
"minLowercase": 1,
|
|
66
|
+
"minSpecialChar": 1,
|
|
67
|
+
"minNumeric": 1,
|
|
68
|
+
"noWhitespace": false,
|
|
69
|
+
"latinOnlyChars": false
|
|
70
|
+
}
|
|
68
71
|
}
|
|
69
72
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
async function afterRecordCreate (body, options = {}, rec) {
|
|
2
|
+
if (!(this.app.masohi && this.app.masohiMail)) return
|
|
3
|
+
options.tpl = 'sumba.template:/_mail/user-signup-success.html'
|
|
4
|
+
const { data } = rec
|
|
5
|
+
const to = `${data.firstName} ${data.lastName} <${data.email}>`
|
|
6
|
+
const subject = options.req.t('newUserSignup')
|
|
7
|
+
try {
|
|
8
|
+
await this.app.masohi.send({ to, subject, message: data, options })
|
|
9
|
+
} catch (err) {}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default afterRecordCreate
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
async function afterRecordUpdate (id, body, options = {}, rec) {
|
|
2
|
+
if (!(this.app.masohi && this.app.masohiMail)) return
|
|
3
|
+
const { data, oldData } = rec
|
|
4
|
+
if (!(oldData.status === 'UNVERIFIED' && data.status === 'ACTIVE')) return
|
|
5
|
+
options.tpl = 'sumba.template:/_mail/user-activation-success.html'
|
|
6
|
+
const to = `${data.firstName} ${data.lastName} <${data.email}>`
|
|
7
|
+
const subject = options.req.t('userActivation')
|
|
8
|
+
try {
|
|
9
|
+
await this.app.masohi.send({ to, subject, message: data, options })
|
|
10
|
+
} catch (err) {}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default afterRecordUpdate
|
|
@@ -2,7 +2,7 @@ import passwordRule from '../../lib/password-rule.js'
|
|
|
2
2
|
|
|
3
3
|
async function doboSumbaUserBeforeRecordValidation (body, options = {}) {
|
|
4
4
|
const { set } = this.app.bajo.lib._
|
|
5
|
-
const password = await passwordRule.call(this)
|
|
5
|
+
const password = await passwordRule.call(this, options.req)
|
|
6
6
|
const rule = { password }
|
|
7
7
|
set(options, 'validation.params.rule', rule)
|
|
8
8
|
}
|
package/bajo/intl/en-US.json
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"signin": "Signin",
|
|
25
25
|
"yourStuff": "Your Stuff",
|
|
26
26
|
"forgotPassword": "Forgot Password",
|
|
27
|
-
"userActivation": "
|
|
27
|
+
"userActivation": "Account Activation",
|
|
28
28
|
"signup": "Signup",
|
|
29
29
|
"newUserSignup": "New User Signup",
|
|
30
30
|
"updateProfile": "Update Profile",
|
|
@@ -58,9 +58,6 @@
|
|
|
58
58
|
"help": "Help",
|
|
59
59
|
"troubleTickets": "Trouble Tickets",
|
|
60
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
61
|
"warningMemberOnly%s": "Please authenticate yourself first, because the <a href=\"%s\">page<a> your're trying to access is a member only page",
|
|
65
62
|
"collectingRouteGuards": "Collecting route guards:",
|
|
66
63
|
"secureRoutes%d": "- Secure routes: %d",
|
|
@@ -72,6 +69,11 @@
|
|
|
72
69
|
"yourReply": "Your Reply",
|
|
73
70
|
"you": "You",
|
|
74
71
|
"us": "Us",
|
|
72
|
+
"support": "Support",
|
|
73
|
+
"unknownUsernameEmailOrInactive": "Unknown username/email or account is inactive",
|
|
74
|
+
"emailSent": "An email has been sent to your address",
|
|
75
|
+
"forgotPasswordLink": "Forgot Password Link",
|
|
76
|
+
"forgotPasswordChanged": "Password Successfully Changed",
|
|
75
77
|
"field": {
|
|
76
78
|
"currentPassword": "Current Password",
|
|
77
79
|
"newPassword": "New Password",
|
package/bajo/intl/id.json
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"signin": "Masuk",
|
|
25
25
|
"yourStuff": "Perkakas Anda",
|
|
26
26
|
"forgotPassword": "Lupa Kata Sandi",
|
|
27
|
-
"userActivation": "Aktivasi
|
|
27
|
+
"userActivation": "Aktivasi Akun",
|
|
28
28
|
"signup": "Pendaftaran",
|
|
29
29
|
"newUserSignup": "Pendaftaran Pengguna Baru",
|
|
30
30
|
"updateProfile": "Perbarui Profil",
|
|
@@ -52,15 +52,12 @@
|
|
|
52
52
|
"cookiePolicy": "Kebijakan Cookie",
|
|
53
53
|
"privacy": "Privasi",
|
|
54
54
|
"privacyStatement": "Pernyataan Privasi",
|
|
55
|
-
"terms": "
|
|
55
|
+
"terms": "Ketentuan",
|
|
56
56
|
"termsConditions": "Term dan Kondisi",
|
|
57
57
|
"legalInfo": "Info Legal",
|
|
58
58
|
"help": "Bantuan",
|
|
59
59
|
"troubleTickets": "Tiket Masalah",
|
|
60
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
61
|
"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
62
|
"collectingRouteGuards": "Mengoleksi pertahanan jalur:",
|
|
66
63
|
"secureRoutes%d": "- Jalur aman: %d",
|
|
@@ -73,6 +70,11 @@
|
|
|
73
70
|
"yourReply": "Balasan Anda",
|
|
74
71
|
"you": "Anda",
|
|
75
72
|
"us": "Kami",
|
|
73
|
+
"support": "Bantuan",
|
|
74
|
+
"unknownUsernameEmailOrInactive": "Nama pengguna/surel tidak dikenal atau akun belum aktif",
|
|
75
|
+
"emailSent": "Sebuah surel telah dikirim ke alamat Anda",
|
|
76
|
+
"forgotPasswordLink": "Tautan Lupa Kata Sandi",
|
|
77
|
+
"forgotPasswordChanged": "Kata Sandi Sukses Diubah",
|
|
76
78
|
"field": {
|
|
77
79
|
"currentPassword": "Kata Sandi Saat Ini",
|
|
78
80
|
"newPassword": "Kata Sandi Baru",
|
package/lib/check-site-id.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
async function mergeSetting (req) {
|
|
2
|
-
const { defaultsDeep } = this.app.bajo
|
|
2
|
+
const { defaultsDeep, parseObject } = this.app.bajo
|
|
3
3
|
const { trim, get, filter } = this.app.bajo.lib._
|
|
4
4
|
const { recordFind } = this.app.dobo
|
|
5
5
|
const defSetting = {}
|
|
@@ -21,7 +21,7 @@ async function mergeSetting (req) {
|
|
|
21
21
|
nsSetting[ns][item.key] = value
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
-
req.site.setting = defaultsDeep({}, nsSetting, defSetting)
|
|
24
|
+
req.site.setting = parseObject(defaultsDeep({}, nsSetting, defSetting))
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const omitted = ['status']
|
package/lib/password-rule.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { joiPasswordExtendCore } from 'joi-password'
|
|
2
2
|
|
|
3
|
-
async function passwordRule () {
|
|
3
|
+
async function passwordRule (req) {
|
|
4
4
|
const { importPkg } = this.app.bajo
|
|
5
5
|
const joi = await importPkg('dobo:joi')
|
|
6
6
|
const joiPassword = joi.extend(joiPasswordExtendCore)
|
|
@@ -9,12 +9,13 @@ async function passwordRule () {
|
|
|
9
9
|
.min(8)
|
|
10
10
|
.max(100)
|
|
11
11
|
.required()
|
|
12
|
-
|
|
13
|
-
if (
|
|
14
|
-
if (
|
|
15
|
-
if (
|
|
16
|
-
if (
|
|
17
|
-
if (
|
|
12
|
+
const cfg = req.site.setting.sumba.userPassword
|
|
13
|
+
if (cfg.minUppercase) password = password.minOfUppercase(cfg.minUppercase)
|
|
14
|
+
if (cfg.minLowercase) password = password.minOfLowercase(cfg.minLowercase)
|
|
15
|
+
if (cfg.minSpecialChar) password = password.minOfSpecialCharacters(cfg.minSpecialChar)
|
|
16
|
+
if (cfg.minNumeric) password = password.minOfNumeric(cfg.minNumeric)
|
|
17
|
+
if (cfg.noWhitespace) password = password.noWhiteSpaces()
|
|
18
|
+
if (cfg.latinOnlyChars) password = password.onlyLatinCharacters()
|
|
18
19
|
return password
|
|
19
20
|
}
|
|
20
21
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<p>Congratulations, your account with username <strong><%= username %></strong>
|
|
2
|
+
has been successfully activated. Thus you can now access the Members-only pages
|
|
3
|
+
according to the permissions set by your Admin.</p>
|
|
4
|
+
|
|
5
|
+
<p>If you still have questions, please don't hesitate to
|
|
6
|
+
fill out the form on the <strong>Help</strong> page.
|
|
7
|
+
We will be happy to answer all your questions.</p>
|
|
8
|
+
|
|
9
|
+
<p>Thank you!</p>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<p>Selamat, akun Anda dengan nama pengguna <strong><%= username %></strong>
|
|
2
|
+
telah berhasil diaktivkan. Dengan demikian Anda telah bisa mengakses
|
|
3
|
+
halaman-halaman khusus Anggota sesuai dengan permisi yang ditetapkan oleh
|
|
4
|
+
Admin Anda.</p>
|
|
5
|
+
|
|
6
|
+
<p>Jika Anda masih memiliki pertanyaan-pertanyaan, silahkan untuk tidak segan
|
|
7
|
+
mengisi formulir di halaman <strong>Bantuan</strong>. Dengan senang hati
|
|
8
|
+
kami akan menjawab semua pertanyaan Anda.</p>
|
|
9
|
+
|
|
10
|
+
<p>Terima kasih!</p>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<p>Here is a special password change link for you:</p>
|
|
2
|
+
|
|
3
|
+
<% const url = 'http://' + _meta.hostHeader + _routePath('sumba:/user/forgot-password') + '/' + fpToken %>
|
|
4
|
+
<p><a href="<%= url %>"><%= url %></a></p>
|
|
5
|
+
|
|
6
|
+
<p>Please make the change immediately because after <strong>5 minutes</strong> the link above
|
|
7
|
+
is no longer valid.</p>
|
|
8
|
+
|
|
9
|
+
<p>Please contact us if you still have questions, thank you!</p>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<p>Berikut adalah tautan khusus pergantian password untuk Anda:</p>
|
|
2
|
+
|
|
3
|
+
<% const url = 'http://' + _meta.hostHeader + _routePath('sumba:/user/forgot-password') + '/' + fpToken %>
|
|
4
|
+
<p><a href="<%= url %>"><%= url %></a></p>
|
|
5
|
+
|
|
6
|
+
<p>Silahkan melakukan pergantian segera karena setelah <strong>5 menit</strong> tautan diatas
|
|
7
|
+
tidak berlaku kembali.</p>
|
|
8
|
+
|
|
9
|
+
<p>Silahkan hubungi kami jika Anda masih memiliki pertanyaan, terima kasih!</p>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<p>Welcome and thank your for joining us in <%= _meta.site.title %>.
|
|
2
|
+
Please click the following link to activate your account:</p>
|
|
3
|
+
|
|
4
|
+
<p><% const url = _meta.hostHeader + _routePath("sumba:/user/activation") + "?key=" + token %>
|
|
5
|
+
<a href="http://<%= url %>">http://<%= url %></a></p>
|
|
6
|
+
|
|
7
|
+
<p>If you are unable to click the link above, please visit our website
|
|
8
|
+
at http://<%= _meta.hostHeader %>, then visit the <strong>Account Activation</strong> from
|
|
9
|
+
the link available in the footer.</p>
|
|
10
|
+
|
|
11
|
+
<p>Please copy the key below, then enter it in the input box on the page and press
|
|
12
|
+
the <strong>Submit</strong> button:</p>
|
|
13
|
+
|
|
14
|
+
<strong><%= token %></strong>
|
|
15
|
+
|
|
16
|
+
<p>Once again, thank you for joining us. We hope our services will benefit you.<p>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<p>Selamat datang dan telah bergabung di <%= _meta.site.title %>. Selanjutnya,
|
|
2
|
+
silahkan mengklik link dibawah ini untuk melakukan aktivasi akun anda:</p>
|
|
3
|
+
|
|
4
|
+
<p><% const url = _meta.hostHeader + _routePath("sumba:/user/activation") + "?key=" + token %>
|
|
5
|
+
<a href="http://<%= url %>">http://<%= url %></a></p>
|
|
6
|
+
|
|
7
|
+
<p>Jika Ada tidak bisa mengklik link diatas, silahkan kunjungi situs kami
|
|
8
|
+
di http://<%= _meta.hostHeader %>, kemudian kunjungi di halaman <strong>Aktivasi
|
|
9
|
+
Akun</strong> dari link yang tersedia di catatan kaki.</p>
|
|
10
|
+
|
|
11
|
+
<p>Silahkan kopi kunci dibawah ini, kemudian masukkan di kotak masukan yang
|
|
12
|
+
ada di halaman tersebut dan tekan tombol <strong>Kirim</strong>:</p>
|
|
13
|
+
|
|
14
|
+
<strong><%= token %></strong>
|
|
15
|
+
|
|
16
|
+
<p>Sekali lagi terimakasih telah bergabung bersama kami, semoga layanan kami
|
|
17
|
+
membawa manfaat untuk Anda.<p>
|
|
@@ -8,4 +8,7 @@
|
|
|
8
8
|
<c:list-item href="sumba:/signin" t:content="signin" />
|
|
9
9
|
<c:list-item href="sumba:/user/signup" t:content="signup" />
|
|
10
10
|
<c:list-item href="sumba:/user/forgot-password" t:content="forgotPassword" />
|
|
11
|
+
<% if (_hasPlugin('masohi') && _hasPlugin('masohiMail')) { %>
|
|
12
|
+
<c:list-item href="sumba:/user/activation" t:content="userActivation" />
|
|
13
|
+
<% } %>
|
|
11
14
|
<% } %>
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
<% if (_hasPlugin('masohiMail')) { %>
|
|
1
2
|
<c:form button reset-validation>
|
|
2
3
|
<c:form-input name="usernameEmail" label-floating wrapper-margin="bottom-2" t:hint="linkPasswordWillbeSent"/>
|
|
3
4
|
</c:form>
|
|
5
|
+
<% } else { %>
|
|
6
|
+
<c:include resource="sumba.partial:/user/forgot-password-nomail.md" />
|
|
7
|
+
<% } %>
|
|
4
8
|
<% if (!page.noLinks) { %>
|
|
5
9
|
<c:list type="unstyled" margin="start-3">
|
|
6
10
|
<c:include resource="sumba.partial:/list-item/signin.html" />
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<c:include resource="sumba.partial:/user/fpl-invalid.md" />
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<c:form button reset-validation referer>
|
|
2
|
+
<c:form-plaintext name="username" label-floating wrapper-margin="bottom-2" />
|
|
3
|
+
<c:form-password name="newPassword" label-floating wrapper-margin="bottom-2" />
|
|
4
|
+
<c:form-password name="verifyNewPassword" label-floating wrapper-margin="bottom-3" />
|
|
5
|
+
</c:form>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
<c:
|
|
3
|
-
|
|
1
|
+
<% if (_hasPlugin('masohiMail')) { %>
|
|
2
|
+
<c:include resource="sumba.partial:/user/signup/success-mail.md" />
|
|
3
|
+
<% } else { %>
|
|
4
|
+
<c:include resource="sumba.partial:/user/signup/success-nomail.md" />
|
|
5
|
+
<% } %>
|
|
4
6
|
<% if (!page.noLinks) { %>
|
|
5
7
|
<c:list type="unstyled" margin="start-3">
|
|
6
8
|
<c:list-item t:content="gotoHome" href="/" icon="house"/>
|
|
@@ -12,7 +12,7 @@ const profile = {
|
|
|
12
12
|
let error
|
|
13
13
|
if (req.method === 'POST') {
|
|
14
14
|
try {
|
|
15
|
-
const newPassword = await passwordRule.call(this)
|
|
15
|
+
const newPassword = await passwordRule.call(this, req)
|
|
16
16
|
const schema = Joi.object({
|
|
17
17
|
currentPassword: Joi.string().min(8).max(50).required(),
|
|
18
18
|
newPassword,
|
|
@@ -26,11 +26,11 @@ const profile = {
|
|
|
26
26
|
const rec = await recordGet(model, req.user.id)
|
|
27
27
|
const verified = await bcrypt.compare(req.body.currentPassword, rec.password)
|
|
28
28
|
if (!verified) throw this.error('invalidCurrentPassword', { details: [{ field: 'currentPassword', error: 'invalidPassword' }], statusCode: 400 })
|
|
29
|
-
await recordUpdate(model, req.user.id, { password: req.body.newPassword })
|
|
29
|
+
await recordUpdate(model, req.user.id, { password: req.body.newPassword }, { req, noFlash: true })
|
|
30
30
|
// signout and redirect to signin
|
|
31
31
|
req.session.user = null
|
|
32
32
|
req.flash('notify', req.t('passwordChangedReSignin'))
|
|
33
|
-
return reply.redirectTo(
|
|
33
|
+
return reply.redirectTo(this.config.redirect.signin)
|
|
34
34
|
} catch (err) {
|
|
35
35
|
error = err
|
|
36
36
|
}
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
+
const model = 'SumbaUser'
|
|
2
|
+
|
|
1
3
|
const userActivation = {
|
|
2
4
|
method: ['GET', 'POST'],
|
|
3
5
|
handler: async function (req, reply) {
|
|
4
6
|
const { defaultsDeep } = this.app.bajo
|
|
5
|
-
const { recordFind, recordUpdate } = this.app.
|
|
7
|
+
const { recordFind, recordUpdate } = this.app.waibuDb
|
|
6
8
|
const form = defaultsDeep(req.body, { key: req.query.key })
|
|
7
9
|
let error
|
|
8
10
|
if (req.method === 'POST') {
|
|
9
11
|
try {
|
|
10
12
|
const query = { status: 'UNVERIFIED', token: req.body.key }
|
|
11
|
-
const result = await recordFind(
|
|
13
|
+
const result = await recordFind({ model, req, reply, options: { dataOnly: true, query, limit: 1, noHook: true } })
|
|
12
14
|
if (result.length === 0) throw this.error('validationError', { details: [{ field: 'key', error: 'invalidActivationKey' }] })
|
|
13
|
-
await recordUpdate(
|
|
15
|
+
await recordUpdate({ model, req, reply, id: result[0].id, body: { status: 'ACTIVE' }, options: { noValidation: true, noFlash: true } })
|
|
14
16
|
req.flash('notify', req.t('userActivated'))
|
|
15
17
|
return reply.redirectTo(this.config.redirect.signin, req)
|
|
16
18
|
} catch (err) {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import passwordRule from '../../../../lib/password-rule.js'
|
|
2
|
+
const model = 'SumbaUser'
|
|
3
|
+
|
|
4
|
+
async function getUser (req, reply) {
|
|
5
|
+
const { dayjs } = this.app.bajo.lib
|
|
6
|
+
const { recordFind } = this.app.waibuDb
|
|
7
|
+
const invalidFpl = 'sumba.template:/user/fpl-invalid.html'
|
|
8
|
+
if (Buffer.from(req.params.fpl, 'base64').toString('base64') !== req.params.fpl) return invalidFpl
|
|
9
|
+
const fpToken = Buffer.from(req.params.fpl, 'base64').toString()
|
|
10
|
+
const [token, sec] = fpToken.split(':')
|
|
11
|
+
if (dayjs().unix() > Number(sec)) return invalidFpl
|
|
12
|
+
const query = { token, status: 'ACTIVE' }
|
|
13
|
+
const users = await recordFind({ model, req, reply, options: { query, limit: 1, dataOnly: true, noHook: true } })
|
|
14
|
+
if (users.length === 0) return invalidFpl
|
|
15
|
+
return users[0]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const forgotPasswordLink = {
|
|
19
|
+
method: ['GET', 'POST'],
|
|
20
|
+
handler: async function (req, reply) {
|
|
21
|
+
const { defaultsDeep, importPkg } = this.app.bajo
|
|
22
|
+
const { isString } = this.app.bajo.lib._
|
|
23
|
+
const { recordUpdate } = this.app.dobo
|
|
24
|
+
const Joi = await importPkg('dobo:joi')
|
|
25
|
+
|
|
26
|
+
const form = defaultsDeep(req.body, {})
|
|
27
|
+
const user = await getUser.call(this, req, reply)
|
|
28
|
+
if (isString(user)) return reply.view(user)
|
|
29
|
+
form.username = user.username
|
|
30
|
+
let error
|
|
31
|
+
if (req.method === 'POST') {
|
|
32
|
+
try {
|
|
33
|
+
const newPassword = await passwordRule.call(this, req)
|
|
34
|
+
const schema = Joi.object({
|
|
35
|
+
newPassword,
|
|
36
|
+
verifyNewPassword: Joi.ref('newPassword')
|
|
37
|
+
})
|
|
38
|
+
try {
|
|
39
|
+
await schema.validateAsync(req.body, this.app.dobo.config.validationParams)
|
|
40
|
+
} catch (err) {
|
|
41
|
+
throw this.error('validationError', { details: err.details, values: err.values, ns: this.name, statusCode: 422, code: 'DB_VALIDATION' })
|
|
42
|
+
}
|
|
43
|
+
await recordUpdate(model, user.id, { password: req.body.newPassword }, { req, noFlash: true })
|
|
44
|
+
const to = `${user.firstName} ${user.lastName} <${user.email}>`
|
|
45
|
+
const subject = req.t('forgotPasswordChanged')
|
|
46
|
+
const options = { req, reply, tpl: 'sumba.template:/_mail/user-forgot-password-changed.html' }
|
|
47
|
+
await this.app.masohi.send({ to, subject, message: user, options })
|
|
48
|
+
req.flash('notify', req.t('passwordChangedReSignin'))
|
|
49
|
+
return reply.redirectTo(this.config.redirect.signin)
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.log(err)
|
|
52
|
+
error = err
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return reply.view('sumba.template:/user/fpl.html', { form, error })
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export default forgotPasswordLink
|
|
@@ -1,7 +1,33 @@
|
|
|
1
|
+
const model = 'SumbaUser'
|
|
2
|
+
|
|
1
3
|
const profile = {
|
|
2
4
|
method: ['GET', 'POST'],
|
|
3
5
|
handler: async function (req, reply) {
|
|
4
|
-
return reply.view('sumba.template:/user/forgot-password.html')
|
|
6
|
+
if (!this.app.masohiMail) return reply.view('sumba.template:/user/forgot-password.html')
|
|
7
|
+
const { defaultsDeep } = this.app.bajo
|
|
8
|
+
const { dayjs } = this.app.bajo.lib
|
|
9
|
+
const { recordFind } = this.app.dobo
|
|
10
|
+
const form = defaultsDeep(req.body, {})
|
|
11
|
+
let error
|
|
12
|
+
if (req.method === 'POST') {
|
|
13
|
+
try {
|
|
14
|
+
const query = { status: 'ACTIVE', $or: [{ username: req.body.usernameEmail }, { email: req.body.usernameEmail }] }
|
|
15
|
+
const result = await recordFind(model, { query, limit: 1 }, { dataOnly: true, noHook: true, forceNoHidden: true })
|
|
16
|
+
if (result.length === 0) throw this.error('validationError', { details: [{ field: 'usernameEmail', error: 'unknownUsernameEmailOrInactive' }] })
|
|
17
|
+
const data = result[0]
|
|
18
|
+
const to = `${data.firstName} ${data.lastName} <${data.email}>`
|
|
19
|
+
const subject = req.t('forgotPasswordLink')
|
|
20
|
+
const options = { req, reply, tpl: 'sumba.template:/_mail/user-forgot-password-link.html' }
|
|
21
|
+
const exp = req.site.setting.sumba.forgotPasswordExpDur
|
|
22
|
+
data.fpToken = Buffer.from(`${data.token}:${dayjs().add(exp, 'ms').unix()}`).toString('base64')
|
|
23
|
+
await this.app.masohi.send({ to, subject, message: data, options })
|
|
24
|
+
req.flash('notify', req.t('emailSent'))
|
|
25
|
+
return reply.redirectTo(this.config.redirect.signin)
|
|
26
|
+
} catch (err) {
|
|
27
|
+
error = err
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return reply.view('sumba.template:/user/forgot-password.html', { form, error })
|
|
5
31
|
}
|
|
6
32
|
}
|
|
7
33
|
|