[REDIS needed] - Implemented reset password

This commit is contained in:
Sagi Dayan 2020-05-17 20:39:53 -04:00
parent 1c857559f8
commit 7972a3fec5
16 changed files with 629 additions and 109 deletions

View file

@ -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();

View file

@ -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;

View file

@ -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;

2
app/Utils/RedisUtils.js Normal file
View file

@ -0,0 +1,2 @@
const ExpiredToken = require('./Redis/ExpiredToken');
module.exports = {ExpiredToken};

View file

@ -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',
}
}
}

View file

@ -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

56
config/redis.js Normal file
View file

@ -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}
]
}
}

156
package-lock.json generated
View file

@ -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",

View file

@ -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",

View file

@ -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

View file

@ -1,99 +1,146 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@include('partials.SEO.meta')
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.1.7/css/fork-awesome.min.css" integrity="sha256-gsmEoJAws/Kd3CjuOQzLie5Q3yshhvmo7YNtBG7aaEY=" crossorigin="anonymous">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@include('partials.SEO.meta')
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.1.7/css/fork-awesome.min.css"
integrity="sha256-gsmEoJAws/Kd3CjuOQzLie5Q3yshhvmo7YNtBG7aaEY=" crossorigin="anonymous">
<title>
@!section('page-title')
@!section('page-title')
</title>
{{ style('style') }}
{{ script('scripts/views/register/app.bundle.js') }}
@!section('scripts')
</head>
<body>
@!section('nav')
@!section('hero')
<section class="section">
<div class="container">
@!section('content')
</div>
</section>
<div class="hero is-light">
<section class="section has-text-centered p-diego">
<div class="title">Join Now</div>
<div></div>
<div class="subtitle">It takes less then a minute. You could have been done by now, but you are still reading...</div>
<form class="form register" method="POST" action="{{ route('register') }}" id="form-register">
{{ csrfField() }}
<nav class="level">
<div class="field">
{{-- <label class="label ">Name</label> --}}
<div class="control has-icons-left">
<input class="input {{ getErrorFor('name') ? 'is-danger' : ''}} is-borderless" required="true" name="name" type="text" placeholder="John Snow" value="{{ old('name', '') }}">
<span class="icon is-small is-left">
<i class="fa fa-id-card"></i>
</span>
</div>
<p class="help is-danger">{{ getErrorFor('name') ? getErrorFor('name') : '' }}</p>
</div>
<div class="field">
{{-- <label class="label ">Email</label> --}}
<div class="control has-icons-left">
<input class="input {{ getErrorFor('email') ? 'is-danger' : ''}} is-borderless" name="email" required="true" type="email" placeholder="j.snow@thewall.com" value="{{ old('email', '') }}">
<span class="icon is-small is-left">
<i class="fa fa-envelope"></i>
</span>
</div>
<p class="help is-danger">{{ getErrorFor('email') ? getErrorFor('email') : '' }}</p>
</div>
<div class="field">
{{-- <label class="label ">Password</label> --}}
<div class="control has-icons-left">
<input class="input {{ getErrorFor('password') ? 'is-danger' : ''}} is-borderless" id="txt-register-password" name="password" required="true" type="password" placeholder="password">
<span class="icon is-small is-left">
<i class="fa fa-lock"></i>
</span>
</div>
<p class="help is-danger">{{ getErrorFor('password') ? getErrorFor('password') : '' }}</p>
</div>
<div class="field m-b-xxs">
{{-- <label class="label ">Confirm Password</label> --}}
<div class="control has-icons-left">
<input class="input is-borderless" type="password" id="txt-register-confirm-password" placeholder="confirm password" required="true">
<span class="icon is-small is-left">
<i class="fa fa-lock"></i>
</span>
</div>
<p class="help is-danger">{{ '' }}</p>
</div>
</nav>
<div class="is-inline-block">
<div class="control m-b-sm">
<label class="checkbox">
<input type="checkbox" id="chk-register-terms" name="terms">
I agree to the <a href="#">terms and conditions</a>
<p class="help is-danger">{{ getErrorFor('terms') ? getErrorFor('terms') : '' }}</p>
</label>
</div>
<div class="control is-inline-block">
<button id="btn-register-submit" class="button is-primary is-rounded" disabled="true">Submit</button>
</div>
</div>
</form>
</section>
@if(flashMessage('notification'))
<div class="notifications">
<div class="notification is-success" id="session-notification">
<button class="delete" id="btn-close-notification"></button>
{{ flashMessage('notification') }}
</div>
@include('partials.footer')
</div>
<script>
(function(){
const notification = document.getElementById("session-notification");
const btn = document.getElementById("btn-close-notification");
btn.onclick = closeNotification;
function closeNotification(){
const parent = notification.parentElement;
parent.removeChild(notification);
}
})();
</script>
@endif
@if(hasErrorFor('InvalidToken'))
<div class="notifications">
<div class="notification is-danger" id="session-notification">
<button class="delete" id="btn-close-notification"></button>
{{ getErrorFor('InvalidToken') }}
</div>
</div>
<script>
(function(){
const notification = document.getElementById("session-notification");
const btn = document.getElementById("btn-close-notification");
btn.onclick = closeNotification;
function closeNotification(){
const parent = notification.parentElement;
parent.removeChild(notification);
}
})();
</script>
@endif
@!section('nav')
@!section('hero')
<section class="section">
<div class="container">
@!section('content')
</div>
</section>
<div class="hero is-light">
<section class="section has-text-centered p-diego">
<div class="title">Join Now</div>
<div></div>
<div class="subtitle">It takes less then a minute. You could have been done by now, but you are still reading...
</div>
<form class="form register" method="POST" action="{{ route('register') }}" id="form-register">
{{ csrfField() }}
<nav class="level">
<div class="field">
{{-- <label class="label ">Name</label> --}}
<div class="control has-icons-left">
<input class="input {{ getErrorFor('name') ? 'is-danger' : ''}} is-borderless" required="true" name="name"
type="text" placeholder="John Snow" value="{{ old('name', '') }}">
<span class="icon is-small is-left">
<i class="fa fa-id-card"></i>
</span>
</div>
<p class="help is-danger">{{ getErrorFor('name') ? getErrorFor('name') : '' }}</p>
</div>
<div class="field">
{{-- <label class="label ">Email</label> --}}
<div class="control has-icons-left">
<input class="input {{ getErrorFor('email') ? 'is-danger' : ''}} is-borderless" name="email"
required="true" type="email" placeholder="j.snow@thewall.com" value="{{ old('email', '') }}">
<span class="icon is-small is-left">
<i class="fa fa-envelope"></i>
</span>
</div>
<p class="help is-danger">{{ getErrorFor('email') ? getErrorFor('email') : '' }}</p>
</div>
<div class="field">
{{-- <label class="label ">Password</label> --}}
<div class="control has-icons-left">
<input class="input {{ getErrorFor('password') ? 'is-danger' : ''}} is-borderless"
id="txt-register-password" name="password" required="true" type="password" placeholder="password">
<span class="icon is-small is-left">
<i class="fa fa-lock"></i>
</span>
</div>
<p class="help is-danger">{{ getErrorFor('password') ? getErrorFor('password') : '' }}</p>
</div>
<div class="field m-b-xxs">
{{-- <label class="label ">Confirm Password</label> --}}
<div class="control has-icons-left">
<input class="input is-borderless" type="password" id="txt-register-confirm-password"
placeholder="confirm password" required="true">
<span class="icon is-small is-left">
<i class="fa fa-lock"></i>
</span>
</div>
<p class="help is-danger">{{ '' }}</p>
</div>
</nav>
<div class="is-inline-block">
<div class="control m-b-sm">
<label class="checkbox">
<input type="checkbox" id="chk-register-terms" name="terms">
I agree to the <a href="#">terms and conditions</a>
<p class="help is-danger">{{ getErrorFor('terms') ? getErrorFor('terms') : '' }}</p>
</label>
</div>
<div class="control is-inline-block">
<button id="btn-register-submit" class="button is-primary is-rounded" disabled="true">Submit</button>
</div>
</div>
</form>
</section>
</div>
@include('partials.footer')
</body>
</html>

