refactor(server): remove files
2
apps/dokploy/emails/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
/node_modules
|
||||
/dist
|
@ -1,113 +0,0 @@
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Container,
|
||||
Head,
|
||||
Heading,
|
||||
Html,
|
||||
Img,
|
||||
Link,
|
||||
Preview,
|
||||
Section,
|
||||
Tailwind,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
export type TemplateProps = {
|
||||
projectName: string;
|
||||
applicationName: string;
|
||||
applicationType: string;
|
||||
errorMessage: string;
|
||||
buildLink: string;
|
||||
date: string;
|
||||
};
|
||||
|
||||
export const BuildFailedEmail = ({
|
||||
projectName = "dokploy",
|
||||
applicationName = "frontend",
|
||||
applicationType = "application",
|
||||
errorMessage = "Error array.length is not a function",
|
||||
buildLink = "https://dokploy.com/projects/dokploy-test/applications/dokploy-test",
|
||||
date = "2023-05-01T00:00:00.000Z",
|
||||
}: TemplateProps) => {
|
||||
const previewText = `Build failed for ${applicationName}`;
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>{previewText}</Preview>
|
||||
<Tailwind
|
||||
config={{
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
brand: "#007291",
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Body className="bg-white my-auto mx-auto font-sans px-2">
|
||||
<Container className="border border-solid border-[#eaeaea] rounded-lg my-[40px] mx-auto p-[20px] max-w-[465px]">
|
||||
<Section className="mt-[32px]">
|
||||
<Img
|
||||
src={
|
||||
"https://raw.githubusercontent.com/Dokploy/dokploy/canary/logo.png"
|
||||
}
|
||||
width="100"
|
||||
height="50"
|
||||
alt="Dokploy"
|
||||
className="my-0 mx-auto"
|
||||
/>
|
||||
</Section>
|
||||
<Heading className="text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0">
|
||||
Build failed for <strong>{applicationName}</strong>
|
||||
</Heading>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
Hello,
|
||||
</Text>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
Your build for <strong>{applicationName}</strong> failed. Please
|
||||
check the error message below.
|
||||
</Text>
|
||||
<Section className="flex text-black text-[14px] leading-[24px] bg-[#F4F4F5] rounded-lg p-2">
|
||||
<Text className="!leading-3 font-bold">Details: </Text>
|
||||
<Text className="!leading-3">
|
||||
Project Name: <strong>{projectName}</strong>
|
||||
</Text>
|
||||
<Text className="!leading-3">
|
||||
Application Name: <strong>{applicationName}</strong>
|
||||
</Text>
|
||||
<Text className="!leading-3">
|
||||
Application Type: <strong>{applicationType}</strong>
|
||||
</Text>
|
||||
<Text className="!leading-3">
|
||||
Date: <strong>{date}</strong>
|
||||
</Text>
|
||||
</Section>
|
||||
<Section className="flex text-black text-[14px] mt-4 leading-[24px] bg-[#F4F4F5] rounded-lg p-2">
|
||||
<Text className="!leading-3 font-bold">Reason: </Text>
|
||||
<Text className="text-[12px] leading-[24px]">{errorMessage}</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[32px] mb-[32px]">
|
||||
<Button
|
||||
href={buildLink}
|
||||
className="bg-[#000000] rounded text-white text-[12px] font-semibold no-underline text-center px-5 py-3"
|
||||
>
|
||||
View build
|
||||
</Button>
|
||||
</Section>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
or copy and paste this URL into your browser:{" "}
|
||||
<Link href={buildLink} className="text-blue-600 no-underline">
|
||||
{buildLink}
|
||||
</Link>
|
||||
</Text>
|
||||
</Container>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export default BuildFailedEmail;
|
@ -1,106 +0,0 @@
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Container,
|
||||
Head,
|
||||
Heading,
|
||||
Html,
|
||||
Img,
|
||||
Link,
|
||||
Preview,
|
||||
Section,
|
||||
Tailwind,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
export type TemplateProps = {
|
||||
projectName: string;
|
||||
applicationName: string;
|
||||
applicationType: string;
|
||||
buildLink: string;
|
||||
date: string;
|
||||
};
|
||||
|
||||
export const BuildSuccessEmail = ({
|
||||
projectName = "dokploy",
|
||||
applicationName = "frontend",
|
||||
applicationType = "application",
|
||||
buildLink = "https://dokploy.com/projects/dokploy-test/applications/dokploy-test",
|
||||
date = "2023-05-01T00:00:00.000Z",
|
||||
}: TemplateProps) => {
|
||||
const previewText = `Build success for ${applicationName}`;
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>{previewText}</Preview>
|
||||
<Tailwind
|
||||
config={{
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
brand: "#007291",
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Body className="bg-white my-auto mx-auto font-sans px-2">
|
||||
<Container className="border border-solid border-[#eaeaea] rounded-lg my-[40px] mx-auto p-[20px] max-w-[465px]">
|
||||
<Section className="mt-[32px]">
|
||||
<Img
|
||||
src={
|
||||
"https://raw.githubusercontent.com/Dokploy/dokploy/canary/logo.png"
|
||||
}
|
||||
width="100"
|
||||
height="50"
|
||||
alt="Dokploy"
|
||||
className="my-0 mx-auto"
|
||||
/>
|
||||
</Section>
|
||||
<Heading className="text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0">
|
||||
Build success for <strong>{applicationName}</strong>
|
||||
</Heading>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
Hello,
|
||||
</Text>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
Your build for <strong>{applicationName}</strong> was successful
|
||||
</Text>
|
||||
<Section className="flex text-black text-[14px] leading-[24px] bg-[#F4F4F5] rounded-lg p-2">
|
||||
<Text className="!leading-3 font-bold">Details: </Text>
|
||||
<Text className="!leading-3">
|
||||
Project Name: <strong>{projectName}</strong>
|
||||
</Text>
|
||||
<Text className="!leading-3">
|
||||
Application Name: <strong>{applicationName}</strong>
|
||||
</Text>
|
||||
<Text className="!leading-3">
|
||||
Application Type: <strong>{applicationType}</strong>
|
||||
</Text>
|
||||
<Text className="!leading-3">
|
||||
Date: <strong>{date}</strong>
|
||||
</Text>
|
||||
</Section>
|
||||
<Section className="text-center mt-[32px] mb-[32px]">
|
||||
<Button
|
||||
href={buildLink}
|
||||
className="bg-[#000000] rounded text-white text-[12px] font-semibold no-underline text-center px-5 py-3"
|
||||
>
|
||||
View build
|
||||
</Button>
|
||||
</Section>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
or copy and paste this URL into your browser:{" "}
|
||||
<Link href={buildLink} className="text-blue-600 no-underline">
|
||||
{buildLink}
|
||||
</Link>
|
||||
</Text>
|
||||
</Container>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export default BuildSuccessEmail;
|
@ -1,105 +0,0 @@
|
||||
import {
|
||||
Body,
|
||||
Container,
|
||||
Head,
|
||||
Heading,
|
||||
Html,
|
||||
Img,
|
||||
Preview,
|
||||
Section,
|
||||
Tailwind,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
export type TemplateProps = {
|
||||
projectName: string;
|
||||
applicationName: string;
|
||||
databaseType: "postgres" | "mysql" | "mongodb" | "mariadb";
|
||||
type: "error" | "success";
|
||||
errorMessage?: string;
|
||||
date: string;
|
||||
};
|
||||
|
||||
export const DatabaseBackupEmail = ({
|
||||
projectName = "dokploy",
|
||||
applicationName = "frontend",
|
||||
databaseType = "postgres",
|
||||
type = "success",
|
||||
errorMessage,
|
||||
date = "2023-05-01T00:00:00.000Z",
|
||||
}: TemplateProps) => {
|
||||
const previewText = `Database backup for ${applicationName} was ${type === "success" ? "successful ✅" : "failed ❌"}`;
|
||||
return (
|
||||
<Html>
|
||||
<Preview>{previewText}</Preview>
|
||||
<Tailwind
|
||||
config={{
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
brand: "#007291",
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Head />
|
||||
|
||||
<Body className="bg-white my-auto mx-auto font-sans px-2">
|
||||
<Container className="border border-solid border-[#eaeaea] rounded-lg my-[40px] mx-auto p-[20px] max-w-[465px]">
|
||||
<Section className="mt-[32px]">
|
||||
<Img
|
||||
src={
|
||||
"https://raw.githubusercontent.com/Dokploy/dokploy/canary/logo.png"
|
||||
}
|
||||
width="100"
|
||||
height="50"
|
||||
alt="Dokploy"
|
||||
className="my-0 mx-auto"
|
||||
/>
|
||||
</Section>
|
||||
<Heading className="text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0">
|
||||
Database backup for <strong>{applicationName}</strong>
|
||||
</Heading>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
Hello,
|
||||
</Text>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
Your database backup for <strong>{applicationName}</strong> was{" "}
|
||||
{type === "success"
|
||||
? "successful ✅"
|
||||
: "failed Please check the error message below. ❌"}
|
||||
.
|
||||
</Text>
|
||||
<Section className="flex text-black text-[14px] leading-[24px] bg-[#F4F4F5] rounded-lg p-2">
|
||||
<Text className="!leading-3 font-bold">Details: </Text>
|
||||
<Text className="!leading-3">
|
||||
Project Name: <strong>{projectName}</strong>
|
||||
</Text>
|
||||
<Text className="!leading-3">
|
||||
Application Name: <strong>{applicationName}</strong>
|
||||
</Text>
|
||||
<Text className="!leading-3">
|
||||
Database Type: <strong>{databaseType}</strong>
|
||||
</Text>
|
||||
<Text className="!leading-3">
|
||||
Date: <strong>{date}</strong>
|
||||
</Text>
|
||||
</Section>
|
||||
{type === "error" && errorMessage ? (
|
||||
<Section className="flex text-black text-[14px] mt-4 leading-[24px] bg-[#F4F4F5] rounded-lg p-2">
|
||||
<Text className="!leading-3 font-bold">Reason: </Text>
|
||||
<Text className="text-[12px] leading-[24px]">
|
||||
{errorMessage || "Error message not provided"}
|
||||
</Text>
|
||||
</Section>
|
||||
) : null}
|
||||
</Container>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatabaseBackupEmail;
|
@ -1,81 +0,0 @@
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Container,
|
||||
Head,
|
||||
Heading,
|
||||
Html,
|
||||
Img,
|
||||
Preview,
|
||||
Section,
|
||||
Tailwind,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
export type TemplateProps = {
|
||||
message: string;
|
||||
date: string;
|
||||
};
|
||||
|
||||
export const DockerCleanupEmail = ({
|
||||
message = "Docker cleanup for dokploy",
|
||||
date = "2023-05-01T00:00:00.000Z",
|
||||
}: TemplateProps) => {
|
||||
const previewText = "Docker cleanup for dokploy";
|
||||
return (
|
||||
<Html>
|
||||
<Preview>{previewText}</Preview>
|
||||
<Tailwind
|
||||
config={{
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
brand: "#007291",
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Head />
|
||||
|
||||
<Body className="bg-white my-auto mx-auto font-sans px-2">
|
||||
<Container className="border border-solid border-[#eaeaea] rounded-lg my-[40px] mx-auto p-[20px] max-w-[465px]">
|
||||
<Section className="mt-[32px]">
|
||||
<Img
|
||||
src={
|
||||
"https://raw.githubusercontent.com/Dokploy/dokploy/canary/logo.png"
|
||||
}
|
||||
width="100"
|
||||
height="50"
|
||||
alt="Dokploy"
|
||||
className="my-0 mx-auto"
|
||||
/>
|
||||
</Section>
|
||||
<Heading className="text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0">
|
||||
Docker cleanup for <strong>dokploy</strong>
|
||||
</Heading>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
Hello,
|
||||
</Text>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
The docker cleanup for <strong>dokploy</strong> was successful ✅
|
||||
</Text>
|
||||
|
||||
<Section className="flex text-black text-[14px] leading-[24px] bg-[#F4F4F5] rounded-lg p-2">
|
||||
<Text className="!leading-3 font-bold">Details: </Text>
|
||||
<Text className="!leading-3">
|
||||
Message: <strong>{message}</strong>
|
||||
</Text>
|
||||
<Text className="!leading-3">
|
||||
Date: <strong>{date}</strong>
|
||||
</Text>
|
||||
</Section>
|
||||
</Container>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export default DockerCleanupEmail;
|
@ -1,75 +0,0 @@
|
||||
import {
|
||||
Body,
|
||||
Container,
|
||||
Head,
|
||||
Heading,
|
||||
Html,
|
||||
Img,
|
||||
Preview,
|
||||
Section,
|
||||
Tailwind,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
export type TemplateProps = {
|
||||
date: string;
|
||||
};
|
||||
|
||||
export const DokployRestartEmail = ({
|
||||
date = "2023-05-01T00:00:00.000Z",
|
||||
}: TemplateProps) => {
|
||||
const previewText = "Your dokploy server was restarted";
|
||||
return (
|
||||
<Html>
|
||||
<Preview>{previewText}</Preview>
|
||||
<Tailwind
|
||||
config={{
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
brand: "#007291",
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Head />
|
||||
|
||||
<Body className="bg-white my-auto mx-auto font-sans px-2">
|
||||
<Container className="border border-solid border-[#eaeaea] rounded-lg my-[40px] mx-auto p-[20px] max-w-[465px]">
|
||||
<Section className="mt-[32px]">
|
||||
<Img
|
||||
src={
|
||||
"https://raw.githubusercontent.com/Dokploy/dokploy/canary/logo.png"
|
||||
}
|
||||
width="100"
|
||||
height="50"
|
||||
alt="Dokploy"
|
||||
className="my-0 mx-auto"
|
||||
/>
|
||||
</Section>
|
||||
<Heading className="text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0">
|
||||
Dokploy Server Restart
|
||||
</Heading>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
Hello,
|
||||
</Text>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
Your dokploy server was restarted ✅
|
||||
</Text>
|
||||
|
||||
<Section className="flex text-black text-[14px] leading-[24px] bg-[#F4F4F5] rounded-lg p-2">
|
||||
<Text className="!leading-3 font-bold">Details: </Text>
|
||||
<Text className="!leading-3">
|
||||
Date: <strong>{date}</strong>
|
||||
</Text>
|
||||
</Section>
|
||||
</Container>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export default DokployRestartEmail;
|
@ -1,98 +0,0 @@
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Container,
|
||||
Head,
|
||||
Heading,
|
||||
Hr,
|
||||
Html,
|
||||
Img,
|
||||
Link,
|
||||
Preview,
|
||||
Section,
|
||||
Tailwind,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
|
||||
export type TemplateProps = {
|
||||
email: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
interface VercelInviteUserEmailProps {
|
||||
inviteLink: string;
|
||||
toEmail: string;
|
||||
}
|
||||
|
||||
export const InvitationEmail = ({
|
||||
inviteLink,
|
||||
toEmail,
|
||||
}: VercelInviteUserEmailProps) => {
|
||||
const previewText = "Join to Dokploy";
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>{previewText}</Preview>
|
||||
<Tailwind
|
||||
config={{
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
brand: "#007291",
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Body className="bg-white my-auto mx-auto font-sans px-2">
|
||||
<Container className="border border-solid border-[#eaeaea] rounded-lg my-[40px] mx-auto p-[20px] max-w-[465px]">
|
||||
<Section className="mt-[32px]">
|
||||
<Img
|
||||
src={
|
||||
"https://raw.githubusercontent.com/Dokploy/dokploy/canary/logo.png"
|
||||
}
|
||||
width="100"
|
||||
height="50"
|
||||
alt="Dokploy"
|
||||
className="my-0 mx-auto"
|
||||
/>
|
||||
</Section>
|
||||
<Heading className="text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0">
|
||||
Join to <strong>Dokploy</strong>
|
||||
</Heading>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
Hello,
|
||||
</Text>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
You have been invited to join <strong>Dokploy</strong>, a platform
|
||||
that helps for deploying your apps to the cloud.
|
||||
</Text>
|
||||
<Section className="text-center mt-[32px] mb-[32px]">
|
||||
<Button
|
||||
href={inviteLink}
|
||||
className="bg-[#000000] rounded text-white text-[12px] font-semibold no-underline text-center px-5 py-3"
|
||||
>
|
||||
Join the team 🚀
|
||||
</Button>
|
||||
</Section>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
or copy and paste this URL into your browser:{" "}
|
||||
<Link href={inviteLink} className="text-blue-600 no-underline">
|
||||
https://dokploy.com
|
||||
</Link>
|
||||
</Text>
|
||||
<Hr className="border border-solid border-[#eaeaea] my-[26px] mx-0 w-full" />
|
||||
<Text className="text-[#666666] text-[12px] leading-[24px]">
|
||||
This invitation was intended for {toEmail}. This invite was sent
|
||||
from <strong className="text-black">dokploy.com</strong>. If you
|
||||
were not expecting this invitation, you can ignore this email. If
|
||||
you are concerned about your account's safety, please reply to
|
||||
</Text>
|
||||
</Container>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export default InvitationEmail;
|
@ -1,150 +0,0 @@
|
||||
import {
|
||||
Body,
|
||||
Container,
|
||||
Head,
|
||||
Heading,
|
||||
Html,
|
||||
Img,
|
||||
Link,
|
||||
Preview,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
interface NotionMagicLinkEmailProps {
|
||||
loginCode?: string;
|
||||
}
|
||||
|
||||
const baseUrl = process.env.VERCEL_URL
|
||||
? `https://${process.env.VERCEL_URL}`
|
||||
: "";
|
||||
|
||||
export const NotionMagicLinkEmail = ({
|
||||
loginCode,
|
||||
}: NotionMagicLinkEmailProps) => (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>Log in with this magic link</Preview>
|
||||
<Body style={main}>
|
||||
<Container style={container}>
|
||||
<Heading style={h1}>Login</Heading>
|
||||
<Link
|
||||
href="https://notion.so"
|
||||
target="_blank"
|
||||
style={{
|
||||
...link,
|
||||
display: "block",
|
||||
marginBottom: "16px",
|
||||
}}
|
||||
>
|
||||
Click here to log in with this magic link
|
||||
</Link>
|
||||
<Text style={{ ...text, marginBottom: "14px" }}>
|
||||
Or, copy and paste this temporary login code:
|
||||
</Text>
|
||||
<code style={code}>{loginCode}</code>
|
||||
<Text
|
||||
style={{
|
||||
...text,
|
||||
color: "#ababab",
|
||||
marginTop: "14px",
|
||||
marginBottom: "16px",
|
||||
}}
|
||||
>
|
||||
If you didn't try to login, you can safely ignore this email.
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
...text,
|
||||
color: "#ababab",
|
||||
marginTop: "12px",
|
||||
marginBottom: "38px",
|
||||
}}
|
||||
>
|
||||
Hint: You can set a permanent password in Settings & members → My
|
||||
account.
|
||||
</Text>
|
||||
<Img
|
||||
src={`${baseUrl}/static/notion-logo.png`}
|
||||
width="32"
|
||||
height="32"
|
||||
alt="Notion's Logo"
|
||||
/>
|
||||
<Text style={footer}>
|
||||
<Link
|
||||
href="https://notion.so"
|
||||
target="_blank"
|
||||
style={{ ...link, color: "#898989" }}
|
||||
>
|
||||
Notion.so
|
||||
</Link>
|
||||
, the all-in-one-workspace
|
||||
<br />
|
||||
for your notes, tasks, wikis, and databases.
|
||||
</Text>
|
||||
</Container>
|
||||
</Body>
|
||||
</Html>
|
||||
);
|
||||
|
||||
NotionMagicLinkEmail.PreviewProps = {
|
||||
loginCode: "sparo-ndigo-amurt-secan",
|
||||
} as NotionMagicLinkEmailProps;
|
||||
|
||||
export default NotionMagicLinkEmail;
|
||||
|
||||
const main = {
|
||||
backgroundColor: "#ffffff",
|
||||
};
|
||||
|
||||
const container = {
|
||||
paddingLeft: "12px",
|
||||
paddingRight: "12px",
|
||||
margin: "0 auto",
|
||||
};
|
||||
|
||||
const h1 = {
|
||||
color: "#333",
|
||||
fontFamily:
|
||||
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
|
||||
fontSize: "24px",
|
||||
fontWeight: "bold",
|
||||
margin: "40px 0",
|
||||
padding: "0",
|
||||
};
|
||||
|
||||
const link = {
|
||||
color: "#2754C5",
|
||||
fontFamily:
|
||||
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
|
||||
fontSize: "14px",
|
||||
textDecoration: "underline",
|
||||
};
|
||||
|
||||
const text = {
|
||||
color: "#333",
|
||||
fontFamily:
|
||||
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
|
||||
fontSize: "14px",
|
||||
margin: "24px 0",
|
||||
};
|
||||
|
||||
const footer = {
|
||||
color: "#898989",
|
||||
fontFamily:
|
||||
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
|
||||
fontSize: "12px",
|
||||
lineHeight: "22px",
|
||||
marginTop: "12px",
|
||||
marginBottom: "24px",
|
||||
};
|
||||
|
||||
const code = {
|
||||
display: "inline-block",
|
||||
padding: "16px 4.5%",
|
||||
width: "90.5%",
|
||||
backgroundColor: "#f4f4f4",
|
||||
borderRadius: "5px",
|
||||
border: "1px solid #eee",
|
||||
color: "#333",
|
||||
};
|
@ -1,158 +0,0 @@
|
||||
import {
|
||||
Body,
|
||||
Container,
|
||||
Head,
|
||||
Heading,
|
||||
Html,
|
||||
Img,
|
||||
Link,
|
||||
Section,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
interface PlaidVerifyIdentityEmailProps {
|
||||
validationCode?: string;
|
||||
}
|
||||
|
||||
const baseUrl = process.env.VERCEL_URL
|
||||
? `https://${process.env.VERCEL_URL}`
|
||||
: "";
|
||||
|
||||
export const PlaidVerifyIdentityEmail = ({
|
||||
validationCode,
|
||||
}: PlaidVerifyIdentityEmailProps) => (
|
||||
<Html>
|
||||
<Head />
|
||||
<Body style={main}>
|
||||
<Container style={container}>
|
||||
<Img
|
||||
src={`${baseUrl}/static/plaid-logo.png`}
|
||||
width="212"
|
||||
height="88"
|
||||
alt="Plaid"
|
||||
style={logo}
|
||||
/>
|
||||
<Text style={tertiary}>Verify Your Identity</Text>
|
||||
<Heading style={secondary}>
|
||||
Enter the following code to finish linking Venmo.
|
||||
</Heading>
|
||||
<Section style={codeContainer}>
|
||||
<Text style={code}>{validationCode}</Text>
|
||||
</Section>
|
||||
<Text style={paragraph}>Not expecting this email?</Text>
|
||||
<Text style={paragraph}>
|
||||
Contact{" "}
|
||||
<Link href="mailto:login@plaid.com" style={link}>
|
||||
login@plaid.com
|
||||
</Link>{" "}
|
||||
if you did not request this code.
|
||||
</Text>
|
||||
</Container>
|
||||
<Text style={footer}>Securely powered by Plaid.</Text>
|
||||
</Body>
|
||||
</Html>
|
||||
);
|
||||
|
||||
PlaidVerifyIdentityEmail.PreviewProps = {
|
||||
validationCode: "144833",
|
||||
} as PlaidVerifyIdentityEmailProps;
|
||||
|
||||
export default PlaidVerifyIdentityEmail;
|
||||
|
||||
const main = {
|
||||
backgroundColor: "#ffffff",
|
||||
fontFamily: "HelveticaNeue,Helvetica,Arial,sans-serif",
|
||||
};
|
||||
|
||||
const container = {
|
||||
backgroundColor: "#ffffff",
|
||||
border: "1px solid #eee",
|
||||
borderRadius: "5px",
|
||||
boxShadow: "0 5px 10px rgba(20,50,70,.2)",
|
||||
marginTop: "20px",
|
||||
maxWidth: "360px",
|
||||
margin: "0 auto",
|
||||
padding: "68px 0 130px",
|
||||
};
|
||||
|
||||
const logo = {
|
||||
margin: "0 auto",
|
||||
};
|
||||
|
||||
const tertiary = {
|
||||
color: "#0a85ea",
|
||||
fontSize: "11px",
|
||||
fontWeight: 700,
|
||||
fontFamily: "HelveticaNeue,Helvetica,Arial,sans-serif",
|
||||
height: "16px",
|
||||
letterSpacing: "0",
|
||||
lineHeight: "16px",
|
||||
margin: "16px 8px 8px 8px",
|
||||
textTransform: "uppercase" as const,
|
||||
textAlign: "center" as const,
|
||||
};
|
||||
|
||||
const secondary = {
|
||||
color: "#000",
|
||||
display: "inline-block",
|
||||
fontFamily: "HelveticaNeue-Medium,Helvetica,Arial,sans-serif",
|
||||
fontSize: "20px",
|
||||
fontWeight: 500,
|
||||
lineHeight: "24px",
|
||||
marginBottom: "0",
|
||||
marginTop: "0",
|
||||
textAlign: "center" as const,
|
||||
};
|
||||
|
||||
const codeContainer = {
|
||||
background: "rgba(0,0,0,.05)",
|
||||
borderRadius: "4px",
|
||||
margin: "16px auto 14px",
|
||||
verticalAlign: "middle",
|
||||
width: "280px",
|
||||
};
|
||||
|
||||
const code = {
|
||||
color: "#000",
|
||||
display: "inline-block",
|
||||
fontFamily: "HelveticaNeue-Bold",
|
||||
fontSize: "32px",
|
||||
fontWeight: 700,
|
||||
letterSpacing: "6px",
|
||||
lineHeight: "40px",
|
||||
paddingBottom: "8px",
|
||||
paddingTop: "8px",
|
||||
margin: "0 auto",
|
||||
width: "100%",
|
||||
textAlign: "center" as const,
|
||||
};
|
||||
|
||||
const paragraph = {
|
||||
color: "#444",
|
||||
fontSize: "15px",
|
||||
fontFamily: "HelveticaNeue,Helvetica,Arial,sans-serif",
|
||||
letterSpacing: "0",
|
||||
lineHeight: "23px",
|
||||
padding: "0 40px",
|
||||
margin: "0",
|
||||
textAlign: "center" as const,
|
||||
};
|
||||
|
||||
const link = {
|
||||
color: "#444",
|
||||
textDecoration: "underline",
|
||||
};
|
||||
|
||||
const footer = {
|
||||
color: "#000",
|
||||
fontSize: "12px",
|
||||
fontWeight: 800,
|
||||
letterSpacing: "0",
|
||||
lineHeight: "23px",
|
||||
margin: "0",
|
||||
marginTop: "20px",
|
||||
fontFamily: "HelveticaNeue,Helvetica,Arial,sans-serif",
|
||||
textAlign: "center" as const,
|
||||
textTransform: "uppercase" as const,
|
||||
};
|
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 426 B |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 54 KiB |
@ -1,152 +0,0 @@
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Container,
|
||||
Head,
|
||||
Hr,
|
||||
Html,
|
||||
Img,
|
||||
Link,
|
||||
Preview,
|
||||
Section,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
const baseUrl = process.env.VERCEL_URL
|
||||
? `https://${process.env.VERCEL_URL}`
|
||||
: "";
|
||||
|
||||
export const StripeWelcomeEmail = () => (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>You're now ready to make live transactions with Stripe!</Preview>
|
||||
<Body style={main}>
|
||||
<Container style={container}>
|
||||
<Section style={box}>
|
||||
<Img
|
||||
src={`${baseUrl}/static/stripe-logo.png`}
|
||||
width="49"
|
||||
height="21"
|
||||
alt="Stripe"
|
||||
/>
|
||||
<Hr style={hr} />
|
||||
<Text style={paragraph}>
|
||||
Thanks for submitting your account information. You're now ready to
|
||||
make live transactions with Stripe!
|
||||
</Text>
|
||||
<Text style={paragraph}>
|
||||
You can view your payments and a variety of other information about
|
||||
your account right from your dashboard.
|
||||
</Text>
|
||||
<Button style={button} href="https://dashboard.stripe.com/login">
|
||||
View your Stripe Dashboard
|
||||
</Button>
|
||||
<Hr style={hr} />
|
||||
<Text style={paragraph}>
|
||||
If you haven't finished your integration, you might find our{" "}
|
||||
<Link style={anchor} href="https://stripe.com/docs">
|
||||
docs
|
||||
</Link>{" "}
|
||||
handy.
|
||||
</Text>
|
||||
<Text style={paragraph}>
|
||||
Once you're ready to start accepting payments, you'll just need to
|
||||
use your live{" "}
|
||||
<Link
|
||||
style={anchor}
|
||||
href="https://dashboard.stripe.com/login?redirect=%2Fapikeys"
|
||||
>
|
||||
API keys
|
||||
</Link>{" "}
|
||||
instead of your test API keys. Your account can simultaneously be
|
||||
used for both test and live requests, so you can continue testing
|
||||
while accepting live payments. Check out our{" "}
|
||||
<Link style={anchor} href="https://stripe.com/docs/dashboard">
|
||||
tutorial about account basics
|
||||
</Link>
|
||||
.
|
||||
</Text>
|
||||
<Text style={paragraph}>
|
||||
Finally, we've put together a{" "}
|
||||
<Link
|
||||
style={anchor}
|
||||
href="https://stripe.com/docs/checklist/website"
|
||||
>
|
||||
quick checklist
|
||||
</Link>{" "}
|
||||
to ensure your website conforms to card network standards.
|
||||
</Text>
|
||||
<Text style={paragraph}>
|
||||
We'll be here to help you with any step along the way. You can find
|
||||
answers to most questions and get in touch with us on our{" "}
|
||||
<Link style={anchor} href="https://support.stripe.com/">
|
||||
support site
|
||||
</Link>
|
||||
.
|
||||
</Text>
|
||||
<Text style={paragraph}>— The Stripe team</Text>
|
||||
<Hr style={hr} />
|
||||
<Text style={footer}>
|
||||
Stripe, 354 Oyster Point Blvd, South San Francisco, CA 94080
|
||||
</Text>
|
||||
</Section>
|
||||
</Container>
|
||||
</Body>
|
||||
</Html>
|
||||
);
|
||||
|
||||
export default StripeWelcomeEmail;
|
||||
|
||||
const main = {
|
||||
backgroundColor: "#f6f9fc",
|
||||
fontFamily:
|
||||
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif',
|
||||
};
|
||||
|
||||
const container = {
|
||||
backgroundColor: "#ffffff",
|
||||
margin: "0 auto",
|
||||
padding: "20px 0 48px",
|
||||
marginBottom: "64px",
|
||||
};
|
||||
|
||||
const box = {
|
||||
padding: "0 48px",
|
||||
};
|
||||
|
||||
const hr = {
|
||||
borderColor: "#e6ebf1",
|
||||
margin: "20px 0",
|
||||
};
|
||||
|
||||
const paragraph = {
|
||||
color: "#525f7f",
|
||||
|
||||
fontSize: "16px",
|
||||
lineHeight: "24px",
|
||||
textAlign: "left" as const,
|
||||
};
|
||||
|
||||
const anchor = {
|
||||
color: "#556cd6",
|
||||
};
|
||||
|
||||
const button = {
|
||||
backgroundColor: "#656ee8",
|
||||
borderRadius: "5px",
|
||||
color: "#fff",
|
||||
fontSize: "16px",
|
||||
fontWeight: "bold",
|
||||
textDecoration: "none",
|
||||
textAlign: "center" as const,
|
||||
display: "block",
|
||||
width: "100%",
|
||||
padding: "10px",
|
||||
};
|
||||
|
||||
const footer = {
|
||||
color: "#8898aa",
|
||||
fontSize: "12px",
|
||||
lineHeight: "16px",
|
||||
};
|
@ -1,154 +0,0 @@
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Column,
|
||||
Container,
|
||||
Head,
|
||||
Heading,
|
||||
Hr,
|
||||
Html,
|
||||
Img,
|
||||
Link,
|
||||
Preview,
|
||||
Row,
|
||||
Section,
|
||||
Tailwind,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
interface VercelInviteUserEmailProps {
|
||||
username?: string;
|
||||
userImage?: string;
|
||||
invitedByUsername?: string;
|
||||
invitedByEmail?: string;
|
||||
teamName?: string;
|
||||
teamImage?: string;
|
||||
inviteLink?: string;
|
||||
inviteFromIp?: string;
|
||||
inviteFromLocation?: string;
|
||||
}
|
||||
|
||||
const baseUrl = process.env.VERCEL_URL
|
||||
? `https://${process.env.VERCEL_URL}`
|
||||
: "";
|
||||
|
||||
export const VercelInviteUserEmail = ({
|
||||
username,
|
||||
userImage,
|
||||
invitedByUsername,
|
||||
invitedByEmail,
|
||||
teamName,
|
||||
teamImage,
|
||||
inviteLink,
|
||||
inviteFromIp,
|
||||
inviteFromLocation,
|
||||
}: VercelInviteUserEmailProps) => {
|
||||
const previewText = `Join ${invitedByUsername} on Vercel`;
|
||||
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>{previewText}</Preview>
|
||||
<Tailwind>
|
||||
<Body className="bg-white my-auto mx-auto font-sans px-2">
|
||||
<Container className="border border-solid border-[#eaeaea] rounded my-[40px] mx-auto p-[20px] max-w-[465px]">
|
||||
<Section className="mt-[32px]">
|
||||
<Img
|
||||
src={`${baseUrl}/static/vercel-logo.png`}
|
||||
width="40"
|
||||
height="37"
|
||||
alt="Vercel"
|
||||
className="my-0 mx-auto"
|
||||
/>
|
||||
</Section>
|
||||
<Heading className="text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0">
|
||||
Join <strong>{teamName}</strong> on <strong>Vercel</strong>
|
||||
</Heading>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
Hello {username},
|
||||
</Text>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
<strong>{invitedByUsername}</strong> (
|
||||
<Link
|
||||
href={`mailto:${invitedByEmail}`}
|
||||
className="text-blue-600 no-underline"
|
||||
>
|
||||
{invitedByEmail}
|
||||
</Link>
|
||||
) has invited you to the <strong>{teamName}</strong> team on{" "}
|
||||
<strong>Vercel</strong>.
|
||||
</Text>
|
||||
<Section>
|
||||
<Row>
|
||||
<Column align="right">
|
||||
<Img
|
||||
className="rounded-full"
|
||||
src={userImage}
|
||||
width="64"
|
||||
height="64"
|
||||
/>
|
||||
</Column>
|
||||
<Column align="center">
|
||||
<Img
|
||||
src={`${baseUrl}/static/vercel-arrow.png`}
|
||||
width="12"
|
||||
height="9"
|
||||
alt="invited you to"
|
||||
/>
|
||||
</Column>
|
||||
<Column align="left">
|
||||
<Img
|
||||
className="rounded-full"
|
||||
src={teamImage}
|
||||
width="64"
|
||||
height="64"
|
||||
/>
|
||||
</Column>
|
||||
</Row>
|
||||
</Section>
|
||||
<Section className="text-center mt-[32px] mb-[32px]">
|
||||
<Button
|
||||
className="bg-[#000000] rounded text-white text-[12px] font-semibold no-underline text-center px-5 py-3"
|
||||
href={inviteLink}
|
||||
>
|
||||
Join the team
|
||||
</Button>
|
||||
</Section>
|
||||
<Text className="text-black text-[14px] leading-[24px]">
|
||||
or copy and paste this URL into your browser:{" "}
|
||||
<Link href={inviteLink} className="text-blue-600 no-underline">
|
||||
{inviteLink}
|
||||
</Link>
|
||||
</Text>
|
||||
<Hr className="border border-solid border-[#eaeaea] my-[26px] mx-0 w-full" />
|
||||
<Text className="text-[#666666] text-[12px] leading-[24px]">
|
||||
This invitation was intended for{" "}
|
||||
<span className="text-black">{username}</span>. This invite was
|
||||
sent from <span className="text-black">{inviteFromIp}</span>{" "}
|
||||
located in{" "}
|
||||
<span className="text-black">{inviteFromLocation}</span>. If you
|
||||
were not expecting this invitation, you can ignore this email. If
|
||||
you are concerned about your account's safety, please reply to
|
||||
this email to get in touch with us.
|
||||
</Text>
|
||||
</Container>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
VercelInviteUserEmail.PreviewProps = {
|
||||
username: "alanturing",
|
||||
userImage: `${baseUrl}/static/vercel-user.png`,
|
||||
invitedByUsername: "Alan",
|
||||
invitedByEmail: "alan.turing@example.com",
|
||||
teamName: "Enigma",
|
||||
teamImage: `${baseUrl}/static/vercel-team.png`,
|
||||
inviteLink: "https://vercel.com/teams/invite/foo",
|
||||
inviteFromIp: "204.13.186.218",
|
||||
inviteFromLocation: "São Paulo, Brazil",
|
||||
} as VercelInviteUserEmailProps;
|
||||
|
||||
export default VercelInviteUserEmail;
|
@ -1,20 +0,0 @@
|
||||
{
|
||||
"name": "emails",
|
||||
"version": "0.0.19",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "email build",
|
||||
"dev": "email dev",
|
||||
"export": "email export"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-email/components": "0.0.21",
|
||||
"react-email": "2.1.5",
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "18.2.33",
|
||||
"@types/react-dom": "18.2.14"
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
# React Email Starter
|
||||
|
||||
A live preview right in your browser so you don't need to keep sending real emails during development.
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, install the dependencies:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
# or
|
||||
yarn
|
||||
```
|
||||
|
||||
Then, run the development server:
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open [localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
## License
|
||||
|
||||
MIT License
|
@ -1,15 +1,8 @@
|
||||
import { type PostgresJsDatabase, drizzle } from "drizzle-orm/postgres-js";
|
||||
import postgres from "postgres";
|
||||
// import { sc } from "@dokploy/schema";
|
||||
// import * as schema from "@dokploy/schema";
|
||||
// schema
|
||||
import * as schema from "./schema";
|
||||
|
||||
// type Schema = typeof schema;
|
||||
|
||||
// type Schema = typeof schema;
|
||||
declare global {
|
||||
// eslint-disable-next-line no-var -- only var works here
|
||||
var db: PostgresJsDatabase<typeof schema> | undefined;
|
||||
}
|
||||
|
||||
|
@ -1,157 +0,0 @@
|
||||
import BuildFailedEmail from "@/emails/emails/build-failed";
|
||||
import { db } from "@/server/db";
|
||||
import { notifications } from "@/server/db/schema";
|
||||
import { renderAsync } from "@react-email/components";
|
||||
import { eq } from "drizzle-orm";
|
||||
import {
|
||||
sendDiscordNotification,
|
||||
sendEmailNotification,
|
||||
sendSlackNotification,
|
||||
sendTelegramNotification,
|
||||
} from "./utils";
|
||||
|
||||
interface Props {
|
||||
projectName: string;
|
||||
applicationName: string;
|
||||
applicationType: string;
|
||||
errorMessage: string;
|
||||
buildLink: string;
|
||||
}
|
||||
|
||||
export const sendBuildErrorNotifications = async ({
|
||||
projectName,
|
||||
applicationName,
|
||||
applicationType,
|
||||
errorMessage,
|
||||
buildLink,
|
||||
}: Props) => {
|
||||
const date = new Date();
|
||||
const notificationList = await db.query.notifications.findMany({
|
||||
where: eq(notifications.appBuildError, true),
|
||||
with: {
|
||||
email: true,
|
||||
discord: true,
|
||||
telegram: true,
|
||||
slack: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const notification of notificationList) {
|
||||
const { email, discord, telegram, slack } = notification;
|
||||
if (email) {
|
||||
const template = await renderAsync(
|
||||
BuildFailedEmail({
|
||||
projectName,
|
||||
applicationName,
|
||||
applicationType,
|
||||
errorMessage: errorMessage,
|
||||
buildLink,
|
||||
date: date.toLocaleString(),
|
||||
}),
|
||||
).catch();
|
||||
await sendEmailNotification(email, "Build failed for dokploy", template);
|
||||
}
|
||||
|
||||
if (discord) {
|
||||
await sendDiscordNotification(discord, {
|
||||
title: "⚠️ Build Failed",
|
||||
color: 0xff0000,
|
||||
fields: [
|
||||
{
|
||||
name: "Project",
|
||||
value: projectName,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Application",
|
||||
value: applicationName,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Type",
|
||||
value: applicationType,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Error",
|
||||
value: errorMessage,
|
||||
},
|
||||
{
|
||||
name: "Build Link",
|
||||
value: buildLink,
|
||||
},
|
||||
],
|
||||
timestamp: date.toISOString(),
|
||||
footer: {
|
||||
text: "Dokploy Build Notification",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (telegram) {
|
||||
await sendTelegramNotification(
|
||||
telegram,
|
||||
`
|
||||
<b>⚠️ Build Failed</b>
|
||||
|
||||
<b>Project:</b> ${projectName}
|
||||
<b>Application:</b> ${applicationName}
|
||||
<b>Type:</b> ${applicationType}
|
||||
<b>Time:</b> ${date.toLocaleString()}
|
||||
|
||||
<b>Error:</b>
|
||||
<pre>${errorMessage}</pre>
|
||||
|
||||
<b>Build Details:</b> ${buildLink}
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
if (slack) {
|
||||
const { channel } = slack;
|
||||
await sendSlackNotification(slack, {
|
||||
channel: channel,
|
||||
attachments: [
|
||||
{
|
||||
color: "#FF0000",
|
||||
pretext: ":warning: *Build Failed*",
|
||||
fields: [
|
||||
{
|
||||
title: "Project",
|
||||
value: projectName,
|
||||
short: true,
|
||||
},
|
||||
{
|
||||
title: "Application",
|
||||
value: applicationName,
|
||||
short: true,
|
||||
},
|
||||
{
|
||||
title: "Type",
|
||||
value: applicationType,
|
||||
short: true,
|
||||
},
|
||||
{
|
||||
title: "Time",
|
||||
value: date.toLocaleString(),
|
||||
short: true,
|
||||
},
|
||||
{
|
||||
title: "Error",
|
||||
value: `\`\`\`${errorMessage}\`\`\``,
|
||||
short: false,
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: "button",
|
||||
text: "View Build Details",
|
||||
url: buildLink,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
@ -1,143 +0,0 @@
|
||||
import BuildSuccessEmail from "@/emails/emails/build-success";
|
||||
import { db } from "@/server/db";
|
||||
import { notifications } from "@/server/db/schema";
|
||||
import { renderAsync } from "@react-email/components";
|
||||
import { eq } from "drizzle-orm";
|
||||
import {
|
||||
sendDiscordNotification,
|
||||
sendEmailNotification,
|
||||
sendSlackNotification,
|
||||
sendTelegramNotification,
|
||||
} from "./utils";
|
||||
|
||||
interface Props {
|
||||
projectName: string;
|
||||
applicationName: string;
|
||||
applicationType: string;
|
||||
buildLink: string;
|
||||
}
|
||||
|
||||
export const sendBuildSuccessNotifications = async ({
|
||||
projectName,
|
||||
applicationName,
|
||||
applicationType,
|
||||
buildLink,
|
||||
}: Props) => {
|
||||
const date = new Date();
|
||||
const notificationList = await db.query.notifications.findMany({
|
||||
where: eq(notifications.appDeploy, true),
|
||||
with: {
|
||||
email: true,
|
||||
discord: true,
|
||||
telegram: true,
|
||||
slack: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const notification of notificationList) {
|
||||
const { email, discord, telegram, slack } = notification;
|
||||
|
||||
if (email) {
|
||||
const template = await renderAsync(
|
||||
BuildSuccessEmail({
|
||||
projectName,
|
||||
applicationName,
|
||||
applicationType,
|
||||
buildLink,
|
||||
date: date.toLocaleString(),
|
||||
}),
|
||||
).catch();
|
||||
await sendEmailNotification(email, "Build success for dokploy", template);
|
||||
}
|
||||
|
||||
if (discord) {
|
||||
await sendDiscordNotification(discord, {
|
||||
title: "✅ Build Success",
|
||||
color: 0x00ff00,
|
||||
fields: [
|
||||
{
|
||||
name: "Project",
|
||||
value: projectName,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Application",
|
||||
value: applicationName,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Type",
|
||||
value: applicationType,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Build Link",
|
||||
value: buildLink,
|
||||
},
|
||||
],
|
||||
timestamp: date.toISOString(),
|
||||
footer: {
|
||||
text: "Dokploy Build Notification",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (telegram) {
|
||||
await sendTelegramNotification(
|
||||
telegram,
|
||||
`
|
||||
<b>✅ Build Success</b>
|
||||
|
||||
<b>Project:</b> ${projectName}
|
||||
<b>Application:</b> ${applicationName}
|
||||
<b>Type:</b> ${applicationType}
|
||||
<b>Time:</b> ${date.toLocaleString()}
|
||||
|
||||
<b>Build Details:</b> ${buildLink}
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
if (slack) {
|
||||
const { channel } = slack;
|
||||
await sendSlackNotification(slack, {
|
||||
channel: channel,
|
||||
attachments: [
|
||||
{
|
||||
color: "#00FF00",
|
||||
pretext: ":white_check_mark: *Build Success*",
|
||||
fields: [
|
||||
{
|
||||
title: "Project",
|
||||
value: projectName,
|
||||
short: true,
|
||||
},
|
||||
{
|
||||
title: "Application",
|
||||
value: applicationName,
|
||||
short: true,
|
||||
},
|
||||
{
|
||||
title: "Type",
|
||||
value: applicationType,
|
||||
short: true,
|
||||
},
|
||||
{
|
||||
title: "Time",
|
||||
value: date.toLocaleString(),
|
||||
short: true,
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: "button",
|
||||
text: "View Build Details",
|
||||
url: buildLink,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
@ -1,177 +0,0 @@
|
||||
import DatabaseBackupEmail from "@/emails/emails/database-backup";
|
||||
import { db } from "@/server/db";
|
||||
import { notifications } from "@/server/db/schema";
|
||||
import { renderAsync } from "@react-email/components";
|
||||
import { eq } from "drizzle-orm";
|
||||
import {
|
||||
sendDiscordNotification,
|
||||
sendEmailNotification,
|
||||
sendSlackNotification,
|
||||
sendTelegramNotification,
|
||||
} from "./utils";
|
||||
|
||||
export const sendDatabaseBackupNotifications = async ({
|
||||
projectName,
|
||||
applicationName,
|
||||
databaseType,
|
||||
type,
|
||||
errorMessage,
|
||||
}: {
|
||||
projectName: string;
|
||||
applicationName: string;
|
||||
databaseType: "postgres" | "mysql" | "mongodb" | "mariadb";
|
||||
type: "error" | "success";
|
||||
errorMessage?: string;
|
||||
}) => {
|
||||
const date = new Date();
|
||||
const notificationList = await db.query.notifications.findMany({
|
||||
where: eq(notifications.databaseBackup, true),
|
||||
with: {
|
||||
email: true,
|
||||
discord: true,
|
||||
telegram: true,
|
||||
slack: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const notification of notificationList) {
|
||||
const { email, discord, telegram, slack } = notification;
|
||||
|
||||
if (email) {
|
||||
const template = await renderAsync(
|
||||
DatabaseBackupEmail({
|
||||
projectName,
|
||||
applicationName,
|
||||
databaseType,
|
||||
type,
|
||||
errorMessage,
|
||||
date: date.toLocaleString(),
|
||||
}),
|
||||
).catch();
|
||||
await sendEmailNotification(
|
||||
email,
|
||||
"Database backup for dokploy",
|
||||
template,
|
||||
);
|
||||
}
|
||||
|
||||
if (discord) {
|
||||
await sendDiscordNotification(discord, {
|
||||
title:
|
||||
type === "success"
|
||||
? "✅ Database Backup Successful"
|
||||
: "❌ Database Backup Failed",
|
||||
color: type === "success" ? 0x00ff00 : 0xff0000,
|
||||
fields: [
|
||||
{
|
||||
name: "Project",
|
||||
value: projectName,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Application",
|
||||
value: applicationName,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Type",
|
||||
value: databaseType,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Time",
|
||||
value: date.toLocaleString(),
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Type",
|
||||
value: type,
|
||||
},
|
||||
...(type === "error" && errorMessage
|
||||
? [
|
||||
{
|
||||
name: "Error Message",
|
||||
value: errorMessage,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
timestamp: date.toISOString(),
|
||||
footer: {
|
||||
text: "Dokploy Database Backup Notification",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (telegram) {
|
||||
const statusEmoji = type === "success" ? "✅" : "❌";
|
||||
const messageText = `
|
||||
<b>${statusEmoji} Database Backup ${type === "success" ? "Successful" : "Failed"}</b>
|
||||
|
||||
<b>Project:</b> ${projectName}
|
||||
<b>Application:</b> ${applicationName}
|
||||
<b>Type:</b> ${databaseType}
|
||||
<b>Time:</b> ${date.toLocaleString()}
|
||||
|
||||
<b>Status:</b> ${type === "success" ? "Successful" : "Failed"}
|
||||
${type === "error" && errorMessage ? `<b>Error:</b> ${errorMessage}` : ""}
|
||||
`;
|
||||
await sendTelegramNotification(telegram, messageText);
|
||||
}
|
||||
|
||||
if (slack) {
|
||||
const { channel } = slack;
|
||||
await sendSlackNotification(slack, {
|
||||
channel: channel,
|
||||
attachments: [
|
||||
{
|
||||
color: type === "success" ? "#00FF00" : "#FF0000",
|
||||
pretext:
|
||||
type === "success"
|
||||
? ":white_check_mark: *Database Backup Successful*"
|
||||
: ":x: *Database Backup Failed*",
|
||||
fields: [
|
||||
...(type === "error" && errorMessage
|
||||
? [
|
||||
{
|
||||
title: "Error Message",
|
||||
value: errorMessage,
|
||||
short: false,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: "Project",
|
||||
value: projectName,
|
||||
short: true,
|
||||
},
|
||||
{
|
||||
title: "Application",
|
||||
value: applicationName,
|
||||
short: true,
|
||||
},
|
||||
{
|
||||
title: "Type",
|
||||
value: databaseType,
|
||||
short: true,
|
||||
},
|
||||
{
|
||||
title: "Time",
|
||||
value: date.toLocaleString(),
|
||||
short: true,
|
||||
},
|
||||
{
|
||||
title: "Type",
|
||||
value: type,
|
||||
},
|
||||
{
|
||||
title: "Status",
|
||||
value: type === "success" ? "Successful" : "Failed",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
@ -1,94 +0,0 @@
|
||||
import DockerCleanupEmail from "@/emails/emails/docker-cleanup";
|
||||
import { db } from "@/server/db";
|
||||
import { notifications } from "@/server/db/schema";
|
||||
import { renderAsync } from "@react-email/components";
|
||||
import { eq } from "drizzle-orm";
|
||||
import {
|
||||
sendDiscordNotification,
|
||||
sendEmailNotification,
|
||||
sendSlackNotification,
|
||||
sendTelegramNotification,
|
||||
} from "./utils";
|
||||
|
||||
export const sendDockerCleanupNotifications = async (
|
||||
message = "Docker cleanup for dokploy",
|
||||
) => {
|
||||
const date = new Date();
|
||||
const notificationList = await db.query.notifications.findMany({
|
||||
where: eq(notifications.dockerCleanup, true),
|
||||
with: {
|
||||
email: true,
|
||||
discord: true,
|
||||
telegram: true,
|
||||
slack: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const notification of notificationList) {
|
||||
const { email, discord, telegram, slack } = notification;
|
||||
|
||||
if (email) {
|
||||
const template = await renderAsync(
|
||||
DockerCleanupEmail({ message, date: date.toLocaleString() }),
|
||||
).catch();
|
||||
|
||||
await sendEmailNotification(
|
||||
email,
|
||||
"Docker cleanup for dokploy",
|
||||
template,
|
||||
);
|
||||
}
|
||||
|
||||
if (discord) {
|
||||
await sendDiscordNotification(discord, {
|
||||
title: "✅ Docker Cleanup",
|
||||
color: 0x00ff00,
|
||||
fields: [
|
||||
{
|
||||
name: "Message",
|
||||
value: message,
|
||||
},
|
||||
],
|
||||
timestamp: date.toISOString(),
|
||||
footer: {
|
||||
text: "Dokploy Docker Cleanup Notification",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (telegram) {
|
||||
await sendTelegramNotification(
|
||||
telegram,
|
||||
`
|
||||
<b>✅ Docker Cleanup</b>
|
||||
<b>Message:</b> ${message}
|
||||
<b>Time:</b> ${date.toLocaleString()}
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
if (slack) {
|
||||
const { channel } = slack;
|
||||
await sendSlackNotification(slack, {
|
||||
channel: channel,
|
||||
attachments: [
|
||||
{
|
||||
color: "#00FF00",
|
||||
pretext: ":white_check_mark: *Docker Cleanup*",
|
||||
fields: [
|
||||
{
|
||||
title: "Message",
|
||||
value: message,
|
||||
},
|
||||
{
|
||||
title: "Time",
|
||||
value: date.toLocaleString(),
|
||||
short: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
@ -1,83 +0,0 @@
|
||||
import DokployRestartEmail from "@/emails/emails/dokploy-restart";
|
||||
import { db } from "@/server/db";
|
||||
import { notifications } from "@/server/db/schema";
|
||||
import { renderAsync } from "@react-email/components";
|
||||
import { eq } from "drizzle-orm";
|
||||
import {
|
||||
sendDiscordNotification,
|
||||
sendEmailNotification,
|
||||
sendSlackNotification,
|
||||
sendTelegramNotification,
|
||||
} from "./utils";
|
||||
|
||||
export const sendDokployRestartNotifications = async () => {
|
||||
const date = new Date();
|
||||
const notificationList = await db.query.notifications.findMany({
|
||||
where: eq(notifications.dokployRestart, true),
|
||||
with: {
|
||||
email: true,
|
||||
discord: true,
|
||||
telegram: true,
|
||||
slack: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const notification of notificationList) {
|
||||
const { email, discord, telegram, slack } = notification;
|
||||
|
||||
if (email) {
|
||||
const template = await renderAsync(
|
||||
DokployRestartEmail({ date: date.toLocaleString() }),
|
||||
).catch();
|
||||
await sendEmailNotification(email, "Dokploy Server Restarted", template);
|
||||
}
|
||||
|
||||
if (discord) {
|
||||
await sendDiscordNotification(discord, {
|
||||
title: "✅ Dokploy Server Restarted",
|
||||
color: 0x00ff00,
|
||||
fields: [
|
||||
{
|
||||
name: "Time",
|
||||
value: date.toLocaleString(),
|
||||
inline: true,
|
||||
},
|
||||
],
|
||||
timestamp: date.toISOString(),
|
||||
footer: {
|
||||
text: "Dokploy Restart Notification",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (telegram) {
|
||||
await sendTelegramNotification(
|
||||
telegram,
|
||||
`
|
||||
<b>✅ Dokploy Serverd Restarted</b>
|
||||
<b>Time:</b> ${date.toLocaleString()}
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
if (slack) {
|
||||
const { channel } = slack;
|
||||
await sendSlackNotification(slack, {
|
||||
channel: channel,
|
||||
attachments: [
|
||||
{
|
||||
color: "#00FF00",
|
||||
pretext: ":white_check_mark: *Dokploy Server Restarted*",
|
||||
fields: [
|
||||
{
|
||||
title: "Time",
|
||||
value: date.toLocaleString(),
|
||||
short: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
@ -1,84 +0,0 @@
|
||||
import type { discord, email, slack, telegram } from "@/server/db/schema";
|
||||
import nodemailer from "nodemailer";
|
||||
|
||||
export const sendEmailNotification = async (
|
||||
connection: typeof email.$inferInsert,
|
||||
subject: string,
|
||||
htmlContent: string,
|
||||
) => {
|
||||
try {
|
||||
const {
|
||||
smtpServer,
|
||||
smtpPort,
|
||||
username,
|
||||
password,
|
||||
fromAddress,
|
||||
toAddresses,
|
||||
} = connection;
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: smtpServer,
|
||||
port: smtpPort,
|
||||
auth: { user: username, pass: password },
|
||||
});
|
||||
|
||||
await transporter.sendMail({
|
||||
from: fromAddress,
|
||||
to: toAddresses.join(", "),
|
||||
subject,
|
||||
html: htmlContent,
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const sendDiscordNotification = async (
|
||||
connection: typeof discord.$inferInsert,
|
||||
embed: any,
|
||||
) => {
|
||||
try {
|
||||
await fetch(connection.webhookUrl, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ embeds: [embed] }),
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const sendTelegramNotification = async (
|
||||
connection: typeof telegram.$inferInsert,
|
||||
messageText: string,
|
||||
) => {
|
||||
try {
|
||||
const url = `https://api.telegram.org/bot${connection.botToken}/sendMessage`;
|
||||
await fetch(url, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
chat_id: connection.chatId,
|
||||
text: messageText,
|
||||
parse_mode: "HTML",
|
||||
disable_web_page_preview: true,
|
||||
}),
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const sendSlackNotification = async (
|
||||
connection: typeof slack.$inferInsert,
|
||||
message: any,
|
||||
) => {
|
||||
try {
|
||||
await fetch(connection.webhookUrl, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(message),
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
@ -33,37 +33,6 @@ export * from "./services/auth";
|
||||
export * from "./services/gitlab";
|
||||
export * from "./services/server";
|
||||
export * from "./services/application";
|
||||
// export * from "./db/schema/application";
|
||||
// export * from "./db/schema/postgres";
|
||||
// export * from "./db/schema/user";
|
||||
// export * from "./db/schema/admin";
|
||||
// export * from "./db/schema/auth";
|
||||
// export * from "./db/schema/project";
|
||||
// export * from "./db/schema/domain";
|
||||
// export * from "./db/schema/mariadb";
|
||||
// export * from "./db/schema/mongo";
|
||||
// export * from "./db/schema/mysql";
|
||||
// export * from "./db/schema/backups";
|
||||
// export * from "./db/schema/destination";
|
||||
// export * from "./db/schema/deployment";
|
||||
// export * from "./db/schema/mount";
|
||||
// export * from "./db/schema/certificate";
|
||||
// export * from "./db/schema/session";
|
||||
// export * from "./db/schema/redirects";
|
||||
// export * from "./db/schema/security";
|
||||
// export * from "./db/schema/port";
|
||||
// export * from "./db/schema/redis";
|
||||
// export * from "./db/schema/shared";
|
||||
// export * from "./db/schema/compose";
|
||||
// export * from "./db/schema/registry";
|
||||
// export * from "./db/schema/notification";
|
||||
// export * from "./db/schema/ssh-key";
|
||||
// export * from "./db/schema/git-provider";
|
||||
// export * from "./db/schema/bitbucket";
|
||||
// export * from "./db/schema/github";
|
||||
// export * from "./db/schema/gitlab";
|
||||
// export * from "./db/schema/server";
|
||||
// export * from "./db/schema/utils";
|
||||
|
||||
export * from "./setup/config-paths";
|
||||
export * from "./setup/postgres-setup";
|
||||
@ -146,3 +115,6 @@ export * from "./utils/access-log/utils";
|
||||
export * from "./constants/index";
|
||||
|
||||
export * from "./monitoring/utilts";
|
||||
|
||||
export * from "./db/validations/domain";
|
||||
export * from "./db/validations/index";
|
||||
|