mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 10:42:08 +00:00
begin extracting user controllers out of routes
This commit is contained in:
@@ -15,7 +15,7 @@ export const verifyConfirmationToken = async (token: string) => {
|
|||||||
const parsed = BasicUserInfoSchema.safeParse(decoded);
|
const parsed = BasicUserInfoSchema.safeParse(decoded);
|
||||||
|
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
throw new Error('Invalid token');
|
throw new ServerError('Invalid token.', 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsed.data;
|
return parsed.data;
|
||||||
|
|||||||
167
src/controllers/auth/index.ts
Normal file
167
src/controllers/auth/index.ts
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
import { removeTokenCookie } from '@/config/auth/cookie';
|
||||||
|
import localStrat from '@/config/auth/localStrat';
|
||||||
|
import { getLoginSession, setLoginSession } from '@/config/auth/session';
|
||||||
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
|
import ServerError from '@/config/util/ServerError';
|
||||||
|
import createNewUser from '@/services/User/createNewUser';
|
||||||
|
import findUserByEmail from '@/services/User/findUserByEmail';
|
||||||
|
import { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
import { expressWrapper } from 'next-connect';
|
||||||
|
import passport from 'passport';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import findUserByUsername from '@/services/User/findUserByUsername';
|
||||||
|
import GetUserSchema from '@/services/User/schema/GetUserSchema';
|
||||||
|
import sendConfirmationEmail from '@/services/User/sendConfirmationEmail';
|
||||||
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
|
import type { NextFunction } from 'express';
|
||||||
|
import { verifyConfirmationToken } from '@/config/jwt';
|
||||||
|
import updateUserToBeConfirmedById from '@/services/User/updateUserToBeConfirmedById';
|
||||||
|
import DBClient from '@/prisma/DBClient';
|
||||||
|
import sendResetPasswordEmail from '@/services/User/sendResetPasswordEmail';
|
||||||
|
import {
|
||||||
|
RegisterUserRequest,
|
||||||
|
ResetPasswordRequest,
|
||||||
|
TokenValidationRequest,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
export const authenticateUser = expressWrapper(
|
||||||
|
async (
|
||||||
|
req: UserExtendedNextApiRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
next: NextFunction,
|
||||||
|
) => {
|
||||||
|
passport.initialize();
|
||||||
|
passport.use(localStrat);
|
||||||
|
passport.authenticate(
|
||||||
|
'local',
|
||||||
|
{ session: false },
|
||||||
|
(error: unknown, token: z.infer<typeof GetUserSchema>) => {
|
||||||
|
if (error) {
|
||||||
|
next(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
req.user = token;
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
)(req, res, next);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const loginUser = async (
|
||||||
|
req: UserExtendedNextApiRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const user = req.user!;
|
||||||
|
await setLoginSession(res, user);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Login successful.',
|
||||||
|
payload: user,
|
||||||
|
statusCode: 200,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const logoutUser = async (
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const session = await getLoginSession(req);
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
throw new ServerError('You are not logged in.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTokenCookie(res);
|
||||||
|
|
||||||
|
res.redirect('/');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const registerUser = async (
|
||||||
|
req: RegisterUserRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const [usernameTaken, emailTaken] = await Promise.all([
|
||||||
|
findUserByUsername(req.body.username),
|
||||||
|
findUserByEmail(req.body.email),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (usernameTaken) {
|
||||||
|
throw new ServerError(
|
||||||
|
'Could not register a user with that username as it is already taken.',
|
||||||
|
409,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emailTaken) {
|
||||||
|
throw new ServerError(
|
||||||
|
'Could not register a user with that email as it is already taken.',
|
||||||
|
409,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await createNewUser(req.body);
|
||||||
|
|
||||||
|
await setLoginSession(res, {
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
});
|
||||||
|
|
||||||
|
await sendConfirmationEmail(user);
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
success: true,
|
||||||
|
statusCode: 201,
|
||||||
|
message: 'User registered successfully.',
|
||||||
|
payload: user,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const confirmUser = async (
|
||||||
|
req: TokenValidationRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { token } = req.query;
|
||||||
|
|
||||||
|
const user = req.user!;
|
||||||
|
const { id } = await verifyConfirmationToken(token);
|
||||||
|
|
||||||
|
if (user.accountIsVerified) {
|
||||||
|
throw new ServerError('Your account is already verified.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.id !== id) {
|
||||||
|
throw new ServerError('Could not confirm user.', 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateUserToBeConfirmedById(id);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'User confirmed successfully.',
|
||||||
|
statusCode: 200,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const resetPassword = async (
|
||||||
|
req: ResetPasswordRequest,
|
||||||
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
|
) => {
|
||||||
|
const { email } = req.body;
|
||||||
|
|
||||||
|
const user = await DBClient.instance.user.findUnique({
|
||||||
|
where: { email },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
await sendResetPasswordEmail(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
statusCode: 200,
|
||||||
|
success: true,
|
||||||
|
message:
|
||||||
|
'If an account with that email exists, we have sent you an email to reset your password.',
|
||||||
|
});
|
||||||
|
};
|
||||||
17
src/controllers/auth/types/index.ts
Normal file
17
src/controllers/auth/types/index.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
|
import { CreateUserValidationSchema } from '@/services/User/schema/CreateUserValidationSchemas';
|
||||||
|
import TokenValidationSchema from '@/services/User/schema/TokenValidationSchema';
|
||||||
|
import { NextApiRequest } from 'next';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export interface RegisterUserRequest extends NextApiRequest {
|
||||||
|
body: z.infer<typeof CreateUserValidationSchema>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TokenValidationRequest extends UserExtendedNextApiRequest {
|
||||||
|
query: z.infer<typeof TokenValidationSchema>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResetPasswordRequest extends NextApiRequest {
|
||||||
|
body: { email: string };
|
||||||
|
}
|
||||||
@@ -1,53 +1,24 @@
|
|||||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
|
||||||
import { verifyConfirmationToken } from '@/config/jwt';
|
|
||||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import ServerError from '@/config/util/ServerError';
|
|
||||||
|
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { createRouter } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
import updateUserToBeConfirmedById from '@/services/User/updateUserToBeConfirmedById';
|
|
||||||
|
|
||||||
const ConfirmUserValidationSchema = z.object({ token: z.string() });
|
import { TokenValidationRequest } from '@/controllers/auth/types';
|
||||||
|
import { confirmUser } from '@/controllers/auth';
|
||||||
interface ConfirmUserRequest extends UserExtendedNextApiRequest {
|
import TokenValidationSchema from '@/services/User/schema/TokenValidationSchema';
|
||||||
query: z.infer<typeof ConfirmUserValidationSchema>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const confirmUser = async (req: ConfirmUserRequest, res: NextApiResponse) => {
|
|
||||||
const { token } = req.query;
|
|
||||||
|
|
||||||
const user = req.user!;
|
|
||||||
const { id } = await verifyConfirmationToken(token);
|
|
||||||
|
|
||||||
if (user.accountIsVerified) {
|
|
||||||
throw new ServerError('Your account is already verified.', 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.id !== id) {
|
|
||||||
throw new ServerError('Could not confirm user.', 401);
|
|
||||||
}
|
|
||||||
|
|
||||||
await updateUserToBeConfirmedById(id);
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
message: 'User confirmed successfully.',
|
|
||||||
statusCode: 200,
|
|
||||||
success: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
ConfirmUserRequest,
|
TokenValidationRequest,
|
||||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||||
>();
|
>();
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
getCurrentUser,
|
getCurrentUser,
|
||||||
validateRequest({ querySchema: ConfirmUserValidationSchema }),
|
validateRequest({ querySchema: TokenValidationSchema }),
|
||||||
confirmUser,
|
confirmUser,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,65 +1,12 @@
|
|||||||
import { generateResetPasswordToken } from '@/config/jwt';
|
|
||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import sendEmail from '@/config/sparkpost/sendEmail';
|
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { createRouter } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import DBClient from '@/prisma/DBClient';
|
|
||||||
import { render } from '@react-email/render';
|
|
||||||
import ForgotEmail from '@/emails/ForgotEmail';
|
|
||||||
import { ReactElement } from 'react';
|
|
||||||
|
|
||||||
import { User } from '@prisma/client';
|
|
||||||
import { BASE_URL } from '@/config/env';
|
|
||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
|
import { resetPassword } from '@/controllers/auth';
|
||||||
interface ResetPasswordRequest extends NextApiRequest {
|
import { ResetPasswordRequest } from '@/controllers/auth/types';
|
||||||
body: { email: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
const sendResetPasswordEmail = async (user: User) => {
|
|
||||||
const token = generateResetPasswordToken({ id: user.id, username: user.username });
|
|
||||||
|
|
||||||
const url = `${BASE_URL}/users/reset-password?token=${token}`;
|
|
||||||
|
|
||||||
const component = ForgotEmail({ name: user.username, url })! as ReactElement<
|
|
||||||
unknown,
|
|
||||||
string
|
|
||||||
>;
|
|
||||||
|
|
||||||
const html = render(component);
|
|
||||||
const text = render(component, { plainText: true });
|
|
||||||
|
|
||||||
await sendEmail({
|
|
||||||
address: user.email,
|
|
||||||
subject: 'Reset Password',
|
|
||||||
html,
|
|
||||||
text,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const forgetPassword = async (
|
|
||||||
req: ResetPasswordRequest,
|
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
|
||||||
) => {
|
|
||||||
const { email } = req.body;
|
|
||||||
|
|
||||||
const user = await DBClient.instance.user.findUnique({
|
|
||||||
where: { email },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (user) {
|
|
||||||
await sendResetPasswordEmail(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
statusCode: 200,
|
|
||||||
success: true,
|
|
||||||
message:
|
|
||||||
'If an account with that email exists, we have sent you an email to reset your password.',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
ResetPasswordRequest,
|
ResetPasswordRequest,
|
||||||
@@ -68,7 +15,7 @@ const router = createRouter<
|
|||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
validateRequest({ bodySchema: z.object({ email: z.string().email() }) }),
|
validateRequest({ bodySchema: z.object({ email: z.string().email() }) }),
|
||||||
forgetPassword,
|
resetPassword,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handler = router.handler(NextConnectOptions);
|
const handler = router.handler(NextConnectOptions);
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import passport from 'passport';
|
import { createRouter } from 'next-connect';
|
||||||
import { createRouter, expressWrapper } from 'next-connect';
|
|
||||||
import localStrat from '@/config/auth/localStrat';
|
|
||||||
import { setLoginSession } from '@/config/auth/session';
|
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import LoginValidationSchema from '@/services/User/schema/LoginValidationSchema';
|
import LoginValidationSchema from '@/services/User/schema/LoginValidationSchema';
|
||||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
import GetUserSchema from '@/services/User/schema/GetUserSchema';
|
import { authenticateUser, loginUser } from '@/controllers/auth';
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
UserExtendedNextApiRequest,
|
UserExtendedNextApiRequest,
|
||||||
@@ -18,33 +15,8 @@ const router = createRouter<
|
|||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
validateRequest({ bodySchema: LoginValidationSchema }),
|
validateRequest({ bodySchema: LoginValidationSchema }),
|
||||||
expressWrapper(async (req, res, next) => {
|
authenticateUser,
|
||||||
passport.initialize();
|
loginUser,
|
||||||
passport.use(localStrat);
|
|
||||||
passport.authenticate(
|
|
||||||
'local',
|
|
||||||
{ session: false },
|
|
||||||
(error: unknown, token: z.infer<typeof GetUserSchema>) => {
|
|
||||||
if (error) {
|
|
||||||
next(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
req.user = token;
|
|
||||||
next();
|
|
||||||
},
|
|
||||||
)(req, res, next);
|
|
||||||
}),
|
|
||||||
async (req, res) => {
|
|
||||||
const user = req.user!;
|
|
||||||
await setLoginSession(res, user);
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
message: 'Login successful.',
|
|
||||||
payload: user,
|
|
||||||
statusCode: 200,
|
|
||||||
success: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const handler = router.handler(NextConnectOptions);
|
const handler = router.handler(NextConnectOptions);
|
||||||
|
|||||||
@@ -1,28 +1,16 @@
|
|||||||
import { getLoginSession } from '@/config/auth/session';
|
|
||||||
import { removeTokenCookie } from '@/config/auth/cookie';
|
|
||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
import { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { createRouter } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import ServerError from '@/config/util/ServerError';
|
import { logoutUser } from '@/controllers/auth';
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
NextApiRequest,
|
NextApiRequest,
|
||||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||||
>();
|
>();
|
||||||
|
|
||||||
router.all(async (req, res) => {
|
router.all(logoutUser);
|
||||||
const session = await getLoginSession(req);
|
|
||||||
|
|
||||||
if (!session) {
|
|
||||||
throw new ServerError('You are not logged in.', 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
removeTokenCookie(res);
|
|
||||||
|
|
||||||
res.redirect('/');
|
|
||||||
});
|
|
||||||
|
|
||||||
const handler = router.handler(NextConnectOptions);
|
const handler = router.handler(NextConnectOptions);
|
||||||
export default handler;
|
export default handler;
|
||||||
|
|||||||
@@ -1,65 +1,24 @@
|
|||||||
import { setLoginSession } from '@/config/auth/session';
|
import { NextApiResponse } from 'next';
|
||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import ServerError from '@/config/util/ServerError';
|
|
||||||
import { createRouter } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import createNewUser from '@/services/User/createNewUser';
|
|
||||||
import { CreateUserValidationSchema } from '@/services/User/schema/CreateUserValidationSchemas';
|
import { CreateUserValidationSchema } from '@/services/User/schema/CreateUserValidationSchemas';
|
||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import findUserByUsername from '@/services/User/findUserByUsername';
|
|
||||||
import findUserByEmail from '@/services/User/findUserByEmail';
|
|
||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
|
import { registerUser } from '@/controllers/auth';
|
||||||
import sendConfirmationEmail from '@/services/User/sendConfirmationEmail';
|
import { RegisterUserRequest } from '@/controllers/auth/types';
|
||||||
|
|
||||||
interface RegisterUserRequest extends NextApiRequest {
|
|
||||||
body: z.infer<typeof CreateUserValidationSchema>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const registerUser = async (req: RegisterUserRequest, res: NextApiResponse) => {
|
|
||||||
const [usernameTaken, emailTaken] = await Promise.all([
|
|
||||||
findUserByUsername(req.body.username),
|
|
||||||
findUserByEmail(req.body.email),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (usernameTaken) {
|
|
||||||
throw new ServerError(
|
|
||||||
'Could not register a user with that username as it is already taken.',
|
|
||||||
409,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (emailTaken) {
|
|
||||||
throw new ServerError(
|
|
||||||
'Could not register a user with that email as it is already taken.',
|
|
||||||
409,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await createNewUser(req.body);
|
|
||||||
|
|
||||||
await setLoginSession(res, {
|
|
||||||
id: user.id,
|
|
||||||
username: user.username,
|
|
||||||
});
|
|
||||||
|
|
||||||
await sendConfirmationEmail(user);
|
|
||||||
|
|
||||||
res.status(201).json({
|
|
||||||
success: true,
|
|
||||||
statusCode: 201,
|
|
||||||
message: 'User registered successfully.',
|
|
||||||
payload: user,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = createRouter<
|
const router = createRouter<
|
||||||
RegisterUserRequest,
|
RegisterUserRequest,
|
||||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||||
>();
|
>();
|
||||||
|
|
||||||
router.post(validateRequest({ bodySchema: CreateUserValidationSchema }), registerUser);
|
router.post(
|
||||||
|
validateRequest({
|
||||||
|
bodySchema: CreateUserValidationSchema,
|
||||||
|
}),
|
||||||
|
registerUser,
|
||||||
|
);
|
||||||
|
|
||||||
const handler = router.handler(NextConnectOptions);
|
const handler = router.handler(NextConnectOptions);
|
||||||
export default handler;
|
export default handler;
|
||||||
|
|||||||
7
src/services/User/schema/TokenValidationSchema.ts
Normal file
7
src/services/User/schema/TokenValidationSchema.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import z from 'zod';
|
||||||
|
|
||||||
|
const TokenValidationSchema = z.object({
|
||||||
|
token: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default TokenValidationSchema;
|
||||||
30
src/services/User/sendResetPasswordEmail.ts
Normal file
30
src/services/User/sendResetPasswordEmail.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { BASE_URL } from '@/config/env';
|
||||||
|
import { generateResetPasswordToken } from '@/config/jwt';
|
||||||
|
import sendEmail from '@/config/sparkpost/sendEmail';
|
||||||
|
import ForgotEmail from '@/emails/ForgotEmail';
|
||||||
|
import { User } from '@prisma/client';
|
||||||
|
import type { ReactElement } from 'react';
|
||||||
|
import { render } from '@react-email/render';
|
||||||
|
|
||||||
|
const sendResetPasswordEmail = async (user: User) => {
|
||||||
|
const token = generateResetPasswordToken({ id: user.id, username: user.username });
|
||||||
|
|
||||||
|
const url = `${BASE_URL}/users/reset-password?token=${token}`;
|
||||||
|
|
||||||
|
const component = ForgotEmail({ name: user.username, url })! as ReactElement<
|
||||||
|
unknown,
|
||||||
|
string
|
||||||
|
>;
|
||||||
|
|
||||||
|
const html = render(component);
|
||||||
|
const text = render(component, { plainText: true });
|
||||||
|
|
||||||
|
await sendEmail({
|
||||||
|
address: user.email,
|
||||||
|
subject: 'Reset Password',
|
||||||
|
html,
|
||||||
|
text,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default sendResetPasswordEmail;
|
||||||
Reference in New Issue
Block a user