View file

@ -0,0 +1,59 @@
@layout('layouts.main')
@section('page-title')
Seepur| Reset Password
@endsection
@section('content')
<h1 class="title">
Don't Pannic!
</h1>
<p class="subtitle">
We'v got you
</p>
@if(hasErrorFor('userNotFound'))
<div class="notification is-danger" id="user-not-found-notification">
<button class="delete" id="btn-close-notification"></button>
{{getErrorFor('userNotFound')}}
</div>
<script>
(function(){
const notification = document.getElementById("user-not-found-notification");
const btn = document.getElementById("btn-close-notification");
console.log(btn);
btn.onclick =() =>{
const parent = notification.parentElement;
parent.removeChild(notification);
}
})();
</script>
@endif
<form class="form register" method="POST" action="{{ route('resetPasswordRequest') }}" id="form-register">
{{ csrfField() }}
<div class="field">
<label class="label">Email</label>
<div class="control has-icons-left">
<input class="input {{ getErrorFor('email') ? 'is-danger' : ''}}" required="true" name="email" type="email"
placeholder="j.snow@thewall.com" value="{{ old('email', '') }}">
<span class="icon is-small is-left">
<i class="fa fa-envelope"></i>
</span>
</div>
<p class="help is-danger">{{ getErrorFor('email') ? getErrorFor('email') : '' }}</p>
</div>
<div class="field is-grouped">
<div class="control">
<button id="btn-register-submit" class="button is-info is-rounded">Yep, Reset!</button>
</div>
<div class="control">
<a class="button is-outlined is-rounded" href='/'>Cancel</a>
</div>
</div>
</form>
@endsection

