aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.env.example8
-rw-r--r--app/Controllers/Http/DashboardController.js61
-rw-r--r--app/Models/Token.js3
-rw-r--r--config/mail.js101
-rw-r--r--config/persona.js98
-rw-r--r--package-lock.json307
-rw-r--r--package.json2
-rw-r--r--resources/views/dashboard/forgotPassword.edge49
-rw-r--r--resources/views/dashboard/login.edge5
-rw-r--r--resources/views/dashboard/resetPassword.edge64
-rw-r--r--start/app.js2
-rw-r--r--start/events.js23
-rw-r--r--start/routes.js23
13 files changed, 744 insertions, 2 deletions
diff --git a/.env.example b/.env.example
index bcc4c7c..3346916 100644
--- a/.env.example
+++ b/.env.example
@@ -21,4 +21,10 @@ HASH_DRIVER=bcrypt
21IS_CREATION_ENABLED=true 21IS_CREATION_ENABLED=true
22IS_DASHBOARD_ENABLED=true 22IS_DASHBOARD_ENABLED=true
23IS_REGISTRATION_ENABLED=true 23IS_REGISTRATION_ENABLED=true
24CONNECT_WITH_FRANZ=true \ No newline at end of file 24CONNECT_WITH_FRANZ=true
25
26MAIL_CONNECTION=smtp
27SMTP_PORT=2525
28SMTP_HOST=127.0.0.1
29MAIL_USERNAME=username
30MAIL_PASSWORD=password \ No newline at end of file
diff --git a/app/Controllers/Http/DashboardController.js b/app/Controllers/Http/DashboardController.js
index a47beb6..3de4816 100644
--- a/app/Controllers/Http/DashboardController.js
+++ b/app/Controllers/Http/DashboardController.js
@@ -5,6 +5,7 @@ const {
5 5
6const Service = use('App/Models/Service'); 6const Service = use('App/Models/Service');
7const Workspace = use('App/Models/Workspace'); 7const Workspace = use('App/Models/Workspace');
8const Persona = use('Persona');
8 9
9const crypto = require('crypto'); 10const crypto = require('crypto');
10const uuid = require('uuid/v4'); 11const uuid = require('uuid/v4');
@@ -47,6 +48,66 @@ class DashboardController {
47 return response.redirect('/user/account'); 48 return response.redirect('/user/account');
48 } 49 }
49 50
51 async forgotPassword({
52 request,
53 view,
54 }) {
55 const validation = await validateAll(request.all(), {
56 mail: 'required|email',
57 });
58 if (validation.fails()) {
59 return view.render('others.message', {
60 heading: 'Cannot reset your password',
61 text: 'If your provided E-Mail address is linked to an account, we have just sent an E-Mail to that address.',
62 });
63 }
64 try {
65 await Persona.forgotPassword(request.input('mail'));
66 } catch(e) {}
67
68 return view.render('others.message', {
69 heading: 'Reset password',
70 text: 'If your provided E-Mail address is linked to an account, we have just sent an E-Mail to that address.',
71 });
72 }
73
74 async resetPassword({
75 request,
76 view,
77 }) {
78 const validation = await validateAll(request.all(), {
79 password: 'required',
80 password_confirmation: 'required',
81 token: 'required',
82 });
83 if (validation.fails()) {
84 session.withErrors({
85 type: 'danger',
86 message: 'Passwords do not match',
87 });
88 return response.redirect('back');
89 }
90
91 const payload = {
92 password: crypto.createHash('sha256').update(request.input('password')).digest('base64'),
93 password_confirmation: crypto.createHash('sha256').update(request.input('password_confirmation')).digest('base64'),
94 }
95
96 try {
97 await Persona.updatePasswordByToken(request.input('token'), payload);
98 } catch(e) {
99 return view.render('others.message', {
100 heading: 'Cannot reset your password',
101 text: 'Please make sure you are using a valid and recent link to reset your password and that your passwords entered match.',
102 });
103 }
104
105 return view.render('others.message', {
106 heading: 'Reset password',
107 text: 'Successfully reset your password. You can now login to your account using your new password.',
108 });
109 }
110
50 async account({ 111 async account({
51 auth, 112 auth,
52 view, 113 view,
diff --git a/app/Models/Token.js b/app/Models/Token.js
index f6bec08..50bcf1d 100644
--- a/app/Models/Token.js
+++ b/app/Models/Token.js
@@ -3,6 +3,9 @@
3const Model = use('Model'); 3const Model = use('Model');
4 4
5class Token extends Model { 5class Token extends Model {
6 user() {
7 return this.belongsTo('App/Models/User')
8 }
6} 9}
7 10
8module.exports = Token; 11module.exports = Token;
diff --git a/config/mail.js b/config/mail.js
new file mode 100644
index 0000000..6b18db2
--- /dev/null
+++ b/config/mail.js
@@ -0,0 +1,101 @@
1'use strict'
2
3const Env = use('Env')
4
5module.exports = {
6 /*
7 |--------------------------------------------------------------------------
8 | Connection
9 |--------------------------------------------------------------------------
10 |
11 | Connection to be used for sending emails. Each connection needs to
12 | define a driver too.
13 |
14 */
15 connection: Env.get('MAIL_CONNECTION', 'smtp'),
16
17 /*
18 |--------------------------------------------------------------------------
19 | SMTP
20 |--------------------------------------------------------------------------
21 |
22 | Here we define configuration for sending emails via SMTP.
23 |
24 */
25 smtp: {
26 driver: 'smtp',
27 pool: true,
28 port: Env.get('SMTP_PORT', 2525),
29 host: Env.get('SMTP_HOST'),
30 secure: false,
31 auth: {
32 user: Env.get('MAIL_USERNAME'),
33 pass: Env.get('MAIL_PASSWORD')
34 },
35 maxConnections: 5,
36 maxMessages: 100,
37 rateLimit: 10
38 },
39
40 /*
41 |--------------------------------------------------------------------------
42 | SparkPost
43 |--------------------------------------------------------------------------
44 |
45 | Here we define configuration for spark post. Extra options can be defined
46 | inside the `extra` object.
47 |
48 | https://developer.sparkpost.com/api/transmissions.html#header-options-attributes
49 |
50 | extras: {
51 | campaign_id: 'sparkpost campaign id',
52 | options: { // sparkpost options }
53 | }
54 |
55 */
56 sparkpost: {
57 driver: 'sparkpost',
58 apiKey: Env.get('SPARKPOST_API_KEY'),
59 extras: {}
60 },
61
62 /*
63 |--------------------------------------------------------------------------
64 | Mailgun
65 |--------------------------------------------------------------------------
66 |
67 | Here we define configuration for mailgun. Extra options can be defined
68 | inside the `extra` object.
69 |
70 | https://mailgun-documentation.readthedocs.io/en/latest/api-sending.html#sending
71 |
72 | extras: {
73 | 'o:tag': '',
74 | 'o:campaign': '',,
75 | . . .
76 | }
77 |
78 */
79 mailgun: {
80 driver: 'mailgun',
81 domain: Env.get('MAILGUN_DOMAIN'),
82 region: Env.get('MAILGUN_API_REGION'),
83 apiKey: Env.get('MAILGUN_API_KEY'),
84 extras: {}
85 },
86
87 /*
88 |--------------------------------------------------------------------------
89 | Ethereal
90 |--------------------------------------------------------------------------
91 |
92 | Ethereal driver to quickly test emails in your browser. A disposable
93 | account is created automatically for you.
94 |
95 | https://ethereal.email
96 |
97 */
98 ethereal: {
99 driver: 'ethereal'
100 }
101}
diff --git a/config/persona.js b/config/persona.js
new file mode 100644
index 0000000..71fbc3f
--- /dev/null
+++ b/config/persona.js
@@ -0,0 +1,98 @@
1'use strict'
2
3/*
4|--------------------------------------------------------------------------
5| Persona
6|--------------------------------------------------------------------------
7|
8| The persona is a simple and opinionated service to register, login and
9| manage user account
10|
11*/
12
13module.exports = {
14 /*
15 |--------------------------------------------------------------------------
16 | Uids
17 |--------------------------------------------------------------------------
18 |
19 | An array of fields, that can be used to indetify a user uniquely. During
20 | login and reset password, these fields be checked against the user
21 | input
22 |
23 */
24 uids: ['email'],
25
26 /*
27 |--------------------------------------------------------------------------
28 | Email field
29 |--------------------------------------------------------------------------
30 |
31 | The name of the email field inside the database and the user payload.
32 |
33 */
34 email: 'email',
35
36 /*
37 |--------------------------------------------------------------------------
38 | Password
39 |--------------------------------------------------------------------------
40 |
41 | The password field to be used for verifying and storing user password
42 |
43 */
44 password: 'password',
45
46 /*
47 |--------------------------------------------------------------------------
48 | New account state
49 |--------------------------------------------------------------------------
50 |
51 | State of user when a new account is created
52 |
53 */
54 newAccountState: 'pending',
55
56 /*
57 |--------------------------------------------------------------------------
58 | Verified account state
59 |--------------------------------------------------------------------------
60 |
61 | State of user after they verify their email address
62 |
63 */
64 verifiedAccountState: 'active',
65
66 /*
67 |--------------------------------------------------------------------------
68 | Model
69 |--------------------------------------------------------------------------
70 |
71 | The model to be used for verifying and creating users
72 |
73 */
74 model: 'App/Models/User',
75
76 /*
77 |--------------------------------------------------------------------------
78 | Date Format
79 |--------------------------------------------------------------------------
80 |
81 | The date format for the tokens table. It is required to calculate the
82 | expiry of a token.
83 |
84 */
85 dateFormat: 'YYYY-MM-DD HH:mm:ss',
86
87 /*
88 |--------------------------------------------------------------------------
89 | Validation messages
90 |--------------------------------------------------------------------------
91 |
92 | An object of validation messages to be used when validation fails.
93 |
94 */
95 validationMessages: () => {
96 return {}
97 }
98}
diff --git a/package-lock.json b/package-lock.json
index dbd4d4d..110409b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -292,6 +292,35 @@
292 } 292 }
293 } 293 }
294 }, 294 },
295 "@adonisjs/mail": {
296 "version": "3.0.10",
297 "resolved": "https://registry.npmjs.org/@adonisjs/mail/-/mail-3.0.10.tgz",
298 "integrity": "sha512-kl+h3TieYllAxcuGA2QGiebBTMAG+p6QkxXnrdtbRp14rfiQZptjDRlhwb/OXZrGHvAXvgYzkhZPlOxsH2uATA==",
299 "requires": {
300 "@adonisjs/generic-exceptions": "^2.0.1",
301 "clone": "^2.1.2",
302 "debug": "^4.0.1",
303 "form-data": "^2.3.2",
304 "get-stream": "^4.0.0",
305 "got": "8.3.0",
306 "nodemailer": "^4.6.8"
307 },
308 "dependencies": {
309 "debug": {
310 "version": "4.1.1",
311 "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
312 "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
313 "requires": {
314 "ms": "^2.1.1"
315 }
316 },
317 "ms": {
318 "version": "2.1.2",
319 "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
320 "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
321 }
322 }
323 },
295 "@adonisjs/middleware-base": { 324 "@adonisjs/middleware-base": {
296 "version": "1.0.0", 325 "version": "1.0.0",
297 "resolved": "https://registry.npmjs.org/@adonisjs/middleware-base/-/middleware-base-1.0.0.tgz", 326 "resolved": "https://registry.npmjs.org/@adonisjs/middleware-base/-/middleware-base-1.0.0.tgz",
@@ -319,6 +348,16 @@
319 } 348 }
320 } 349 }
321 }, 350 },
351 "@adonisjs/persona": {
352 "version": "1.0.5",
353 "resolved": "https://registry.npmjs.org/@adonisjs/persona/-/persona-1.0.5.tgz",
354 "integrity": "sha512-VFdZ2CAx8WhDih/AkIf9ZG+62QESiInC4oAz+p4HNsY7fG0DDlbDX4TBPwSK1YOdiZCE6t2T0AcpChtXiBp5xg==",
355 "requires": {
356 "@adonisjs/generic-exceptions": "^2.0.0",
357 "moment": "^2.22.1",
358 "rand-token": "^0.4.0"
359 }
360 },
322 "@adonisjs/session": { 361 "@adonisjs/session": {
323 "version": "1.0.29", 362 "version": "1.0.29",
324 "resolved": "https://registry.npmjs.org/@adonisjs/session/-/session-1.0.29.tgz", 363 "resolved": "https://registry.npmjs.org/@adonisjs/session/-/session-1.0.29.tgz",
@@ -523,6 +562,11 @@
523 "tslib": "^1.9.3" 562 "tslib": "^1.9.3"
524 } 563 }
525 }, 564 },
565 "@sindresorhus/is": {
566 "version": "0.7.0",
567 "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz",
568 "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow=="
569 },
526 "@slynova/flydrive": { 570 "@slynova/flydrive": {
527 "version": "0.3.1", 571 "version": "0.3.1",
528 "resolved": "https://registry.npmjs.org/@slynova/flydrive/-/flydrive-0.3.1.tgz", 572 "resolved": "https://registry.npmjs.org/@slynova/flydrive/-/flydrive-0.3.1.tgz",
@@ -1281,6 +1325,32 @@
1281 } 1325 }
1282 } 1326 }
1283 }, 1327 },
1328 "cacheable-request": {
1329 "version": "2.1.4",
1330 "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz",
1331 "integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=",
1332 "requires": {
1333 "clone-response": "1.0.2",
1334 "get-stream": "3.0.0",
1335 "http-cache-semantics": "3.8.1",
1336 "keyv": "3.0.0",
1337 "lowercase-keys": "1.0.0",
1338 "normalize-url": "2.0.1",
1339 "responselike": "1.0.2"
1340 },
1341 "dependencies": {
1342 "get-stream": {
1343 "version": "3.0.0",
1344 "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
1345 "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
1346 },
1347 "lowercase-keys": {
1348 "version": "1.0.0",
1349 "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz",
1350 "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY="
1351 }
1352 }
1353 },
1284 "caller": { 1354 "caller": {
1285 "version": "1.0.1", 1355 "version": "1.0.1",
1286 "resolved": "https://registry.npmjs.org/caller/-/caller-1.0.1.tgz", 1356 "resolved": "https://registry.npmjs.org/caller/-/caller-1.0.1.tgz",
@@ -1458,6 +1528,14 @@
1458 } 1528 }
1459 } 1529 }
1460 }, 1530 },
1531 "clone-response": {
1532 "version": "1.0.2",
1533 "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
1534 "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
1535 "requires": {
1536 "mimic-response": "^1.0.0"
1537 }
1538 },
1461 "co-body": { 1539 "co-body": {
1462 "version": "6.0.0", 1540 "version": "6.0.0",
1463 "resolved": "https://registry.npmjs.org/co-body/-/co-body-6.0.0.tgz", 1541 "resolved": "https://registry.npmjs.org/co-body/-/co-body-6.0.0.tgz",
@@ -1693,6 +1771,14 @@
1693 "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", 1771 "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
1694 "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" 1772 "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
1695 }, 1773 },
1774 "decompress-response": {
1775 "version": "3.3.0",
1776 "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
1777 "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
1778 "requires": {
1779 "mimic-response": "^1.0.0"
1780 }
1781 },
1696 "deep-extend": { 1782 "deep-extend": {
1697 "version": "0.6.0", 1783 "version": "0.6.0",
1698 "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 1784 "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
@@ -1775,6 +1861,11 @@
1775 "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz", 1861 "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz",
1776 "integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==" 1862 "integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w=="
1777 }, 1863 },
1864 "duplexer3": {
1865 "version": "0.1.4",
1866 "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
1867 "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
1868 },
1778 "ecc-jsbn": { 1869 "ecc-jsbn": {
1779 "version": "0.1.2", 1870 "version": "0.1.2",
1780 "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 1871 "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
@@ -2673,6 +2764,15 @@
2673 "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 2764 "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
2674 "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 2765 "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
2675 }, 2766 },
2767 "from2": {
2768 "version": "2.3.0",
2769 "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
2770 "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
2771 "requires": {
2772 "inherits": "^2.0.1",
2773 "readable-stream": "^2.0.0"
2774 }
2775 },
2676 "fs-constants": { 2776 "fs-constants": {
2677 "version": "1.0.0", 2777 "version": "1.0.0",
2678 "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 2778 "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
@@ -2830,6 +2930,42 @@
2830 "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", 2930 "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
2831 "dev": true 2931 "dev": true
2832 }, 2932 },
2933 "got": {
2934 "version": "8.3.0",
2935 "resolved": "https://registry.npmjs.org/got/-/got-8.3.0.tgz",
2936 "integrity": "sha512-kBNy/S2CGwrYgDSec5KTWGKUvupwkkTVAjIsVFF2shXO13xpZdFP4d4kxa//CLX2tN/rV0aYwK8vY6UKWGn2vQ==",
2937 "requires": {
2938 "@sindresorhus/is": "^0.7.0",
2939 "cacheable-request": "^2.1.1",
2940 "decompress-response": "^3.3.0",
2941 "duplexer3": "^0.1.4",
2942 "get-stream": "^3.0.0",
2943 "into-stream": "^3.1.0",
2944 "is-retry-allowed": "^1.1.0",
2945 "isurl": "^1.0.0-alpha5",
2946 "lowercase-keys": "^1.0.0",
2947 "mimic-response": "^1.0.0",
2948 "p-cancelable": "^0.4.0",
2949 "p-timeout": "^2.0.1",
2950 "pify": "^3.0.0",
2951 "safe-buffer": "^5.1.1",
2952 "timed-out": "^4.0.1",
2953 "url-parse-lax": "^3.0.0",
2954 "url-to-options": "^1.0.1"
2955 },
2956 "dependencies": {
2957 "get-stream": {
2958 "version": "3.0.0",
2959 "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
2960 "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
2961 },
2962 "pify": {
2963 "version": "3.0.0",
2964 "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
2965 "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
2966 }
2967 }
2968 },
2833 "graceful-fs": { 2969 "graceful-fs": {
2834 "version": "4.2.2", 2970 "version": "4.2.2",
2835 "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", 2971 "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz",
@@ -2863,12 +2999,25 @@
2863 "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 2999 "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
2864 "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 3000 "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
2865 }, 3001 },
3002 "has-symbol-support-x": {
3003 "version": "1.4.2",
3004 "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz",
3005 "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw=="
3006 },
2866 "has-symbols": { 3007 "has-symbols": {
2867 "version": "1.0.0", 3008 "version": "1.0.0",
2868 "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", 3009 "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
2869 "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", 3010 "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
2870 "dev": true 3011 "dev": true
2871 }, 3012 },
3013 "has-to-string-tag-x": {
3014 "version": "1.4.1",
3015 "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz",
3016 "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==",
3017 "requires": {
3018 "has-symbol-support-x": "^1.4.1"
3019 }
3020 },
2872 "has-unicode": { 3021 "has-unicode": {
2873 "version": "2.0.1", 3022 "version": "2.0.1",
2874 "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", 3023 "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
@@ -2922,6 +3071,11 @@
2922 "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", 3071 "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
2923 "dev": true 3072 "dev": true
2924 }, 3073 },
3074 "http-cache-semantics": {
3075 "version": "3.8.1",
3076 "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz",
3077 "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w=="
3078 },
2925 "http-errors": { 3079 "http-errors": {
2926 "version": "1.7.3", 3080 "version": "1.7.3",
2927 "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", 3081 "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
@@ -3199,6 +3353,15 @@
3199 "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", 3353 "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
3200 "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==" 3354 "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw=="
3201 }, 3355 },
3356 "into-stream": {
3357 "version": "3.1.0",
3358 "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz",
3359 "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=",
3360 "requires": {
3361 "from2": "^2.1.1",
3362 "p-is-promise": "^1.1.0"
3363 }
3364 },
3202 "ipaddr.js": { 3365 "ipaddr.js": {
3203 "version": "1.9.0", 3366 "version": "1.9.0",
3204 "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", 3367 "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
@@ -3309,6 +3472,16 @@
3309 } 3472 }
3310 } 3473 }
3311 }, 3474 },
3475 "is-object": {
3476 "version": "1.0.1",
3477 "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz",
3478 "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA="
3479 },
3480 "is-plain-obj": {
3481 "version": "1.1.0",
3482 "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
3483 "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4="
3484 },
3312 "is-plain-object": { 3485 "is-plain-object": {
3313 "version": "2.0.4", 3486 "version": "2.0.4",
3314 "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", 3487 "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@@ -3340,6 +3513,11 @@
3340 "is-unc-path": "^1.0.0" 3513 "is-unc-path": "^1.0.0"
3341 } 3514 }
3342 }, 3515 },
3516 "is-retry-allowed": {
3517 "version": "1.2.0",
3518 "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz",
3519 "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg=="
3520 },
3343 "is-stream": { 3521 "is-stream": {
3344 "version": "1.1.0", 3522 "version": "1.1.0",
3345 "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 3523 "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
@@ -3398,6 +3576,15 @@
3398 "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 3576 "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
3399 "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 3577 "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
3400 }, 3578 },
3579 "isurl": {
3580 "version": "1.0.0",
3581 "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz",
3582 "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==",
3583 "requires": {
3584 "has-to-string-tag-x": "^1.2.0",
3585 "is-object": "^1.0.1"
3586 }
3587 },
3401 "js-tokens": { 3588 "js-tokens": {
3402 "version": "4.0.0", 3589 "version": "4.0.0",
3403 "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 3590 "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -3419,6 +3606,11 @@
3419 "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 3606 "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
3420 "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" 3607 "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
3421 }, 3608 },
3609 "json-buffer": {
3610 "version": "3.0.0",
3611 "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
3612 "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg="
3613 },
3422 "json-schema": { 3614 "json-schema": {
3423 "version": "0.2.3", 3615 "version": "0.2.3",
3424 "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 3616 "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
@@ -3512,6 +3704,14 @@
3512 "safe-buffer": "^5.0.1" 3704 "safe-buffer": "^5.0.1"
3513 } 3705 }
3514 }, 3706 },
3707 "keyv": {
3708 "version": "3.0.0",
3709 "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz",
3710 "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==",
3711 "requires": {
3712 "json-buffer": "3.0.0"
3713 }
3714 },
3515 "kind-of": { 3715 "kind-of": {
3516 "version": "6.0.2", 3716 "version": "6.0.2",
3517 "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", 3717 "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
@@ -3737,6 +3937,11 @@
3737 "js-tokens": "^3.0.0 || ^4.0.0" 3937 "js-tokens": "^3.0.0 || ^4.0.0"
3738 } 3938 }
3739 }, 3939 },
3940 "lowercase-keys": {
3941 "version": "1.0.1",
3942 "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
3943 "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="
3944 },
3740 "lru-cache": { 3945 "lru-cache": {
3741 "version": "4.1.5", 3946 "version": "4.1.5",
3742 "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", 3947 "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
@@ -3871,6 +4076,11 @@
3871 "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", 4076 "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
3872 "dev": true 4077 "dev": true
3873 }, 4078 },
4079 "mimic-response": {
4080 "version": "1.0.1",
4081 "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
4082 "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
4083 },
3874 "minimatch": { 4084 "minimatch": {
3875 "version": "3.0.4", 4085 "version": "3.0.4",
3876 "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 4086 "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -4166,6 +4376,11 @@
4166 "vary": "^1.1.2" 4376 "vary": "^1.1.2"
4167 } 4377 }
4168 }, 4378 },
4379 "nodemailer": {
4380 "version": "4.7.0",
4381 "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.7.0.tgz",
4382 "integrity": "sha512-IludxDypFpYw4xpzKdMAozBSkzKHmNBvGanUREjJItgJ2NYcK/s8+PggVhj7c2yGFQykKsnnmv1+Aqo0ZfjHmw=="
4383 },
4169 "nopt": { 4384 "nopt": {
4170 "version": "4.0.1", 4385 "version": "4.0.1",
4171 "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", 4386 "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
@@ -4187,6 +4402,16 @@
4187 "validate-npm-package-license": "^3.0.1" 4402 "validate-npm-package-license": "^3.0.1"
4188 } 4403 }
4189 }, 4404 },
4405 "normalize-url": {
4406 "version": "2.0.1",
4407 "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz",
4408 "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==",
4409 "requires": {
4410 "prepend-http": "^2.0.0",
4411 "query-string": "^5.0.1",
4412 "sort-keys": "^2.0.0"
4413 }
4414 },
4190 "npm-bundled": { 4415 "npm-bundled": {
4191 "version": "1.0.6", 4416 "version": "1.0.6",
4192 "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", 4417 "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz",
@@ -4690,6 +4915,21 @@
4690 "os-tmpdir": "^1.0.0" 4915 "os-tmpdir": "^1.0.0"
4691 } 4916 }
4692 }, 4917 },
4918 "p-cancelable": {
4919 "version": "0.4.1",
4920 "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz",
4921 "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ=="
4922 },
4923 "p-finally": {
4924 "version": "1.0.0",
4925 "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
4926 "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
4927 },
4928 "p-is-promise": {
4929 "version": "1.1.0",
4930 "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz",
4931 "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4="
4932 },
4693 "p-limit": { 4933 "p-limit": {
4694 "version": "1.3.0", 4934 "version": "1.3.0",
4695 "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", 4935 "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
@@ -4708,6 +4948,14 @@
4708 "p-limit": "^1.1.0" 4948 "p-limit": "^1.1.0"
4709 } 4949 }
4710 }, 4950 },
4951 "p-timeout": {
4952 "version": "2.0.1",
4953 "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz",
4954 "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==",
4955 "requires": {
4956 "p-finally": "^1.0.0"
4957 }
4958 },
4711 "p-try": { 4959 "p-try": {
4712 "version": "1.0.0", 4960 "version": "1.0.0",
4713 "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", 4961 "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
@@ -4961,6 +5209,11 @@
4961 "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 5209 "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
4962 "dev": true 5210 "dev": true
4963 }, 5211 },
5212 "prepend-http": {
5213 "version": "2.0.0",
5214 "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
5215 "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc="
5216 },
4964 "pretty-hrtime": { 5217 "pretty-hrtime": {
4965 "version": "1.0.3", 5218 "version": "1.0.3",
4966 "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", 5219 "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
@@ -5375,6 +5628,16 @@
5375 "resolved": "https://registry.npmjs.org/qs/-/qs-6.8.0.tgz", 5628 "resolved": "https://registry.npmjs.org/qs/-/qs-6.8.0.tgz",
5376 "integrity": "sha512-tPSkj8y92PfZVbinY1n84i1Qdx75lZjMQYx9WZhnkofyxzw2r7Ho39G3/aEvSUdebxpnnM4LZJCtvE/Aq3+s9w==" 5629 "integrity": "sha512-tPSkj8y92PfZVbinY1n84i1Qdx75lZjMQYx9WZhnkofyxzw2r7Ho39G3/aEvSUdebxpnnM4LZJCtvE/Aq3+s9w=="
5377 }, 5630 },
5631 "query-string": {
5632 "version": "5.1.1",
5633 "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
5634 "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
5635 "requires": {
5636 "decode-uri-component": "^0.2.0",
5637 "object-assign": "^4.1.0",
5638 "strict-uri-encode": "^1.0.0"
5639 }
5640 },
5378 "radio-symbol": { 5641 "radio-symbol": {
5379 "version": "2.0.0", 5642 "version": "2.0.0",
5380 "resolved": "https://registry.npmjs.org/radio-symbol/-/radio-symbol-2.0.0.tgz", 5643 "resolved": "https://registry.npmjs.org/radio-symbol/-/radio-symbol-2.0.0.tgz",
@@ -5385,6 +5648,11 @@
5385 "is-windows": "^1.0.1" 5648 "is-windows": "^1.0.1"
5386 } 5649 }
5387 }, 5650 },
5651 "rand-token": {
5652 "version": "0.4.0",
5653 "resolved": "https://registry.npmjs.org/rand-token/-/rand-token-0.4.0.tgz",
5654 "integrity": "sha512-FLNNsir2R+XY8LKsZ+8u/w0qZ4sGit7cpNdznsI77cAVob6UlVPueDKRyjJ3W1Q6FJLgAVH98JvlqqpSaL7NEQ=="
5655 },
5388 "random-bytes": { 5656 "random-bytes": {
5389 "version": "1.0.0", 5657 "version": "1.0.0",
5390 "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", 5658 "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
@@ -5736,6 +6004,14 @@
5736 "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", 6004 "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
5737 "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" 6005 "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
5738 }, 6006 },
6007 "responselike": {
6008 "version": "1.0.2",
6009 "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
6010 "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=",
6011 "requires": {
6012 "lowercase-keys": "^1.0.0"
6013 }
6014 },
5739 "restore-cursor": { 6015 "restore-cursor": {
5740 "version": "2.0.0", 6016 "version": "2.0.0",
5741 "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", 6017 "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
@@ -6192,6 +6468,14 @@
6192 } 6468 }
6193 } 6469 }
6194 }, 6470 },
6471 "sort-keys": {
6472 "version": "2.0.0",
6473 "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz",
6474 "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=",
6475 "requires": {
6476 "is-plain-obj": "^1.0.0"
6477 }
6478 },
6195 "source-map": { 6479 "source-map": {
6196 "version": "0.5.7", 6480 "version": "0.5.7",
6197 "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 6481 "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@@ -6400,6 +6684,11 @@
6400 "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 6684 "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
6401 "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 6685 "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
6402 }, 6686 },
6687 "strict-uri-encode": {
6688 "version": "1.1.0",
6689 "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
6690 "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
6691 },
6403 "string-width": { 6692 "string-width": {
6404 "version": "2.1.1", 6693 "version": "2.1.1",
6405 "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 6694 "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
@@ -6923,6 +7212,11 @@
6923 "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", 7212 "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz",
6924 "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=" 7213 "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM="
6925 }, 7214 },
7215 "timed-out": {
7216 "version": "4.0.1",
7217 "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
7218 "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
7219 },
6926 "tmp": { 7220 "tmp": {
6927 "version": "0.0.33", 7221 "version": "0.0.33",
6928 "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", 7222 "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@@ -7186,6 +7480,19 @@
7186 "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", 7480 "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
7187 "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" 7481 "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI="
7188 }, 7482 },
7483 "url-parse-lax": {
7484 "version": "3.0.0",
7485 "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
7486 "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=",
7487 "requires": {
7488 "prepend-http": "^2.0.0"
7489 }
7490 },
7491 "url-to-options": {
7492 "version": "1.0.1",
7493 "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz",
7494 "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k="
7495 },
7189 "use": { 7496 "use": {
7190 "version": "3.1.1", 7497 "version": "3.1.1",
7191 "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", 7498 "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
diff --git a/package.json b/package.json
index ece349d..43e0ac0 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,8 @@
26 "@adonisjs/framework": "^5.0.9", 26 "@adonisjs/framework": "^5.0.9",
27 "@adonisjs/ignitor": "^2.0.8", 27 "@adonisjs/ignitor": "^2.0.8",
28 "@adonisjs/lucid": "^6.1.3", 28 "@adonisjs/lucid": "^6.1.3",
29 "@adonisjs/mail": "^3.0.10",
30 "@adonisjs/persona": "^1.0.5",
29 "@adonisjs/session": "^1.0.29", 31 "@adonisjs/session": "^1.0.29",
30 "@adonisjs/shield": "^1.0.8", 32 "@adonisjs/shield": "^1.0.8",
31 "@adonisjs/validator": "^5.0.6", 33 "@adonisjs/validator": "^5.0.6",
diff --git a/resources/views/dashboard/forgotPassword.edge b/resources/views/dashboard/forgotPassword.edge
new file mode 100644
index 0000000..755c300
--- /dev/null
+++ b/resources/views/dashboard/forgotPassword.edge
@@ -0,0 +1,49 @@
1@layout('layouts.v2')
2
3@section('content')
4<div class="w-screen h-screen bg-purple-200 flex items-center justify-center">
5<form class="w-full max-w-sm bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4"
6 action="/user/forgot" method="POST">
7 <h1 class="text-gray-700 text-center text-2xl pb-5">
8 Forgot Password?
9 </h1>
10
11 @if(flashMessage('error'))
12 <div class="bg-orange-100 border-l-4 border-orange-500 text-orange-700 p-4">
13 {{ flashMessage('error') }}
14 </div>
15 @endif
16 @if(old('message'))
17 <div class="bg-orange-100 border-l-4 border-orange-500 text-orange-700 p-4">
18 {{ old('message') }}
19 </div>
20 @endif
21
22 {{ csrfField() }}
23 <div class="mb-4">
24 <label class="block text-gray-700 text-sm font-bold mb-2" for="username">
25 E-Mail
26 </label>
27 <input
28 class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
29 id="mail"
30 name="mail"
31 type="text"
32 placeholder="E-Mail"
33 value="{{ old('mail', '') }}"
34 required>
35 </div>
36
37 <button
38 class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
39 type="submit">
40 Reset password
41 </button>
42 <div class="text-center">
43 <a class="w-full font-bold text-sm text-blue-500 hover:text-blue-800" href="/user/login">
44 Login instead
45 </a>
46 </div>
47</form>
48</div>
49@endsection
diff --git a/resources/views/dashboard/login.edge b/resources/views/dashboard/login.edge
index 9b4b52a..dce7360 100644
--- a/resources/views/dashboard/login.edge
+++ b/resources/views/dashboard/login.edge
@@ -55,6 +55,11 @@
55 By logging in, you accept the <a href="/terms">Terms of Service</a> and <a href="/privacy">Privacy policy</a>. This website uses cookies to log you in. 55 By logging in, you accept the <a href="/terms">Terms of Service</a> and <a href="/privacy">Privacy policy</a>. This website uses cookies to log you in.
56 </p> 56 </p>
57 <div class="text-center"> 57 <div class="text-center">
58 <a class="w-full font-bold text-sm text-blue-500 hover:text-blue-800" href="/user/forgot">
59 Forgot my Password
60 </a>
61 </div>
62 <div class="text-center">
58 <a class="w-full font-bold text-sm text-blue-500 hover:text-blue-800" href="../import"> 63 <a class="w-full font-bold text-sm text-blue-500 hover:text-blue-800" href="../import">
59 Import your Franz account 64 Import your Franz account
60 </a> 65 </a>
diff --git a/resources/views/dashboard/resetPassword.edge b/resources/views/dashboard/resetPassword.edge
new file mode 100644
index 0000000..d663020
--- /dev/null
+++ b/resources/views/dashboard/resetPassword.edge
@@ -0,0 +1,64 @@
1@layout('layouts.v2')
2
3@section('content')
4<div class="w-screen h-screen bg-purple-200 flex items-center justify-center">
5<form class="w-full max-w-sm bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4"
6 action="/user/reset" method="POST">
7 <h1 class="text-gray-700 text-center text-2xl pb-5">
8 Reset Your Password
9 </h1>
10
11 @if(flashMessage('error'))
12 <div class="bg-orange-100 border-l-4 border-orange-500 text-orange-700 p-4">
13 {{ flashMessage('error') }}
14 </div>
15 @endif
16 @if(old('message'))
17 <div class="bg-orange-100 border-l-4 border-orange-500 text-orange-700 p-4">
18 {{ old('message') }}
19 </div>
20 @endif
21
22 {{ csrfField() }}
23 <div class="mb-4">
24 <label class="block text-gray-700 text-sm font-bold mb-2" for="password">
25 New Password
26 </label>
27 <input
28 class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
29 id="password"
30 name="password"
31 type="password"
32 placeholder="New Password"
33 value="{{ old('password', '') }}"
34 required>
35 </div>
36 <div class="mb-4">
37 <label class="block text-gray-700 text-sm font-bold mb-2" for="password_confirmation">
38 Confirm Password
39 </label>
40 <input
41 class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
42 id="password_confirmation"
43 name="password_confirmation"
44 type="password"
45 placeholder="Confirm Password"
46 value="{{ old('password_confirmation', '') }}"
47 required>
48 </div>
49
50 <input type="hidden" name="token" value="{{ token }}">
51
52 <button
53 class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
54 type="submit">
55 Reset password
56 </button>
57 <div class="text-center">
58 <a class="w-full font-bold text-sm text-blue-500 hover:text-blue-800" href="/user/login">
59 Login instead
60 </a>
61 </div>
62</form>
63</div>
64@endsection
diff --git a/start/app.js b/start/app.js
index d0986fe..62a5ac3 100644
--- a/start/app.js
+++ b/start/app.js
@@ -20,6 +20,8 @@ const providers = [
20 '@adonisjs/framework/providers/ViewProvider', 20 '@adonisjs/framework/providers/ViewProvider',
21 '@adonisjs/session/providers/SessionProvider', 21 '@adonisjs/session/providers/SessionProvider',
22 '@adonisjs/shield/providers/ShieldProvider', 22 '@adonisjs/shield/providers/ShieldProvider',
23 '@adonisjs/persona/providers/PersonaProvider',
24 '@adonisjs/mail/providers/MailProvider',
23]; 25];
24 26
25/* 27/*
diff --git a/start/events.js b/start/events.js
new file mode 100644
index 0000000..0b7e28a
--- /dev/null
+++ b/start/events.js
@@ -0,0 +1,23 @@
1const Event = use('Event');
2const Mail = use('Mail');
3const Env = use('Env');
4
5Event.on('forgot::password', async ({ user, token }) => {
6 const body = `
7Hello ${user.username},
8we just recieved a request to reset your password of your Ferdi account.
9Use the link below to reset your password. If you havn't requested this, please ignore this message.
10
11${Env.get('APP_URL')}/user/reset?token=${encodeURIComponent(token)}
12
13This message was sent automatically. Please do not reply.
14`;
15console.log('Sending message', body);
16 try {
17 await Mail.raw(body, (message) => {
18 message.subject('[Ferdi] Reset your password')
19 message.from('noreply@getferdi.com')
20 message.to(user.email)
21 });
22 } catch(e) {}
23}); \ No newline at end of file
diff --git a/start/routes.js b/start/routes.js
index b96fa16..1c2d4f3 100644
--- a/start/routes.js
+++ b/start/routes.js
@@ -65,16 +65,37 @@ if (Env.get('IS_DASHBOARD_ENABLED') != 'false') {
65 // Auth 65 // Auth
66 Route.get('login', ({ view }) => view.render('dashboard.login')).middleware('guest'); 66 Route.get('login', ({ view }) => view.render('dashboard.login')).middleware('guest');
67 Route.post('login', 'DashboardController.login').middleware('guest').as('login'); 67 Route.post('login', 'DashboardController.login').middleware('guest').as('login');
68 68
69 // Reset password
70 Route.get('forgot', ({ view }) => view.render('dashboard.forgotPassword')).middleware('guest');
71 Route.post('forgot', 'DashboardController.forgotPassword').middleware('guest');
72
73 Route.get('reset', ({ view, request }) => {
74 const token = request.get().token;
75 if (token) {
76 return view.render('dashboard.resetPassword', { token })
77 } else {
78 return view.render('others.message', {
79 heading: 'Invalid token',
80 text: 'Please make sure you are using a valid and recent link to reset your password.',
81 });
82 }
83 }).middleware('guest');
84 Route.post('reset', 'DashboardController.resetPassword').middleware('guest');
85
69 // Dashboard 86 // Dashboard
70 Route.get('account', 'DashboardController.account').middleware('auth:session'); 87 Route.get('account', 'DashboardController.account').middleware('auth:session');
71 Route.post('account', 'DashboardController.edit').middleware('auth:session'); 88 Route.post('account', 'DashboardController.edit').middleware('auth:session');
89
72 Route.get('data', 'DashboardController.data').middleware('auth:session'); 90 Route.get('data', 'DashboardController.data').middleware('auth:session');
91
73 Route.get('export', 'DashboardController.export').middleware('auth:session'); 92 Route.get('export', 'DashboardController.export').middleware('auth:session');
74 Route.post('transfer', 'DashboardController.import').middleware('auth:session'); 93 Route.post('transfer', 'DashboardController.import').middleware('auth:session');
75 Route.get('transfer', ({ view }) => view.render('dashboard.transfer')).middleware('auth:session'); 94 Route.get('transfer', ({ view }) => view.render('dashboard.transfer')).middleware('auth:session');
95
76 Route.get('delete', ({ view }) => view.render('dashboard.delete')).middleware('auth:session'); 96 Route.get('delete', ({ view }) => view.render('dashboard.delete')).middleware('auth:session');
77 Route.post('delete', 'DashboardController.delete').middleware('auth:session'); 97 Route.post('delete', 'DashboardController.delete').middleware('auth:session');
98
78 Route.get('logout', 'DashboardController.logout').middleware('auth:session'); 99 Route.get('logout', 'DashboardController.logout').middleware('auth:session');
79 100
80 Route.get('*', ({ response }) => response.redirect('/user/account')); 101 Route.get('*', ({ response }) => response.redirect('/user/account'));