feat: SMTP server (#47)

* WIP: SMTP server

* Card and minor changes in Server

---------

Co-authored-by: KM Koushik <koushikmohan1996@gmail.com>
This commit is contained in:
Harsh Shrikant Bhat
2024-08-11 05:27:13 +05:30
committed by GitHub
parent d74b20bac8
commit 0b82eb2266
8 changed files with 576 additions and 46 deletions

View File

@@ -0,0 +1,28 @@
{
"name": "smtp-server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc",
"start": "node dist/server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@types/dotenv": "^8.2.0",
"@types/mailparser": "^3.4.4",
"@types/smtp-server": "^3.5.10",
"dotenv": "^16.4.5",
"mailparser": "^3.7.1",
"nodemailer": "^6.9.14",
"smtp-server": "^3.13.4"
},
"devDependencies": {
"@types/node": "^22.1.0",
"@types/nodemailer": "^6.4.15",
"typescript": "^5.5.4"
}
}

View File

@@ -0,0 +1,101 @@
import { SMTPServer, SMTPServerOptions } from 'smtp-server';
import { Readable } from 'stream';
import dotenv from 'dotenv';
import { simpleParser } from 'mailparser';
dotenv.config();
const AUTH_USERNAME = process.env.SMTP_AUTH_USERNAME!;
const UNSEND_BASE_URL = process.env.UNSEND_BASE_URL!;
let API_KEY = '';
const ports = [25, 465, 2465, 587, 2587]; // Array of ports to listen on
const serverOptions: SMTPServerOptions = {
onData(stream: Readable, session: any, callback: (error?: Error) => void) {
console.log('Receiving email data...'); // Debug statement
simpleParser(stream, (err, parsed) => {
if (err) {
console.error('Failed to parse email data:', err.message);
return callback(err);
}
const emailObject: any = {
to: Array.isArray(parsed.to) ? parsed.to.map(addr => addr.text).join(', ') : parsed.to?.text,
from: Array.isArray(parsed.from) ? parsed.from.map(addr => addr.text).join(', ') : parsed.from?.text,
subject: parsed.subject,
text: parsed.text,
html: parsed.html,
};
console.log('Parsed email data:', emailObject); // Debug statement
sendEmailToUnsend(emailObject)
.then(() => callback())
.catch((error) => {
console.error('Failed to send email:', error.message);
callback(error);
});
});
},
onAuth(auth: any, session: any, callback: (error?: Error, user?: any) => void) {
API_KEY = auth.password;
if (auth.username === AUTH_USERNAME) {
console.log('Authenticated successfully'); // Debug statement
callback(undefined, { user: AUTH_USERNAME });
} else {
console.error('Invalid username or password');
callback(new Error('Invalid username or password'));
}
},
};
async function sendEmailToUnsend(emailData: any) {
try {
const apiEndpoint = '/api/v1/emails';
const url = new URL(apiEndpoint, UNSEND_BASE_URL); // Combine base URL with endpoint
console.log('Sending email to Unsend API at:', url.href); // Debug statement
const response = await fetch(url.href, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(emailData),
});
if (!response.ok) {
const errorData = await response.json();
console.error('Unsend API error response:', errorData);
throw new Error(`Failed to send email: ${errorData.message || 'Unknown error from server'}`);
}
const responseData = await response.json();
console.log('Unsend API response:', responseData);
} catch (error) {
if (error instanceof Error) {
console.error('Error message:', error.message);
throw new Error(`Failed to send email: ${error.message}`);
} else {
console.error('Unexpected error:', error);
throw new Error('Failed to send email: Unexpected error occurred');
}
}
}
function startServers() {
ports.forEach(port => {
const server = new SMTPServer(serverOptions);
server.listen(port, () => {
console.log(`SMTP server is listening on port ${port}`);
});
server.on('error', (err) => {
console.error(`Error occurred on port ${port}:`, err);
});
});
}
startServers();

View File

@@ -0,0 +1,37 @@
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
host: "localhost",
port: 2587,
secure: false,
auth: {
user: "unsend",
pass: "us_38de56vwa7_cc90a91b01a402de0c15516b3554adc1",
},
tls: {
rejectUnauthorized: false,
}
});
const mailOptions = {
to: "harsh121102@gmail.com",
from: "hello@support.harshbhat.me",
subject: "Testing SMTP",
html: "<strong>THIS IS USING SMTP,</strong><p>Unsend is the best open source sending platform<p><p>check out <a href='https://unsend.dev'>unsend.dev</a>",
text: "hello,\n\nUnsend is the best open source sending platform",
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
console.error('Error sending email:', error);
} else {
console.log('Email sent successfully:', info.response);
}
});

