improvemet(email-templates): migrate email templates to react email

This commit is contained in:
Scott Wilson
2025-05-01 14:57:24 -07:00
parent f2d9593660
commit 66fbcc6806
69 changed files with 4224 additions and 874 deletions
@@ -0,0 +1,95 @@
import { Button, Heading, Link, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface AccessApprovalRequestTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
projectName: string;
requesterFullName: string;
requesterEmail: string;
isTemporary: boolean;
secretPath: string;
environment: string;
expiresIn: string;
permissions: string[];
note?: string;
approvalUrl: string;
}
export const AccessApprovalRequestTemplate = ({
projectName,
siteUrl,
requesterFullName,
requesterEmail,
isTemporary,
secretPath,
environment,
expiresIn,
permissions,
note,
approvalUrl
}: AccessApprovalRequestTemplateProps) => {
return (
<BaseEmailWrapper
title="Access Approval Request"
preview="A new access approval request is pending your review."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
You have a new access approval request pending review for the project <strong>{projectName}</strong>
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-black text-[14px] leading-[24px]">
<strong>{requesterFullName}</strong> (
<Link href={`mailto:${requesterEmail}`} className="text-slate-700 no-underline">
{requesterEmail}
</Link>
) has requested {isTemporary ? "temporary" : "permanent"} access to <strong>{secretPath}</strong> in the{" "}
<strong>{environment}</strong> environment.
</Text>
{isTemporary && (
<Text className="text-[14px] text-red-500 leading-[24px]">
<strong>This access will expire {expiresIn} after approval.</strong>
</Text>
)}
<Text className="text-[14px] leading-[24px] mb-[4px]">
<strong>The following permissions are requested:</strong>
</Text>
{permissions.map((permission) => (
<Text key={permission} className="text-[14px] my-[2px] leading-[24px]">
- {permission}
</Text>
))}
{note && (
<Text className="text-[14px] text-slate-700 leading-[24px]">
<strong className="text-black">User Note:</strong> "{note}"
</Text>
)}
</Section>
<Section className="text-center mt-[28px]">
<Button
href={approvalUrl}
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
>
Review Request
</Button>
</Section>
</BaseEmailWrapper>
);
};
export default AccessApprovalRequestTemplate;
AccessApprovalRequestTemplate.PreviewProps = {
requesterFullName: "Abigail Williams",
requesterEmail: "[email protected]",
isTemporary: true,
secretPath: "/api/secrets",
environment: "Production",
siteUrl: "https://infisical.com",
projectName: "Example Project",
expiresIn: "1 day",
permissions: ["Read Secret", "Delete Project", "Create Dynamic Secret"],
note: "I need access to these permissions for the new initiative for HR."
} as AccessApprovalRequestTemplateProps;
@@ -0,0 +1,45 @@
import { Body, Container, Head, Hr, Html, Img, Link, Preview, Section, Tailwind, Text } from "@react-email/components";
import React, { ReactNode } from "react";
export interface BaseEmailWrapperProps {
title: string;
preview: string;
siteUrl: string;
children?: ReactNode;
}
export const BaseEmailWrapper = ({ title, preview, children, siteUrl }: BaseEmailWrapperProps) => {
return (
<Html>
<Head title={title} />
<Tailwind>
<Body className="bg-gray-300 my-auto mx-auto font-sans px-[8px]">
<Preview>{preview}</Preview>
<Container className="bg-white rounded-xl my-[40px] mx-auto pb-[0px] max-w-[500px]">
<Section className="border-0 border-b border-[#d1e309] border-solid bg-[#EBF852] mb-[44px] h-[10px] rounded-t-xl" />
<Section className="px-[32px] mb-[18px]">
<Section className="w-[48px] h-[48px] border border-solid border-gray-300 rounded-full bg-gray-100 mx-auto">
<Img
src={`https://infisical.com/_next/image?url=%2Fimages%2Flogo-black.png&w=64&q=75`}
width="32"
alt="Infisical Logo"
className="mx-auto"
/>
</Section>
</Section>
<Section className="px-[28px]">{children}</Section>
<Hr className=" mt-[32px] mb-[0px] h-[1px]" />
<Section className="px-[24px] text-center">
<Text className="text-gray-500 text-[12px]">
Email sent via{" "}
<Link href={siteUrl} className="text-slate-700 no-underline">
Infisical
</Link>
</Text>
</Section>
</Container>
</Body>
</Tailwind>
</Html>
);
};
@@ -0,0 +1,50 @@
import { Heading, Link, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface EmailMfaTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
code: string;
isCloud: boolean;
}
export const EmailMfaTemplate = ({ code, siteUrl, isCloud }: EmailMfaTemplateProps) => {
return (
<BaseEmailWrapper title="MFA Code" preview="Sign-in attempt requires further verification." siteUrl={siteUrl}>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
<strong>MFA required</strong>
</Heading>
<Section className="px-[24px] mt-[36px] pt-[8px] text-center pb-[8px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text>Enter the MFA code below in the browser where you started sign-in.</Text>
<Text className="text-[24px] mt-[16px]">
<strong>{code}</strong>
</Text>
</Section>
<Section className="mt-[24px] bg-gray-50 pt-[2px] pb-[16px] border border-solid border-gray-200 px-[24px] rounded-md text-gray-800">
<Text className="mb-[0px]">
<strong>Not you?</strong>{" "}
{isCloud ? (
<>
Contact us at{" "}
<Link href={`mailto:[email protected]`} className="text-slate-700 no-underline">
support@infisical.com
</Link>{" "}
immediately
</>
) : (
"Contact your administrator immediately"
)}
.
</Text>
</Section>
</BaseEmailWrapper>
);
};
export default EmailMfaTemplate;
EmailMfaTemplate.PreviewProps = {
code: "124356",
isCloud: true,
siteUrl: "https://infisical.com"
} as EmailMfaTemplateProps;
@@ -0,0 +1,53 @@
import { Heading, Link, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface EmailVerificationTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
code: string;
isCloud: boolean;
}
export const EmailVerificationTemplate = ({ code, siteUrl, isCloud }: EmailVerificationTemplateProps) => {
return (
<BaseEmailWrapper
title="Confirm Your Email Address"
preview="Verify your email address to get continue with Infisical."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
<strong>Confirm your email address</strong>
</Heading>
<Section className="px-[24px] mt-[36px] pt-[8px] text-center pb-[8px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text>Enter the confirmation code below in the browser window requiring confirmation.</Text>
<Text className="text-[24px] mt-[16px]">
<strong>{code}</strong>
</Text>
</Section>
<Section className="mt-[24px] bg-gray-50 pt-[2px] pb-[16px] border border-solid border-gray-200 px-[24px] rounded-md text-gray-800">
<Text className="mb-[0px]">
<strong>Questions about Infisical?</strong>{" "}
{isCloud ? (
<>
Email us at{" "}
<Link href={`mailto:[email protected]`} className="text-slate-700 no-underline">
support@infisical.com
</Link>
</>
) : (
"Contact your administrator"
)}
.
</Text>
</Section>
</BaseEmailWrapper>
);
};
export default EmailVerificationTemplate;
EmailVerificationTemplate.PreviewProps = {
code: "124356",
isCloud: true,
siteUrl: "https://infisical.com"
} as EmailVerificationTemplateProps;
@@ -0,0 +1,43 @@
import { Heading, Link, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface ExternalImportFailedTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
error: string;
provider: string;
}
export const ExternalImportFailedTemplate = ({ error, siteUrl, provider }: ExternalImportFailedTemplateProps) => {
return (
<BaseEmailWrapper title="Import Failed" preview={`An import from ${provider} has failed.`} siteUrl={siteUrl}>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
An import from <strong>{provider}</strong> to Infisical has failed
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-black text-[14px] leading-[24px]">
An import from <strong>{provider}</strong> to Infisical has failed due to unforeseen circumstances. Please
re-try your import.
</Text>
<Text className="text-black text-[14px] leading-[24px]">
If your issue persists, you can contact the Infisical team at{" "}
<Link href={`mailto:[email protected]`} className="text-slate-700 no-underline">
support@infisical.com
</Link>
.
</Text>
<Text className="text-[14px] text-red-500 leading-[24px]">
<strong>Error:</strong> "{error}"
</Text>
</Section>
</BaseEmailWrapper>
);
};
export default ExternalImportFailedTemplate;
ExternalImportFailedTemplate.PreviewProps = {
provider: "EnvKey",
error: "Something went wrong. Please try again.",
siteUrl: "https://infisical.com"
} as ExternalImportFailedTemplateProps;
@@ -0,0 +1,30 @@
import { Heading, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface ExternalImportStartedTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
provider: string;
}
export const ExternalImportStartedTemplate = ({ siteUrl, provider }: ExternalImportStartedTemplateProps) => {
return (
<BaseEmailWrapper title="Import in Progress" preview={`An import from ${provider} has started.`} siteUrl={siteUrl}>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
An import from <strong>{provider}</strong> to Infisical has been started
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-black text-[14px] leading-[24px]">
An import from <strong>{provider}</strong> to Infisical is in progress. The import process may take up to 30
minutes. You will receive an email once the import has completed.
</Text>
</Section>
</BaseEmailWrapper>
);
};
export default ExternalImportStartedTemplate;
ExternalImportStartedTemplate.PreviewProps = {
provider: "EnvKey"
} as ExternalImportStartedTemplateProps;
@@ -0,0 +1,31 @@
import { Heading, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface ExternalImportSucceededTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
provider: string;
}
export const ExternalImportSucceededTemplate = ({ siteUrl, provider }: ExternalImportSucceededTemplateProps) => {
return (
<BaseEmailWrapper title="Import Complete" preview={`An import from ${provider} has completed.`} siteUrl={siteUrl}>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
An import from <strong>{provider}</strong> to Infisical has completed
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-black text-[14px] leading-[24px]">
An import from <strong>{provider}</strong> to Infisical was successful. Your data is now available in
Infisical.
</Text>
</Section>
</BaseEmailWrapper>
);
};
export default ExternalImportSucceededTemplate;
ExternalImportSucceededTemplate.PreviewProps = {
provider: "EnvKey",
siteUrl: "https://infisical.com"
} as ExternalImportSucceededTemplateProps;
@@ -0,0 +1,65 @@
import { Button, Heading, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface IntegrationSyncFailedTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
count: number;
projectName: string;
secretPath: string;
environment: string;
syncMessage: string;
integrationUrl: string;
}
export const IntegrationSyncFailedTemplate = ({
count,
siteUrl,
projectName,
secretPath,
environment,
syncMessage,
integrationUrl
}: IntegrationSyncFailedTemplateProps) => {
return (
<BaseEmailWrapper
title="Integration Sync Failed"
preview="An integration sync has error has occurred."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
<strong>{count}</strong> integration(s) failed to sync
</Heading>
<Section className="px-[24px] mt-[36px] pt-[26px] pb-[4px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
<strong>Project</strong>
<Text className="text-[14px] mt-[4px]">{projectName}</Text>
<strong>Environment</strong>
<Text className="text-[14px] mt-[4px]">{environment}</Text>
<strong>Secret Path</strong>
<Text className="text-[14px] mt-[4px]">{secretPath}</Text>
<strong className="text-black">Failure Reason:</strong>
<Text className="text-[14px] mt-[4px] text-red-500 leading-[24px]">"{syncMessage}"</Text>
</Section>
<Section className="text-center mt-[28px]">
<Button
href={integrationUrl}
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
>
View Integrations
</Button>
</Section>
</BaseEmailWrapper>
);
};
export default IntegrationSyncFailedTemplate;
IntegrationSyncFailedTemplate.PreviewProps = {
projectName: "Example Project",
secretPath: "/api/secrets",
environment: "Production",
siteUrl: "https://infisical.com",
integrationUrl: "https://infisical.com",
count: 2,
syncMessage: "Secret key cannot contain a colon (:)"
} as IntegrationSyncFailedTemplateProps;
@@ -0,0 +1,68 @@
import { Heading, Link, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface NewDeviceLoginTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
email: string;
timestamp: string;
ip: string;
userAgent: string;
isCloud: boolean;
}
export const NewDeviceLoginTemplate = ({
email,
timestamp,
ip,
userAgent,
siteUrl,
isCloud
}: NewDeviceLoginTemplateProps) => {
return (
<BaseEmailWrapper
title="Successful Login from New Device"
preview="New device login from Infisical."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
We're verifying a recent login for
<br />
<strong>{email}</strong>
</Heading>
<Section className="px-[24px] mt-[36px] pt-[26px] pb-[4px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
<strong>Timestamp</strong>
<Text className="text-[14px] mt-[4px]">{timestamp}</Text>
<strong>IP Address</strong>
<Text className="text-[14px] mt-[4px]">{ip}</Text>
<strong>User Agent</strong>
<Text className="text-[14px] mt-[4px]">{userAgent}</Text>
</Section>
<Section className="mt-[24px] bg-gray-50 px-[24px] pt-[2px] pb-[16px] border border-solid border-gray-200 rounded-md text-gray-800">
<Text className="mb-[0px]">
If you believe that this login is suspicious, please contact{" "}
{isCloud ? (
<Link href={`mailto:[email protected]`} className="text-slate-700 no-underline">
support@infisical.com
</Link>
) : (
"your administrator"
)}{" "}
or reset your password immediately.
</Text>
</Section>
</BaseEmailWrapper>
);
};
export default NewDeviceLoginTemplate;
NewDeviceLoginTemplate.PreviewProps = {
email: "[email protected]",
ip: "127.0.0.1",
userAgent:
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3.1 Safari/605.1.15",
timestamp: "Tue Apr 29 2025 23:03:27 GMT+0000 (Coordinated Universal Time)",
isCloud: true,
siteUrl: "https://infisical.com"
} as NewDeviceLoginTemplateProps;
@@ -0,0 +1,57 @@
import { Heading, Link, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface OrgAdminBreakglassAccessTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
email: string;
timestamp: string;
ip: string;
userAgent: string;
}
export const OrgAdminBreakglassAccessTemplate = ({
email,
siteUrl,
timestamp,
ip,
userAgent
}: OrgAdminBreakglassAccessTemplateProps) => {
return (
<BaseEmailWrapper
title="Organization Admin has Bypassed SSO"
preview="An organization admin has bypassed SSO."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
The organization admin <strong>{email}</strong> has bypassed enforced SSO login.
</Heading>
<Section className="px-[24px] mt-[36px] pt-[24px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
<strong className="text-[14px]">Timestamp</strong>
<Text className="text-[14px] mt-[4px]">{timestamp}</Text>
<strong className="text-[14px]">IP Address</strong>
<Text className="text-[14px] mt-[4px]">{ip}</Text>
<strong className="text-[14px]">User Agent</strong>
<Text className="text-[14px] mt-[4px]">{userAgent}</Text>
<Text className="text-[14px]">
If you'd like to disable Admin SSO Bypass, please visit{" "}
<Link href={`${siteUrl}/organization/settings`} className="text-slate-700 no-underline">
Organization Security Settings
</Link>
.
</Text>
</Section>
</BaseEmailWrapper>
);
};
export default OrgAdminBreakglassAccessTemplate;
OrgAdminBreakglassAccessTemplate.PreviewProps = {
ip: "127.0.0.1",
userAgent:
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3.1 Safari/605.1.15",
timestamp: "Tue Apr 29 2025 23:03:27 GMT+0000 (Coordinated Universal Time)",
siteUrl: "https://infisical.com",
email: "[email protected]"
} as OrgAdminBreakglassAccessTemplateProps;
@@ -0,0 +1,40 @@
import { Heading, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface OrgAdminProjectGrantAccessTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview"> {
email: string;
projectName: string;
}
export const OrgAdminProjectGrantAccessTemplate = ({
email,
siteUrl,
projectName
}: OrgAdminProjectGrantAccessTemplateProps) => {
return (
<BaseEmailWrapper
title="Project Access Granted to Organization Admin"
preview="An organization admin has self-issued direct access to a project in Infisical."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
An organization admin has joined the project <strong>{projectName}</strong>
</Heading>
<Section className="px-[24px] mt-[36px] pt-[24px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-[14px] mt-[4px]">
The organization admin <strong>{email}</strong> has self-issued direct access to the project{" "}
<strong>{projectName}</strong>.
</Text>
</Section>
</BaseEmailWrapper>
);
};
export default OrgAdminProjectGrantAccessTemplate;
OrgAdminProjectGrantAccessTemplate.PreviewProps = {
email: "[email protected]",
projectName: "Example Project"
} as OrgAdminProjectGrantAccessTemplateProps;
@@ -0,0 +1,75 @@
import { Button, Heading, Link, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface OrganizationInvitationTemplateProps extends Omit<BaseEmailWrapperProps, "preview" | "title"> {
metadata?: string;
inviterFirstName: string;
inviterUsername: string;
organizationName: string;
email: string;
organizationId: string;
token: string;
callback_url: string;
}
export const OrganizationInvitationTemplate = ({
organizationName,
inviterFirstName,
inviterUsername,
token,
callback_url,
metadata,
email,
organizationId,
siteUrl
}: OrganizationInvitationTemplateProps) => {
return (
<BaseEmailWrapper
title="Organization Invitation"
preview="You've been invited to join an organization on Infisical."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
You've been invited to join
<br />
<strong>{organizationName}</strong> on <strong>Infisical</strong>
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border text-center border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-black text-[14px] leading-[24px]">
<strong>{inviterFirstName}</strong> (
<Link href={`mailto:${inviterUsername}`} className="text-slate-700 no-underline">
{inviterUsername}
</Link>
) has invited you to collaborate on <strong>{organizationName}</strong>.
</Text>
</Section>
<Section className="text-center mt-[28px]">
<Button
href={`${callback_url}?token=${token}${metadata ? `&metadata=${metadata}` : ""}&to=${email}&organization_id=${organizationId}`}
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
>
Accept Invite
</Button>
</Section>
<Section className="mt-[24px] bg-gray-50 pt-[2px] pb-[16px] border border-solid border-gray-200 px-[24px] rounded-md text-gray-800">
<Text className="mb-[0px]">
<strong>About Infisical:</strong> Infisical is an all-in-one platform to securely manage application secrets,
certificates, SSH keys, and configurations across your team and infrastructure.
</Text>
</Section>
</BaseEmailWrapper>
);
};
export default OrganizationInvitationTemplate;
OrganizationInvitationTemplate.PreviewProps = {
organizationName: "Example Organization",
inviterFirstName: "Jane",
inviterUsername: "[email protected]",
email: "[email protected]",
siteUrl: "https://infisical.com",
callback_url: "https://app.infisical.com"
} as OrganizationInvitationTemplateProps;
@@ -0,0 +1,58 @@
import { Button, Heading, Link, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface PasswordResetTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
email: string;
callback_url: string;
token: string;
isCloud: boolean;
}
export const PasswordResetTemplate = ({ email, isCloud, siteUrl, callback_url, token }: PasswordResetTemplateProps) => {
return (
<BaseEmailWrapper
title="Account Recovery"
preview="A password reset was requested for your Infisical account."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
<strong>Account Recovery</strong>
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-[14px]">A password reset was requested for your Infisical account.</Text>
<Text className="text-[14px]">
If you did not initiate this request, please contact{" "}
{isCloud ? (
<>
us immediately at{" "}
<Link href={`mailto:[email protected]`} className="text-slate-700 no-underline">
support@infisical.com
</Link>
</>
) : (
"your administrator immediately"
)}
.
</Text>
</Section>
<Section className="text-center mt-[28px]">
<Button
href={`${callback_url}?token=${token}&to=${email}`}
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
>
Reset Password
</Button>
</Section>
</BaseEmailWrapper>
);
};
export default PasswordResetTemplate;
PasswordResetTemplate.PreviewProps = {
email: "[email protected]",
callback_url: "https://app.infisical.com",
isCloud: true
} as PasswordResetTemplateProps;
@@ -0,0 +1,57 @@
import { Button, Heading, Link, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface PasswordSetupTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
email: string;
callback_url: string;
token: string;
isCloud: boolean;
}
export const PasswordSetupTemplate = ({ email, isCloud, siteUrl, callback_url, token }: PasswordSetupTemplateProps) => {
return (
<BaseEmailWrapper title="Password Setup" preview="Setup your password for Infisical." siteUrl={siteUrl}>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
<strong>Password Setup</strong>
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-[14px]">Someone requested to set up a password for your Infisical account.</Text>
<Text className="text-[14px] text-red-500">
Make sure you are already logged in to Infisical in the current browser before clicking the link below.
</Text>
<Text className="text-[14px]">
If you did not initiate this request, please contact{" "}
{isCloud ? (
<>
us immediately at{" "}
<Link href={`mailto:[email protected]`} className="text-slate-700 no-underline">
support@infisical.com
</Link>
</>
) : (
"your administrator immediately"
)}
.
</Text>
</Section>
<Section className="text-center mt-[28px]">
<Button
href={`${callback_url}?token=${token}&to=${email}`}
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
>
Set Up Password
</Button>
</Section>
</BaseEmailWrapper>
);
};
export default PasswordSetupTemplate;
PasswordSetupTemplate.PreviewProps = {
email: "[email protected]",
callback_url: "https://app.infisical.com",
isCloud: true
} as PasswordSetupTemplateProps;
@@ -0,0 +1,68 @@
import { Heading, Hr, Section, Text } from "@react-email/components";
import React, { Fragment } from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface PkiExpirationAlertTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
alertName: string;
alertBeforeDays: number;
items: { type: string; friendlyName: string; serialNumber: string; expiryDate: string }[];
}
export const PkiExpirationAlertTemplate = ({
alertName,
siteUrl,
alertBeforeDays,
items
}: PkiExpirationAlertTemplateProps) => {
return (
<BaseEmailWrapper
title="Infisical CA/Certificate Expiration Notice"
preview="One or more of your Infisical certificates is about to expire."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
<strong>CA/Certificate Expiration Notice</strong>
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text>Hello,</Text>
<Text className="text-black text-[14px] leading-[24px]">
This is an automated alert for <strong>{alertName}</strong> triggered for CAs/Certificates expiring in{" "}
<strong>{alertBeforeDays}</strong> days.
</Text>
<Text className="text-[14px] leading-[24px] mb-[4px]">
<strong>Expiring Items:</strong>
</Text>
{items.map((item) => (
<Fragment key={item.serialNumber}>
<Hr className="mb-[16px]" />
<strong className="text-[14px]">{item.type}:</strong>
<Text className="text-[14px] my-[2px] leading-[24px]">{item.friendlyName}</Text>
<strong className="text-[14px]">Serial Number:</strong>
<Text className="text-[14px] my-[2px] leading-[24px]">{item.serialNumber}</Text>
<strong className="text-[14px]">Expires On:</strong>
<Text className="text-[14px] mt-[2px] mb-[16px] leading-[24px]">{item.expiryDate}</Text>
</Fragment>
))}
<Hr />
<Text className="text-[14px] leading-[24px]">
Please take the necessary actions to renew these items before they expire.
</Text>
<Text className="text-[14px] leading-[24px]">
For more details, please log in to your Infisical account and check your PKI management section.
</Text>
</Section>
</BaseEmailWrapper>
);
};
export default PkiExpirationAlertTemplate;
PkiExpirationAlertTemplate.PreviewProps = {
alertBeforeDays: 5,
items: [
{ type: "CA", friendlyName: "Example CA", serialNumber: "1234567890", expiryDate: "2022-01-01" },
{ type: "Certificate", friendlyName: "Example Certificate", serialNumber: "1234567890", expiryDate: "2022-01-01" }
],
alertName: "My PKI Alert"
} as PkiExpirationAlertTemplateProps;
@@ -0,0 +1,68 @@
import { Button, Heading, Link, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface ProjectAccessRequestTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
projectName: string;
requesterName: string;
requesterEmail: string;
orgName: string;
environment: string;
note: string;
callback_url: string;
}
export const ProjectAccessRequestTemplate = ({
projectName,
siteUrl,
requesterName,
requesterEmail,
orgName,
note,
callback_url
}: ProjectAccessRequestTemplateProps) => {
return (
<BaseEmailWrapper
title="Project Access Request"
preview="A user has requested access to an Infisical project."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
A user has requested access to the project <strong>{projectName}</strong>
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-black text-[14px] leading-[24px]">
<strong>{requesterName}</strong> (
<Link href={`mailto:${requesterEmail}`} className="text-slate-700 no-underline">
{requesterEmail}
</Link>
) has requested access to the project <strong>{projectName}</strong> in the organization{" "}
<strong>{orgName}</strong>.
</Text>
<Text className="text-[14px] text-slate-700 leading-[24px]">
<strong className="text-black">User note:</strong> "{note}"
</Text>
</Section>
<Section className="text-center mt-[28px]">
<Button
href={callback_url}
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
>
Grant Access
</Button>
</Section>
</BaseEmailWrapper>
);
};
export default ProjectAccessRequestTemplate;
ProjectAccessRequestTemplate.PreviewProps = {
requesterName: "Abigail Williams",
requesterEmail: "[email protected]",
orgName: "Example Org",
siteUrl: "https://infisical.com",
projectName: "Example Project",
note: "I need access to the project for the new initiative for HR."
} as ProjectAccessRequestTemplateProps;
@@ -0,0 +1,50 @@
import { Button, Heading, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface ProjectInvitationTemplateProps extends Omit<BaseEmailWrapperProps, "preview" | "title"> {
callback_url: string;
workspaceName: string;
}
export const ProjectInvitationTemplate = ({ callback_url, workspaceName, siteUrl }: ProjectInvitationTemplateProps) => {
return (
<BaseEmailWrapper
title="Project Invitation"
preview="You've been invited to join a project on Infisical."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
You've been invited to join a new project on Infisical
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border text-center border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-black text-[14px] leading-[24px]">
You've been invited to join the project <strong>{workspaceName}</strong>.
</Text>
</Section>
<Section className="text-center mt-[28px]">
<Button
href={callback_url}
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
>
Join Project
</Button>
</Section>
<Section className="mt-[24px] bg-gray-50 pt-[2px] pb-[16px] border border-solid border-gray-200 px-[24px] rounded-md text-gray-800">
<Text className="mb-[0px]">
<strong>About Infisical:</strong> Infisical is an all-in-one platform to securely manage application secrets,
certificates, SSH keys, and configurations across your team and infrastructure.
</Text>
</Section>
</BaseEmailWrapper>
);
};
export default ProjectInvitationTemplate;
ProjectInvitationTemplate.PreviewProps = {
workspaceName: "Example Project",
siteUrl: "https://infisical.com",
callback_url: "https://app.infisical.com"
} as ProjectInvitationTemplateProps;
@@ -0,0 +1,56 @@
import { Button, Heading, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface ScimUserProvisionedTemplateProps extends Omit<BaseEmailWrapperProps, "preview" | "title"> {
organizationName: string;
callback_url: string;
}
export const ScimUserProvisionedTemplate = ({
organizationName,
callback_url,
siteUrl
}: ScimUserProvisionedTemplateProps) => {
return (
<BaseEmailWrapper
title="Organization Invitation"
preview="You've been invited to join an organization on Infisical."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
You've been invited to join
<br />
<strong>{organizationName}</strong> on <strong>Infisical</strong>
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border text-center border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-black text-[14px] leading-[24px]">
You've been invited you to collaborate on <strong>{organizationName}</strong>.
</Text>
</Section>
<Section className="text-center mt-[28px]">
<Button
href={callback_url}
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
>
Accept Invite
</Button>
</Section>
<Section className="mt-[24px] bg-gray-50 pt-[2px] pb-[16px] border border-solid border-gray-200 px-[24px] rounded-md text-gray-800">
<Text className="mb-[0px]">
<strong>About Infisical:</strong> Infisical is an all-in-one platform to securely manage application secrets,
certificates, SSH keys, and configurations across your team and infrastructure.
</Text>
</Section>
</BaseEmailWrapper>
);
};
export default ScimUserProvisionedTemplate;
ScimUserProvisionedTemplate.PreviewProps = {
organizationName: "Example Organization",
callback_url: "https://app.infisical.com",
siteUrl: "https://app.infisical.com"
} as ScimUserProvisionedTemplateProps;
@@ -0,0 +1,72 @@
import { Button, Heading, Link, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface SecretApprovalRequestBypassedTemplateProps
extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
projectName: string;
requesterFullName: string;
requesterEmail: string;
secretPath: string;
environment: string;
bypassReason: string;
approvalUrl: string;
}
export const SecretApprovalRequestBypassedTemplate = ({
projectName,
siteUrl,
requesterFullName,
requesterEmail,
secretPath,
environment,
bypassReason,
approvalUrl
}: SecretApprovalRequestBypassedTemplateProps) => {
return (
<BaseEmailWrapper
title="Secret Approval Request Bypassed"
preview="A secret approval request has been bypassed."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
A secret approval request has been bypassed in the project <strong>{projectName}</strong>
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-black text-[14px] leading-[24px]">
<strong>{requesterFullName}</strong> (
<Link href={`mailto:${requesterEmail}`} className="text-slate-700 no-underline">
{requesterEmail}
</Link>
) has merged a secret to <strong>{secretPath}</strong> in the <strong>{environment}</strong> environment
without obtaining the required approval.
</Text>
<Text className="text-[14px] text-slate-700 leading-[24px]">
<strong className="text-black">The following reason was provided for bypassing the policy:</strong> "
{bypassReason}"
</Text>
</Section>
<Section className="text-center mt-[28px]">
<Button
href={approvalUrl}
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
>
Review Bypass
</Button>
</Section>
</BaseEmailWrapper>
);
};
export default SecretApprovalRequestBypassedTemplate;
SecretApprovalRequestBypassedTemplate.PreviewProps = {
requesterFullName: "Abigail Williams",
requesterEmail: "[email protected]",
secretPath: "/api/secrets",
environment: "Production",
siteUrl: "https://infisical.com",
projectName: "Example Project",
bypassReason: "I needed urgent access for a production misconfiguration."
} as SecretApprovalRequestBypassedTemplateProps;
@@ -0,0 +1,57 @@
import { Button, Heading, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface SecretApprovalRequestNeedsReviewTemplateProps
extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
projectName: string;
firstName: string;
organizationName: string;
approvalUrl: string;
}
export const SecretApprovalRequestNeedsReviewTemplate = ({
projectName,
siteUrl,
firstName,
organizationName,
approvalUrl
}: SecretApprovalRequestNeedsReviewTemplateProps) => {
return (
<BaseEmailWrapper
title="Secret Change Approval Request"
preview="A secret change approval request requires review."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
A secret approval request for the project <strong>{projectName}</strong> requires review.
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-14px">Hello {firstName},</Text>
<Text className="text-black text-[14px] leading-[24px]">
You have a new secret change request pending your review for the project <strong>{projectName}</strong> in the
organization <strong>{organizationName}</strong>.
</Text>
</Section>
<Section className="text-center mt-[28px]">
<Button
href={approvalUrl}
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
>
Review Changes
</Button>
</Section>
</BaseEmailWrapper>
);
};
export default SecretApprovalRequestNeedsReviewTemplate;
SecretApprovalRequestNeedsReviewTemplate.PreviewProps = {
firstName: "Gordon",
organizationName: "Example Org",
siteUrl: "https://infisical.com",
approvalUrl: "https://infisical.com",
projectName: "Example Project"
} as SecretApprovalRequestNeedsReviewTemplateProps;
@@ -0,0 +1,82 @@
import { Button, Heading, Link, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface SecretLeakIncidentTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
numberOfSecrets: number;
pusher_email: string;
pusher_name: string;
}
export const SecretLeakIncidentTemplate = ({
numberOfSecrets,
siteUrl,
pusher_name,
pusher_email
}: SecretLeakIncidentTemplateProps) => {
return (
<BaseEmailWrapper
title="Incident Alert: Secret(s) Leaked"
preview="Infisical uncovered one or more leaked secrets."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
Infisical has uncovered <strong>{numberOfSecrets}</strong> secret(s) from a recent commit
</Heading>
<Section className="px-[24px] mt-[36px] pt-[8px] pb-[8px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-[14px]">
You are receiving this notification because one or more leaked secrets have been detected in a recent commit
{(pusher_email || pusher_name) && (
<>
{" "}
pushed by <strong>{pusher_name ?? "Unknown Pusher"}</strong>{" "}
{pusher_email && (
<>
(
<Link href={`mailto:${pusher_email}`} className="text-slate-700 no-underline">
{pusher_email}
</Link>
)
</>
)}
</>
)}
.
</Text>
<Text className="text-[14px]">
If these are test secrets, please add `infisical-scan:ignore` at the end of the line containing the secret as
a comment in the given programming language. This will prevent future notifications from being sent out for
these secrets.
</Text>
<Text className="text-[14px] text-red-500">
If these are production secrets, please rotate them immediately.
</Text>
<Text className="text-[14px]">
Once you have taken action, be sure to update the status of the risk in the{" "}
<Link href={`${siteUrl}/secret-scanning`} className="text-slate-700 no-underline">
Infisical Dashboard
</Link>
.
</Text>
</Section>
<Section className="text-center mt-[28px]">
<Button
href={`${siteUrl}/secret-scanning`}
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
>
View Leaked Secrets
</Button>
</Section>
</BaseEmailWrapper>
);
};
export default SecretLeakIncidentTemplate;
SecretLeakIncidentTemplate.PreviewProps = {
pusher_name: "Jim",
pusher_email: "[email protected]",
numberOfSecrets: 3,
siteUrl: "https://infisical.com"
} as SecretLeakIncidentTemplateProps;
@@ -0,0 +1,45 @@
import { Heading, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface SecretReminderTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
projectName: string;
organizationName: string;
reminderNote?: string;
}
export const SecretReminderTemplate = ({
siteUrl,
reminderNote,
projectName,
organizationName
}: SecretReminderTemplateProps) => {
return (
<BaseEmailWrapper title="Secret Reminder" preview="You have a new secret reminder." siteUrl={siteUrl}>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
Secret Reminder
</Heading>
<Section className="px-[24px] mt-[36px] pt-[8px] pb-[8px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-[14px]">
You have a new secret reminder from the project <strong>{projectName}</strong> in the{" "}
<strong>{organizationName}</strong> organization.
</Text>
{reminderNote && (
<Text className="text-[14px] text-slate-700">
<strong className="text-black">Reminder Note:</strong> "{reminderNote}"
</Text>
)}
</Section>
</BaseEmailWrapper>
);
};
export default SecretReminderTemplate;
SecretReminderTemplate.PreviewProps = {
reminderNote: "Remember to rotate secret.",
projectName: "Example Project",
organizationName: "Example Organization",
siteUrl: "https://infisical.com"
} as SecretReminderTemplateProps;
@@ -0,0 +1,53 @@
import { Button, Heading, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface ShareSecretTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
name?: string;
respondentUsername: string;
secretRequestUrl: string;
}
export const SecretRequestCompletedTemplate = ({
name,
siteUrl,
respondentUsername,
secretRequestUrl
}: ShareSecretTemplateProps) => {
return (
<BaseEmailWrapper title="Shared Secret" preview="A secret has been shared with you." siteUrl={siteUrl}>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
<strong>A secret has been shared with you</strong>
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] text-center pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-14px">
{respondentUsername ? <strong>{respondentUsername}</strong> : "Someone"} shared a secret{" "}
{name && (
<>
<strong>{name}</strong>{" "}
</>
)}{" "}
with you.
</Text>
</Section>
<Section className="text-center mt-[28px]">
<Button
href={secretRequestUrl}
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
>
View Secret
</Button>
</Section>
</BaseEmailWrapper>
);
};
export default SecretRequestCompletedTemplate;
SecretRequestCompletedTemplate.PreviewProps = {
respondentUsername: "Gracie",
siteUrl: "https://infisical.com",
secretRequestUrl: "https://infisical.com",
name: "API_TOKEN"
} as ShareSecretTemplateProps;
@@ -0,0 +1,68 @@
import { Button, Heading, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface SecretRotationFailedTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
rotationType: string;
rotationName: string;
rotationUrl: string;
projectName: string;
environment: string;
secretPath: string;
content: string;
}
export const SecretRotationFailedTemplate = ({
rotationType,
rotationName,
rotationUrl,
projectName,
siteUrl,
environment,
secretPath,
content
}: SecretRotationFailedTemplateProps) => {
return (
<BaseEmailWrapper title="Secret Rotation Failed" preview="A secret rotation failed." siteUrl={siteUrl}>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
Your <strong>{rotationType}</strong> Rotation <strong>{rotationName}</strong> failed to rotate
</Heading>
<Section className="px-[24px] mt-[36px] pt-[26px] pb-[4px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
<strong>Name</strong>
<Text className="text-[14px] mt-[4px]">{rotationName}</Text>
<strong>Type</strong>
<Text className="text-[14px] mt-[4px]">{rotationType}</Text>
<strong>Project</strong>
<Text className="text-[14px] mt-[4px]">{projectName}</Text>
<strong>Environment</strong>
<Text className="text-[14px] mt-[4px]">{environment}</Text>
<strong>Secret Path</strong>
<Text className="text-[14px] mt-[4px]">{secretPath}</Text>
<strong>Reason:</strong>
<Text className="text-[14px] text-red-500 mt-[4px]">{content}</Text>
</Section>
<Section className="text-center mt-[28px]">
<Button
href={`${rotationUrl}?search=${rotationName}&secretPath=${secretPath}`}
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
>
View in Infiscal
</Button>
</Section>
</BaseEmailWrapper>
);
};
export default SecretRotationFailedTemplate;
SecretRotationFailedTemplate.PreviewProps = {
rotationType: "Auth0 Client Secret",
rotationUrl: "https://infisical.com",
content: "See Rotation status for details",
projectName: "Example Project",
secretPath: "/api/secrets",
environment: "Production",
rotationName: "my-auth0-rotation",
siteUrl: "https://infisical.com"
} as SecretRotationFailedTemplateProps;
@@ -0,0 +1,80 @@
import { Button, Heading, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface SecretSyncFailedTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
syncDestination: string;
syncName: string;
syncUrl: string;
projectName: string;
environment: string;
secretPath: string;
failureMessage: string;
}
export const SecretSyncFailedTemplate = ({
syncDestination,
syncName,
syncUrl,
projectName,
siteUrl,
environment,
secretPath,
failureMessage
}: SecretSyncFailedTemplateProps) => {
return (
<BaseEmailWrapper title="Secret Sync Failed" preview="A secret sync failed." siteUrl={siteUrl}>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
Your <strong>{syncDestination}</strong> Sync <strong>{syncName}</strong> failed to sync
</Heading>
<Section className="px-[24px] mt-[36px] pt-[26px] pb-[4px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
<strong>Name</strong>
<Text className="text-[14px] mt-[4px]">{syncName}</Text>
<strong>Destination</strong>
<Text className="text-[14px] mt-[4px]">{syncDestination}</Text>
<strong>Project</strong>
<Text className="text-[14px] mt-[4px]">{projectName}</Text>
{environment && (
<>
<strong>Environment</strong>
<Text className="text-[14px] mt-[4px]">{environment}</Text>
</>
)}
{secretPath && (
<>
<strong>Secret Path</strong>
<Text className="text-[14px] mt-[4px]">{secretPath}</Text>
</>
)}
{failureMessage && (
<>
<strong>Reason:</strong>
<Text className="text-[14px] text-red-500 mt-[4px]">{failureMessage}</Text>
</>
)}
</Section>
<Section className="text-center mt-[28px]">
<Button
href={syncUrl}
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
>
View in Infiscal
</Button>
</Section>
</BaseEmailWrapper>
);
};
export default SecretSyncFailedTemplate;
SecretSyncFailedTemplate.PreviewProps = {
syncDestination: "AWS Parameter Store",
syncUrl: "https://infisical.com",
failureMessage: "Key name cannot contain a colon (:) or a forward slash (/).",
projectName: "Example Project",
secretPath: "/api/secrets",
environment: "Production",
syncName: "my-aws-sync",
siteUrl: "https://infisical.com"
} as SecretSyncFailedTemplateProps;
@@ -0,0 +1,53 @@
import { Button, Heading, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface ServiceTokenExpiryNoticeTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
tokenName: string;
projectName: string;
url: string;
}
export const ServiceTokenExpiryNoticeTemplate = ({
tokenName,
siteUrl,
projectName,
url
}: ServiceTokenExpiryNoticeTemplateProps) => {
return (
<BaseEmailWrapper
title="Service Token Expiring Soon"
preview="A service token is about to expire."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
<strong>Service token expiry notice</strong>
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-14px">
Your service token <strong>{tokenName}</strong> for the project <strong>{projectName}</strong> will expire
within 24 hours.
</Text>
<Text>If this token is still needed for your workflow, please create a new one before it expires.</Text>
</Section>
<Section className="text-center mt-[28px]">
<Button
href={url}
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
>
Create New Token
</Button>
</Section>
</BaseEmailWrapper>
);
};
export default ServiceTokenExpiryNoticeTemplate;
ServiceTokenExpiryNoticeTemplate.PreviewProps = {
projectName: "Example Project",
siteUrl: "https://infisical.com",
url: "https://infisical.com",
tokenName: "Example Token"
} as ServiceTokenExpiryNoticeTemplateProps;
@@ -0,0 +1,53 @@
import { Heading, Link, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface SignupEmailVerificationTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
code: string;
isCloud: boolean;
}
export const SignupEmailVerificationTemplate = ({ code, siteUrl, isCloud }: SignupEmailVerificationTemplateProps) => {
return (
<BaseEmailWrapper
title="Confirm Your Email Address"
preview="Verify your email address to get started with Infisical."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
<strong>Confirm your email address</strong>
</Heading>
<Section className="px-[24px] mt-[36px] pt-[8px] text-center pb-[8px] text-[14px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text>Enter the confirmation code below in the browser where you started sign-up.</Text>
<Text className="text-[24px] mt-[16px]">
<strong>{code}</strong>
</Text>
</Section>
<Section className="mt-[24px] bg-gray-50 pt-[2px] pb-[16px] border border-solid border-gray-200 px-[24px] rounded-md text-gray-800">
<Text className="mb-[0px]">
<strong>Questions about setting up Infisical?</strong>{" "}
{isCloud ? (
<>
Email us at{" "}
<Link href={`mailto:[email protected]`} className="text-slate-700 no-underline">
support@infisical.com
</Link>
</>
) : (
"Contact your administrator"
)}
.
</Text>
</Section>
</BaseEmailWrapper>
);
};
export default SignupEmailVerificationTemplate;
SignupEmailVerificationTemplate.PreviewProps = {
code: "124356",
isCloud: true,
siteUrl: "https://infisical.com"
} as SignupEmailVerificationTemplateProps;
@@ -0,0 +1,46 @@
import { Button, Heading, Section, Text } from "@react-email/components";
import React from "react";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface UnlockAccountTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
token: string;
callback_url: string;
}
export const UnlockAccountTemplate = ({ token, siteUrl, callback_url }: UnlockAccountTemplateProps) => {
return (
<BaseEmailWrapper
title="Your Infisical Account Has Been Locked"
preview="Unlock your Infisical account to continue."
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
<strong>Unlock your Infisical account</strong>
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-14px">
Your account has been temporarily locked due to multiple failed login attempts.
</Text>
<Text>If these attempts were not made by you, reset your password immediately.</Text>
</Section>
<Section className="text-center mt-[28px]">
<Button
href={`${callback_url}?token=${token}`}
className="rounded-md p-3 px-[28px] my-[8px] text-center text-[16px] bg-[#EBF852] border-solid border border-[#d1e309] text-black font-medium"
>
Unlock Account
</Button>
</Section>
</BaseEmailWrapper>
);
};
export default UnlockAccountTemplate;
UnlockAccountTemplate.PreviewProps = {
callback_url: "Example Project",
siteUrl: "https://infisical.com",
url: "https://infisical.com",
token: "Example Token"
} as UnlockAccountTemplateProps;
+28
View File
@@ -0,0 +1,28 @@
export * from "./AccessApprovalRequestTemplate";
export * from "./EmailMfaTemplate";
export * from "./EmailVerificationTemplate";
export * from "./ExternalImportFailedTemplate";
export * from "./ExternalImportStartedTemplate";
export * from "./ExternalImportSucceededTemplate";
export * from "./IntegrationSyncFailedTemplate";
export * from "./NewDeviceLoginTemplate";
export * from "./OrgAdminBreakglassAccessTemplate";
export * from "./OrgAdminProjectGrantAccessTemplate";
export * from "./OrganizationInvitationTemplate";
export * from "./OrganizationInvitationTemplate";
export * from "./PasswordResetTemplate";
export * from "./PasswordSetupTemplate";
export * from "./PkiExpirationAlertTemplate";
export * from "./ProjectAccessRequestTemplate";
export * from "./ProjectInvitationTemplate";
export * from "./ScimUserProvisionedTemplate";
export * from "./SecretApprovalRequestBypassedTemplate";
export * from "./SecretApprovalRequestNeedsReviewTemplate";
export * from "./SecretLeakIncidentTemplate";
export * from "./SecretReminderTemplate";
export * from "./SecretRequestCompletedTemplate";
export * from "./SecretRotationFailedTemplate";
export * from "./SecretSyncFailedTemplate";
export * from "./ServiceTokenExpiryNoticeTemplate";
export * from "./SignupEmailVerificationTemplate";
export * from "./UnlockAccountTemplate";