Skip to content

Commit 4a271c1

Browse files
authored
Merge pull request #4239 from Dokploy/feat/resend-verification-email-on-signin
feat: resend verification email on sign-in and improve template
2 parents b060f80 + fda367b commit 4a271c1

4 files changed

Lines changed: 147 additions & 6 deletions

File tree

apps/dokploy/pages/index.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,16 @@ export default function Home({ IS_CLOUD }: Props) {
8282
});
8383

8484
if (error) {
85+
const isEmailNotVerified =
86+
error.code === "EMAIL_NOT_VERIFIED" ||
87+
error.message?.toLowerCase().includes("email not verified");
88+
if (isEmailNotVerified) {
89+
const msg =
90+
"Your email is not verified. We've sent a new verification link to your email.";
91+
toast.info(msg);
92+
setError(msg);
93+
return;
94+
}
8595
toast.error(error.message);
8696
setError(error.message || "An error occurred while logging in");
8797
return;
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import {
2+
Body,
3+
Button,
4+
Container,
5+
Head,
6+
Heading,
7+
Html,
8+
Img,
9+
Link,
10+
Preview,
11+
Section,
12+
Tailwind,
13+
Text,
14+
} from "@react-email/components";
15+
16+
export type TemplateProps = {
17+
userName: string;
18+
verificationUrl: string;
19+
};
20+
21+
export const VerifyEmailTemplate = ({
22+
userName = "User",
23+
verificationUrl = "https://app.dokploy.com/verify",
24+
}: TemplateProps) => {
25+
const previewText = "Verify your email address to get started with Dokploy";
26+
return (
27+
<Html>
28+
<Head />
29+
<Preview>{previewText}</Preview>
30+
<Tailwind
31+
config={{
32+
theme: {
33+
extend: {
34+
colors: {
35+
brand: "#007291",
36+
},
37+
},
38+
},
39+
}}
40+
>
41+
<Body className="bg-[#f4f4f5] my-auto mx-auto font-sans">
42+
<Container className="my-[40px] mx-auto max-w-[520px]">
43+
{/* Header */}
44+
<Section className="bg-[#09090b] rounded-t-xl px-[40px] py-[32px] text-center">
45+
<Img
46+
src="https://raw.githubusercontent.com/Dokploy/website/refs/heads/main/apps/docs/public/logo-dokploy-blackpng.png"
47+
width="190"
48+
height="120"
49+
alt="Dokploy"
50+
className="my-0 mx-auto"
51+
/>
52+
</Section>
53+
54+
{/* Body */}
55+
<Section className="bg-white px-[40px] py-[32px]">
56+
<Heading className="text-[#09090b] text-[22px] font-semibold m-0 mb-[8px]">
57+
Verify Your Email
58+
</Heading>
59+
<Text className="text-[#71717a] text-[14px] leading-[22px] m-0 mb-[24px]">
60+
Hello {userName}, thank you for signing up for Dokploy. Please
61+
verify your email address to activate your account.
62+
</Text>
63+
64+
{/* CTA Button */}
65+
<Section className="text-center mb-[24px]">
66+
<Button
67+
href={verificationUrl}
68+
className="bg-[#09090b] rounded-lg text-white text-[14px] font-semibold no-underline text-center px-[24px] py-[12px]"
69+
>
70+
Verify Email Address
71+
</Button>
72+
</Section>
73+
74+
<Text className="text-[#a1a1aa] text-[13px] leading-[20px] m-0 text-center mb-[16px]">
75+
If the button above doesn't work, copy and paste the following
76+
link into your browser:
77+
</Text>
78+
<Text className="text-[#71717a] text-[12px] leading-[18px] m-0 text-center break-all">
79+
{verificationUrl}
80+
</Text>
81+
</Section>
82+
83+
{/* Footer */}
84+
<Section className="bg-[#fafafa] rounded-b-xl px-[40px] py-[24px] text-center border-t border-solid border-[#e4e4e7]">
85+
<Text className="text-[#a1a1aa] text-[12px] leading-[18px] m-0">
86+
This is an automated email from{" "}
87+
<Link
88+
href="https://dokploy.com"
89+
className="text-[#71717a] underline"
90+
>
91+
Dokploy Cloud
92+
</Link>
93+
. If you didn't create an account, you can safely ignore this
94+
email.
95+
</Text>
96+
</Section>
97+
</Container>
98+
</Body>
99+
</Tailwind>
100+
</Html>
101+
);
102+
};
103+
104+
export default VerifyEmailTemplate;

packages/server/src/lib/auth.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ import {
2121
updateWebServerSettings,
2222
} from "../services/web-server-settings";
2323
import { getHubSpotUTK, submitToHubSpot } from "../utils/tracking/hubspot";
24-
import { sendEmail } from "../verification/send-verification-email";
24+
import {
25+
sendEmail,
26+
sendVerificationEmail,
27+
} from "../verification/send-verification-email";
2528
import { getPublicIpWithFallback } from "../wss/utils";
2629
import { ac, adminRole, memberRole, ownerRole } from "./access-control";
2730

@@ -106,14 +109,13 @@ const { handler, api } = betterAuth({
106109
emailVerification: {
107110
sendOnSignUp: true,
108111
autoSignInAfterVerification: true,
112+
sendOnSignIn: true,
109113
sendVerificationEmail: async ({ user, url }) => {
110114
if (IS_CLOUD) {
111-
await sendEmail({
115+
await sendVerificationEmail({
116+
userName: user.name || "User",
112117
email: user.email,
113-
subject: "Verify your email",
114-
text: `
115-
<p>Click the link to verify your email: <a href="${url}">Verify Email</a></p>
116-
`,
118+
verificationUrl: url,
117119
});
118120
}
119121
},

packages/server/src/verification/send-verification-email.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import { renderAsync } from "@react-email/components";
2+
import VerifyEmailTemplate from "../emails/emails/verify-email";
13
import { sendEmailNotification } from "../utils/notifications/utils";
4+
25
export const sendEmail = async ({
36
email,
47
subject,
@@ -26,3 +29,25 @@ export const sendEmail = async ({
2629

2730
return true;
2831
};
32+
33+
export const sendVerificationEmail = async ({
34+
userName,
35+
email,
36+
verificationUrl,
37+
}: {
38+
userName: string;
39+
email: string;
40+
verificationUrl: string;
41+
}) => {
42+
const html = await renderAsync(
43+
VerifyEmailTemplate({
44+
userName: userName || "User",
45+
verificationUrl,
46+
}),
47+
);
48+
await sendEmail({
49+
email,
50+
subject: "Verify your email",
51+
text: html,
52+
});
53+
};

0 commit comments

Comments
 (0)