mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge branch 'main' into ENG-2647
This commit is contained in:
27
.github/workflows/release_helm_gateway.yaml
vendored
Normal file
27
.github/workflows/release_helm_gateway.yaml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Release Gateway Helm Chart
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release-helm:
|
||||
name: Release Helm Chart
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Helm
|
||||
uses: azure/setup-helm@v3
|
||||
with:
|
||||
version: v3.10.0
|
||||
|
||||
- name: Install python
|
||||
uses: actions/setup-python@v4
|
||||
|
||||
- name: Install Cloudsmith CLI
|
||||
run: pip install --upgrade cloudsmith-cli
|
||||
|
||||
- name: Build and push helm package to CloudSmith
|
||||
run: cd helm-charts && sh upload-gateway-cloudsmith.sh
|
||||
env:
|
||||
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
|
||||
@@ -69,6 +69,15 @@ module.exports = {
|
||||
["^\\."]
|
||||
]
|
||||
}
|
||||
],
|
||||
"import/extensions": [
|
||||
"error",
|
||||
"ignorePackages",
|
||||
{
|
||||
"": "never", // this is required to get the .tsx to work...
|
||||
ts: "never",
|
||||
tsx: "never"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
2493
backend/package-lock.json
generated
2493
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -72,7 +72,8 @@
|
||||
"seed:new": "tsx ./scripts/create-seed-file.ts",
|
||||
"seed": "knex --knexfile ./dist/db/knexfile.ts --client pg seed:run",
|
||||
"seed-dev": "knex --knexfile ./src/db/knexfile.ts --client pg seed:run",
|
||||
"db:reset": "npm run migration:rollback -- --all && npm run migration:latest"
|
||||
"db:reset": "npm run migration:rollback -- --all && npm run migration:latest",
|
||||
"email:dev": "email dev --dir src/services/smtp/emails"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
@@ -96,6 +97,7 @@
|
||||
"@types/picomatch": "^2.3.3",
|
||||
"@types/pkcs11js": "^1.0.4",
|
||||
"@types/prompt-sync": "^4.2.3",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/resolve": "^1.20.6",
|
||||
"@types/safe-regex": "^1.1.6",
|
||||
"@types/sjcl": "^1.0.34",
|
||||
@@ -115,6 +117,7 @@
|
||||
"nodemon": "^3.0.2",
|
||||
"pino-pretty": "^10.2.3",
|
||||
"prompt-sync": "^4.2.0",
|
||||
"react-email": "4.0.7",
|
||||
"rimraf": "^5.0.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsc-alias": "^1.8.8",
|
||||
@@ -164,6 +167,7 @@
|
||||
"@opentelemetry/semantic-conventions": "^1.27.0",
|
||||
"@peculiar/asn1-schema": "^2.3.8",
|
||||
"@peculiar/x509": "^1.12.1",
|
||||
"@react-email/components": "0.0.36",
|
||||
"@serdnam/pino-cloudwatch-transport": "^1.0.4",
|
||||
"@sindresorhus/slugify": "1.1.0",
|
||||
"@slack/oauth": "^3.0.2",
|
||||
@@ -223,6 +227,8 @@
|
||||
"posthog-node": "^3.6.2",
|
||||
"probot": "^13.3.8",
|
||||
"re2": "^1.21.4",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"safe-regex": "^2.1.1",
|
||||
"scim-patch": "^0.8.3",
|
||||
"scim2-parse-filter": "^0.2.10",
|
||||
|
||||
@@ -219,7 +219,7 @@ export const parseRotationErrorMessage = (err: unknown): string => {
|
||||
if (err instanceof AxiosError) {
|
||||
errorMessage += err?.response?.data
|
||||
? JSON.stringify(err?.response?.data)
|
||||
: err?.message ?? "An unknown error occurred.";
|
||||
: (err?.message ?? "An unknown error occurred.");
|
||||
} else {
|
||||
errorMessage += (err as Error)?.message || "An unknown error occurred.";
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ export const sshCertificateAuthorityServiceFactory = ({
|
||||
|
||||
// set [keyId] depending on if [allowCustomKeyIds] is true or false
|
||||
const keyId = sshCertificateTemplate.allowCustomKeyIds
|
||||
? requestedKeyId ?? `${actor}-${actorId}`
|
||||
? (requestedKeyId ?? `${actor}-${actorId}`)
|
||||
: `${actor}-${actorId}`;
|
||||
|
||||
const sshCaSecret = await sshCertificateAuthoritySecretDAL.findOne({ sshCaId: sshCertificateTemplate.sshCaId });
|
||||
@@ -404,7 +404,7 @@ export const sshCertificateAuthorityServiceFactory = ({
|
||||
|
||||
// set [keyId] depending on if [allowCustomKeyIds] is true or false
|
||||
const keyId = sshCertificateTemplate.allowCustomKeyIds
|
||||
? requestedKeyId ?? `${actor}-${actorId}`
|
||||
? (requestedKeyId ?? `${actor}-${actorId}`)
|
||||
: `${actor}-${actorId}`;
|
||||
|
||||
const sshCaSecret = await sshCertificateAuthoritySecretDAL.findOne({ sshCaId: sshCertificateTemplate.sshCaId });
|
||||
|
||||
@@ -401,8 +401,8 @@ export const authLoginServiceFactory = ({
|
||||
}
|
||||
|
||||
const shouldCheckMfa = selectedOrg.enforceMfa || user.isMfaEnabled;
|
||||
const orgMfaMethod = selectedOrg.enforceMfa ? selectedOrg.selectedMfaMethod ?? MfaMethod.EMAIL : undefined;
|
||||
const userMfaMethod = user.isMfaEnabled ? user.selectedMfaMethod ?? MfaMethod.EMAIL : undefined;
|
||||
const orgMfaMethod = selectedOrg.enforceMfa ? (selectedOrg.selectedMfaMethod ?? MfaMethod.EMAIL) : undefined;
|
||||
const userMfaMethod = user.isMfaEnabled ? (user.selectedMfaMethod ?? MfaMethod.EMAIL) : undefined;
|
||||
const mfaMethod = orgMfaMethod ?? userMfaMethod;
|
||||
|
||||
if (shouldCheckMfa && (!decodedToken.isMfaVerified || decodedToken.mfaMethod !== mfaMethod)) {
|
||||
@@ -573,9 +573,9 @@ export const authLoginServiceFactory = ({
|
||||
}: TVerifyMfaTokenDTO) => {
|
||||
const appCfg = getConfig();
|
||||
const user = await userDAL.findById(userId);
|
||||
enforceUserLockStatus(Boolean(user.isLocked), user.temporaryLockDateEnd);
|
||||
|
||||
try {
|
||||
enforceUserLockStatus(Boolean(user.isLocked), user.temporaryLockDateEnd);
|
||||
if (mfaMethod === MfaMethod.EMAIL) {
|
||||
await tokenService.validateTokenForUser({
|
||||
type: TokenType.TOKEN_EMAIL_MFA,
|
||||
|
||||
@@ -698,6 +698,8 @@ export const orgServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Member);
|
||||
|
||||
const invitingUser = await userDAL.findOne({ id: actorId });
|
||||
|
||||
const org = await orgDAL.findOrgById(orgId);
|
||||
|
||||
const [inviteeOrgMembership] = await orgDAL.findMembership({
|
||||
@@ -731,8 +733,8 @@ export const orgServiceFactory = ({
|
||||
subjectLine: "Infisical organization invitation",
|
||||
recipients: [inviteeOrgMembership.email as string],
|
||||
substitutions: {
|
||||
inviterFirstName: inviteeOrgMembership.firstName,
|
||||
inviterUsername: inviteeOrgMembership.email,
|
||||
inviterFirstName: invitingUser.firstName,
|
||||
inviterUsername: invitingUser.email,
|
||||
organizationName: org?.name,
|
||||
email: inviteeOrgMembership.email,
|
||||
organizationId: org?.id.toString(),
|
||||
@@ -761,6 +763,8 @@ export const orgServiceFactory = ({
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
|
||||
const invitingUser = await userDAL.findOne({ id: actorId });
|
||||
|
||||
const org = await orgDAL.findOrgById(orgId);
|
||||
|
||||
const isEmailInvalid = await isDisposableEmail(inviteeEmails);
|
||||
@@ -1179,8 +1183,8 @@ export const orgServiceFactory = ({
|
||||
subjectLine: "Infisical organization invitation",
|
||||
recipients: [el.email],
|
||||
substitutions: {
|
||||
inviterFirstName: el.firstName,
|
||||
inviterUsername: el.email,
|
||||
inviterFirstName: invitingUser.firstName,
|
||||
inviterUsername: invitingUser.email,
|
||||
organizationName: org?.name,
|
||||
email: el.email,
|
||||
organizationId: org?.id.toString(),
|
||||
|
||||
@@ -291,7 +291,7 @@ export const parseSyncErrorMessage = (err: unknown): string => {
|
||||
} else if (err instanceof AxiosError) {
|
||||
errorMessage = err?.response?.data
|
||||
? JSON.stringify(err?.response?.data)
|
||||
: err?.message ?? "An unknown error occurred.";
|
||||
: (err?.message ?? "An unknown error occurred.");
|
||||
} else {
|
||||
errorMessage = (err as Error)?.message || "An unknown error occurred.";
|
||||
}
|
||||
|
||||
@@ -834,7 +834,7 @@ export const secretSyncQueueFactory = ({
|
||||
secretPath: folder?.path,
|
||||
environment: environment?.name,
|
||||
projectName: project.name,
|
||||
syncUrl: `${appCfg.SITE_URL}/integrations/secret-syncs/${destination}/${secretSync.id}`
|
||||
syncUrl: `${appCfg.SITE_URL}/secret-manager/${projectId}/integrations/secret-syncs/${destination}/${secretSync.id}`
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -740,7 +740,7 @@ export const secretQueueFactory = ({
|
||||
environment: jobPayload.environmentName,
|
||||
count: jobPayload.count,
|
||||
projectName: project.name,
|
||||
integrationUrl: `${appCfg.SITE_URL}/integrations/${project.id}`
|
||||
integrationUrl: `${appCfg.SITE_URL}/secret-manager/${project.id}/integrations?selectedTab=native-integrations`
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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: "abigail@infisical.com",
|
||||
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;
|
||||
45
backend/src/services/smtp/emails/BaseEmailWrapper.tsx
Normal file
45
backend/src/services/smtp/emails/BaseEmailWrapper.tsx
Normal file
@@ -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>
|
||||
);
|
||||
};
|
||||
50
backend/src/services/smtp/emails/EmailMfaTemplate.tsx
Normal file
50
backend/src/services/smtp/emails/EmailMfaTemplate.tsx
Normal file
@@ -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 shown 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:support@infisical.com" 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 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 shown 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:support@infisical.com" 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:support@infisical.com" 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,31 @@
|
||||
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",
|
||||
siteUrl: "https://infisical.com"
|
||||
} 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 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;
|
||||
68
backend/src/services/smtp/emails/NewDeviceLoginTemplate.tsx
Normal file
68
backend/src/services/smtp/emails/NewDeviceLoginTemplate.tsx
Normal file
@@ -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:support@infisical.com" 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: "john@infisical.com",
|
||||
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: "august@infisical.com"
|
||||
} as OrgAdminBreakglassAccessTemplateProps;
|
||||
@@ -0,0 +1,41 @@
|
||||
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: "kevin@infisical.com",
|
||||
projectName: "Example Project",
|
||||
siteUrl: "https://infisical.com"
|
||||
} as OrgAdminProjectGrantAccessTemplateProps;
|
||||
@@ -0,0 +1,77 @@
|
||||
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=${encodeURIComponent(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: "jane@infisical.com",
|
||||
email: "john@infisical.com",
|
||||
siteUrl: "https://infisical.com",
|
||||
callback_url: "https://app.infisical.com",
|
||||
token: "preview-token",
|
||||
organizationId: "1ae1c2c7-8068-461c-b15e-421737868a6a"
|
||||
} as OrganizationInvitationTemplateProps;
|
||||
60
backend/src/services/smtp/emails/PasswordResetTemplate.tsx
Normal file
60
backend/src/services/smtp/emails/PasswordResetTemplate.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
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:support@infisical.com" 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=${encodeURIComponent(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: "kevin@infisical.com",
|
||||
callback_url: "https://app.infisical.com",
|
||||
isCloud: true,
|
||||
token: "preview-token",
|
||||
siteUrl: "https://infisical.com"
|
||||
} as PasswordResetTemplateProps;
|
||||
59
backend/src/services/smtp/emails/PasswordSetupTemplate.tsx
Normal file
59
backend/src/services/smtp/emails/PasswordSetupTemplate.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
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:support@infisical.com" 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=${encodeURIComponent(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: "casey@infisical.com",
|
||||
callback_url: "https://app.infisical.com",
|
||||
isCloud: true,
|
||||
siteUrl: "https://infisical.com",
|
||||
token: "preview-token"
|
||||
} as PasswordSetupTemplateProps;
|
||||
@@ -0,0 +1,69 @@
|
||||
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: "2032-01-01" },
|
||||
{ type: "Certificate", friendlyName: "Example Certificate", serialNumber: "2345678901", expiryDate: "2032-01-01" }
|
||||
],
|
||||
alertName: "My PKI Alert",
|
||||
siteUrl: "https://infisical.com"
|
||||
} 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;
|
||||
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: "abigail@infisical.com",
|
||||
orgName: "Example Org",
|
||||
siteUrl: "https://infisical.com",
|
||||
projectName: "Example Project",
|
||||
note: "I need access to the project for the new initiative for HR.",
|
||||
callback_url: "https://infisical.com"
|
||||
} 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 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 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: "abigail@infisical.com",
|
||||
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}/organization/secret-scanning`} className="text-slate-700 no-underline">
|
||||
Infisical Dashboard
|
||||
</Link>
|
||||
.
|
||||
</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[28px]">
|
||||
<Button
|
||||
href={`${siteUrl}/organization/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: "jim@infisical.com",
|
||||
numberOfSecrets: 3,
|
||||
siteUrl: "https://infisical.com"
|
||||
} as SecretLeakIncidentTemplateProps;
|
||||
45
backend/src/services/smtp/emails/SecretReminderTemplate.tsx
Normal file
45
backend/src/services/smtp/emails/SecretReminderTemplate.tsx
Normal file
@@ -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">
|
||||
<strong>Secret Reminder</strong>
|
||||
</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 SecretRequestCompletedTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
name?: string;
|
||||
respondentUsername: string;
|
||||
secretRequestUrl: string;
|
||||
}
|
||||
|
||||
export const SecretRequestCompletedTemplate = ({
|
||||
name,
|
||||
siteUrl,
|
||||
respondentUsername,
|
||||
secretRequestUrl
|
||||
}: SecretRequestCompletedTemplateProps) => {
|
||||
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 SecretRequestCompletedTemplateProps;
|
||||
@@ -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 Infisical
|
||||
</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 complete
|
||||
</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 Infisical
|
||||
</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 shown 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:support@infisical.com" 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;
|
||||
45
backend/src/services/smtp/emails/UnlockAccountTemplate.tsx
Normal file
45
backend/src/services/smtp/emails/UnlockAccountTemplate.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
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",
|
||||
token: "preview-token"
|
||||
} as UnlockAccountTemplateProps;
|
||||
27
backend/src/services/smtp/emails/index.ts
Normal file
27
backend/src/services/smtp/emails/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
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 "./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";
|
||||
@@ -1,13 +1,41 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
import handlebars from "handlebars";
|
||||
import { render } from "@react-email/components";
|
||||
import { createTransport } from "nodemailer";
|
||||
import SMTPTransport from "nodemailer/lib/smtp-transport";
|
||||
import React from "react";
|
||||
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { logger } from "@app/lib/logger";
|
||||
|
||||
import {
|
||||
AccessApprovalRequestTemplate,
|
||||
EmailMfaTemplate,
|
||||
EmailVerificationTemplate,
|
||||
ExternalImportFailedTemplate,
|
||||
ExternalImportStartedTemplate,
|
||||
ExternalImportSucceededTemplate,
|
||||
IntegrationSyncFailedTemplate,
|
||||
NewDeviceLoginTemplate,
|
||||
OrgAdminBreakglassAccessTemplate,
|
||||
OrgAdminProjectGrantAccessTemplate,
|
||||
OrganizationInvitationTemplate,
|
||||
PasswordResetTemplate,
|
||||
PasswordSetupTemplate,
|
||||
PkiExpirationAlertTemplate,
|
||||
ProjectAccessRequestTemplate,
|
||||
ProjectInvitationTemplate,
|
||||
ScimUserProvisionedTemplate,
|
||||
SecretApprovalRequestBypassedTemplate,
|
||||
SecretApprovalRequestNeedsReviewTemplate,
|
||||
SecretLeakIncidentTemplate,
|
||||
SecretReminderTemplate,
|
||||
SecretRequestCompletedTemplate,
|
||||
SecretRotationFailedTemplate,
|
||||
SecretSyncFailedTemplate,
|
||||
ServiceTokenExpiryNoticeTemplate,
|
||||
SignupEmailVerificationTemplate,
|
||||
UnlockAccountTemplate
|
||||
} from "./emails";
|
||||
|
||||
export type TSmtpConfig = SMTPTransport.Options;
|
||||
export type TSmtpSendMail = {
|
||||
template: SmtpTemplates;
|
||||
@@ -18,61 +46,96 @@ export type TSmtpSendMail = {
|
||||
export type TSmtpService = ReturnType<typeof smtpServiceFactory>;
|
||||
|
||||
export enum SmtpTemplates {
|
||||
SignupEmailVerification = "signupEmailVerification.handlebars",
|
||||
EmailVerification = "emailVerification.handlebars",
|
||||
SecretReminder = "secretReminder.handlebars",
|
||||
EmailMfa = "emailMfa.handlebars",
|
||||
UnlockAccount = "unlockAccount.handlebars",
|
||||
AccessApprovalRequest = "accessApprovalRequest.handlebars",
|
||||
AccessSecretRequestBypassed = "accessSecretRequestBypassed.handlebars",
|
||||
SecretApprovalRequestNeedsReview = "secretApprovalRequestNeedsReview.handlebars",
|
||||
HistoricalSecretList = "historicalSecretLeakIncident.handlebars",
|
||||
NewDeviceJoin = "newDevice.handlebars",
|
||||
OrgInvite = "organizationInvitation.handlebars",
|
||||
ResetPassword = "passwordReset.handlebars",
|
||||
SetupPassword = "passwordSetup.handlebars",
|
||||
SecretLeakIncident = "secretLeakIncident.handlebars",
|
||||
WorkspaceInvite = "workspaceInvitation.handlebars",
|
||||
ScimUserProvisioned = "scimUserProvisioned.handlebars",
|
||||
PkiExpirationAlert = "pkiExpirationAlert.handlebars",
|
||||
IntegrationSyncFailed = "integrationSyncFailed.handlebars",
|
||||
SecretSyncFailed = "secretSyncFailed.handlebars",
|
||||
ExternalImportSuccessful = "externalImportSuccessful.handlebars",
|
||||
ExternalImportFailed = "externalImportFailed.handlebars",
|
||||
ExternalImportStarted = "externalImportStarted.handlebars",
|
||||
SecretRequestCompleted = "secretRequestCompleted.handlebars",
|
||||
SecretRotationFailed = "secretRotationFailed.handlebars",
|
||||
ProjectAccessRequest = "projectAccess.handlebars",
|
||||
OrgAdminProjectDirectAccess = "orgAdminProjectGrantAccess.handlebars",
|
||||
OrgAdminBreakglassAccess = "orgAdminBreakglassAccess.handlebars",
|
||||
ServiceTokenExpired = "serviceTokenExpired.handlebars"
|
||||
SignupEmailVerification = "signupEmailVerification",
|
||||
EmailVerification = "emailVerification",
|
||||
SecretReminder = "secretReminder",
|
||||
EmailMfa = "emailMfa",
|
||||
UnlockAccount = "unlockAccount",
|
||||
AccessApprovalRequest = "accessApprovalRequest",
|
||||
AccessSecretRequestBypassed = "accessSecretRequestBypassed",
|
||||
SecretApprovalRequestNeedsReview = "secretApprovalRequestNeedsReview",
|
||||
// HistoricalSecretList = "historicalSecretLeakIncident", not used anymore?
|
||||
NewDeviceJoin = "newDevice",
|
||||
OrgInvite = "organizationInvitation",
|
||||
ResetPassword = "passwordReset",
|
||||
SetupPassword = "passwordSetup",
|
||||
SecretLeakIncident = "secretLeakIncident",
|
||||
WorkspaceInvite = "workspaceInvitation",
|
||||
ScimUserProvisioned = "scimUserProvisioned",
|
||||
PkiExpirationAlert = "pkiExpirationAlert",
|
||||
IntegrationSyncFailed = "integrationSyncFailed",
|
||||
SecretSyncFailed = "secretSyncFailed",
|
||||
ExternalImportSuccessful = "externalImportSuccessful",
|
||||
ExternalImportFailed = "externalImportFailed",
|
||||
ExternalImportStarted = "externalImportStarted",
|
||||
SecretRequestCompleted = "secretRequestCompleted",
|
||||
SecretRotationFailed = "secretRotationFailed",
|
||||
ProjectAccessRequest = "projectAccess",
|
||||
OrgAdminProjectDirectAccess = "orgAdminProjectGrantAccess",
|
||||
OrgAdminBreakglassAccess = "orgAdminBreakglassAccess",
|
||||
ServiceTokenExpired = "serviceTokenExpired"
|
||||
}
|
||||
|
||||
export enum SmtpHost {
|
||||
Sendgrid = "smtp.sendgrid.net",
|
||||
Mailgun = "smtp.mailgun.org",
|
||||
SocketLabs = "smtp.sockerlabs.com",
|
||||
SocketLabs = "smtp.socketlabs.com",
|
||||
Zohomail = "smtp.zoho.com",
|
||||
Gmail = "smtp.gmail.com",
|
||||
Office365 = "smtp.office365.com"
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const EmailTemplateMap: Record<SmtpTemplates, React.FC<any>> = {
|
||||
[SmtpTemplates.OrgInvite]: OrganizationInvitationTemplate,
|
||||
[SmtpTemplates.NewDeviceJoin]: NewDeviceLoginTemplate,
|
||||
[SmtpTemplates.SignupEmailVerification]: SignupEmailVerificationTemplate,
|
||||
[SmtpTemplates.EmailMfa]: EmailMfaTemplate,
|
||||
[SmtpTemplates.AccessApprovalRequest]: AccessApprovalRequestTemplate,
|
||||
[SmtpTemplates.EmailVerification]: EmailVerificationTemplate,
|
||||
[SmtpTemplates.ExternalImportFailed]: ExternalImportFailedTemplate,
|
||||
[SmtpTemplates.ExternalImportStarted]: ExternalImportStartedTemplate,
|
||||
[SmtpTemplates.ExternalImportSuccessful]: ExternalImportSucceededTemplate,
|
||||
[SmtpTemplates.AccessSecretRequestBypassed]: SecretApprovalRequestBypassedTemplate,
|
||||
[SmtpTemplates.IntegrationSyncFailed]: IntegrationSyncFailedTemplate,
|
||||
[SmtpTemplates.OrgAdminBreakglassAccess]: OrgAdminBreakglassAccessTemplate,
|
||||
[SmtpTemplates.SecretLeakIncident]: SecretLeakIncidentTemplate,
|
||||
[SmtpTemplates.WorkspaceInvite]: ProjectInvitationTemplate,
|
||||
[SmtpTemplates.ScimUserProvisioned]: ScimUserProvisionedTemplate,
|
||||
[SmtpTemplates.SecretRequestCompleted]: SecretRequestCompletedTemplate,
|
||||
[SmtpTemplates.UnlockAccount]: UnlockAccountTemplate,
|
||||
[SmtpTemplates.ServiceTokenExpired]: ServiceTokenExpiryNoticeTemplate,
|
||||
[SmtpTemplates.SecretReminder]: SecretReminderTemplate,
|
||||
[SmtpTemplates.SecretRotationFailed]: SecretRotationFailedTemplate,
|
||||
[SmtpTemplates.SecretSyncFailed]: SecretSyncFailedTemplate,
|
||||
[SmtpTemplates.OrgAdminProjectDirectAccess]: OrgAdminProjectGrantAccessTemplate,
|
||||
[SmtpTemplates.ProjectAccessRequest]: ProjectAccessRequestTemplate,
|
||||
[SmtpTemplates.SecretApprovalRequestNeedsReview]: SecretApprovalRequestNeedsReviewTemplate,
|
||||
[SmtpTemplates.ResetPassword]: PasswordResetTemplate,
|
||||
[SmtpTemplates.SetupPassword]: PasswordSetupTemplate,
|
||||
[SmtpTemplates.PkiExpirationAlert]: PkiExpirationAlertTemplate
|
||||
};
|
||||
|
||||
export const smtpServiceFactory = (cfg: TSmtpConfig) => {
|
||||
const smtp = createTransport(cfg);
|
||||
const isSmtpOn = Boolean(cfg.host);
|
||||
|
||||
handlebars.registerHelper("emailFooter", () => {
|
||||
const { SITE_URL } = getConfig();
|
||||
return new handlebars.SafeString(
|
||||
`<p style="font-size: 12px;">Email sent via Infisical at <a href="${SITE_URL}">${SITE_URL}</a></p>`
|
||||
);
|
||||
});
|
||||
|
||||
const sendMail = async ({ substitutions, recipients, template, subjectLine }: TSmtpSendMail) => {
|
||||
const appCfg = getConfig();
|
||||
const html = await fs.readFile(path.resolve(__dirname, "./templates/", template), "utf8");
|
||||
const temp = handlebars.compile(html);
|
||||
const htmlToSend = temp({ isCloud: appCfg.isCloud, siteUrl: appCfg.SITE_URL, ...substitutions });
|
||||
|
||||
const EmailTemplate = EmailTemplateMap[template];
|
||||
|
||||
if (!EmailTemplate) {
|
||||
throw new Error(`Email template ${template} not found`);
|
||||
}
|
||||
|
||||
const htmlToSend = await render(
|
||||
React.createElement(EmailTemplate, {
|
||||
...substitutions,
|
||||
isCloud: appCfg.isCloud,
|
||||
siteUrl: appCfg.SITE_URL
|
||||
})
|
||||
);
|
||||
|
||||
if (isSmtpOn) {
|
||||
await smtp.sendMail({
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Access Approval Request</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Infisical</h2>
|
||||
<h2>New access approval request pending your review</h2>
|
||||
<p>You have a new access approval request pending review in project "{{projectName}}".</p>
|
||||
|
||||
<p>
|
||||
{{requesterFullName}}
|
||||
({{requesterEmail}}) has requested
|
||||
{{#if isTemporary}}
|
||||
temporary
|
||||
{{else}}
|
||||
permanent
|
||||
{{/if}}
|
||||
access to
|
||||
{{secretPath}}
|
||||
in the
|
||||
{{environment}}
|
||||
environment.
|
||||
|
||||
{{#if isTemporary}}
|
||||
<br />
|
||||
This access will expire
|
||||
{{expiresIn}}
|
||||
after it has been approved.
|
||||
{{/if}}
|
||||
</p>
|
||||
<p>
|
||||
The following permissions are requested:
|
||||
<ul>
|
||||
{{#each permissions}}
|
||||
<li>{{this}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</p>
|
||||
{{#if note}}
|
||||
<p>User Note: "{{note}}"</p>
|
||||
{{/if}}
|
||||
|
||||
<p>
|
||||
View the request and approve or deny it
|
||||
<a href="{{approvalUrl}}">here</a>.
|
||||
</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,33 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Secret Approval Request Policy Bypassed</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Infisical</h1>
|
||||
<h2>Secret Approval Request Bypassed</h2>
|
||||
<p>A secret approval request has been bypassed in the project "{{projectName}}".</p>
|
||||
|
||||
<p>
|
||||
{{requesterFullName}}
|
||||
({{requesterEmail}}) has merged a secret to environment
|
||||
{{environment}}
|
||||
at secret path
|
||||
{{secretPath}}
|
||||
without obtaining the required approvals.
|
||||
</p>
|
||||
<p>
|
||||
The following reason was provided for bypassing the policy:
|
||||
<em>{{bypassReason}}</em>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To review this action, please visit the request panel
|
||||
<a href="{{approvalUrl}}">here</a>.
|
||||
</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,20 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>MFA Code</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Infisical</h2>
|
||||
<h2>Sign in attempt requires further verification</h2>
|
||||
<p>Your MFA code is below — enter it where you started signing in to Infisical.</p>
|
||||
<h2>{{code}}</h2>
|
||||
<p>The MFA code will be valid for 2 minutes.</p>
|
||||
<p>Not you? Contact {{#if isCloud}}Infisical{{else}}your administrator{{/if}} immediately.</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,17 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Code</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Confirm your email address</h2>
|
||||
<p>Your confirmation code is below — enter it in the browser window where you've started confirming your email.</p>
|
||||
<h1>{{code}}</h1>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,22 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Import failed</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>An import from {{provider}} to Infisical has failed</h2>
|
||||
<p>An import from
|
||||
{{provider}}
|
||||
to Infisical has failed due to unforeseen circumstances. Please re-try your import, and if the issue persists, you
|
||||
can contact the Infisical team at team@infisical.com.
|
||||
</p>
|
||||
|
||||
<p>Error: {{error}}</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,19 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Import in progress</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>An import from {{provider}} to Infisical is in progress</h2>
|
||||
<p>An import from
|
||||
{{provider}}
|
||||
to Infisical is in progress. The import process may take up to 30 minutes, and you will receive once the import
|
||||
has finished or if it fails.</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Import successful</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>An import from {{provider}} to Infisical was successful</h2>
|
||||
<p>An import from {{provider}} was successful. Your data is now available in Infisical.</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,21 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Incident alert: secrets potentially leaked</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h3>Infisical has uncovered {{numberOfSecrets}} secret(s) from historical commits to your repo</h3>
|
||||
<p><a href="{{siteUrl}}/secret-scanning"><strong>View leaked secrets</strong></a></p>
|
||||
|
||||
<p>If these are production secrets, please rotate them immediately.</p>
|
||||
|
||||
<p>Once you have taken action, be sure to update the status of the risk in your
|
||||
<a href="{{siteUrl}}">Infisical dashboard</a>.</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,33 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Integration Sync Failed</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Infisical</h2>
|
||||
|
||||
<div>
|
||||
<p>{{count}} integration(s) failed to sync.</p>
|
||||
<a href="{{integrationUrl}}">
|
||||
View your project integrations.
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<div>
|
||||
<p><strong>Project</strong>: {{projectName}}</p>
|
||||
<p><strong>Environment</strong>: {{environment}}</p>
|
||||
<p><strong>Secret Path</strong>: {{secretPath}}</p>
|
||||
</div>
|
||||
|
||||
{{#if syncMessage}}
|
||||
<p><b>Reason: </b>{{syncMessage}}</p>
|
||||
{{/if}}
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,22 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Successful login for {{email}} from new device</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Infisical</h2>
|
||||
<p>We're verifying a recent login for {{email}}:</p>
|
||||
<p><strong>Timestamp</strong>: {{timestamp}}</p>
|
||||
<p><strong>IP address</strong>: {{ip}}</p>
|
||||
<p><strong>User agent</strong>: {{userAgent}}</p>
|
||||
<p>If you believe that this login is suspicious, please contact
|
||||
{{#if isCloud}}Infisical{{else}}your administrator{{/if}}
|
||||
or reset your password immediately.</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,20 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Organization admin has bypassed SSO</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Infisical</h2>
|
||||
<p>The organization admin {{email}} has bypassed enforced SSO login.</p>
|
||||
<p><strong>Timestamp</strong>: {{timestamp}}</p>
|
||||
<p><strong>IP address</strong>: {{ip}}</p>
|
||||
<p><strong>User agent</strong>: {{userAgent}}</p>
|
||||
<p>If you'd like to disable Admin SSO Bypass, please visit <a href="{{siteUrl}}/organization/settings">Organization Settings</a> > Security.</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Organization admin issued direct access to project</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Infisical</h2>
|
||||
<p>The organization admin {{email}} has granted direct access to the project "{{projectName}}".</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,18 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
<title>Organization Invitation</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Join your organization on Infisical</h2>
|
||||
<p>{{inviterFirstName}} ({{inviterUsername}}) has invited you to their Infisical organization named {{organizationName}}</p>
|
||||
<a href="{{callback_url}}?token={{token}}{{#if metadata}}&metadata={{metadata}}{{/if}}&to={{email}}&organization_id={{organizationId}}">Click to join</a>
|
||||
<h3>What is Infisical?</h3>
|
||||
<p>Infisical is an easy-to-use end-to-end encrypted tool that enables developers to sync and manage their secrets and configs.</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Account Recovery</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Reset your password</h2>
|
||||
<p>Someone requested a password reset.</p>
|
||||
<a href="{{callback_url}}?token={{token}}&to={{email}}">Reset password</a>
|
||||
<p>If you didn't initiate this request, please contact
|
||||
{{#if isCloud}}us immediately at team@infisical.com.{{else}}your administrator immediately.{{/if}}</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,17 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Password Setup</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Setup your password</h2>
|
||||
<p>Someone requested to set up a password for your account.</p>
|
||||
<p><strong>Make sure you are already logged in to Infisical in the current browser before clicking the link below.</strong></p>
|
||||
<a href="{{callback_url}}?token={{token}}&to={{email}}">Setup password</a>
|
||||
<p>If you didn't initiate this request, please contact
|
||||
{{#if isCloud}}us immediately at team@infisical.com.{{else}}your administrator immediately.{{/if}}</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,33 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Infisical CA/Certificate expiration notice</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Hello,</p>
|
||||
<p>This is an automated alert for "{{alertName}}" triggered for CAs/Certificates expiring in
|
||||
{{alertBeforeDays}}
|
||||
days.</p>
|
||||
|
||||
<p>Expiring Items:</p>
|
||||
<ul>
|
||||
{{#each items}}
|
||||
<li>
|
||||
{{type}}:
|
||||
<strong>{{friendlyName}}</strong>
|
||||
<br />Serial Number:
|
||||
{{serialNumber}}
|
||||
<br />Expires On:
|
||||
{{expiryDate}}
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
<p>Please take necessary actions to renew these items before they expire.</p>
|
||||
|
||||
<p>For more details, please log in to your Infisical account and check your PKI management section.</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,26 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Project Access Request</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Infisical</h2>
|
||||
<h2>You have a new project access request!</h2>
|
||||
<ul>
|
||||
<li>Requester Name: "{{requesterName}}"</li>
|
||||
<li>Requester Email: "{{requesterEmail}}"</li>
|
||||
<li>Project Name: "{{projectName}}"</li>
|
||||
<li>Organization Name: "{{orgName}}"</li>
|
||||
<li>User Note: "{{note}}"</li>
|
||||
</ul>
|
||||
<p>
|
||||
Please click on the link below to grant access
|
||||
</p>
|
||||
<a href="{{callback_url}}">Grant Access</a>
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,18 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Organization Invitation</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Join your organization on Infisical</h2>
|
||||
<p>You've been invited to join the Infisical organization — {{organizationName}}</p>
|
||||
<a href="{{callback_url}}">Join now</a>
|
||||
<h3>What is Infisical?</h3>
|
||||
<p>Infisical is an easy-to-use end-to-end encrypted tool that enables developers to sync and manage their secrets
|
||||
and configs.</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,24 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Secret Change Approval Request</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Hi {{firstName}},</h2>
|
||||
<h2>New secret change requests are pending review.</h2>
|
||||
<br />
|
||||
<p>You have a secret change request pending your review in project "{{projectName}}", in the "{{organizationName}}"
|
||||
organization.</p>
|
||||
|
||||
<p>
|
||||
View the request and approve or deny it
|
||||
<a href="{{approvalUrl}}">here</a>.
|
||||
</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,27 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Incident alert: secret leaked</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h3>Infisical has uncovered {{numberOfSecrets}} secret(s) from your recent push</h3>
|
||||
<p><a href="{{siteUrl}}/secret-scanning"><strong>View leaked secrets</strong></a></p>
|
||||
<p>You are receiving this notification because one or more secret leaks have been detected in a recent commit pushed
|
||||
by
|
||||
{{pusher_name}}
|
||||
({{pusher_email}}). If these are test secrets, please add `infisical-scan:ignore` at the end of the line
|
||||
containing the secret as comment in the given programming. This will prevent future notifications from being sent
|
||||
out for those secret(s).</p>
|
||||
|
||||
<p>If these are production secrets, please rotate them immediately.</p>
|
||||
|
||||
<p>Once you have taken action, be sure to update the status of the risk in your
|
||||
<a href="{{siteUrl}}">Infisical dashboard</a>.</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,20 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Secret Reminder</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Infisical</h2>
|
||||
<h2>You have a new secret reminder!</h2>
|
||||
<p>You have a new secret reminder from project "{{projectName}}", in {{organizationName}}</p>
|
||||
{{#if reminderNote}}
|
||||
<p>Here's the note included with the reminder: {{reminderNote}}</p>
|
||||
{{/if}}
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,33 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Secret Request Completed</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Infisical</h2>
|
||||
<h2>A secret has been shared with you</h2>
|
||||
|
||||
{{#if name}}
|
||||
<p>Secret request name: {{name}}</p>
|
||||
{{/if}}
|
||||
{{#if respondentUsername}}
|
||||
<p>Shared by: {{respondentUsername}}</p>
|
||||
{{/if}}
|
||||
|
||||
<br />
|
||||
<br/>
|
||||
|
||||
<p>
|
||||
You can access the secret by clicking the link below.
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{secretRequestUrl}}">Access Secret</a>
|
||||
</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,31 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Your {{rotationType}} Rotation "{{rotationName}}" Failed to Rotate</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Infisical</h2>
|
||||
|
||||
<div>
|
||||
<p>{{content}}</p>
|
||||
<a href="{{rotationUrl}}?search={{rotationName}}&secretPath={{secretPath}}">
|
||||
View in Infisical to see the cause of failure.
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<div>
|
||||
<p><strong>Name</strong>: {{rotationName}}</p>
|
||||
<p><strong>Type</strong>: {{rotationType}}</p>
|
||||
<p><strong>Project</strong>: {{projectName}}</p>
|
||||
<p><strong>Environment</strong>: {{environment}}</p>
|
||||
<p><strong>Secret Path</strong>: {{secretPath}}</p>
|
||||
</div>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,39 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>{{syncDestination}} Sync "{{syncName}}" Failed</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Infisical</h2>
|
||||
|
||||
<div>
|
||||
<p>{{content}}</p>
|
||||
<a href="{{syncUrl}}">
|
||||
View in Infisical.
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<div>
|
||||
<p><strong>Name</strong>: {{syncName}}</p>
|
||||
<p><strong>Destination</strong>: {{syncDestination}}</p>
|
||||
<p><strong>Project</strong>: {{projectName}}</p>
|
||||
{{#if environment}}
|
||||
<p><strong>Environment</strong>: {{environment}}</p>
|
||||
{{/if}}
|
||||
{{#if secretPath}}
|
||||
<p><strong>Secret Path</strong>: {{secretPath}}</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if failureMessage}}
|
||||
<p><b>Reason: </b>{{failureMessage}}</p>
|
||||
{{/if}}
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,19 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Service Token Expiring Soon</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Service Token Expiry Notice</h2>
|
||||
<p>Your service token <strong>"{{tokenName}}"</strong> will expire within 24 hours.</p>
|
||||
|
||||
<p>This token is currently being used on project "{{projectName}}". If this token is still needed for your workflow, please create a new one before it expires.</p>
|
||||
|
||||
<a href="{{url}}">Create New Token</a>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,19 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Code</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Confirm your email address</h2>
|
||||
<p>Your confirmation code is below — enter it in the browser window where you've started signing up for Infisical.</p>
|
||||
<h1>{{code}}</h1>
|
||||
<p>Questions about setting up Infisical?
|
||||
{{#if isCloud}}Email us at support@infisical.com{{else}}Contact your administrator{{/if}}.</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,18 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Your Infisical account has been locked</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Unlock your Infisical account</h2>
|
||||
<p>Your account has been temporarily locked due to multiple failed login attempts. </h2>
|
||||
<a href="{{callback_url}}?token={{token}}">To unlock your account, follow the link here</a>
|
||||
<p>If these attempts were not made by you, reset your password immediately.</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,17 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>Project Invitation</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Join your team on Infisical</h2>
|
||||
<p>You have been invited to a new Infisical project named {{workspaceName}}</p>
|
||||
<a href="{{callback_url}}">Click to join</a>
|
||||
<h3>What is Infisical?</h3>
|
||||
<p>Infisical is an easy-to-use end-to-end encrypted tool that enables developers to sync and manage their secrets
|
||||
and configs.</p>
|
||||
|
||||
{{emailFooter}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -176,8 +176,8 @@ export const superAdminServiceFactory = ({
|
||||
|
||||
const canServerAdminAccessAfterApply =
|
||||
data.enabledLoginMethods.some((loginMethod) =>
|
||||
loginMethodToAuthMethod[loginMethod as LoginMethod].some(
|
||||
(authMethod) => superAdminUser.authMethods?.includes(authMethod)
|
||||
loginMethodToAuthMethod[loginMethod as LoginMethod].some((authMethod) =>
|
||||
superAdminUser.authMethods?.includes(authMethod)
|
||||
)
|
||||
) ||
|
||||
isUserSamlAccessEnabled ||
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@app/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src/**/*", "scripts/**/*", "e2e-test/**/*", "./.eslintrc.js", "./tsup.config.js"],
|
||||
"exclude": ["node_modules"]
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import path from "node:path";
|
||||
|
||||
import fs from "fs/promises";
|
||||
import { replaceTscAliasPaths } from "tsc-alias";
|
||||
import { defineConfig } from "tsup";
|
||||
import {replaceTscAliasPaths} from "tsc-alias";
|
||||
import {defineConfig} from "tsup";
|
||||
|
||||
// Instead of using tsx or tsc for building, consider using tsup.
|
||||
// TSX serves as an alternative to Node.js, allowing you to build directly on the Node.js runtime.
|
||||
@@ -50,7 +50,17 @@ export default defineConfig({
|
||||
const isFile = await fs
|
||||
.stat(`${absPath}.ts`)
|
||||
.then((el) => el.isFile)
|
||||
.catch((err) => err.code === "ENOTDIR");
|
||||
.catch(async (err) => {
|
||||
if (err.code === "ENOTDIR") {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If .ts file doesn't exist, try checking for .tsx file
|
||||
return fs
|
||||
.stat(`${absPath}.tsx`)
|
||||
.then((el) => el.isFile)
|
||||
.catch((err) => err.code === "ENOTDIR");
|
||||
});
|
||||
|
||||
return {
|
||||
path: isFile ? `${args.path}.mjs` : `${args.path}/index.mjs`,
|
||||
|
||||
@@ -73,6 +73,61 @@ Once authenticated, the Gateway establishes a secure connection with Infisical t
|
||||
</Warning>
|
||||
</Tab>
|
||||
|
||||
<Tab title="Production (Helm)">
|
||||
|
||||
The Gateway can be installed via [Helm](https://helm.sh/). Helm is a package manager for Kubernetes that allows you to define, install, and upgrade Kubernetes applications.
|
||||
|
||||
For production deployments on Kubernetes, install the Gateway using the Infisical Helm chart:
|
||||
|
||||
### Install the latest Helm Chart repository
|
||||
```bash
|
||||
helm repo add infisical-helm-charts 'https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/'
|
||||
```
|
||||
|
||||
### Update the Helm Chart repository
|
||||
```bash
|
||||
helm repo update
|
||||
```
|
||||
|
||||
### Create a Kubernetes Secret with the gateway token
|
||||
|
||||
Create a new Kubernetes secret containing the gateway token as the `TOKEN` key. You can optionally also set the `INFISICAL_API_URL` key to your Infisical instance URL. By default, `INFISICAL_API_URL` is set to `https://app.infisical.com`.
|
||||
|
||||
|
||||
```bash
|
||||
kubectl create secret generic infisical-gateway-environment --from-literal=TOKEN=<your-machine-identity-access-token>
|
||||
```
|
||||
|
||||
<Note>
|
||||
The secret name is `infisical-gateway-environment` by default. The `TOKEN` key is required, and the `INFISICAL_API_URL` key is optional.
|
||||
</Note>
|
||||
|
||||
### Install the Infisical Gateway Helm Chart
|
||||
```bash
|
||||
helm install infisical-gateway infisical-helm-charts/infisical-gateway
|
||||
```
|
||||
|
||||
### Check the gateway logs
|
||||
After installing the gateway, you can check the logs to ensure it's running as expected.
|
||||
|
||||
```bash
|
||||
kubectl logs deployment/infisical-gateway
|
||||
```
|
||||
|
||||
You should see the following output which indicates the gateway is running as expected.
|
||||
```bash
|
||||
$ kubectl logs deployment/infisical-gateway
|
||||
INF Provided relay port 5349. Using TLS
|
||||
INF Connected with relay
|
||||
INF 10.0.101.112:56735
|
||||
INF Starting relay connection health check
|
||||
INF Gateway started successfully
|
||||
INF New connection from: 10.0.1.8:34051
|
||||
INF Gateway is reachable by Infisical
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="Development (direct)">
|
||||
For development or testing, you can run the Gateway directly. Log in with your machine identity and start the Gateway in one command:
|
||||
```bash
|
||||
|
||||
@@ -1230,13 +1230,13 @@ To address this, we added functionality to automatically redeploy your deploymen
|
||||
|
||||
#### Enabling Automatic Redeployment
|
||||
|
||||
To enable auto redeployment you simply have to add the following annotation to the deployment, statefulset, or daemonset that consumes a managed secret.
|
||||
To enable auto redeployment you simply have to add the following annotation to the Deployment, StatefulSet, or DaemonSet that consumes a managed secret.
|
||||
|
||||
```yaml
|
||||
secrets.infisical.com/auto-reload: "true"
|
||||
```
|
||||
|
||||
<Accordion title="Deployment example with auto redeploy enabled">
|
||||
<Accordion title="Deployment example">
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -1266,10 +1266,82 @@ secrets.infisical.com/auto-reload: "true"
|
||||
- containerPort: 80
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="DaemonSet example">
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: log-agent
|
||||
labels:
|
||||
app: log-agent
|
||||
annotations:
|
||||
secrets.infisical.com/auto-reload: "true" # <- redeployment annotation
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: log-agent
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: log-agent
|
||||
spec:
|
||||
containers:
|
||||
- name: log-agent
|
||||
image: mycompany/log-agent:latest
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: managed-secret # <- name of the managed secret
|
||||
volumeMounts:
|
||||
- name: config-volume
|
||||
mountPath: /etc/config
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: config-volume
|
||||
secret:
|
||||
secretName: managed-secret
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="StatefulSet example">
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: db-worker
|
||||
labels:
|
||||
app: db-worker
|
||||
annotations:
|
||||
secrets.infisical.com/auto-reload: "true" # <- redeployment annotation
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: db-worker
|
||||
serviceName: "db-worker"
|
||||
replicas: 2
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: db-worker
|
||||
spec:
|
||||
containers:
|
||||
- name: db-worker
|
||||
image: mycompany/db-worker:stable
|
||||
env:
|
||||
- name: DATABASE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: managed-secret
|
||||
key: DB_PASSWORD
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Info>
|
||||
#### How it works
|
||||
When a secret change occurs, the operator will check to see which deployments are using the operator-managed Kubernetes secret that received the update.
|
||||
Then, for each deployment that has this annotation present, a rolling update will be triggered.
|
||||
When a managed secret is updated, the operator checks for any Deployments, DaemonSets, or StatefulSets that consume the updated secret and have the annotation
|
||||
`secrets.infisical.com/auto-reload: "true"`. For each matching workload, the operator triggers a rolling restart to ensure it picks up the latest secret values.
|
||||
</Info>
|
||||
|
||||
## Using Managed ConfigMap In Your Deployment
|
||||
|
||||
23
helm-charts/infisical-gateway/.helmignore
Normal file
23
helm-charts/infisical-gateway/.helmignore
Normal file
@@ -0,0 +1,23 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
||||
3
helm-charts/infisical-gateway/CHANGELOG.md
Normal file
3
helm-charts/infisical-gateway/CHANGELOG.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.0.1 (May 1, 2025)
|
||||
|
||||
* Initial helm release
|
||||
24
helm-charts/infisical-gateway/Chart.yaml
Normal file
24
helm-charts/infisical-gateway/Chart.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
apiVersion: v2
|
||||
name: infisical-gateway
|
||||
description: A Helm chart to deploy Infisical Gateway
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||
# to be deployed.
|
||||
#
|
||||
# Library charts provide useful utilities or functions for the chart developer. They're included as
|
||||
# a dependency of application charts to inject those utilities and functions into the rendering
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.0.1
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "0.0.1"
|
||||
62
helm-charts/infisical-gateway/templates/_helpers.tpl
Normal file
62
helm-charts/infisical-gateway/templates/_helpers.tpl
Normal file
@@ -0,0 +1,62 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "infisical-gateway.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "infisical-gateway.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "infisical-gateway.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "infisical-gateway.labels" -}}
|
||||
helm.sh/chart: {{ include "infisical-gateway.chart" . }}
|
||||
{{ include "infisical-gateway.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "infisical-gateway.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "infisical-gateway.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "infisical-gateway.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "infisical-gateway.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
73
helm-charts/infisical-gateway/templates/deployment.yaml
Normal file
73
helm-charts/infisical-gateway/templates/deployment.yaml
Normal file
@@ -0,0 +1,73 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "infisical-gateway.fullname" . }}
|
||||
labels:
|
||||
{{- include "infisical-gateway.labels" . | nindent 4 }}
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "infisical-gateway.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "infisical-gateway.labels" . | nindent 8 }}
|
||||
{{- with .Values.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "infisical-gateway.serviceAccountName" . }}
|
||||
{{- with .Values.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
{{- with .Values.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
image: "infisical/cli:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
args:
|
||||
- gateway
|
||||
- --token
|
||||
- $(TOKEN)
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: {{ .Values.secret.name }}
|
||||
env:
|
||||
- name: TOKEN_VALIDATION
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.secret.name }}
|
||||
key: TOKEN
|
||||
optional: false
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.service.port }}
|
||||
protocol: TCP
|
||||
{{- with .Values.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
15
helm-charts/infisical-gateway/templates/service.yaml
Normal file
15
helm-charts/infisical-gateway/templates/service.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "infisical-gateway.fullname" . }}
|
||||
labels:
|
||||
{{- include "infisical-gateway.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
{{- include "infisical-gateway.selectorLabels" . | nindent 4 }}
|
||||
13
helm-charts/infisical-gateway/templates/serviceaccount.yaml
Normal file
13
helm-charts/infisical-gateway/templates/serviceaccount.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
{{- if .Values.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "infisical-gateway.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "infisical-gateway.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
automountServiceAccountToken: {{ .Values.serviceAccount.automount }}
|
||||
{{- end }}
|
||||
44
helm-charts/infisical-gateway/values.yaml
Normal file
44
helm-charts/infisical-gateway/values.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
image:
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "0.41.1"
|
||||
|
||||
secret:
|
||||
# The secret that contains the environment variables to be used by the gateway, such as INFISICAL_API_URL and TOKEN
|
||||
name: "infisical-gateway-environment"
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 128Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
serviceAccount:
|
||||
create: true
|
||||
automount: true
|
||||
annotations: {}
|
||||
name: ""
|
||||
|
||||
podAnnotations: {}
|
||||
podLabels: {}
|
||||
podSecurityContext:
|
||||
runAsNonRoot: true
|
||||
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
|
||||
affinity: {}
|
||||
tolerations: {}
|
||||
nodeSelector: {}
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
8
helm-charts/upload-gateway-cloudsmith.sh
Executable file
8
helm-charts/upload-gateway-cloudsmith.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
cd infisical-gateway
|
||||
helm dependency update
|
||||
helm package .
|
||||
for i in *.tgz; do
|
||||
[ -f "$i" ] || break
|
||||
cloudsmith push helm --republish infisical/helm-charts "$i"
|
||||
done
|
||||
cd ..
|
||||
Reference in New Issue
Block a user