send confirm password reset email

This commit is contained in:
Milo Schwartz 2024-12-22 17:27:09 -05:00
parent 4b34353354
commit af2d78cbfb
No known key found for this signature in database
7 changed files with 114 additions and 13 deletions

View file

@ -0,0 +1,70 @@
import {
Body,
Container,
Head,
Heading,
Html,
Preview,
Section,
Text,
Tailwind
} from "@react-email/components";
import * as React from "react";
interface Props {
email: string;
}
export const ConfirmPasswordReset = ({ email }: Props) => {
const previewText = `Your password has been reset`;
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind
config={{
theme: {
extend: {
colors: {
primary: "#16A34A"
}
}
}
}}
>
<Body className="font-sans">
<Container className="bg-white border border-solid border-gray-200 p-6 max-w-lg mx-auto my-8 rounded-lg">
<Heading className="text-2xl font-semibold text-gray-800 text-center">
Your password has been successfully reset
</Heading>
<Text className="text-base text-gray-700 mt-4">
Hi {email || "there"},
</Text>
<Text className="text-base text-gray-700 mt-2">
This email confirms that your password has just been
reset. If you made this change, no further action is
required.
</Text>
<Section className="text-center my-6">
<Text className="text-base text-gray-700">
If you did not request this change, please
contact our support team immediately.
</Text>
</Section>
<Text className="text-base text-gray-700 mt-2">
Thank you for keeping your account secure.
</Text>
<Text className="text-sm text-gray-500 mt-6">
Best regards,
<br />
Fossorial
</Text>
</Container>
</Body>
</Tailwind>
</Html>
);
};
export default ConfirmPasswordReset;

View file

@ -48,8 +48,8 @@ export const ResetPasswordCode = ({ email, code, link }: Props) => {
<a href={link} className="text-primary">
click here
</a>{" "}
and follow the instructions to reset your
password, or manually enter the following code:
and follow the instructions to reset your password,
or manually enter the following code:
</Text>
<Section className="text-center my-6">
<Text className="inline-block bg-primary text-xl font-bold text-white py-2 px-4 border border-gray-300 rounded-xl">
@ -60,6 +60,11 @@ export const ResetPasswordCode = ({ email, code, link }: Props) => {
If you didnt request this, you can safely ignore
this email.
</Text>
<Text className="text-sm text-gray-500 mt-6">
Best regards,
<br />
Fossorial
</Text>
</Container>
</Body>
</Tailwind>

View file

@ -61,6 +61,12 @@ export const ResourceOTPCode = ({
{otp}
</Text>
</Section>
<Text className="text-sm text-gray-500 mt-6">
Best regards,
<br />
Fossorial
</Text>
</Container>
</Body>
</Tailwind>

View file

@ -8,7 +8,7 @@ import {
Section,
Text,
Tailwind,
Button,
Button
} from "@react-email/components";
import * as React from "react";
@ -25,7 +25,7 @@ export const SendInviteLink = ({
inviteLink,
orgName,
inviterName,
expiresInDays,
expiresInDays
}: SendInviteLinkProps) => {
const previewText = `${inviterName} invited to join ${orgName}`;
@ -33,7 +33,8 @@ export const SendInviteLink = ({
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind config={{
<Tailwind
config={{
theme: {
extend: {
colors: {
@ -41,7 +42,8 @@ export const SendInviteLink = ({
}
}
}
}}>
}}
>
<Body className="font-sans">
<Container className="bg-white border border-solid border-gray-200 p-6 max-w-lg mx-auto my-8 rounded-lg">
<Heading className="text-2xl font-semibold text-gray-800 text-center">
@ -71,6 +73,12 @@ export const SendInviteLink = ({
Accept invitation to {orgName}
</Button>
</Section>
<Text className="text-sm text-gray-500 mt-6">
Best regards,
<br />
Fossorial
</Text>
</Container>
</Body>
</Tailwind>

View file

@ -63,6 +63,11 @@ export const VerifyEmail = ({
If you didnt request this, you can safely ignore
this email.
</Text>
<Text className="text-sm text-gray-500 mt-6">
Best regards,
<br />
Fossorial
</Text>
</Container>
</Body>
</Tailwind>

View file

@ -1,3 +1,4 @@
import config from "@server/config";
import { Request, Response, NextFunction } from "express";
import createHttpError from "http-errors";
import { z } from "zod";
@ -15,6 +16,8 @@ import { encodeHex } from "oslo/encoding";
import { isWithinExpirationDate } from "oslo";
import { invalidateAllSessions } from "@server/auth";
import logger from "@server/logger";
import ConfirmPasswordReset from "@server/emails/templates/NotifyResetPassword";
import { sendEmail } from "@server/emails";
export const resetPasswordBody = z
.object({
@ -141,7 +144,11 @@ export async function resetPassword(
.delete(passwordResetTokens)
.where(eq(passwordResetTokens.email, email));
// TODO: send email to user confirming password reset
await sendEmail(ConfirmPasswordReset({ email }), {
from: config.email?.no_reply,
to: email,
subject: "Password Reset Confirmation"
})
return response<ResetPasswordResponse>(res, {
data: null,

View file

@ -62,7 +62,7 @@ export default function LoginForm({ redirect, onLogin }: LoginFormProps) {
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const [mfaRequested, setMfaRequested] = useState(true);
const [mfaRequested, setMfaRequested] = useState(false);
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),