feat: Implement forgotPassword mutation
This commit is contained in:
@@ -0,0 +1,13 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
return knex.schema.table('users', (table) => {
|
||||||
|
table.string('reset_password_token');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
return knex.schema.table('users', (table) => {
|
||||||
|
table.dropColumn('reset_password_token');
|
||||||
|
});
|
||||||
|
}
|
@@ -0,0 +1,13 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
return knex.schema.table('users', (table) => {
|
||||||
|
table.timestamp('reset_password_token_sent_at');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
return knex.schema.table('users', (table) => {
|
||||||
|
table.dropColumn('reset_password_token_sent_at');
|
||||||
|
});
|
||||||
|
}
|
@@ -14,6 +14,7 @@ import updateStep from './mutations/update-step';
|
|||||||
import deleteStep from './mutations/delete-step';
|
import deleteStep from './mutations/delete-step';
|
||||||
import createUser from './mutations/create-user.ee';
|
import createUser from './mutations/create-user.ee';
|
||||||
import updateUser from './mutations/update-user';
|
import updateUser from './mutations/update-user';
|
||||||
|
import forgotPassword from './mutations/forgot-password.ee';
|
||||||
import login from './mutations/login';
|
import login from './mutations/login';
|
||||||
|
|
||||||
const mutationResolvers = {
|
const mutationResolvers = {
|
||||||
@@ -33,6 +34,7 @@ const mutationResolvers = {
|
|||||||
deleteStep,
|
deleteStep,
|
||||||
createUser,
|
createUser,
|
||||||
updateUser,
|
updateUser,
|
||||||
|
forgotPassword,
|
||||||
login,
|
login,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
24
packages/backend/src/graphql/mutations/forgot-password.ee.ts
Normal file
24
packages/backend/src/graphql/mutations/forgot-password.ee.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import User from '../../models/user';
|
||||||
|
|
||||||
|
type Params = {
|
||||||
|
input: {
|
||||||
|
email: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const forgotPassword = async (_parent: unknown, params: Params) => {
|
||||||
|
const { email } = params.input;
|
||||||
|
|
||||||
|
const user = await User.query().findOne({ email });
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new Error('Email address not found!');
|
||||||
|
}
|
||||||
|
|
||||||
|
await user.generateResetPasswordToken();
|
||||||
|
// TODO: Send email with reset password link
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default forgotPassword;
|
@@ -50,6 +50,7 @@ type Mutation {
|
|||||||
deleteStep(input: DeleteStepInput): Step
|
deleteStep(input: DeleteStepInput): Step
|
||||||
createUser(input: CreateUserInput): User
|
createUser(input: CreateUserInput): User
|
||||||
updateUser(input: UpdateUserInput): User
|
updateUser(input: UpdateUserInput): User
|
||||||
|
forgotPassword(input: ForgotPasswordInput): Boolean
|
||||||
login(input: LoginInput): Auth
|
login(input: LoginInput): Auth
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,8 +303,8 @@ input DeleteStepInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input CreateUserInput {
|
input CreateUserInput {
|
||||||
email: String
|
email: String!
|
||||||
password: String
|
password: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input UpdateUserInput {
|
input UpdateUserInput {
|
||||||
@@ -311,6 +312,10 @@ input UpdateUserInput {
|
|||||||
password: String
|
password: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input ForgotPasswordInput {
|
||||||
|
email: String
|
||||||
|
}
|
||||||
|
|
||||||
input LoginInput {
|
input LoginInput {
|
||||||
email: String!
|
email: String!
|
||||||
password: String!
|
password: String!
|
||||||
|
@@ -30,6 +30,7 @@ const authentication = shield(
|
|||||||
'*': isAuthenticated,
|
'*': isAuthenticated,
|
||||||
login: allow,
|
login: allow,
|
||||||
createUser: allow,
|
createUser: allow,
|
||||||
|
forgotPassword: allow,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -5,12 +5,15 @@ import Flow from './flow';
|
|||||||
import Step from './step';
|
import Step from './step';
|
||||||
import Execution from './execution';
|
import Execution from './execution';
|
||||||
import bcrypt from 'bcrypt';
|
import bcrypt from 'bcrypt';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
class User extends Base {
|
class User extends Base {
|
||||||
id!: string;
|
id!: string;
|
||||||
email!: string;
|
email!: string;
|
||||||
password!: string;
|
password!: string;
|
||||||
role: string;
|
role: string;
|
||||||
|
resetPasswordToken: string;
|
||||||
|
resetPasswordTokenSentAt: string;
|
||||||
connections?: Connection[];
|
connections?: Connection[];
|
||||||
flows?: Flow[];
|
flows?: Flow[];
|
||||||
steps?: Step[];
|
steps?: Step[];
|
||||||
@@ -77,6 +80,13 @@ class User extends Base {
|
|||||||
return bcrypt.compare(password, this.password);
|
return bcrypt.compare(password, this.password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async generateResetPasswordToken() {
|
||||||
|
const resetPasswordToken = crypto.randomBytes(64).toString('hex');
|
||||||
|
const resetPasswordTokenSentAt = new Date().toISOString();
|
||||||
|
|
||||||
|
await this.$query().patch({ resetPasswordToken, resetPasswordTokenSentAt });
|
||||||
|
}
|
||||||
|
|
||||||
async generateHash() {
|
async generateHash() {
|
||||||
this.password = await bcrypt.hash(this.password, 10);
|
this.password = await bcrypt.hash(this.password, 10);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user