View file

@ -6,30 +6,67 @@ Seepur| Reset Password
@section('content')
<h1 class="title">
Don't Pannic!
Reset Password
</h1>
<p class="subtitle">
We'v got you
Tip: keep your password safe
</p>
@if(hasErrorFor('userNotFound'))
<div class="notification is-danger" id="user-not-found-notification">
<button class="delete" id="btn-close-notification"></button>
{{getErrorFor('userNotFound')}}
</div>
<script>
(function(){
const notification = document.getElementById("user-not-found-notification");
const btn = document.getElementById("btn-close-notification");
console.log(btn);
btn.onclick =() =>{
const parent = notification.parentElement;
parent.removeChild(notification);
}
})();
</script>
@endif
<form class="form register" method="POST" action="{{ route('resetPassword') }}" id="form-register">
{{ csrfField() }}
<div class="field">
<label class="label">Email</label>
<label class="label">Password</label>
<div class="control has-icons-left">
<input class="input {{ getErrorFor('email') ? 'is-danger' : ''}}" required="true" name="email" type="email" placeholder="j.snow@thewall.com" value="{{ old('email', '') }}">
<input class="input {{ getErrorFor('password') ? 'is-danger' : ''}}" required="true" name="password"
type="password" placeholder="super secret password">
<span class="icon is-small is-left">
<i class="fa fa-envelope"></i>
<i class="fa fa-lock"></i>
</span>
</div>
<p class="help is-danger">{{ getErrorFor('email') ? getErrorFor('email') : '' }}</p>
<p class="help is-danger">{{ getErrorFor('password') ? getErrorFor('password') : '' }}</p>
</div>
<div class="field">
<label class="label">Confirm</label>
<div class="control has-icons-left">
<input class="input {{ getErrorFor('confirm') ? 'is-danger' : ''}}" required="true" type="password"
name="confirm" placeholder="confirm super secret password">
<span class="icon is-small is-left">
<i class="fa fa-lock"></i>
</span>
</div>
<p class="help is-danger">{{ getErrorFor('confirm') ? getErrorFor('confirm') : '' }}</p>
</div>
{{-- Hidden token --}}
<div class="field" style="display: none">
<div class="control">
<input class="input" required="true" type="password" name="token" value="{{ token }}">
</div>
</div>
<div class="field is-grouped">
<div class="control">
<button id="btn-register-submit" class="button is-info is-rounded" >Yep, Reset!</button>
<button id="btn-register-submit" class="button is-info is-rounded" type="submit">Submit</button>
</div>
<div class="control">
<a class="button is-outlined is-rounded" href='/'>Cancel</a>

View file

@ -24,6 +24,7 @@ const providers =
'@adonisjs/drive/providers/DriveProvider',
'@adonisjs/websocket/providers/WsProvider',
'@adonisjs/mail/providers/MailProvider',
'@adonisjs/redis/providers/RedisProvider',
]
/*

24
start/redis.js Normal file
View file

@ -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')

View file

@ -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