This commit is contained in:
2026-02-16 19:34:35 +00:00
commit 9ab0940ca4
784 changed files with 41710 additions and 0 deletions
@@ -0,0 +1,18 @@
import Layout from 'emailer/emails/layouts/MainLayout.jsx';
import { Container } from '@react-email/components';
import { Text, Button } from '@react-email/components';
const Email = ({ email, url }: { email: string; url: string }) => {
return (
<Layout>
<Container>
<Text className="pt-4 text-2xl">Password Reset</Text>
<Text>A password reset was requested for your account {email || '<email>'}</Text>
<Text>You can ignore this email if you didn't request a password reset.</Text>
<Button href={url}>Click here to Reset your password</Button>
</Container>
</Layout>
);
};
export default Email;
@@ -0,0 +1,15 @@
import Layout from './layouts/MainLayout.js';
import { Container, Text } from '@react-email/components';
const Email = ({ email }: { email: string }) => {
return (
<Layout>
<Container>
<Text className="pt-4 text-2xl">We have finished processing your request.</Text>
<Text>the Google Analytics data from {email || '<example@gmail.com>'} is now up to date on our platform</Text>
</Container>
</Layout>
);
};
export default Email;
@@ -0,0 +1,29 @@
import Layout from 'emailer/emails/layouts/MainLayout.jsx';
import { Container } from '@react-email/components';
type MassEmailProps = {
subject: string;
content: string;
greeting?: string;
firstName?: string;
fullName?: string;
};
const Email = ({ content, greeting, firstName, fullName }: MassEmailProps) => {
const processGreeting = (text: string) => {
return text.replace(/\{\{firstName\}\}/g, firstName || '').replace(/\{\{fullName\}\}/g, fullName || '');
};
const processedGreeting = greeting ? processGreeting(greeting) : '';
const greetingHtml = processedGreeting ? `<p>${processedGreeting}</p>` : '';
return (
<Layout>
<Container>
<div dangerouslySetInnerHTML={{ __html: greetingHtml + content }} />
</Container>
</Layout>
);
};
export default Email;
@@ -0,0 +1,17 @@
import Layout from './layouts/MainLayout.jsx';
import { Container } from '@react-email/components';
import { Text, Button } from '@react-email/components';
const Email = ({ label }: { label: string }) => {
return (
<Layout>
<Container>
<Text className="pt-4 text-2xl">Hello Obi Wan here 🔥</Text>
<Text>Here you go</Text>
<Button href="https://developedbyed.com">{label || 'Check out the courses'}</Button>
</Container>
</Layout>
);
};
export default Email;
@@ -0,0 +1,42 @@
import { Container } from '@react-email/components';
import { Text, Tailwind } from '@react-email/components';
import { CodeBlock, dracula } from '@react-email/code-block';
const Email = ({ stderr }: { stderr: string }) => {
return (
<Tailwind>
<Container>
<Text className="pt-4 text-5xl text-center">Tests have failed</Text>
<CodeBlock code={stderr || sampleCode} theme={dracula} language="javascript" />
</Container>
</Tailwind>
);
};
export default Email;
const sampleCode = `Failed with code 1
bun test v1.1.8 (89d25807)
src/index.test.ts:
(pass) Auth Server > should signin [99.65ms]
(pass) Auth Server > should return 200 [0.39ms]
27 | const data = await response.json();
28 | expect(data.pertentoAuthenticationServer).toBeTruthy();
29 | });
30 |
31 | test('should return 401', async () => {
32 | expect(true).toBeFalsy();
^
error: expect(received).toBeFalsy()
Received: true
at /Users/andrepadez/develop/pertento/testing/auth-server/src/index.test.ts:32:5
at /Users/andrepadez/develop/pertento/testing/auth-server/src/index.test.ts:31:29
(fail) Auth Server > should return 401 [0.25ms]
2 pass
1 fail
5 expect() calls
Ran 3 tests across 1 files. [108.00ms]`;
@@ -0,0 +1,124 @@
import { Container } from '@react-email/components';
import { Text, Tailwind } from '@react-email/components';
import { CodeBlock } from '@react-email/code-block';
type EmailProps = {
serverName: string;
timestamp: string;
url: string;
method: string;
stack: string;
body: string;
message: string;
headers: Record<string, unknown>;
user: Record<string, unknown>;
};
const Email = (props: EmailProps) => {
const { serverName, timestamp, url, method, stack, body, message, headers, user } = props;
return (
<Tailwind>
<Container className="max-w-[80%]">
<Text className="pt-4 text-center text-5xl">Unexpected Error in Server</Text>
<Text className="pt-4 text-center text-2xl">{serverName || 'Auth Server'}</Text>
<CustomCodeBlock code={timestamp} />
<CustomCodeBlock code={`${method || 'GET'}: ${url || 'http://auth.pertento.ai/'}`} />
<CustomCodeBlock code={message || messageSample} />
<CustomCodeBlock code={stack || stackSample} />
{headers && <CustomCodeBlock code={`req.headers: ${JSON.stringify(headers, null, 2)}`} />}
{user && <CustomCodeBlock code={`req.user = ${JSON.stringify(user, null, 2)}`} />}
{body && <CustomCodeBlock code={`req.body =${JSON.stringify(body, null, 2)}`} />}
</Container>
</Tailwind>
);
};
const CustomCodeBlock = ({ code }: { code: string }) => {
return <CodeBlock code={` \n${code}\n\ `} theme={coyTheme} language="js" />;
};
export default Email;
const coyTheme = {
base: {
color: 'black',
background: 'none',
fontFamily: "Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace",
fontSize: '1em',
textAlign: 'left' as const,
whiteSpace: 'pre' as const,
wordSpacing: 'normal',
wordBreak: 'break-all' as const,
lineHeight: '1.5',
tabSize: 4,
hyphens: 'none' as const,
position: 'relative' as const,
borderLeft: '10px solid #358ccb',
boxShadow: '-1px 0 0 0 #358ccb, 0 0 0 1px #dfdfdf',
backgroundColor: '#fdfdfd',
backgroundImage: 'linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%)',
backgroundSize: '3em 3em',
backgroundOrigin: 'content-box' as const,
backgroundAttachment: 'local' as const,
margin: '.5em 0',
padding: '0 1em',
} as const,
comment: { color: '#7D8B99' } as const,
'block-comment': { color: '#7D8B99' } as const,
prolog: { color: '#7D8B99' } as const,
doctype: { color: '#7D8B99' } as const,
cdata: { color: '#7D8B99' } as const,
punctuation: { color: '#5F6364' } as const,
property: { color: '#c92c2c' } as const,
tag: { color: '#c92c2c' } as const,
boolean: { color: '#c92c2c' } as const,
number: { color: '#c92c2c' } as const,
'function-name': { color: '#c92c2c' } as const,
constant: { color: '#c92c2c' } as const,
symbol: { color: '#c92c2c' } as const,
deleted: { color: '#c92c2c' } as const,
selector: { color: '#2f9c0a' } as const,
'attr-name': { color: '#2f9c0a' } as const,
string: { color: '#2f9c0a' } as const,
char: { color: '#2f9c0a' } as const,
function: { color: '#2f9c0a' } as const,
builtin: { color: '#2f9c0a' } as const,
inserted: { color: '#2f9c0a' } as const,
operator: {
color: '#a67f59',
background: 'rgba(255, 255, 255, 0.5)',
} as const,
entity: {
color: '#a67f59',
background: 'rgba(255, 255, 255, 0.5)',
cursor: 'help',
} as const,
url: { color: '#a67f59', background: 'rgba(255, 255, 255, 0.5)' } as const,
variable: {
color: '#a67f59',
background: 'rgba(255, 255, 255, 0.5)',
} as const,
atrule: { color: '#1990b8' } as const,
'attr-value': { color: '#1990b8' } as const,
keyword: { color: '#1990b8' } as const,
'class-name': { color: '#1990b8' } as const,
regex: { color: '#e90' } as const,
important: { color: '#e90', fontWeight: 'normal' } as const,
bold: { fontWeight: 'bold' } as const,
italic: { fontStyle: 'italic' } as const,
namespace: { opacity: '.7' } as const,
} as const;
const stackSample = `Error: Something went wrong
at <anonymous> (/Users/andrepadez/develop/pertento/workspaces/servers/api-server/src/index.ts:9:1)
at <anonymous> (/Users/andrepadez/develop/pertento/node_modules/hono/dist/compose.js:29:28)
at dispatch (/Users/andrepadez/develop/pertento/node_modules/hono/dist/compose.js:7:15)
at <anonymous> (/Users/andrepadez/develop/pertento/workspaces/servers/_hono_server/src/middlewares/body-parser.ts:1:32)
at <anonymous> (/Users/andrepadez/develop/pertento/node_modules/hono/dist/compose.js:29:28)
at dispatch (/Users/andrepadez/develop/pertento/node_modules/hono/dist/compose.js:7:15)
at <anonymous> (/Users/andrepadez/develop/pertento/node_modules/hono/dist/middleware/cors/index.js:64:15)
at cors2 (/Users/andrepadez/develop/pertento/node_modules/hono/dist/middleware/cors/index.js:22:29)
at <anonymous> (/Users/andrepadez/develop/pertento/node_modules/hono/dist/compose.js:29:28)
at dispatch (/Users/andrepadez/develop/pertento/node_modules/hono/dist/compose.js:7:15)`;
const messageSample = `Error: Something went wrong`;
@@ -0,0 +1,24 @@
import Layout from './layouts/MainLayout.jsx';
import { Container } from '@react-email/components';
import { Text, Button } from '@react-email/components';
type EmailProps = {
invitedBy: string;
companyName: string;
url: string;
};
const Email = ({ invitedBy, companyName, url }: EmailProps) => {
return (
<Layout>
<Container>
<Text className="pt-4 text-2xl">Invitation to our platform</Text>
<Text>You have been invited by {invitedBy || '<invitedBy>'} to join Pertento.ai</Text>
<Text>as a member of {companyName || '<companyName>'}</Text>
<Button href={url || 'https://example.com'}>Click here to accept our invitation</Button>
</Container>
</Layout>
);
};
export default Email;
@@ -0,0 +1,21 @@
import Layout from './layouts/MainLayout.jsx';
import { Container } from '@react-email/components';
import { Text, Button } from '@react-email/components';
type EmailProps = {
name: string;
url: string;
};
const Email = ({ name, url }: EmailProps) => {
return (
<Layout>
<Container>
<Text className="pt-4 text-2xl">Welcome, {name || 'there'}</Text>
<Text className="text-xs">You can ignore this email if you didn't signup for our site.</Text>
<Button href={url || 'https://example.com'}>Click here to set up your admin account</Button>
</Container>
</Layout>
);
};
export default Email;
@@ -0,0 +1,21 @@
import Layout from './layouts/MainLayout.jsx';
import { Container } from '@react-email/components';
import { Text, Button } from '@react-email/components';
type EmailProps = {
name: string;
url: string;
};
const Email = ({ name, url }: EmailProps) => {
return (
<Layout>
<Container>
<Text className="pt-4 text-2xl">Welcome, {name || 'there'}</Text>
<Text className="text-xs">You can ignore this email if you didn't signup for our site.</Text>
<Button href={url || 'https://example.com'}>Click here to Confirm your Registration</Button>
</Container>
</Layout>
);
};
export default Email;
@@ -0,0 +1,25 @@
import { Body, Html, Head, Container, Img, Tailwind } from '@react-email/components';
import type { ReactNode } from 'react';
const MainLayout = ({ children }: { children: ReactNode }) => {
return (
<Html>
<Head />
<Tailwind>
<Body className="mx-auto my-12 bg-white font-sans">
<Container className="rounded-lg bg-white p-8 shadow-lg">
<Img
className="mx-auto block"
src={`${process.env.PUBLIC_URL}/static/officer-logo.svg`}
width="240"
alt="Officer Logo"
/>
{children}
</Container>
</Body>
</Tailwind>
</Html>
);
};
export default MainLayout;
+16
View File
@@ -0,0 +1,16 @@
{
"name": "emailer",
"version": "0.0.19",
"main": "src/index.ts",
"private": true,
"scripts": {
"build": "email build",
"dev": "email dev --port 3009"
},
"dependencies": {
"@react-email/code-block": "^0.0.11",
"@react-email/components": "^0.0.31",
"@react-email/render": "^1.0.3",
"react-email": "^3.0.4"
}
}
+31
View File
@@ -0,0 +1,31 @@
import { render } from '@react-email/render';
import transport from './transport';
import { templates } from './templates';
type SendMailArgs = {
template: string;
subject: string;
to: string;
data?: any;
from?: string;
};
export async function sendMail({ template, subject, to, data, from = 'Officer <no-reply@officer.dev>' }: SendMailArgs) {
const options = {
from,
to,
subject,
};
const theTemplate = templates[template];
const templated = theTemplate(data);
// try {
const emailHtml = await render(templated);
transport.sendMail({ ...options, html: emailHtml });
// } catch (ex) {
// console.log("ex", ex);
// }
}
export { templates };
+17
View File
@@ -0,0 +1,17 @@
import path from 'node:path';
import { readdir, stat } from 'node:fs/promises';
export const templates: any = {};
const templateDir = path.resolve(__dirname, '..', 'emails');
const files = await readdir(templateDir);
for (let file of files) {
const fpath = path.join(templateDir, file);
const [name, ext] = file.split('.');
const fsStat = await stat(fpath);
if (!fsStat.isDirectory() && ext === 'tsx' && name) {
const { default: template } = await import(fpath);
templates[name] = template;
}
}
+6
View File
@@ -0,0 +1,6 @@
import { createTransport } from 'nodemailer';
const { MAIL_TRANSPORT } = process.env;
const transport = createTransport(MAIL_TRANSPORT);
export default transport;