View File

@@ -0,0 +1,24 @@
{
"compilerOptions": {
/* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"module": "commonjs", /* Specify what module code is generated. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Modules */
"rootDir": "./src", /* Specify the root folder within your source files. */
"outDir": "./dist", /* Specify an output folder for all emitted files. */
/* Emit */
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
"removeComments": true, /* Disable emitting comments. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"include": ["src/**/*.ts"], /* Include all TypeScript files in the src directory. */
"exclude": ["node_modules"] /* Exclude node_modules from compilation. */
}

View File

@@ -2,15 +2,43 @@
import ApiList from "./api-list"; import ApiList from "./api-list";
import AddApiKey from "./add-api-key"; import AddApiKey from "./add-api-key";
import Smtp from "./smtp";
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@unsend/ui/src/tabs";
import { useState } from 'react';
export default function ApiKeysPage() { export default function ApiKeysPage() {
const [activeTab, setActiveTab] = useState("apiKeys");
const disableSmtp = true;
const handleTabChange = (value: any) => {
if (value === "smtp" && disableSmtp) {
return;
}
setActiveTab(value);
};
return ( return (
<div> <div>
<div className="flex justify-between items-center"> <Tabs defaultValue="apiKeys" value={activeTab} onValueChange={handleTabChange}>
<h1 className="font-bold text-lg">API Keys</h1> <TabsList>
<AddApiKey /> <TabsTrigger value="apiKeys">API keys</TabsTrigger>
</div> <TabsTrigger
<ApiList /> value="smtp"
className={`cursor-pointer ${disableSmtp ? 'opacity-50 pointer-events-none' : ''}`}
>
SMTP
</TabsTrigger>
</TabsList>
<TabsContent value="apiKeys">
<div className="flex justify-between items-center">
<h1 className="font-bold text-lg">API Keys</h1>
<AddApiKey />
</div>
<ApiList />
</TabsContent>
<TabsContent value="smtp">
<Smtp />
</TabsContent>
</Tabs>
</div> </div>
); );
} }

View File

@@ -0,0 +1,54 @@
"use client";
import * as React from "react";
import { Code } from "@unsend/ui/src/code";
import { Button } from "@unsend/ui/src/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@unsend/ui/src/card";
import { TextWithCopyButton } from "@unsend/ui/src/text-with-copy";
export default function ExampleCard() {
const smtpDetails = {
smtp: "smtp.example.com",
port: "587",
user: "user@example.com",
password: "supersecretpassword"
};
return (
<Card className="mt-9">
<CardHeader>
<CardTitle>SMTP</CardTitle>
<CardDescription>
Send emails using SMTP instead of the REST API. See documentation for more information.
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div>
<strong>Host:</strong>
<TextWithCopyButton className="ml-1 text-zinc-500 rounded-lg mt-1 p-2 w-full bg-gray-900" value={"smtp.unsend.dev"}></TextWithCopyButton>
</div>
<div>
<strong>Port:</strong>
<TextWithCopyButton className="ml-1 text-zinc-500 rounded-lg mt-1 p-2 w-full bg-gray-900" value={"465"}></TextWithCopyButton>
<p className="ml-1 mt-1 text-zinc-500 ">For encrypted/TLS connections use <strong>2465</strong>, <strong>587</strong> or <strong>2587</strong></p>
</div>
<div>
<strong>User:</strong>
<TextWithCopyButton className="ml-1 text-zinc-500 rounded-lg mt-1 p-2 w-full bg-gray-900" value={"unsend"}></TextWithCopyButton>
</div>
<div>
<strong>Password:</strong>
<TextWithCopyButton className="ml-1 text-zinc-500 rounded-lg mt-1 p-2 w-full bg-gray-900" value={"YOUR_API_KEY"}></TextWithCopyButton>
</div>
</div>
</CardContent>
</Card>
);
}

76
packages/ui/src/card.tsx Normal file
View File

@@ -0,0 +1,76 @@
import * as React from "react"
import { cn } from "../lib/utils"
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-xl border bg-card text-card-foreground shadow",
className
)}
{...props}
/>
))
Card.displayName = "Card"
const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn("font-semibold leading-none tracking-tight", className)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
CardFooter.displayName = "CardFooter"
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }

264
pnpm-lock.yaml generated
View File

@@ -92,6 +92,40 @@ importers:
specifier: ^5 specifier: ^5
version: 5.4.2 version: 5.4.2
apps/smtp-server:
dependencies:
'@types/dotenv':
specifier: ^8.2.0
version: 8.2.0
'@types/mailparser':
specifier: ^3.4.4
version: 3.4.4
'@types/smtp-server':
specifier: ^3.5.10
version: 3.5.10
dotenv:
specifier: ^16.4.5
version: 16.4.5
mailparser:
specifier: ^3.7.1
version: 3.7.1
nodemailer:
specifier: ^6.9.14
version: 6.9.14
smtp-server:
specifier: ^3.13.4
version: 3.13.4
devDependencies:
'@types/node':
specifier: ^22.1.0
version: 22.1.0
'@types/nodemailer':
specifier: ^6.4.15
version: 6.4.15
typescript:
specifier: ^5.5.4
version: 5.5.4
apps/web: apps/web:
dependencies: dependencies:
'@auth/prisma-adapter': '@auth/prisma-adapter':
@@ -6360,7 +6394,7 @@ packages:
/@types/cors@2.8.17: /@types/cors@2.8.17:
resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==}
dependencies: dependencies:
'@types/node': 20.11.27 '@types/node': 20.12.12
dev: true dev: true
/@types/d3-array@3.2.1: /@types/d3-array@3.2.1:
@@ -6411,6 +6445,13 @@ packages:
'@types/ms': 0.7.34 '@types/ms': 0.7.34
dev: true dev: true
/@types/dotenv@8.2.0:
resolution: {integrity: sha512-ylSC9GhfRH7m1EUXBXofhgx4lUWmFeQDINW5oLuS+gxWdfUeW4zJdeVTYVkexEW+e2VUvlZR2kGnGGipAWR7kw==}
deprecated: This is a stub types definition. dotenv provides its own type definitions, so you do not need this installed.
dependencies:
dotenv: 16.4.5
dev: false
/@types/eslint@8.56.5: /@types/eslint@8.56.5:
resolution: {integrity: sha512-u5/YPJHo1tvkSF2CE0USEkxon82Z5DBy2xR+qfyYNszpX9qcs4sT6uq2kBbj4BXY1+DBGDPnrhMZV3pKWGNukw==} resolution: {integrity: sha512-u5/YPJHo1tvkSF2CE0USEkxon82Z5DBy2xR+qfyYNszpX9qcs4sT6uq2kBbj4BXY1+DBGDPnrhMZV3pKWGNukw==}
dependencies: dependencies:
@@ -6461,6 +6502,13 @@ packages:
resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==}
dev: true dev: true
/@types/mailparser@3.4.4:
resolution: {integrity: sha512-C6Znp2QVS25JqtuPyxj38Qh+QoFcLycdxsvcc6IZCGekhaMBzbdTXzwGzhGoYb3TfKu8IRCNV0sV1o3Od97cEQ==}
dependencies:
'@types/node': 20.12.12
iconv-lite: 0.6.3
dev: false
/@types/mdast@3.0.15: /@types/mdast@3.0.15:
resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==}
dependencies: dependencies:
@@ -6501,8 +6549,18 @@ packages:
resolution: {integrity: sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==} resolution: {integrity: sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==}
dependencies: dependencies:
undici-types: 5.26.5 undici-types: 5.26.5
/@types/node@22.1.0:
resolution: {integrity: sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==}
dependencies:
undici-types: 6.13.0
dev: true dev: true
/@types/nodemailer@6.4.15:
resolution: {integrity: sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==}
dependencies:
'@types/node': 20.12.12
/@types/normalize-package-data@2.4.4: /@types/normalize-package-data@2.4.4:
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
dev: true dev: true
@@ -6559,6 +6617,13 @@ packages:
resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==}
dev: true dev: true
/@types/smtp-server@3.5.10:
resolution: {integrity: sha512-i3Jx7sJ2qF52vjaOf3HguulXlWRFf6BSfsRLsIdmytDyVGv7KkhSs+gR9BXJnJWg1Ljkh/56Fh1Xqwa6u6X7zw==}
dependencies:
'@types/node': 20.12.12
'@types/nodemailer': 6.4.15
dev: false
/@types/ua-parser-js@0.7.39: /@types/ua-parser-js@0.7.39:
resolution: {integrity: sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==} resolution: {integrity: sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==}
dev: true dev: true
@@ -7428,6 +7493,10 @@ packages:
/balanced-match@1.0.2: /balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
/base32.js@0.1.0:
resolution: {integrity: sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==}
engines: {node: '>=0.12.0'}
/balanced-match@2.0.0: /balanced-match@2.0.0:
resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==}
dev: false dev: false
@@ -8497,7 +8566,6 @@ packages:
/dotenv@16.4.5: /dotenv@16.4.5:
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
engines: {node: '>=12'} engines: {node: '>=12'}
dev: true
/eastasianwidth@0.2.0: /eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
@@ -8543,6 +8611,16 @@ packages:
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
dev: true dev: true
/encoding-japanese@2.0.0:
resolution: {integrity: sha512-++P0RhebUC8MJAwJOsT93dT+5oc5oPImp1HubZpAuCZ5kTLnhuuBhKHj2jJeO/Gj93idPBWmIuQ9QWMe5rX3pQ==}
engines: {node: '>=8.10.0'}
dev: false
/encoding-japanese@2.1.0:
resolution: {integrity: sha512-58XySVxUgVlBikBTbQ8WdDxBDHIdXucB16LO5PBHR8t75D54wQrNo4cg+58+R1CtJfKnsVsvt9XlteRaR8xw1w==}
engines: {node: '>=8.10.0'}
dev: false
/engine.io-parser@5.2.2: /engine.io-parser@5.2.2:
resolution: {integrity: sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==} resolution: {integrity: sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
@@ -8554,7 +8632,7 @@ packages:
dependencies: dependencies:
'@types/cookie': 0.4.1 '@types/cookie': 0.4.1
'@types/cors': 2.8.17 '@types/cors': 2.8.17
'@types/node': 20.11.27 '@types/node': 20.12.12
accepts: 1.3.8 accepts: 1.3.8
base64id: 2.0.0 base64id: 2.0.0
cookie: 0.4.2 cookie: 0.4.2
@@ -8831,7 +8909,7 @@ packages:
eslint: 8.57.0 eslint: 8.57.0
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
eslint-plugin-import: 2.29.1(eslint@8.57.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0)
eslint-plugin-react: 7.34.0(eslint@8.57.0) eslint-plugin-react: 7.34.0(eslint@8.57.0)
eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0)
@@ -8889,7 +8967,7 @@ packages:
enhanced-resolve: 5.16.0 enhanced-resolve: 5.16.0
eslint: 8.57.0 eslint: 8.57.0
eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0)(eslint@8.57.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
fast-glob: 3.3.2 fast-glob: 3.3.2
get-tsconfig: 4.7.3 get-tsconfig: 4.7.3
is-core-module: 2.13.1 is-core-module: 2.13.1
@@ -9023,6 +9101,41 @@ packages:
ignore: 5.3.1 ignore: 5.3.1
dev: true dev: true
/eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==}
engines: {node: '>=4'}
peerDependencies:
'@typescript-eslint/parser': '*'
eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
peerDependenciesMeta:
'@typescript-eslint/parser':
optional: true
dependencies:
'@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.2)
array-includes: 3.1.7
array.prototype.findlastindex: 1.2.4
array.prototype.flat: 1.3.2
array.prototype.flatmap: 1.3.2
debug: 3.2.7
doctrine: 2.1.0
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
hasown: 2.0.2
is-core-module: 2.13.1
is-glob: 4.0.3
minimatch: 3.1.2
object.fromentries: 2.0.7
object.groupby: 1.0.2
object.values: 1.1.7
semver: 6.3.1
tsconfig-paths: 3.15.0
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
dev: true
/eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0)(eslint@8.57.0): /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0)(eslint@8.57.0):
resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==}
engines: {node: '>=4'} engines: {node: '>=4'}
@@ -9058,40 +9171,6 @@ packages:
- supports-color - supports-color
dev: true dev: true
/eslint-plugin-import@2.29.1(eslint@8.57.0):
resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==}
engines: {node: '>=4'}
peerDependencies:
'@typescript-eslint/parser': '*'
eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
peerDependenciesMeta:
'@typescript-eslint/parser':
optional: true
dependencies:
array-includes: 3.1.7
array.prototype.findlastindex: 1.2.4
array.prototype.flat: 1.3.2
array.prototype.flatmap: 1.3.2
debug: 3.2.7
doctrine: 2.1.0
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
hasown: 2.0.2
is-core-module: 2.13.1
is-glob: 4.0.3
minimatch: 3.1.2
object.fromentries: 2.0.7
object.groupby: 1.0.2
object.values: 1.1.7
semver: 6.3.1
tsconfig-paths: 3.15.0
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
dev: true
/eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint@8.57.0)(typescript@5.4.2): /eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint@8.57.0)(typescript@5.4.2):
resolution: {integrity: sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==} resolution: {integrity: sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -10399,6 +10478,11 @@ packages:
property-information: 6.5.0 property-information: 6.5.0
space-separated-tokens: 2.0.2 space-separated-tokens: 2.0.2
/he@1.2.0:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
hasBin: true
dev: false
/highlight.js@10.7.3: /highlight.js@10.7.3:
resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
dev: false dev: false
@@ -10500,6 +10584,13 @@ packages:
safer-buffer: 2.1.2 safer-buffer: 2.1.2
dev: true dev: true
/iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
dependencies:
safer-buffer: 2.1.2
dev: false
/ieee754@1.2.1: /ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
@@ -10631,6 +10722,10 @@ packages:
engines: {node: '>= 0.10'} engines: {node: '>= 0.10'}
dev: true dev: true
/ipv6-normalize@1.0.1:
resolution: {integrity: sha512-Bm6H79i01DjgGTCWjUuCjJ6QDo1HB96PT/xCYuyJUP9WFbVDrLSbG4EZCvOCun2rNswZb0c3e4Jt/ws795esHA==}
dev: false
/is-absolute-url@4.0.1: /is-absolute-url@4.0.1:
resolution: {integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==} resolution: {integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -11230,6 +11325,40 @@ packages:
type-check: 0.4.0 type-check: 0.4.0
dev: true dev: true
/libbase64@1.2.1:
resolution: {integrity: sha512-l+nePcPbIG1fNlqMzrh68MLkX/gTxk/+vdvAb388Ssi7UuUN31MI44w4Yf33mM3Cm4xDfw48mdf3rkdHszLNew==}
dev: false
/libbase64@1.3.0:
resolution: {integrity: sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==}
dev: false
/libmime@5.2.0:
resolution: {integrity: sha512-X2U5Wx0YmK0rXFbk67ASMeqYIkZ6E5vY7pNWRKtnNzqjvdYYG8xtPDpCnuUEnPU9vlgNev+JoSrcaKSUaNvfsw==}
dependencies:
encoding-japanese: 2.0.0
iconv-lite: 0.6.3
libbase64: 1.2.1
libqp: 2.0.1
dev: false
/libmime@5.3.5:
resolution: {integrity: sha512-nSlR1yRZ43L3cZCiWEw7ali3jY29Hz9CQQ96Oy+sSspYnIP5N54ucOPHqooBsXzwrX1pwn13VUE05q4WmzfaLg==}
dependencies:
encoding-japanese: 2.1.0
iconv-lite: 0.6.3
libbase64: 1.3.0
libqp: 2.1.0
dev: false
/libqp@2.0.1:
resolution: {integrity: sha512-Ka0eC5LkF3IPNQHJmYBWljJsw0UvM6j+QdKRbWyCdTmYwvIDE6a7bCm0UkTAL/K+3KXK5qXT/ClcInU01OpdLg==}
dev: false
/libqp@2.1.0:
resolution: {integrity: sha512-O6O6/fsG5jiUVbvdgT7YX3xY3uIadR6wEZ7+vy9u7PKHAlSEB6blvC1o5pHBjgsi95Uo0aiBBdkyFecj6jtb7A==}
dev: false
/lilconfig@2.1.0: /lilconfig@2.1.0:
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -11362,6 +11491,27 @@ packages:
engines: {node: '>=12'} engines: {node: '>=12'}
dev: false dev: false
/mailparser@3.7.1:
resolution: {integrity: sha512-RCnBhy5q8XtB3mXzxcAfT1huNqN93HTYYyL6XawlIKycfxM/rXPg9tXoZ7D46+SgCS1zxKzw+BayDQSvncSTTw==}
dependencies:
encoding-japanese: 2.1.0
he: 1.2.0
html-to-text: 9.0.5
iconv-lite: 0.6.3
libmime: 5.3.5
linkify-it: 5.0.0
mailsplit: 5.4.0
nodemailer: 6.9.13
punycode.js: 2.3.1
tlds: 1.252.0
dev: false
/mailsplit@5.4.0:
resolution: {integrity: sha512-wnYxX5D5qymGIPYLwnp6h8n1+6P6vz/MJn5AzGjZ8pwICWssL+CCQjWBIToOVHASmATot4ktvlLo6CyLfOXWYA==}
dependencies:
libbase64: 1.2.1
libmime: 5.2.0
libqp: 2.0.1
/magic-string@0.30.10: /magic-string@0.30.10:
resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==}
dependencies: dependencies:
@@ -12468,6 +12618,16 @@ packages:
vm-browserify: 1.1.2 vm-browserify: 1.1.2
dev: false dev: false
/nodemailer@6.9.13:
resolution: {integrity: sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==}
engines: {node: '>=6.0.0'}
dev: false
/nodemailer@6.9.14:
resolution: {integrity: sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA==}
engines: {node: '>=6.0.0'}
dev: false
/nopt@7.2.1: /nopt@7.2.1:
resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
@@ -13604,6 +13764,7 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
dev: false dev: false
/punycode@1.4.1: /punycode@1.4.1:
resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==}
dev: false dev: false
@@ -13611,7 +13772,6 @@ packages:
/punycode@2.3.1: /punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'} engines: {node: '>=6'}
dev: true
/qs@6.11.0: /qs@6.11.0:
resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
@@ -14562,7 +14722,6 @@ packages:
/safer-buffer@2.1.2: /safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
dev: true
/sax@1.3.0: /sax@1.3.0:
resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==}
@@ -14781,6 +14940,16 @@ packages:
engines: {node: '>=12'} engines: {node: '>=12'}
dev: true dev: true
/smtp-server@3.13.4:
resolution: {integrity: sha512-BbElv5UP+HgPtCZtcRW35N/GFoc4DzPkrbSMLioXsrVMmQT1mMBoO0k+egl264hxWaWczoVvadSPY2pLUINFXg==}
engines: {node: '>=12.0.0'}
dependencies:
base32.js: 0.1.0
ipv6-normalize: 1.0.1
nodemailer: 6.9.13
punycode: 2.3.1
dev: false
/socket.io-adapter@2.5.4: /socket.io-adapter@2.5.4:
resolution: {integrity: sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==} resolution: {integrity: sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==}
dependencies: dependencies:
@@ -15322,6 +15491,10 @@ packages:
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
dev: false dev: false
/tlds@1.252.0:
resolution: {integrity: sha512-GA16+8HXvqtfEnw/DTcwB0UU354QE1n3+wh08oFjr6Znl7ZLAeUgYzCcK+/CCrOyE0vnHR8/pu3XXG3vDijXpQ==}
hasBin: true
/tippy.js@6.3.7: /tippy.js@6.3.7:
resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==} resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==}
dependencies: dependencies:
@@ -15647,6 +15820,12 @@ packages:
hasBin: true hasBin: true
dev: true dev: true
/typescript@5.5.4:
resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==}
engines: {node: '>=14.17'}
hasBin: true
dev: true
/ua-parser-js@1.0.38: /ua-parser-js@1.0.38:
resolution: {integrity: sha512-Aq5ppTOfvrCMgAPneW1HfWj66Xi7XL+/mIy996R1/CLS/rcyJQm6QZdsKrUeivDFQ+Oc9Wyuwor8Ze8peEoUoQ==} resolution: {integrity: sha512-Aq5ppTOfvrCMgAPneW1HfWj66Xi7XL+/mIy996R1/CLS/rcyJQm6QZdsKrUeivDFQ+Oc9Wyuwor8Ze8peEoUoQ==}
dev: false dev: false
@@ -15666,6 +15845,9 @@ packages:
/undici-types@5.26.5: /undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
/undici-types@6.13.0:
resolution: {integrity: sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==}
dev: true dev: true
/undici@5.28.4: /undici@5.28.4: