Move account-related APIs down one level into /api/account and the former /api/sign_u...
[ndcode_site.git] / api / account / password_reset.json.jst
1 let crypto = require('crypto')
2 let logjson = (await import('@ndcode/logjson')).default
3 let XDate = require('xdate')
4
5 return async env => {
6   let globals = await env.site.get_json('/_config/globals.json')
7   let nodemailer_noreply = await env.site.get_nodemailer(
8     '/_config/nodemailer_noreply.json'
9   )
10   let post_request = await _require('/_lib/post_request.jst')
11   let session_cookie = await _require('/_lib/session_cookie.jst')
12   let Problem = await _require('/_lib/Problem.jst')
13
14   await post_request(
15     // env
16     env,
17     // handler
18     async (email, password) => {
19       // coerce and/or validate
20       email = email.slice(0, 256).toLowerCase()
21       password = password.slice(0, 256)
22       if (email.length === 0 || password.length < 8)
23         throw new Problem(
24           'Bad request',
25           'Minimum length check failed',
26           400
27         )
28
29       let transaction = await env.site.database.Transaction()
30       try {
31         // initialize env.session_key, set cookie in env.response
32         await session_cookie(env, transaction)
33
34         let account = await (
35           await (
36             await transaction.get({})
37           ).get('accounts', {})
38         ).get(email)
39         if (account === undefined)
40           throw new Problem(
41             'Account does not exist',
42             `Please create the account for "${email}" before attempting to reset its password.`
43             421
44           )
45
46         let link_code = crypto.randomBytes(16).toString('hex')
47         let expires = new XDate()
48         expires.addDays(1)
49         account.set(
50           'verify_password',
51           transaction.json_to_logjson(
52             {
53               password,
54               link_code,
55               expires: expires.getTime()
56             }
57           )
58         )
59
60         let given_names = await logjson.logjson_to_json(
61           await account.get('given_names', '')
62         )
63         let family_name = await logjson.logjson_to_json(
64           await account.get('family_name', '')
65         )
66         let name =
67           family_name.length ? `${given_names} ${family_name}` : given_names
68
69         await nodemailer_noreply.sendMail(
70           {
71             from: globals.noreply_from,
72             to: `${name} <${email}>`,
73             subject: 'Password reset',
74             text: `Dear ${given_names},
75
76 We have received a request to reset the account password for your email address.
77
78 If this request is valid, please verify the new password by visiting the below link:
79 ${globals.site_url}/my_account/verify_password/index.html?email=${encodeURIComponent(email)}&link_code=${encodeURIComponent(link_code)}
80
81 The link is valid for 24 hours.
82
83 Thanks,
84 ${globals.noreply_signature}
85 `
86           }
87         )
88
89         await transaction.commit()
90       }
91       catch (error) {
92         transaction.rollback()
93         throw error
94       }
95     }
96   )
97 }