forked from sagi/seepur
[REDIS needed] - Implemented reset password
This commit is contained in:
parent
1c857559f8
commit
7972a3fec5
16 changed files with 629 additions and 109 deletions
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
57
app/Utils/Redis/ExpiredToken.js
Normal file
57
app/Utils/Redis/ExpiredToken.js
Normal 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
2
app/Utils/RedisUtils.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
const ExpiredToken = require('./Redis/ExpiredToken');
|
||||
module.exports = {ExpiredToken};
|
|
@ -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',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
16
app/Validators/ResetPasswordRequest.js
Normal file
16
app/Validators/ResetPasswordRequest.js
Normal 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
56
config/redis.js
Normal 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
156
package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
13
resources/views/emails/reset-password-text.edge
Normal file
13
resources/views/emails/reset-password-text.edge
Normal 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
|
|
@ -1,11 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
<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">
|
||||
<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')
|
||||
</title>
|
||||
|
@ -13,7 +15,46 @@
|
|||
{{ script('scripts/views/register/app.bundle.js') }}
|
||||
@!section('scripts')
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@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>
|
||||
</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')
|
||||
|
||||
|
@ -28,14 +69,16 @@
|
|||
<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>
|
||||
<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', '') }}">
|
||||
<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>
|
||||
|
@ -47,7 +90,8 @@
|
|||
<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', '') }}">
|
||||
<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>
|
||||
|
@ -59,7 +103,8 @@
|
|||
<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">
|
||||
<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>
|
||||
|
@ -69,7 +114,8 @@
|
|||
<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">
|
||||
<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>
|
||||
|
@ -96,4 +142,5 @@
|
|||
</div>
|
||||
@include('partials.footer')
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
59
resources/views/reset-password-request.edge
Normal file
59
resources/views/reset-password-request.edge
Normal 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
|
|
@ -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>
|
||||
|
|
|
@ -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
24
start/redis.js
Normal 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')
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue