From 7972a3fec507c5edc49cdcbe5f3067949806af98 Mon Sep 17 00:00:00 2001 From: Sagi Dayan Date: Sun, 17 May 2020 20:39:53 -0400 Subject: [PATCH] [REDIS needed] - Implemented reset password --- app/Controllers/Http/AuthController.js | 62 ++++- app/Utils/EmailUtils.js | 5 +- app/Utils/Redis/ExpiredToken.js | 57 +++++ app/Utils/RedisUtils.js | 2 + app/Validators/ResetPassword.js | 5 +- app/Validators/ResetPasswordRequest.js | 16 ++ config/redis.js | 56 +++++ package-lock.json | 156 +++++++++++++ package.json | 1 + .../views/emails/reset-password-text.edge | 13 ++ resources/views/layouts/landing.edge | 221 +++++++++++------- resources/views/reset-password-request.edge | 59 +++++ resources/views/reset-password.edge | 53 ++++- start/app.js | 1 + start/redis.js | 24 ++ start/routes.js | 7 +- 16 files changed, 629 insertions(+), 109 deletions(-) create mode 100644 app/Utils/Redis/ExpiredToken.js create mode 100644 app/Utils/RedisUtils.js create mode 100644 app/Validators/ResetPasswordRequest.js create mode 100644 config/redis.js create mode 100644 resources/views/emails/reset-password-text.edge create mode 100644 resources/views/reset-password-request.edge create mode 100644 start/redis.js diff --git a/app/Controllers/Http/AuthController.js b/app/Controllers/Http/AuthController.js index a821472..48acf2b 100644 --- a/app/Controllers/Http/AuthController.js +++ b/app/Controllers/Http/AuthController.js @@ -1,6 +1,7 @@ 'use strict' const User = use('App/Models/User'); const EmailUtils = use('App/Utils/EmailUtils'); +const RedisUtils = use('App/Utils/RedisUtils'); class AuthController { async registerIndex({view, auth, response}) { if (auth.user) { @@ -38,7 +39,6 @@ class AuthController { async login({request, response, auth, session}) { console.log('login'); const {email, password} = request.all() - console.log({email, password}) try { const token = await auth.attempt(email, password); const user = auth.user; @@ -55,26 +55,72 @@ class AuthController { response.redirect('/'); } - async resetPassword({request, response, session}) { + async resetPasswordRequest({request, response, session}) { const email = request.body.email; - const token = 'token'; // TODO: Token system + // check if user exists + const queryResp = (await User.query().where({email}).fetch()).rows; + if (!queryResp.length) { + session.withErrors({userNotFound: 'No User with that email'}).flashAll(); + return response.redirect('back'); + } + const user = queryResp[0]; + // + const tokenPayload = {id: user.id, email: user.email}; + const token = + await RedisUtils.ExpiredToken.generateResetPasswordToken(tokenPayload); const sent = - await EmailUtils.sendResetPassword({name: 'test name', email}, token); + await EmailUtils.sendResetPassword({name: user.name, email}, token); if (sent) { + session.flash({notification: `Email sent to ${email} !`}); response.redirect('/'); return; } - session.withErrors({message: 'Email provider error'}).flashAll() + session.withErrors({message: 'Email provider error'}).flashAll(); return response.redirect('back') } - async resetPasswordIndex({request, auth, response, view}) { + async resetPasswordRequestIndex({request, auth, response, view}) { if (auth.user) { response.redirect('/'); return; } - return view.render('reset-password'); + return view.render('reset-password-request'); + } + async resetPasswordIndex({request, response, session, view}) { + const token = request.params.token; + const tokenPayload = await RedisUtils.ExpiredToken.getTokenPayload(token); + if (!tokenPayload) { + session.withErrors({InvalidToken: 'Token Expired or Invalid...'}) + .flashAll(); + response.redirect('/'); + return; + } else { + return view.render('reset-password', {token}); + } + } + + async resetPassword({request, view, session, response}) { + console.log('In function...'); + const {token, password, confirm} = request.all(); + if (password != confirm) { + session.withErrors({confirm: 'Please make sure passwords match'}) + .flashAll(); + return response.redirect('back'); + } + const tokenPayload = await RedisUtils.ExpiredToken.getTokenPayload(token); + if (!tokenPayload) { + session.withErrors({InvalidToken: 'Token Expired or Invalid...'}) + .flashAll(); + response.redirect('/'); + return; + } + const user = await User.find(tokenPayload.id); + user.password = password; + await user.save(); + /// TODO: send an email to notify user about the change; + await RedisUtils.ExpiredToken.remove(token); + session.flash({notification: `Password changed successfully`}); + return response.redirect('/'); } - async resetPasswordForm({request}) {} async logout({auth, response}) { await auth.logout(); diff --git a/app/Utils/EmailUtils.js b/app/Utils/EmailUtils.js index b8ccf38..2b35b47 100644 --- a/app/Utils/EmailUtils.js +++ b/app/Utils/EmailUtils.js @@ -27,13 +27,14 @@ class EmailUtils { if (!emailEnabled) return true; const to = user.email; const link = { - href: `${appUrl}/password/reset/${code}`, + href: `${baseUrl}/password/reset/${code}`, text: 'Reset your password' }; Logger.info(`Sending test email to ${user.email}`); try { await Mail.send( - 'emails.reset-password', {user, code, link, baseUrl}, (message) => { + ['emails.reset-password', 'emails.reset-password-text'], + {user, code, link, baseUrl}, (message) => { message.from(from).to(to).subject('Reset Password'); }); return true; diff --git a/app/Utils/Redis/ExpiredToken.js b/app/Utils/Redis/ExpiredToken.js new file mode 100644 index 0000000..62a24ef --- /dev/null +++ b/app/Utils/Redis/ExpiredToken.js @@ -0,0 +1,57 @@ + +const Redis = use('Redis'); +const Logger = use('Logger'); +const {v4: uuidv4} = require('uuid'); + + +const TOKEN_NAMESPACE = 'expired_token'; +const TokenTypes = { + RP: 'rp', +}; +class ExpiredToken { + /** + * + * @param {{id:number; email:string}} payload + * @param {number} ttl in seconds default 86400 (24 hours) + */ + static async generateResetPasswordToken(payload, ttl = 86400) { + try { + Logger.debug(`Generating RP token`); + const token = `${TokenTypes.RP}_${uuidv4()}`; + const rdKey = `${TOKEN_NAMESPACE}:${token}`; + await Redis.set(rdKey, JSON.stringify(payload)); + await Redis.expire(rdKey, ttl); + return token; + } catch (e) { + Logger.error(`Failed to generate RP token. Error: ${e.message}`); + return null; + } + } + + /** + * + * @param {string} token + */ + static async getTokenPayload(token) { + try { + const response = await Redis.get(`${TOKEN_NAMESPACE}:${token}`); + if (response) return JSON.parse(response); + return null; + } catch (e) { + Logger.error(`Failed to generate RP token. Error: ${e.message}`); + return null; + } + } + + static async remove(token) { + try { + await Redis.del(`${TOKEN_NAMESPACE}:${token}`); + return true; + } catch (e) { + Logger.error(`Failed to generate RP token. Error: ${e.message}`); + return false; + } + } +} + +module.exports = ExpiredToken; diff --git a/app/Utils/RedisUtils.js b/app/Utils/RedisUtils.js new file mode 100644 index 0000000..8833e62 --- /dev/null +++ b/app/Utils/RedisUtils.js @@ -0,0 +1,2 @@ +const ExpiredToken = require('./Redis/ExpiredToken'); +module.exports = {ExpiredToken}; diff --git a/app/Validators/ResetPassword.js b/app/Validators/ResetPassword.js index 877246f..7e3adad 100644 --- a/app/Validators/ResetPassword.js +++ b/app/Validators/ResetPassword.js @@ -3,12 +3,13 @@ class ResetPassword { get rules() { return { - email: 'required|email' + password: 'required|string|min:6', 'confirm': 'required|string|min:6', } } get messages() { return { - 'exists': 'User does not exists', 'required': 'This is required', + 'required': 'This is required', + 'min': 'Password must be at least 6 chars long', } } } diff --git a/app/Validators/ResetPasswordRequest.js b/app/Validators/ResetPasswordRequest.js new file mode 100644 index 0000000..d6d6bd8 --- /dev/null +++ b/app/Validators/ResetPasswordRequest.js @@ -0,0 +1,16 @@ +'use strict' + +class ResetPasswordRequest { + get rules() { + return { + email: 'required|email' + } + } + get messages() { + return { + 'exists': 'User does not exists', 'required': 'This is required', + } + } +} + +module.exports = ResetPasswordRequest diff --git a/config/redis.js b/config/redis.js new file mode 100644 index 0000000..0749047 --- /dev/null +++ b/config/redis.js @@ -0,0 +1,56 @@ +'use strict' + +/* +|-------------------------------------------------------------------------- +| Redis Configuaration +|-------------------------------------------------------------------------- +| +| Here we define the configuration for redis server. A single application +| can make use of multiple redis connections using the redis provider. +| +*/ + +const Env = use('Env') + +module.exports = { + /* + |-------------------------------------------------------------------------- + | connection + |-------------------------------------------------------------------------- + | + | Redis connection to be used by default. + | + */ + connection: Env.get('REDIS_CONNECTION', 'local'), + + /* + |-------------------------------------------------------------------------- + | local connection config + |-------------------------------------------------------------------------- + | + | Configuration for a named connection. + | + */ + local: { + host: Env.get('REDIS_CONNECTION', '127.0.0.1'), + port: Number(Env.get('REDIS_PORT', 6379)), + password: Env.get('REDIS_PASSWORD', null), + db: Number(Env.get('REDIS_DB', 0)), + keyPrefix: '' + }, + + /* + |-------------------------------------------------------------------------- + | cluster config + |-------------------------------------------------------------------------- + | + | Below is the configuration for the redis cluster. + | + */ + cluster: { + clusters: [ + {host: '127.0.0.1', port: 6379, password: null, db: 0}, + {host: '127.0.0.1', port: 6380, password: null, db: 0} + ] + } +} diff --git a/package-lock.json b/package-lock.json index f402880..2ff56a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -352,6 +352,27 @@ } } }, + "@adonisjs/redis": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@adonisjs/redis/-/redis-2.0.7.tgz", + "integrity": "sha512-2wnZKZU/pB2HiwQP2HW/68t5XdB7aRvyFLMVDKPdPPmcbZcVXZH19RWkRs91eTnYzsC14PzUf471FsUkT93lBg==", + "requires": { + "@adonisjs/generic-exceptions": "^2.0.1", + "debug": "^3.1.0", + "ioredis": "^3.2.2", + "lodash": "^4.17.10" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, "@adonisjs/session": { "version": "1.0.29", "resolved": "https://registry.npmjs.org/@adonisjs/session/-/session-1.0.29.tgz", @@ -2998,6 +3019,11 @@ "mimic-response": "^1.0.0" } }, + "cluster-key-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", + "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" + }, "co-body": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/co-body/-/co-body-6.0.0.tgz", @@ -3489,6 +3515,11 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, + "denque": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", + "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -4069,6 +4100,11 @@ "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==" }, + "flexbuffer": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/flexbuffer/-/flexbuffer-0.0.6.tgz", + "integrity": "sha1-A5/fI/iCPkQMOPMnfm/vEXQhWzA=" + }, "flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", @@ -4732,6 +4768,36 @@ "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", "dev": true }, + "ioredis": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-3.2.2.tgz", + "integrity": "sha512-g+ShTQYLsCcOUkNOK6CCEZbj3aRDVPw3WOwXk+LxlUKvuS9ujEqP2MppBHyRVYrNNFW/vcPaTBUZ2ctGNSiOCA==", + "requires": { + "bluebird": "^3.3.4", + "cluster-key-slot": "^1.0.6", + "debug": "^2.6.9", + "denque": "^1.1.0", + "flexbuffer": "0.0.6", + "lodash.assign": "^4.2.0", + "lodash.bind": "^4.2.1", + "lodash.clone": "^4.5.0", + "lodash.clonedeep": "^4.5.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.foreach": "^4.5.0", + "lodash.isempty": "^4.4.0", + "lodash.keys": "^4.2.0", + "lodash.noop": "^3.0.1", + "lodash.partial": "^4.2.1", + "lodash.pick": "^4.4.0", + "lodash.sample": "^4.2.1", + "lodash.shuffle": "^4.2.0", + "lodash.values": "^4.3.0", + "redis-commands": "^1.2.0", + "redis-parser": "^2.4.0" + } + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -5324,6 +5390,46 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" + }, + "lodash.bind": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", + "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" + }, + "lodash.clone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", + "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=" + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, + "lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" + }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -5334,6 +5440,11 @@ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" }, + "lodash.isempty": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4=" + }, "lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -5354,16 +5465,51 @@ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" }, + "lodash.keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz", + "integrity": "sha1-oIYCrBLk+4P5H8H7ejYKTZujUgU=" + }, + "lodash.noop": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-3.0.1.tgz", + "integrity": "sha1-OBiPTWUKOkdCWEObluxFsyYXEzw=" + }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, + "lodash.partial": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.partial/-/lodash.partial-4.2.1.tgz", + "integrity": "sha1-SfPYz9qjv/izqR0SfpIyRUGJYdQ=" + }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" + }, + "lodash.sample": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.sample/-/lodash.sample-4.2.1.tgz", + "integrity": "sha1-XkKRsMdT+hq+sKq4+ynfG2bwf20=" + }, + "lodash.shuffle": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.shuffle/-/lodash.shuffle-4.2.0.tgz", + "integrity": "sha1-FFtQU8+HX29cKjP0i26ZSMbse0s=" + }, "lodash.toarray": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=" }, + "lodash.values": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-4.3.0.tgz", + "integrity": "sha1-o6bCsOvsxcLLocF+bmIP6BtT00c=" + }, "log-ok": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/log-ok/-/log-ok-0.1.1.tgz", @@ -7547,6 +7693,16 @@ } } }, + "redis-commands": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz", + "integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg==" + }, + "redis-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", + "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=" + }, "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", diff --git a/package.json b/package.json index 7e522c7..296c4c5 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@adonisjs/ignitor": "^2.0.8", "@adonisjs/lucid": "^6.1.3", "@adonisjs/mail": "^3.0.10", + "@adonisjs/redis": "^2.0.7", "@adonisjs/session": "^1.0.27", "@adonisjs/shield": "^1.0.8", "@adonisjs/validator": "^5.0.6", diff --git a/resources/views/emails/reset-password-text.edge b/resources/views/emails/reset-password-text.edge new file mode 100644 index 0000000..b02f100 --- /dev/null +++ b/resources/views/emails/reset-password-text.edge @@ -0,0 +1,13 @@ +Hi {{user.name}}, +The following URL will allow you to reset your password. +If you have not requested the password reset, then ignore this email. + +Navigate to: +{{link.href}} + + +Do note that this link will expire within 24 hours. + + +Have a great time, +Team Seepur diff --git a/resources/views/layouts/landing.edge b/resources/views/layouts/landing.edge index 2691a42..6688a38 100644 --- a/resources/views/layouts/landing.edge +++ b/resources/views/layouts/landing.edge @@ -1,99 +1,146 @@ - - - - - @include('partials.SEO.meta') - + + + + + + @include('partials.SEO.meta') + - @!section('page-title') + @!section('page-title') {{ style('style') }} {{ script('scripts/views/register/app.bundle.js') }} @!section('scripts') + - - @!section('nav') - - @!section('hero') -
-
- @!section('content') -
- -
-
-
-
Join Now
-
-
It takes less then a minute. You could have been done by now, but you are still reading...
-
- {{ csrfField() }} - -
-
- -
-
- -
- -
- -
-
+ @if(flashMessage('notification')) +
+
+ + {{ flashMessage('notification') }}
- @include('partials.footer') +
+ + @endif + @if(hasErrorFor('InvalidToken')) +
+
+ + {{ getErrorFor('InvalidToken') }} +
+
+ + @endif + + @!section('nav') + + @!section('hero') +
+
+ @!section('content') +
+ +
+
+
+
Join Now
+
+
It takes less then a minute. You could have been done by now, but you are still reading... +
+
+ {{ csrfField() }} + +
+
+ +
+
+ +
+ +
+ +
+
+
+ @include('partials.footer') + diff --git a/resources/views/reset-password-request.edge b/resources/views/reset-password-request.edge new file mode 100644 index 0000000..bb53c01 --- /dev/null +++ b/resources/views/reset-password-request.edge @@ -0,0 +1,59 @@ +@layout('layouts.main') + +@section('page-title') +Seepur| Reset Password +@endsection + +@section('content') +

+ Don't Pannic! +

+ +

+ We'v got you +

+ +@if(hasErrorFor('userNotFound')) +
+ + {{getErrorFor('userNotFound')}} +
+ +@endif + +
+ {{ csrfField() }} +
+ +
+ + + + + +
+

{{ getErrorFor('email') ? getErrorFor('email') : '' }}

+
+ +
+
+ +
+
+ Cancel +
+
+
+ +@endsection diff --git a/resources/views/reset-password.edge b/resources/views/reset-password.edge index 32dcf64..5ff0ff6 100644 --- a/resources/views/reset-password.edge +++ b/resources/views/reset-password.edge @@ -6,30 +6,67 @@ Seepur| Reset Password @section('content')

- Don't Pannic! + Reset Password

- We'v got you + Tip: keep your password safe

+@if(hasErrorFor('userNotFound')) +
+ + {{getErrorFor('userNotFound')}} +
+ +@endif +
{{ csrfField() }}
- +
- + - + -
-

{{ getErrorFor('email') ? getErrorFor('email') : '' }}

+

{{ getErrorFor('password') ? getErrorFor('password') : '' }}

+
+ +
+ +
+ + + + +
+

{{ getErrorFor('confirm') ? getErrorFor('confirm') : '' }}

+
+ + {{-- Hidden token --}} +
- +
Cancel diff --git a/start/app.js b/start/app.js index f27ecaa..a1e8d28 100644 --- a/start/app.js +++ b/start/app.js @@ -24,6 +24,7 @@ const providers = '@adonisjs/drive/providers/DriveProvider', '@adonisjs/websocket/providers/WsProvider', '@adonisjs/mail/providers/MailProvider', + '@adonisjs/redis/providers/RedisProvider', ] /* diff --git a/start/redis.js b/start/redis.js new file mode 100644 index 0000000..98fae1f --- /dev/null +++ b/start/redis.js @@ -0,0 +1,24 @@ +'use strict' + +/* +|-------------------------------------------------------------------------- +| Redis Subscribers +|-------------------------------------------------------------------------- +| +| Here you can register the subscribers to redis channels. Adonis assumes +| your listeners are stored inside `app/Listeners` directory. +| +*/ + +// const Redis = use('Redis') + +/** + * Inline subscriber + */ +// Redis.subscribe('news', async () => { +// }) + +/** + * Binding method from a module saved inside `app/Listeners/News` + */ +// Redis.subcribe('news', 'News.onMessage') diff --git a/start/routes.js b/start/routes.js index 863c7ba..f5889ed 100644 --- a/start/routes.js +++ b/start/routes.js @@ -27,11 +27,14 @@ Route.post('/register', 'AuthController.register').validator('Register'); Route.post('/login', 'AuthController.login').validator('Login'); // reset-password +Route.post('/password/request/reset', 'AuthController.resetPasswordRequest') + .validator('ResetPasswordRequest') + .as('resetPasswordRequest'); +Route.get('/password/reset', 'AuthController.resetPasswordRequestIndex'); +Route.get('/password/reset/:token', 'AuthController.resetPasswordIndex'); Route.post('/password/reset', 'AuthController.resetPassword') .validator('ResetPassword') .as('resetPassword'); -Route.get('/password/reset', 'AuthController.resetPasswordIndex'); -Route.get('/password/reset/:token', 'AuthController.resetPasswordForm'); /* / Client API