Compare commits
1 Commits
main
...
feature/se
| Author | SHA1 | Date | |
|---|---|---|---|
| eeed88eba4 |
12
.env.example
@@ -1,6 +1,6 @@
|
|||||||
ADMIN_EMAIL=""
|
ADMIN_EMAIL=""
|
||||||
CRON_SECRET=""
|
CRON_SECRET=""
|
||||||
NEXT_PUBLIC_HOME_URL=""
|
HOME_URL=""
|
||||||
OVHCLOUD_API_KEY=""
|
OVHCLOUD_API_KEY=""
|
||||||
MAINTENANCE_MODE="0"
|
MAINTENANCE_MODE="0"
|
||||||
NEWS_LIMIT="50"
|
NEWS_LIMIT="50"
|
||||||
@@ -8,8 +8,12 @@ NEWS_TO_USE="10"
|
|||||||
NEXT_PUBLIC_BRAND_COUNTRY=""
|
NEXT_PUBLIC_BRAND_COUNTRY=""
|
||||||
NEXT_PUBLIC_BRAND_EMAIL=""
|
NEXT_PUBLIC_BRAND_EMAIL=""
|
||||||
NEXT_PUBLIC_BRAND_NAME=""
|
NEXT_PUBLIC_BRAND_NAME=""
|
||||||
NEXT_PUBLIC_BRAND_OWNER_NAME=""
|
|
||||||
DATABASE_URL=""
|
DATABASE_URL=""
|
||||||
SWEEGO_API_KEY=""
|
|
||||||
SWEEGO_FROM=""
|
|
||||||
SECRET_HASH=""
|
SECRET_HASH=""
|
||||||
|
|
||||||
|
EMAIL_HOST="postfix"
|
||||||
|
EMAIL_PORT="25"
|
||||||
|
EMAIL_FROM=""
|
||||||
|
MAIL_DOMAIN=""
|
||||||
|
MAIL_HOSTNAME=""
|
||||||
|
DKIM_SELECTOR="mail"
|
||||||
|
|||||||
@@ -36,6 +36,6 @@ jobs:
|
|||||||
ssh debian@51.210.247.57 << 'EOF'
|
ssh debian@51.210.247.57 << 'EOF'
|
||||||
cd /home/debian/newsletter-hackernews
|
cd /home/debian/newsletter-hackernews
|
||||||
git pull origin main
|
git pull origin main
|
||||||
cd /home/debian/infrastructure
|
cd /home/debian/gitea
|
||||||
docker compose up -d --build newsletter
|
docker compose up -d --build newsletter
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
@@ -11,14 +11,10 @@ COPY . .
|
|||||||
ARG NEXT_PUBLIC_BRAND_NAME
|
ARG NEXT_PUBLIC_BRAND_NAME
|
||||||
ARG NEXT_PUBLIC_BRAND_EMAIL
|
ARG NEXT_PUBLIC_BRAND_EMAIL
|
||||||
ARG NEXT_PUBLIC_BRAND_COUNTRY
|
ARG NEXT_PUBLIC_BRAND_COUNTRY
|
||||||
ARG NEXT_PUBLIC_HOME_URL
|
|
||||||
ARG NEXT_PUBLIC_BRAND_OWNER_NAME
|
|
||||||
|
|
||||||
ENV NEXT_PUBLIC_BRAND_NAME=$NEXT_PUBLIC_BRAND_NAME
|
ENV NEXT_PUBLIC_BRAND_NAME=$NEXT_PUBLIC_BRAND_NAME
|
||||||
ENV NEXT_PUBLIC_BRAND_EMAIL=$NEXT_PUBLIC_BRAND_EMAIL
|
ENV NEXT_PUBLIC_BRAND_EMAIL=$NEXT_PUBLIC_BRAND_EMAIL
|
||||||
ENV NEXT_PUBLIC_BRAND_COUNTRY=$NEXT_PUBLIC_BRAND_COUNTRY
|
ENV NEXT_PUBLIC_BRAND_COUNTRY=$NEXT_PUBLIC_BRAND_COUNTRY
|
||||||
ENV NEXT_PUBLIC_HOME_URL=$NEXT_PUBLIC_HOME_URL
|
|
||||||
ENV NEXT_PUBLIC_BRAND_OWNER_NAME=$NEXT_PUBLIC_BRAND_OWNER_NAME
|
|
||||||
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ export const dynamic = 'force-dynamic'; // defaults to force-static
|
|||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
if (!process.env.SWEEGO_API_KEY) {
|
if (!process.env.RESEND_KEY) {
|
||||||
throw new Error('SWEEGO_API_KEY is not set');
|
throw new Error('Resend variables not set');
|
||||||
}
|
}
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
const validation = ConfirmationSchema.safeParse(body);
|
const validation = ConfirmationSchema.safeParse(body);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { NewsletterTemplate } from '@components/email/Newsletter';
|
import { NewsletterTemplate } from '@components/email/Newsletter';
|
||||||
import prisma from '@prisma/prisma';
|
import prisma from '@prisma/prisma';
|
||||||
import { formatApiResponse } from '@utils/formatApiResponse';
|
import { formatApiResponse } from '@utils/formatApiResponse';
|
||||||
import { sender } from '@utils/sweego';
|
import { sender } from '@utils/mailer';
|
||||||
import {
|
import {
|
||||||
INTERNAL_SERVER_ERROR,
|
INTERNAL_SERVER_ERROR,
|
||||||
STATUS_INTERNAL_SERVER_ERROR,
|
STATUS_INTERNAL_SERVER_ERROR,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ConfirmationTemplate } from '@components/email/Confirmation';
|
import { ConfirmationTemplate } from '@components/email/Confirmation';
|
||||||
import prisma from '@prisma/prisma';
|
import prisma from '@prisma/prisma';
|
||||||
import { formatApiResponse } from '@utils/formatApiResponse';
|
import { formatApiResponse } from '@utils/formatApiResponse';
|
||||||
import { sender } from '@utils/sweego';
|
import { sender } from '@utils/mailer';
|
||||||
import {
|
import {
|
||||||
BAD_REQUEST,
|
BAD_REQUEST,
|
||||||
INTERNAL_SERVER_ERROR,
|
INTERNAL_SERVER_ERROR,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { UnsubscribeTemplate } from '@components/email/Unsubscribe';
|
import { UnsubscribeTemplate } from '@components/email/Unsubscribe';
|
||||||
import prisma from '@prisma/prisma';
|
import prisma from '@prisma/prisma';
|
||||||
import { formatApiResponse } from '@utils/formatApiResponse';
|
import { formatApiResponse } from '@utils/formatApiResponse';
|
||||||
import { sender } from '@utils/sweego';
|
import { sender } from '@utils/mailer';
|
||||||
import {
|
import {
|
||||||
BAD_REQUEST,
|
BAD_REQUEST,
|
||||||
INTERNAL_SERVER_ERROR,
|
INTERNAL_SERVER_ERROR,
|
||||||
@@ -30,10 +30,13 @@ export async function POST(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (user) {
|
if (user && !user.deleted) {
|
||||||
await prisma.user.delete({
|
await prisma.user.update({
|
||||||
where: {
|
where: {
|
||||||
email
|
email
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
deleted: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ const Confirmation = () => {
|
|||||||
'@type': 'WebSite',
|
'@type': 'WebSite',
|
||||||
name: 'Hackernews Newsletter',
|
name: 'Hackernews Newsletter',
|
||||||
title: 'Subscription Confirmation',
|
title: 'Subscription Confirmation',
|
||||||
url: `${process.env.NEXT_PUBLIC_HOME_URL}/confirmation`
|
url: `${process.env.HOME_URL}/confirmation`
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Tiles } from '@components/tiles/Tiles';
|
import { Tiles } from '@components/tiles/Tiles';
|
||||||
import { cn } from '@utils/cn';
|
import { cn } from '@utils/cn';
|
||||||
|
import { Analytics } from '@vercel/analytics/react';
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import { Inter as FontSans } from 'next/font/google';
|
import { Inter as FontSans } from 'next/font/google';
|
||||||
import Script from 'next/script';
|
|
||||||
import './globals.css';
|
import './globals.css';
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
@@ -34,12 +34,8 @@ export default function RootLayout({
|
|||||||
<Tiles>
|
<Tiles>
|
||||||
<div className='z-10'>{children}</div>
|
<div className='z-10'>{children}</div>
|
||||||
</Tiles>
|
</Tiles>
|
||||||
|
<Analytics />
|
||||||
</body>
|
</body>
|
||||||
<Script
|
|
||||||
defer
|
|
||||||
src='https://analytics.frompixels.com/script.js'
|
|
||||||
data-website-id='588e7b7d-e9cd-4b96-94bf-8269c499b0a2'
|
|
||||||
/>
|
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export const Home = () => {
|
|||||||
'@type': 'WebSite',
|
'@type': 'WebSite',
|
||||||
name: 'Hackernews Newsletter',
|
name: 'Hackernews Newsletter',
|
||||||
title: 'Home',
|
title: 'Home',
|
||||||
url: process.env.NEXT_PUBLIC_HOME_URL
|
url: process.env.HOME_URL
|
||||||
};
|
};
|
||||||
|
|
||||||
const form = useForm<SubscribeFormType>({
|
const form = useForm<SubscribeFormType>({
|
||||||
|
|||||||
@@ -2,113 +2,458 @@
|
|||||||
|
|
||||||
import { CustomCard } from '@components/CustomCard';
|
import { CustomCard } from '@components/CustomCard';
|
||||||
import { SchemaOrg } from '@components/SchemaOrg';
|
import { SchemaOrg } from '@components/SchemaOrg';
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
const useObfuscatedEmail = () => {
|
||||||
|
const [email, setEmail] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setEmail(process.env.NEXT_PUBLIC_BRAND_EMAIL || null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return email;
|
||||||
|
};
|
||||||
|
|
||||||
const Privacy = () => {
|
const Privacy = () => {
|
||||||
|
const email = useObfuscatedEmail();
|
||||||
|
|
||||||
const schema = {
|
const schema = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'WebSite',
|
'@type': 'WebSite',
|
||||||
name: 'Hackernews Newsletter',
|
name: 'Hackernews Newsletter',
|
||||||
title: 'Privacy Policy',
|
title: 'Privacy Policy',
|
||||||
url: `${process.env.NEXT_PUBLIC_HOME_URL}/privacy`
|
url: `${process.env.HOME_URL}/privacy`
|
||||||
};
|
};
|
||||||
|
|
||||||
const body = (
|
const body = (
|
||||||
<div className='privacy-content my-2 max-h-[50vh] space-y-1 overflow-auto'>
|
<div className='privacy-content my-2 max-h-[50vh] space-y-1 overflow-auto'>
|
||||||
<h2>Who We Are</h2>
|
|
||||||
<p className='leading-relaxed'>
|
<p className='leading-relaxed'>
|
||||||
Data controller: {process.env.NEXT_PUBLIC_BRAND_OWNER_NAME}, an
|
This Privacy Policy describes Our policies and procedures on the
|
||||||
individual based in {process.env.NEXT_PUBLIC_BRAND_COUNTRY}.
|
collection, use and disclosure of Your information when You use the
|
||||||
|
Service and tells You about Your privacy rights and how the law protects
|
||||||
|
You.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Contact:{' '}
|
We use Your Personal data to provide and improve the Service. By using
|
||||||
<a
|
the Service, You agree to the collection and use of information in
|
||||||
href={`mailto:${process.env.NEXT_PUBLIC_BRAND_EMAIL}`}
|
accordance with this Privacy Policy.
|
||||||
className='text-purple-600 hover:text-purple-700'
|
|
||||||
>
|
|
||||||
{process.env.NEXT_PUBLIC_BRAND_EMAIL}
|
|
||||||
</a>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>What We Collect</h2>
|
<h2>Interpretation and Definitions</h2>
|
||||||
|
<h3>Interpretation</h3>
|
||||||
<p className='leading-relaxed'>
|
<p className='leading-relaxed'>
|
||||||
Your email address (required to send the newsletter).
|
The words of which the initial letter is capitalized have meanings
|
||||||
|
defined under the following conditions. The following definitions shall
|
||||||
|
have the same meaning regardless of whether they appear in singular or
|
||||||
|
in plural.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Why We Collect It</h2>
|
<h3>Definitions</h3>
|
||||||
<p className='leading-relaxed'>
|
<p className='leading-relaxed'>
|
||||||
To deliver the daily {process.env.NEXT_PUBLIC_BRAND_NAME}{' '}
|
For the purposes of this Privacy Policy:
|
||||||
newsletter—a digest of top Hacker News stories with AI-generated
|
|
||||||
commentary.
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
|
||||||
We do not sell products, track your activity beyond essential delivery,
|
|
||||||
or share your data for marketing.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Legal Basis</h2>
|
|
||||||
<p className='leading-relaxed'>
|
|
||||||
Your explicit consent via double opt-in signup (you receive a
|
|
||||||
confirmation email with an activation link).
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Third Parties</h2>
|
|
||||||
<ul className='list-disc space-y-4 pl-6'>
|
<ul className='list-disc space-y-4 pl-6'>
|
||||||
<li>
|
<li>
|
||||||
<strong>Email delivery:</strong> Resend (US). GDPR-compliant email
|
<p>
|
||||||
infrastructure.
|
<strong>Account</strong> means a unique account created for You to
|
||||||
|
access our Service or parts of our Service.
|
||||||
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Hosting & analytics:</strong> Vercel (US). Analytics are
|
<p>
|
||||||
anonymized and aggregated—no personal data beyond your email is
|
<strong>Affiliate</strong> means an entity that controls, is
|
||||||
collected.
|
controlled by or is under common control with a party, where
|
||||||
|
"control" means ownership of 50% or more of the shares,
|
||||||
|
equity interest or other securities entitled to vote for election of
|
||||||
|
directors or other managing authority.
|
||||||
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Content source:</strong> Public Hacker News API (Y Combinator,
|
<p>
|
||||||
US).
|
<strong>Application</strong> refers to{' '}
|
||||||
|
{process.env.NEXT_PUBLIC_BRAND_NAME}, the software program provided
|
||||||
|
by the Company.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<strong>Company</strong> (referred to as either "the
|
||||||
|
Company", "We", "Us" or "Our" in
|
||||||
|
this Agreement) refers to {process.env.NEXT_PUBLIC_BRAND_NAME}.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<strong>Country</strong> refers to:{' '}
|
||||||
|
{process.env.NEXT_PUBLIC_BRAND_COUNTRY}
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<strong>Device</strong> means any device that can access the Service
|
||||||
|
such as a computer, a cellphone or a digital tablet.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<strong>Personal Data</strong> is any information that relates to an
|
||||||
|
identified or identifiable individual.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<strong>Service</strong> refers to the Application.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<strong>Service Provider</strong> means any natural or legal person
|
||||||
|
who processes the data on behalf of the Company. It refers to
|
||||||
|
third-party companies or individuals employed by the Company to
|
||||||
|
facilitate the Service, to provide the Service on behalf of the
|
||||||
|
Company, to perform services related to the Service or to assist the
|
||||||
|
Company in analyzing how the Service is used.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<strong>Usage Data</strong> refers to data collected automatically,
|
||||||
|
either generated by the use of the Service or from the Service
|
||||||
|
infrastructure itself (for example, the duration of a page visit).
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<strong>You</strong> means the individual accessing or using the
|
||||||
|
Service, or the company, or other legal entity on behalf of which
|
||||||
|
such individual is accessing or using the Service, as applicable.
|
||||||
|
</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>Your Rights</h2>
|
<h2>Data Collection and Usage</h2>
|
||||||
|
<h3>Types of Data Collected</h3>
|
||||||
|
|
||||||
|
<h4>Personal Data</h4>
|
||||||
|
<p className='leading-relaxed'>
|
||||||
|
While using Our Service, We may ask You to provide Us with certain
|
||||||
|
personally identifiable information that can be used to contact or
|
||||||
|
identify You. Personally identifiable information may include, but is
|
||||||
|
not limited to:
|
||||||
|
</p>
|
||||||
<ul className='list-disc space-y-4 pl-6'>
|
<ul className='list-disc space-y-4 pl-6'>
|
||||||
<li>Unsubscribe anytime via the link in every email</li>
|
|
||||||
<li>
|
<li>
|
||||||
Request deletion of your email by contacting{' '}
|
<p>Email address</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Usage Data</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h4>Usage Data</h4>
|
||||||
|
<p className='leading-relaxed'>
|
||||||
|
Usage Data is collected automatically when using the Service.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Usage Data may include information such as Your Device's Internet
|
||||||
|
Protocol address (e.g. IP address), browser type, browser version, the
|
||||||
|
pages of our Service that You visit, the time and date of Your visit,
|
||||||
|
the time spent on those pages, unique device identifiers and other
|
||||||
|
diagnostic data.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
When You access the Service by or through a mobile device, We may
|
||||||
|
collect certain information automatically, including, but not limited
|
||||||
|
to, the type of mobile device You use, Your mobile device unique ID, the
|
||||||
|
IP address of Your mobile device, Your mobile operating system, the type
|
||||||
|
of mobile Internet browser You use, unique device identifiers and other
|
||||||
|
diagnostic data.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
We may also collect information that Your browser sends whenever You
|
||||||
|
visit our Service or when You access the Service by or through a mobile
|
||||||
|
device.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Use of Your Personal Data</h2>
|
||||||
|
<p className='leading-relaxed'>
|
||||||
|
The Company may use Personal Data for the following purposes:
|
||||||
|
</p>
|
||||||
|
<ul className='list-disc space-y-4 pl-6'>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<strong>To provide and maintain our Service</strong>, including to
|
||||||
|
monitor the usage of our Service.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<strong>To manage Your Account:</strong> to manage Your registration
|
||||||
|
as a user of the Service. The Personal Data You provide can give You
|
||||||
|
access to different functionalities of the Service that are
|
||||||
|
available to You as a registered user.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<strong>For the performance of a contract:</strong> the development,
|
||||||
|
compliance and undertaking of the purchase contract for the
|
||||||
|
products, items or services You have purchased or of any other
|
||||||
|
contract with Us through the Service.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<strong>To contact You:</strong> To contact You by email, telephone
|
||||||
|
calls, SMS, or other equivalent forms of electronic communication,
|
||||||
|
such as a mobile application's push notifications regarding
|
||||||
|
updates or informative communications related to the
|
||||||
|
functionalities, products or contracted services, including the
|
||||||
|
security updates, when necessary or reasonable for their
|
||||||
|
implementation.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<strong>To provide You</strong> with news, special offers and
|
||||||
|
general information about other goods, services and events which we
|
||||||
|
offer that are similar to those that you have already purchased or
|
||||||
|
enquired about unless You have opted not to receive such
|
||||||
|
information.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<strong>To manage Your requests:</strong> To attend and manage Your
|
||||||
|
requests to Us.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<strong>For business transfers:</strong> We may use Your information
|
||||||
|
to evaluate or conduct a merger, divestiture, restructuring,
|
||||||
|
reorganization, dissolution, or other sale or transfer of some or
|
||||||
|
all of Our assets, whether as a going concern or as part of
|
||||||
|
bankruptcy, liquidation, or similar proceeding, in which Personal
|
||||||
|
Data held by Us about our Service users is among the assets
|
||||||
|
transferred.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<strong>For other purposes</strong>: We may use Your information for
|
||||||
|
other purposes, such as data analysis, identifying usage trends,
|
||||||
|
determining the effectiveness of our promotional campaigns and to
|
||||||
|
evaluate and improve our Service, products, services, marketing and
|
||||||
|
your experience.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>We may share Your personal information in the following situations:</p>
|
||||||
|
<ul className='list-disc space-y-4 pl-6'>
|
||||||
|
<li>
|
||||||
|
<strong>With Service Providers:</strong> We may share Your personal
|
||||||
|
information with Service Providers to monitor and analyze the use of
|
||||||
|
our Service, to contact You.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>For business transfers:</strong> We may share or transfer Your
|
||||||
|
personal information in connection with, or during negotiations of,
|
||||||
|
any merger, sale of Company assets, financing, or acquisition of all
|
||||||
|
or a portion of Our business to another company.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>With Affiliates:</strong> We may share Your information with
|
||||||
|
Our affiliates, in which case we will require those affiliates to
|
||||||
|
honor this Privacy Policy. Affiliates include Our parent company and
|
||||||
|
any other subsidiaries, joint venture partners or other companies that
|
||||||
|
We control or that are under common control with Us.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>With business partners:</strong> We may share Your information
|
||||||
|
with Our business partners to offer You certain products, services or
|
||||||
|
promotions.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>With other users:</strong> when You share personal information
|
||||||
|
or otherwise interact in the public areas with other users, such
|
||||||
|
information may be viewed by all users and may be publicly distributed
|
||||||
|
outside.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>With Your consent</strong>: We may disclose Your personal
|
||||||
|
information for any other purpose with Your consent.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Data Handling and Security</h2>
|
||||||
|
|
||||||
|
<h3>Retention of Your Personal Data</h3>
|
||||||
|
<p className='leading-relaxed'>
|
||||||
|
The Company will retain Your Personal Data only for as long as is
|
||||||
|
necessary for the purposes set out in this Privacy Policy. We will
|
||||||
|
retain and use Your Personal Data to the extent necessary to comply with
|
||||||
|
our legal obligations (for example, if we are required to retain your
|
||||||
|
data to comply with applicable laws), resolve disputes, and enforce our
|
||||||
|
legal agreements and policies.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The Company will also retain Usage Data for internal analysis purposes.
|
||||||
|
Usage Data is generally retained for a shorter period of time, except
|
||||||
|
when this data is used to strengthen the security or to improve the
|
||||||
|
functionality of Our Service, or We are legally obligated to retain this
|
||||||
|
data for longer time periods.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Transfer of Your Personal Data</h3>
|
||||||
|
<p className='leading-relaxed'>
|
||||||
|
Your information, including Personal Data, is processed at the
|
||||||
|
Company's operating offices and in any other places where the
|
||||||
|
parties involved in the processing are located. It means that this
|
||||||
|
information may be transferred to — and maintained on — computers
|
||||||
|
located outside of Your state, province, country or other governmental
|
||||||
|
jurisdiction where the data protection laws may differ than those from
|
||||||
|
Your jurisdiction.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Security of Your Personal Data</h3>
|
||||||
|
<p className='leading-relaxed'>
|
||||||
|
Your consent to this Privacy Policy followed by Your submission of such
|
||||||
|
information represents Your agreement to that transfer.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The Company will take all steps reasonably necessary to ensure that Your
|
||||||
|
data is treated securely and in accordance with this Privacy Policy and
|
||||||
|
no transfer of Your Personal Data will take place to an organization or
|
||||||
|
a country unless there are adequate controls in place including the
|
||||||
|
security of Your data and other personal information.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4 className='text-lg font-medium text-gray-800'>
|
||||||
|
Delete Your Personal Data
|
||||||
|
</h4>
|
||||||
|
<p className='leading-relaxed'>
|
||||||
|
You have the right to delete or request that We assist in deleting the
|
||||||
|
Personal Data that We have collected about You.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Our Service may give You the ability to delete certain information about
|
||||||
|
You from within the Service.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
You may update, amend, or delete Your information at any time by signing
|
||||||
|
in to Your Account, if you have one, and visiting the account settings
|
||||||
|
section that allows you to manage Your personal information. You may
|
||||||
|
also contact Us to request access to, correct, or delete any personal
|
||||||
|
information that You have provided to Us.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Please note, however, that We may need to retain certain information
|
||||||
|
when we have a legal obligation or lawful basis to do so.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Legal Disclosures</h2>
|
||||||
|
|
||||||
|
<h3>Business Transactions</h3>
|
||||||
|
<p className='leading-relaxed'>
|
||||||
|
If the Company is involved in a merger, acquisition or asset sale, Your
|
||||||
|
Personal Data may be transferred. We will provide notice before Your
|
||||||
|
Personal Data is transferred and becomes subject to a different Privacy
|
||||||
|
Policy.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Law Enforcement</h3>
|
||||||
|
<p className='leading-relaxed'>
|
||||||
|
Under certain circumstances, the Company may be required to disclose
|
||||||
|
Your Personal Data if required to do so by law or in response to valid
|
||||||
|
requests by public authorities (e.g. a court or a government agency).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Other Legal Requirements</h3>
|
||||||
|
<p className='leading-relaxed'>
|
||||||
|
The Company may disclose Your Personal Data in the good faith belief
|
||||||
|
that such action is necessary to:
|
||||||
|
</p>
|
||||||
|
<ul className='list-disc space-y-4 pl-6'>
|
||||||
|
<li>Comply with a legal obligation</li>
|
||||||
|
<li>Protect and defend the rights or property of the Company</li>
|
||||||
|
<li>
|
||||||
|
Prevent or investigate possible wrongdoing in connection with the
|
||||||
|
Service
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Protect the personal safety of Users of the Service or the public
|
||||||
|
</li>
|
||||||
|
<li>Protect against legal liability</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Additional Information</h2>
|
||||||
|
<p className='leading-relaxed'>
|
||||||
|
The security of Your Personal Data is important to Us, but remember that
|
||||||
|
no method of transmission over the Internet, or method of electronic
|
||||||
|
storage is 100% secure. While We strive to use commercially acceptable
|
||||||
|
means to protect Your Personal Data, We cannot guarantee its absolute
|
||||||
|
security.
|
||||||
|
</p>
|
||||||
|
<h3>Children's Privacy</h3>
|
||||||
|
<p className='leading-relaxed'>
|
||||||
|
Our Service does not address anyone under the age of 13. We do not
|
||||||
|
knowingly collect personally identifiable information from anyone under
|
||||||
|
the age of 13. If You are a parent or guardian and You are aware that
|
||||||
|
Your child has provided Us with Personal Data, please contact Us. If We
|
||||||
|
become aware that We have collected Personal Data from anyone under the
|
||||||
|
age of 13 without verification of parental consent, We take steps to
|
||||||
|
remove that information from Our servers.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If We need to rely on consent as a legal basis for processing Your
|
||||||
|
information and Your country requires consent from a parent, We may
|
||||||
|
require Your parent's consent before We collect and use that
|
||||||
|
information.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Links to Other Websites</h3>
|
||||||
|
<p className='leading-relaxed'>
|
||||||
|
Our Service may contain links to other websites that are not operated by
|
||||||
|
Us. If You click on a third party link, You will be directed to that
|
||||||
|
third party's site. We strongly advise You to review the Privacy
|
||||||
|
Policy of every site You visit.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
We have no control over and assume no responsibility for the content,
|
||||||
|
privacy policies or practices of any third party sites or services.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Changes to this Privacy Policy</h3>
|
||||||
|
<p className='leading-relaxed'>
|
||||||
|
We may update Our Privacy Policy from time to time. We will notify You
|
||||||
|
of any changes by posting the new Privacy Policy on this page.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
We will let You know via email and/or a prominent notice on Our Service,
|
||||||
|
prior to the change becoming effective and update the "Last
|
||||||
|
updated" date at the top of this Privacy Policy.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
You are advised to review this Privacy Policy periodically for any
|
||||||
|
changes. Changes to this Privacy Policy are effective when they are
|
||||||
|
posted on this page.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Contact Information</h2>
|
||||||
|
<p className='leading-relaxed'>
|
||||||
|
If you have any questions about this Privacy Policy, You can contact us
|
||||||
|
by writing to{' '}
|
||||||
|
{email ? (
|
||||||
<a
|
<a
|
||||||
href={`mailto:${process.env.NEXT_PUBLIC_BRAND_EMAIL}`}
|
href={`mailto:${email}`}
|
||||||
className='text-purple-600 hover:text-purple-700'
|
className='text-purple-600 hover:text-purple-700'
|
||||||
>
|
>
|
||||||
{process.env.NEXT_PUBLIC_BRAND_EMAIL}
|
{email}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
) : (
|
||||||
<li>
|
<span className='text-gray-400'>loading...</span>
|
||||||
Upon unsubscribe or deletion request, your email is permanently
|
)}
|
||||||
deleted from our database
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Data Retention</h2>
|
|
||||||
<p className='leading-relaxed'>
|
|
||||||
We retain your email address only until you unsubscribe.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>International Transfers</h2>
|
|
||||||
<p className='leading-relaxed'>
|
|
||||||
We rely on EU-approved safeguards including Standard Contractual Clauses
|
|
||||||
to ensure adequate protection when using US-based services.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Changes</h2>
|
|
||||||
<p className='leading-relaxed'>
|
|
||||||
We may update this policy occasionally. The latest version will always
|
|
||||||
be available at{' '}
|
|
||||||
<a
|
|
||||||
href={`${process.env.NEXT_PUBLIC_HOME_URL}/privacy`}
|
|
||||||
target='_blank'
|
|
||||||
rel='noopener noreferrer'
|
|
||||||
className='text-purple-600 hover:text-purple-700'
|
|
||||||
>
|
|
||||||
{process.env.NEXT_PUBLIC_HOME_URL}/privacy
|
|
||||||
</a>
|
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -120,7 +465,7 @@ const Privacy = () => {
|
|||||||
<CustomCard
|
<CustomCard
|
||||||
className='max-90vh max-90vw'
|
className='max-90vh max-90vw'
|
||||||
title='Privacy Policy'
|
title='Privacy Policy'
|
||||||
description='Last updated: January 28, 2026'
|
description='Last updated: November 23, 2024'
|
||||||
content={body}
|
content={body}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ export default function robots(): MetadataRoute.Robots {
|
|||||||
userAgent: '*',
|
userAgent: '*',
|
||||||
disallow: ''
|
disallow: ''
|
||||||
},
|
},
|
||||||
sitemap: `${process.env.NEXT_PUBLIC_HOME_URL!}/sitemap.xml`
|
sitemap: `${process.env.HOME_URL!}/sitemap.xml`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,19 +3,19 @@ import { MetadataRoute } from 'next';
|
|||||||
export default function sitemap(): MetadataRoute.Sitemap {
|
export default function sitemap(): MetadataRoute.Sitemap {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
url: process.env.NEXT_PUBLIC_HOME_URL!,
|
url: process.env.HOME_URL!,
|
||||||
lastModified: new Date(),
|
lastModified: new Date(),
|
||||||
changeFrequency: 'yearly',
|
changeFrequency: 'yearly',
|
||||||
priority: 1
|
priority: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
url: `${process.env.NEXT_PUBLIC_HOME_URL!}/privacy`,
|
url: `${process.env.HOME_URL!}/privacy`,
|
||||||
lastModified: new Date(),
|
lastModified: new Date(),
|
||||||
changeFrequency: 'yearly',
|
changeFrequency: 'yearly',
|
||||||
priority: 0.5
|
priority: 0.5
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
url: `${process.env.NEXT_PUBLIC_HOME_URL!}/unsubscribe`,
|
url: `${process.env.HOME_URL!}/unsubscribe`,
|
||||||
lastModified: new Date(),
|
lastModified: new Date(),
|
||||||
changeFrequency: 'yearly',
|
changeFrequency: 'yearly',
|
||||||
priority: 0.2
|
priority: 0.2
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const Unsubscribe = () => {
|
|||||||
'@type': 'WebSite',
|
'@type': 'WebSite',
|
||||||
name: 'Hackernews Newsletter',
|
name: 'Hackernews Newsletter',
|
||||||
title: 'Unsubscribe',
|
title: 'Unsubscribe',
|
||||||
url: `${process.env.NEXT_PUBLIC_HOME_URL}/unsubscribe`
|
url: `${process.env.HOME_URL}/unsubscribe`
|
||||||
};
|
};
|
||||||
|
|
||||||
const form = useForm<UnsubscribeFormType>({
|
const form = useForm<UnsubscribeFormType>({
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const ConfirmationTemplate = (code: string) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href={`${process.env.NEXT_PUBLIC_HOME_URL}/confirmation?code=${code}`}
|
href={`${process.env.HOME_URL}/confirmation?code=${code}`}
|
||||||
style={{
|
style={{
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
padding: '12px 24px',
|
padding: '12px 24px',
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const UnsubscribeTemplate = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href={`${process.env.NEXT_PUBLIC_HOME_URL}/`}
|
href={`${process.env.HOME_URL}/`}
|
||||||
style={{
|
style={{
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
padding: '12px 24px',
|
padding: '12px 24px',
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
import {
|
const iconStyle = {
|
||||||
User,
|
display: 'inline-block',
|
||||||
Building2,
|
verticalAlign: 'middle'
|
||||||
Mail,
|
};
|
||||||
LogOut,
|
|
||||||
LayoutGrid,
|
const Icon = ({ name, size = 16 }: { name: string; size?: number }) => (
|
||||||
Shield,
|
<img
|
||||||
Home
|
src={`${process.env.HOME_URL}/email-icons/${name}.png`}
|
||||||
} from 'lucide-react';
|
width={size}
|
||||||
|
height={size}
|
||||||
|
alt=""
|
||||||
|
style={iconStyle}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
export const Footer = () => {
|
export const Footer = () => {
|
||||||
return (
|
return (
|
||||||
@@ -44,7 +49,7 @@ export const Footer = () => {
|
|||||||
letterSpacing: '0.05em'
|
letterSpacing: '0.05em'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<User size={16} color='#386FA4' />
|
<Icon name="user" size={16} />
|
||||||
Contact Us
|
Contact Us
|
||||||
</h4>
|
</h4>
|
||||||
<p
|
<p
|
||||||
@@ -57,7 +62,7 @@ export const Footer = () => {
|
|||||||
color: '#4A5568'
|
color: '#4A5568'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Building2 size={14} color='#386FA4' />
|
<Icon name="building-2" size={14} />
|
||||||
{process.env.NEXT_PUBLIC_BRAND_NAME}
|
{process.env.NEXT_PUBLIC_BRAND_NAME}
|
||||||
</p>
|
</p>
|
||||||
<p
|
<p
|
||||||
@@ -70,7 +75,7 @@ export const Footer = () => {
|
|||||||
color: '#4A5568'
|
color: '#4A5568'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Mail size={14} color='#386FA4' />
|
<Icon name="mail" size={14} />
|
||||||
<a
|
<a
|
||||||
href={`mailto:${process.env.NEXT_PUBLIC_BRAND_EMAIL}`}
|
href={`mailto:${process.env.NEXT_PUBLIC_BRAND_EMAIL}`}
|
||||||
style={{ color: '#386FA4', textDecoration: 'none' }}
|
style={{ color: '#386FA4', textDecoration: 'none' }}
|
||||||
@@ -88,11 +93,11 @@ export const Footer = () => {
|
|||||||
color: '#4A5568'
|
color: '#4A5568'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LogOut size={14} color='#386FA4' />
|
<Icon name="log-out" size={14} />
|
||||||
<span>
|
<span>
|
||||||
Click{' '}
|
Click{' '}
|
||||||
<a
|
<a
|
||||||
href={`${process.env.NEXT_PUBLIC_HOME_URL}/unsubscribe`}
|
href={`${process.env.HOME_URL}/unsubscribe`}
|
||||||
style={{ color: '#386FA4', textDecoration: 'none' }}
|
style={{ color: '#386FA4', textDecoration: 'none' }}
|
||||||
>
|
>
|
||||||
here
|
here
|
||||||
@@ -124,7 +129,7 @@ export const Footer = () => {
|
|||||||
letterSpacing: '0.05em'
|
letterSpacing: '0.05em'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LayoutGrid size={16} color='#386FA4' />
|
<Icon name="layout-grid" size={16} />
|
||||||
Quick Links
|
Quick Links
|
||||||
</h4>
|
</h4>
|
||||||
<p
|
<p
|
||||||
@@ -137,9 +142,9 @@ export const Footer = () => {
|
|||||||
color: '#4A5568'
|
color: '#4A5568'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Shield size={14} color='#386FA4' />
|
<Icon name="shield" size={14} />
|
||||||
<a
|
<a
|
||||||
href={`${process.env.NEXT_PUBLIC_HOME_URL}/privacy`}
|
href={`${process.env.HOME_URL}/privacy`}
|
||||||
style={{ color: '#386FA4', textDecoration: 'none' }}
|
style={{ color: '#386FA4', textDecoration: 'none' }}
|
||||||
>
|
>
|
||||||
Privacy Policy
|
Privacy Policy
|
||||||
@@ -155,9 +160,9 @@ export const Footer = () => {
|
|||||||
color: '#4A5568'
|
color: '#4A5568'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Home size={14} color='#386FA4' />
|
<Icon name="house" size={14} />
|
||||||
<a
|
<a
|
||||||
href={process.env.NEXT_PUBLIC_HOME_URL}
|
href={process.env.HOME_URL}
|
||||||
style={{ color: '#386FA4', textDecoration: 'none' }}
|
style={{ color: '#386FA4', textDecoration: 'none' }}
|
||||||
>
|
>
|
||||||
Visit Website
|
Visit Website
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
version: '3.8'
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:16-alpine
|
image: postgres:16-alpine
|
||||||
@@ -11,6 +10,37 @@ services:
|
|||||||
- '5432:5432'
|
- '5432:5432'
|
||||||
volumes:
|
volumes:
|
||||||
- postgres-data:/var/lib/postgresql/data
|
- postgres-data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U newsletter"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
# Postfix mail server with OpenDKIM for self-hosted email
|
||||||
|
postfix:
|
||||||
|
build: ./docker/postfix
|
||||||
|
container_name: newsletter-postfix
|
||||||
|
restart: always
|
||||||
|
# dns: Uses VPS default DNS. If MX lookups fail, uncomment and set explicit DNS:
|
||||||
|
# - 213.186.33.99 # OVH DNS
|
||||||
|
environment:
|
||||||
|
- MAIL_DOMAIN=${MAIL_DOMAIN:-example.com}
|
||||||
|
- MAIL_HOSTNAME=${MAIL_HOSTNAME:-mail.example.com}
|
||||||
|
- DKIM_SELECTOR=${DKIM_SELECTOR:-mail}
|
||||||
|
volumes:
|
||||||
|
# Persist DKIM keys across container rebuilds
|
||||||
|
- postfix-dkim:/etc/opendkim/keys
|
||||||
|
# Persist mail queue
|
||||||
|
- postfix-spool:/var/spool/postfix
|
||||||
|
# Port 25 not exposed to host - only accessible within Docker network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "echo 'QUIT' | nc -w 5 localhost 25 | grep -q '220' || exit 1"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
start_period: 10s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres-data:
|
postgres-data:
|
||||||
|
postfix-dkim:
|
||||||
|
postfix-spool:
|
||||||
|
|||||||
30
docker/postfix/Dockerfile
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
|
# install oostfix and OpenDKIM
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
postfix \
|
||||||
|
opendkim \
|
||||||
|
opendkim-tools \
|
||||||
|
mailutils \
|
||||||
|
ca-certificates \
|
||||||
|
netcat-openbsd \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# create OpenDKIM paths
|
||||||
|
RUN mkdir -p /etc/opendkim/keys && \
|
||||||
|
chown -R opendkim:opendkim /etc/opendkim && \
|
||||||
|
chmod 700 /etc/opendkim/keys
|
||||||
|
|
||||||
|
# copy config
|
||||||
|
COPY postfix-main.cf /etc/postfix/main.cf
|
||||||
|
COPY opendkim.conf /etc/opendkim.conf
|
||||||
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
|
||||||
|
EXPOSE 25
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
|
||||||
|
CMD echo "QUIT" | nc -w 5 localhost 25 | grep -q "220" || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
90
docker/postfix/entrypoint.sh
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# environment variables
|
||||||
|
MAIL_DOMAIN="${MAIL_DOMAIN:-example.com}"
|
||||||
|
MAIL_HOSTNAME="${MAIL_HOSTNAME:-mail.example.com}"
|
||||||
|
DKIM_SELECTOR="${DKIM_SELECTOR:-mail}"
|
||||||
|
|
||||||
|
echo "Setting up postfix for domain: ${MAIL_DOMAIN}"
|
||||||
|
echo "Hostname: ${MAIL_HOSTNAME}"
|
||||||
|
|
||||||
|
# configure postfix domain
|
||||||
|
postconf -e "myhostname=${MAIL_HOSTNAME}"
|
||||||
|
postconf -e "mydomain=${MAIL_DOMAIN}"
|
||||||
|
postconf -e "myorigin=\$mydomain"
|
||||||
|
postconf -e "mydestination=\$myhostname, localhost.\$mydomain, localhost"
|
||||||
|
|
||||||
|
# create OpenDKIM key folder for domain
|
||||||
|
DKIM_KEY_DIR="/etc/opendkim/keys/${MAIL_DOMAIN}"
|
||||||
|
mkdir -p "${DKIM_KEY_DIR}"
|
||||||
|
|
||||||
|
# generate DKIM keys if they don't exist
|
||||||
|
if [ ! -f "${DKIM_KEY_DIR}/${DKIM_SELECTOR}.private" ]; then
|
||||||
|
echo "Generating DKIM keys for ${MAIL_DOMAIN}..."
|
||||||
|
opendkim-genkey -b 2048 -d "${MAIL_DOMAIN}" -D "${DKIM_KEY_DIR}" -s "${DKIM_SELECTOR}" -v
|
||||||
|
chown -R opendkim:opendkim "${DKIM_KEY_DIR}"
|
||||||
|
chmod 600 "${DKIM_KEY_DIR}/${DKIM_SELECTOR}.private"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo "DKIM PUBLIC KEY - ADD THIS TO YOUR DNS:"
|
||||||
|
echo "============================================"
|
||||||
|
echo "Record Type: TXT"
|
||||||
|
echo "Name: ${DKIM_SELECTOR}._domainkey.${MAIL_DOMAIN}"
|
||||||
|
echo ""
|
||||||
|
cat "${DKIM_KEY_DIR}/${DKIM_SELECTOR}.txt"
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
echo "Using existing DKIM keys"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# configure OpenDKIM KeyTable
|
||||||
|
cat > /etc/opendkim/KeyTable << EOF
|
||||||
|
${DKIM_SELECTOR}._domainkey.${MAIL_DOMAIN} ${MAIL_DOMAIN}:${DKIM_SELECTOR}:${DKIM_KEY_DIR}/${DKIM_SELECTOR}.private
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# configure OpenDKIM SigningTable
|
||||||
|
cat > /etc/opendkim/SigningTable << EOF
|
||||||
|
*@${MAIL_DOMAIN} ${DKIM_SELECTOR}._domainkey.${MAIL_DOMAIN}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# configure OpenDKIM TrustedHosts
|
||||||
|
cat > /etc/opendkim/TrustedHosts << EOF
|
||||||
|
127.0.0.1
|
||||||
|
localhost
|
||||||
|
${MAIL_DOMAIN}
|
||||||
|
*.${MAIL_DOMAIN}
|
||||||
|
172.16.0.0/12
|
||||||
|
192.168.0.0/16
|
||||||
|
10.0.0.0/8
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# set permissions
|
||||||
|
chown -R opendkim:opendkim /etc/opendkim
|
||||||
|
chmod 600 /etc/opendkim/KeyTable
|
||||||
|
chmod 600 /etc/opendkim/SigningTable
|
||||||
|
|
||||||
|
# create postfix spool folders
|
||||||
|
mkdir -p /var/spool/postfix/pid
|
||||||
|
chown root:root /var/spool/postfix
|
||||||
|
chown root:root /var/spool/postfix/pid
|
||||||
|
|
||||||
|
# start OpenDKIM in background
|
||||||
|
echo "Starting OpenDKIM..."
|
||||||
|
opendkim -f &
|
||||||
|
|
||||||
|
# wait for OpenDKIM to start
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# copy DNS config to postfix chroot
|
||||||
|
mkdir -p /var/spool/postfix/etc
|
||||||
|
cp /etc/resolv.conf /var/spool/postfix/etc/
|
||||||
|
cp /etc/services /var/spool/postfix/etc/
|
||||||
|
cp /etc/hosts /var/spool/postfix/etc/
|
||||||
|
|
||||||
|
# start postfix in foreground
|
||||||
|
echo "Starting Postfix..."
|
||||||
|
postfix start-fg
|
||||||
30
docker/postfix/opendkim.conf
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# OpenDKIM configuration for email signing
|
||||||
|
|
||||||
|
# log to syslog
|
||||||
|
Syslog yes
|
||||||
|
SyslogSuccess yes
|
||||||
|
LogWhy yes
|
||||||
|
|
||||||
|
# required for verification
|
||||||
|
Canonicalization relaxed/simple
|
||||||
|
|
||||||
|
# sign mode (s=sign, v=verify)
|
||||||
|
Mode sv
|
||||||
|
|
||||||
|
# sign subdomains
|
||||||
|
SubDomains no
|
||||||
|
|
||||||
|
# key configuration (will be set by entrypoint)
|
||||||
|
KeyTable /etc/opendkim/KeyTable
|
||||||
|
SigningTable refile:/etc/opendkim/SigningTable
|
||||||
|
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
|
||||||
|
InternalHosts refile:/etc/opendkim/TrustedHosts
|
||||||
|
|
||||||
|
# socket for postfix connection
|
||||||
|
Socket inet:8891@localhost
|
||||||
|
|
||||||
|
# user
|
||||||
|
UserID opendkim
|
||||||
|
|
||||||
|
# permissions
|
||||||
|
RequireSafeKeys false
|
||||||
50
docker/postfix/postfix-main.cf
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# Postfix main configuration for newsletter sending
|
||||||
|
# Domain and hostname will be set by entrypoint script
|
||||||
|
|
||||||
|
smtpd_banner = $myhostname ESMTP
|
||||||
|
biff = no
|
||||||
|
append_dot_mydomain = no
|
||||||
|
readme_directory = no
|
||||||
|
|
||||||
|
# TLS parameters (for outbound connections)
|
||||||
|
smtp_tls_security_level = may
|
||||||
|
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
|
||||||
|
|
||||||
|
# Network settings
|
||||||
|
inet_interfaces = all
|
||||||
|
inet_protocols = ipv4
|
||||||
|
|
||||||
|
# Relay settings (don't relay for others)
|
||||||
|
relayhost =
|
||||||
|
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 172.16.0.0/12 192.168.0.0/16 10.0.0.0/8
|
||||||
|
|
||||||
|
# Message size limit (10MB)
|
||||||
|
message_size_limit = 10240000
|
||||||
|
|
||||||
|
# Queue settings
|
||||||
|
maximal_queue_lifetime = 5d
|
||||||
|
bounce_queue_lifetime = 5d
|
||||||
|
|
||||||
|
# Security settings
|
||||||
|
smtpd_helo_required = yes
|
||||||
|
disable_vrfy_command = yes
|
||||||
|
|
||||||
|
smtpd_helo_restrictions =
|
||||||
|
permit_mynetworks,
|
||||||
|
reject_invalid_helo_hostname,
|
||||||
|
reject_non_fqdn_helo_hostname,
|
||||||
|
permit
|
||||||
|
|
||||||
|
smtpd_recipient_restrictions =
|
||||||
|
permit_mynetworks,
|
||||||
|
reject_unauth_destination,
|
||||||
|
permit
|
||||||
|
|
||||||
|
# OpenDKIM integration
|
||||||
|
milter_default_action = accept
|
||||||
|
milter_protocol = 6
|
||||||
|
smtpd_milters = inet:localhost:8891
|
||||||
|
non_smtpd_milters = inet:localhost:8891
|
||||||
|
|
||||||
|
# Notify on bounces
|
||||||
|
notify_classes = bounce, delay, resource, software
|
||||||
1465
package-lock.json
generated
@@ -26,12 +26,14 @@
|
|||||||
"@prisma/client": "^5.6.0",
|
"@prisma/client": "^5.6.0",
|
||||||
"@radix-ui/react-label": "^2.0.2",
|
"@radix-ui/react-label": "^2.0.2",
|
||||||
"@radix-ui/react-slot": "^1.0.2",
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
|
"@vercel/analytics": "^1.1.1",
|
||||||
"axios": "^1.12.0",
|
"axios": "^1.12.0",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"isomorphic-dompurify": "^2.15.0",
|
"isomorphic-dompurify": "^2.15.0",
|
||||||
"lucide-react": "^0.460.0",
|
"lucide-react": "^0.460.0",
|
||||||
"next": "^15.5.9",
|
"next": "^15.5.9",
|
||||||
|
"nodemailer": "^7.0.12",
|
||||||
"openai": "^4.77.0",
|
"openai": "^4.77.0",
|
||||||
"postcss-nesting": "^12.0.2",
|
"postcss-nesting": "^12.0.2",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
@@ -45,6 +47,7 @@
|
|||||||
"@commitlint/cli": "^18.4.3",
|
"@commitlint/cli": "^18.4.3",
|
||||||
"@commitlint/config-conventional": "^18.4.3",
|
"@commitlint/config-conventional": "^18.4.3",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
|
"@types/nodemailer": "^7.0.5",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "email_logs" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"recipient" TEXT NOT NULL,
|
||||||
|
"subject" TEXT,
|
||||||
|
"message_id" TEXT,
|
||||||
|
"status" TEXT NOT NULL,
|
||||||
|
"sent_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"error_message" TEXT,
|
||||||
|
"bounce_type" TEXT,
|
||||||
|
"bounce_details" JSONB,
|
||||||
|
|
||||||
|
CONSTRAINT "email_logs_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "email_logs_recipient_idx" ON "email_logs"("recipient");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "email_logs_status_idx" ON "email_logs"("status");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "email_logs_sent_at_idx" ON "email_logs"("sent_at");
|
||||||
@@ -32,3 +32,20 @@ model News {
|
|||||||
|
|
||||||
@@map(name: "news")
|
@@map(name: "news")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model EmailLog {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
recipient String
|
||||||
|
subject String?
|
||||||
|
messageId String? @map("message_id")
|
||||||
|
status String // 'sent', 'failed', 'bounced'
|
||||||
|
sentAt DateTime @default(now()) @map("sent_at")
|
||||||
|
errorMessage String? @map("error_message")
|
||||||
|
bounceType String? @map("bounce_type")
|
||||||
|
bounceDetails Json? @map("bounce_details")
|
||||||
|
|
||||||
|
@@index([recipient])
|
||||||
|
@@index([status])
|
||||||
|
@@index([sentAt])
|
||||||
|
@@map(name: "email_logs")
|
||||||
|
}
|
||||||
|
|||||||
BIN
public/email-icons/building-2.png
Normal file
|
After Width: | Height: | Size: 397 B |
BIN
public/email-icons/house.png
Normal file
|
After Width: | Height: | Size: 416 B |
BIN
public/email-icons/layout-grid.png
Normal file
|
After Width: | Height: | Size: 305 B |
BIN
public/email-icons/log-out.png
Normal file
|
After Width: | Height: | Size: 328 B |
BIN
public/email-icons/mail.png
Normal file
|
After Width: | Height: | Size: 414 B |
BIN
public/email-icons/shield.png
Normal file
|
After Width: | Height: | Size: 480 B |
BIN
public/email-icons/user.png
Normal file
|
After Width: | Height: | Size: 394 B |
213
utils/mailer.ts
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
import nodemailer from 'nodemailer';
|
||||||
|
import prisma from '@prisma/prisma';
|
||||||
|
|
||||||
|
interface EmailTemplate {
|
||||||
|
subject: string;
|
||||||
|
template: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SendResult {
|
||||||
|
success: boolean;
|
||||||
|
messageId?: string;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create nodemailer transporter for Postfix container
|
||||||
|
const createTransporter = () => {
|
||||||
|
// Default to 'postfix' for Docker networking (service name in docker-compose)
|
||||||
|
// Override with EMAIL_HOST for different setups
|
||||||
|
const host = process.env.EMAIL_HOST || 'postfix';
|
||||||
|
const port = parseInt(process.env.EMAIL_PORT || '25', 10);
|
||||||
|
|
||||||
|
return nodemailer.createTransport({
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
secure: false, // true for 465, false for other ports
|
||||||
|
tls: {
|
||||||
|
rejectUnauthorized: false
|
||||||
|
},
|
||||||
|
// Connection pooling for better performance
|
||||||
|
pool: true,
|
||||||
|
maxConnections: 5,
|
||||||
|
maxMessages: 100
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Render React component to HTML string using dynamic import
|
||||||
|
const renderTemplate = async (template: JSX.Element): Promise<string> => {
|
||||||
|
// Dynamic import to avoid Next.js bundling issues with react-dom/server
|
||||||
|
const { renderToStaticMarkup } = await import('react-dom/server');
|
||||||
|
const html = renderToStaticMarkup(template);
|
||||||
|
return `<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
</head>
|
||||||
|
<body style="margin: 0; padding: 0; background-color: #f4f4f5;">
|
||||||
|
${html}
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate plain text version from HTML (basic extraction)
|
||||||
|
const htmlToPlainText = (html: string): string => {
|
||||||
|
return html
|
||||||
|
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
|
||||||
|
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
|
||||||
|
.replace(/<[^>]+>/g, '')
|
||||||
|
.replace(/ /g, ' ')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, "'")
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Log email send attempt to database
|
||||||
|
const logEmailSend = async (
|
||||||
|
recipient: string,
|
||||||
|
subject: string,
|
||||||
|
status: 'sent' | 'failed',
|
||||||
|
messageId?: string,
|
||||||
|
errorMessage?: string
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
await prisma.emailLog.create({
|
||||||
|
data: {
|
||||||
|
recipient,
|
||||||
|
subject,
|
||||||
|
status,
|
||||||
|
messageId,
|
||||||
|
errorMessage
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to log email send:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send a single email
|
||||||
|
const sendSingleEmail = async (
|
||||||
|
transporter: nodemailer.Transporter,
|
||||||
|
recipient: string,
|
||||||
|
subject: string,
|
||||||
|
htmlContent: string,
|
||||||
|
textContent: string
|
||||||
|
): Promise<SendResult> => {
|
||||||
|
const fromAddress = process.env.EMAIL_FROM || process.env.RESEND_FROM;
|
||||||
|
const fromName = process.env.NEXT_PUBLIC_BRAND_NAME || 'HackerNews Newsletter';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const info = await transporter.sendMail({
|
||||||
|
from: `"${fromName}" <${fromAddress}>`,
|
||||||
|
to: recipient,
|
||||||
|
subject,
|
||||||
|
text: textContent,
|
||||||
|
html: htmlContent,
|
||||||
|
headers: {
|
||||||
|
'List-Unsubscribe': `<${process.env.HOME_URL}/unsubscribe>`,
|
||||||
|
'X-Newsletter-ID': `hackernews-${Date.now()}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await logEmailSend(recipient, subject, 'sent', info.messageId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
messageId: info.messageId
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||||
|
await logEmailSend(recipient, subject, 'failed', undefined, errorMessage);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: errorMessage
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Main sender function - maintains same API as Resend version
|
||||||
|
export async function sender(
|
||||||
|
recipients: string[],
|
||||||
|
{ subject, template }: EmailTemplate
|
||||||
|
): Promise<boolean> {
|
||||||
|
const fromAddress = process.env.EMAIL_FROM || process.env.RESEND_FROM;
|
||||||
|
|
||||||
|
if (!fromAddress) {
|
||||||
|
throw new Error('EMAIL_FROM or RESEND_FROM environment variable is not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recipients.length === 0) {
|
||||||
|
console.info(`${subject} email skipped for having zero recipients`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transporter = createTransporter();
|
||||||
|
|
||||||
|
// Render template to HTML
|
||||||
|
const htmlContent = await renderTemplate(template);
|
||||||
|
const textContent = htmlToPlainText(htmlContent);
|
||||||
|
|
||||||
|
// Add small delay between sends to avoid overwhelming mail server
|
||||||
|
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
let successCount = 0;
|
||||||
|
let failCount = 0;
|
||||||
|
const errors: Array<{ email: string; error: string }> = [];
|
||||||
|
|
||||||
|
for (const recipient of recipients) {
|
||||||
|
const result = await sendSingleEmail(
|
||||||
|
transporter,
|
||||||
|
recipient,
|
||||||
|
subject,
|
||||||
|
htmlContent,
|
||||||
|
textContent
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
successCount++;
|
||||||
|
} else {
|
||||||
|
failCount++;
|
||||||
|
errors.push({ email: recipient, error: result.error || 'Unknown error' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add 100ms delay between sends
|
||||||
|
if (recipients.length > 1) {
|
||||||
|
await delay(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the transporter pool
|
||||||
|
transporter.close();
|
||||||
|
|
||||||
|
if (errors.length > 0) {
|
||||||
|
console.error('Email send errors:', errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.info(
|
||||||
|
`${subject} email: ${successCount} sent, ${failCount} failed out of ${recipients.length} recipients`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Return true if at least one email was sent successfully
|
||||||
|
return successCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify transporter connection (useful for health checks)
|
||||||
|
export async function verifyMailer(): Promise<boolean> {
|
||||||
|
const transporter = createTransporter();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await transporter.verify();
|
||||||
|
console.log('Email server connection verified');
|
||||||
|
transporter.close();
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Email server connection failed:', error);
|
||||||
|
transporter.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
interface EmailTemplate {
|
|
||||||
subject: string;
|
|
||||||
template: JSX.Element;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SWEEGO_API_URL = 'https://api.sweego.io/send';
|
|
||||||
|
|
||||||
const renderTemplate = async (template: JSX.Element): Promise<string> => {
|
|
||||||
const { renderToStaticMarkup } = await import('react-dom/server');
|
|
||||||
const html = renderToStaticMarkup(template);
|
|
||||||
return `<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
</head>
|
|
||||||
<body style="margin: 0; padding: 0; background-color: #f4f4f5;">
|
|
||||||
${html}
|
|
||||||
</body>
|
|
||||||
</html>`;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function sender(
|
|
||||||
recipients: string[],
|
|
||||||
{ subject, template }: EmailTemplate
|
|
||||||
): Promise<boolean> {
|
|
||||||
if (!process.env.SWEEGO_API_KEY) {
|
|
||||||
throw new Error('SWEEGO_API_KEY is not set');
|
|
||||||
}
|
|
||||||
if (!process.env.SWEEGO_FROM) {
|
|
||||||
throw new Error('SWEEGO_FROM is not set');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recipients.length === 0) {
|
|
||||||
console.info(`${subject} email skipped for having zero recipients`);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const htmlContent = await renderTemplate(template);
|
|
||||||
const fromName = process.env.NEXT_PUBLIC_BRAND_NAME || 'Newsletter';
|
|
||||||
|
|
||||||
let successCount = 0;
|
|
||||||
let failCount = 0;
|
|
||||||
|
|
||||||
for (const recipient of recipients) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(SWEEGO_API_URL, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Api-Key': process.env.SWEEGO_API_KEY
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
channel: 'email',
|
|
||||||
provider: 'sweego',
|
|
||||||
recipients: [{ email: recipient }],
|
|
||||||
from: {
|
|
||||||
name: fromName,
|
|
||||||
email: process.env.SWEEGO_FROM
|
|
||||||
},
|
|
||||||
subject,
|
|
||||||
'message-html': htmlContent,
|
|
||||||
headers: {
|
|
||||||
'List-Unsubscribe': `<mailto:${process.env.NEXT_PUBLIC_BRAND_EMAIL}>`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const error = await response.text();
|
|
||||||
console.error(
|
|
||||||
`Failed to send to ${recipient}: ${response.status} ${error}`
|
|
||||||
);
|
|
||||||
failCount++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
successCount++;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Failed to send to ${recipient}:`, error);
|
|
||||||
failCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.info(
|
|
||||||
`${subject} email: ${successCount} sent, ${failCount} failed out of ${recipients.length} recipients`
|
|
||||||
);
|
|
||||||
|
|
||||||
return successCount > 0;
|
|
||||||
}
|
|
||||||
920
yarn.lock
@@ -34,6 +34,482 @@
|
|||||||
resolved "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz"
|
resolved "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz"
|
||||||
integrity sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==
|
integrity sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==
|
||||||
|
|
||||||
|
"@aws-crypto/sha256-browser@5.2.0":
|
||||||
|
version "5.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz"
|
||||||
|
integrity sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==
|
||||||
|
dependencies:
|
||||||
|
"@aws-crypto/sha256-js" "^5.2.0"
|
||||||
|
"@aws-crypto/supports-web-crypto" "^5.2.0"
|
||||||
|
"@aws-crypto/util" "^5.2.0"
|
||||||
|
"@aws-sdk/types" "^3.222.0"
|
||||||
|
"@aws-sdk/util-locate-window" "^3.0.0"
|
||||||
|
"@smithy/util-utf8" "^2.0.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-crypto/sha256-js@^5.2.0", "@aws-crypto/sha256-js@5.2.0":
|
||||||
|
version "5.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz"
|
||||||
|
integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==
|
||||||
|
dependencies:
|
||||||
|
"@aws-crypto/util" "^5.2.0"
|
||||||
|
"@aws-sdk/types" "^3.222.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-crypto/supports-web-crypto@^5.2.0":
|
||||||
|
version "5.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz"
|
||||||
|
integrity sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-crypto/util@^5.2.0":
|
||||||
|
version "5.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz"
|
||||||
|
integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/types" "^3.222.0"
|
||||||
|
"@smithy/util-utf8" "^2.0.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/client-sesv2@^3.839.0":
|
||||||
|
version "3.971.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.971.0.tgz"
|
||||||
|
integrity sha512-NP/lbf3mfY10Txzl0ml2YnTjnZwflp1+faOotMCrXi4fb6kInosdW0ZSHXNlNulFo9cW+llq07lD59Sw3nny+A==
|
||||||
|
dependencies:
|
||||||
|
"@aws-crypto/sha256-browser" "5.2.0"
|
||||||
|
"@aws-crypto/sha256-js" "5.2.0"
|
||||||
|
"@aws-sdk/core" "3.970.0"
|
||||||
|
"@aws-sdk/credential-provider-node" "3.971.0"
|
||||||
|
"@aws-sdk/middleware-host-header" "3.969.0"
|
||||||
|
"@aws-sdk/middleware-logger" "3.969.0"
|
||||||
|
"@aws-sdk/middleware-recursion-detection" "3.969.0"
|
||||||
|
"@aws-sdk/middleware-user-agent" "3.970.0"
|
||||||
|
"@aws-sdk/region-config-resolver" "3.969.0"
|
||||||
|
"@aws-sdk/signature-v4-multi-region" "3.970.0"
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@aws-sdk/util-endpoints" "3.970.0"
|
||||||
|
"@aws-sdk/util-user-agent-browser" "3.969.0"
|
||||||
|
"@aws-sdk/util-user-agent-node" "3.971.0"
|
||||||
|
"@smithy/config-resolver" "^4.4.6"
|
||||||
|
"@smithy/core" "^3.20.6"
|
||||||
|
"@smithy/fetch-http-handler" "^5.3.9"
|
||||||
|
"@smithy/hash-node" "^4.2.8"
|
||||||
|
"@smithy/invalid-dependency" "^4.2.8"
|
||||||
|
"@smithy/middleware-content-length" "^4.2.8"
|
||||||
|
"@smithy/middleware-endpoint" "^4.4.7"
|
||||||
|
"@smithy/middleware-retry" "^4.4.23"
|
||||||
|
"@smithy/middleware-serde" "^4.2.9"
|
||||||
|
"@smithy/middleware-stack" "^4.2.8"
|
||||||
|
"@smithy/node-config-provider" "^4.3.8"
|
||||||
|
"@smithy/node-http-handler" "^4.4.8"
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/smithy-client" "^4.10.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/url-parser" "^4.2.8"
|
||||||
|
"@smithy/util-base64" "^4.3.0"
|
||||||
|
"@smithy/util-body-length-browser" "^4.2.0"
|
||||||
|
"@smithy/util-body-length-node" "^4.2.1"
|
||||||
|
"@smithy/util-defaults-mode-browser" "^4.3.22"
|
||||||
|
"@smithy/util-defaults-mode-node" "^4.2.25"
|
||||||
|
"@smithy/util-endpoints" "^3.2.8"
|
||||||
|
"@smithy/util-middleware" "^4.2.8"
|
||||||
|
"@smithy/util-retry" "^4.2.8"
|
||||||
|
"@smithy/util-utf8" "^4.2.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/client-sso@3.971.0":
|
||||||
|
version "3.971.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.971.0.tgz"
|
||||||
|
integrity sha512-Xx+w6DQqJxDdymYyIxyKJnRzPvVJ4e/Aw0czO7aC9L/iraaV7AG8QtRe93OGW6aoHSh72CIiinnpJJfLsQqP4g==
|
||||||
|
dependencies:
|
||||||
|
"@aws-crypto/sha256-browser" "5.2.0"
|
||||||
|
"@aws-crypto/sha256-js" "5.2.0"
|
||||||
|
"@aws-sdk/core" "3.970.0"
|
||||||
|
"@aws-sdk/middleware-host-header" "3.969.0"
|
||||||
|
"@aws-sdk/middleware-logger" "3.969.0"
|
||||||
|
"@aws-sdk/middleware-recursion-detection" "3.969.0"
|
||||||
|
"@aws-sdk/middleware-user-agent" "3.970.0"
|
||||||
|
"@aws-sdk/region-config-resolver" "3.969.0"
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@aws-sdk/util-endpoints" "3.970.0"
|
||||||
|
"@aws-sdk/util-user-agent-browser" "3.969.0"
|
||||||
|
"@aws-sdk/util-user-agent-node" "3.971.0"
|
||||||
|
"@smithy/config-resolver" "^4.4.6"
|
||||||
|
"@smithy/core" "^3.20.6"
|
||||||
|
"@smithy/fetch-http-handler" "^5.3.9"
|
||||||
|
"@smithy/hash-node" "^4.2.8"
|
||||||
|
"@smithy/invalid-dependency" "^4.2.8"
|
||||||
|
"@smithy/middleware-content-length" "^4.2.8"
|
||||||
|
"@smithy/middleware-endpoint" "^4.4.7"
|
||||||
|
"@smithy/middleware-retry" "^4.4.23"
|
||||||
|
"@smithy/middleware-serde" "^4.2.9"
|
||||||
|
"@smithy/middleware-stack" "^4.2.8"
|
||||||
|
"@smithy/node-config-provider" "^4.3.8"
|
||||||
|
"@smithy/node-http-handler" "^4.4.8"
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/smithy-client" "^4.10.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/url-parser" "^4.2.8"
|
||||||
|
"@smithy/util-base64" "^4.3.0"
|
||||||
|
"@smithy/util-body-length-browser" "^4.2.0"
|
||||||
|
"@smithy/util-body-length-node" "^4.2.1"
|
||||||
|
"@smithy/util-defaults-mode-browser" "^4.3.22"
|
||||||
|
"@smithy/util-defaults-mode-node" "^4.2.25"
|
||||||
|
"@smithy/util-endpoints" "^3.2.8"
|
||||||
|
"@smithy/util-middleware" "^4.2.8"
|
||||||
|
"@smithy/util-retry" "^4.2.8"
|
||||||
|
"@smithy/util-utf8" "^4.2.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/core@3.970.0":
|
||||||
|
version "3.970.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/core/-/core-3.970.0.tgz"
|
||||||
|
integrity sha512-klpzObldOq8HXzDjDlY6K8rMhYZU6mXRz6P9F9N+tWnjoYFfeBMra8wYApydElTUYQKP1O7RLHwH1OKFfKcqIA==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@aws-sdk/xml-builder" "3.969.0"
|
||||||
|
"@smithy/core" "^3.20.6"
|
||||||
|
"@smithy/node-config-provider" "^4.3.8"
|
||||||
|
"@smithy/property-provider" "^4.2.8"
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/signature-v4" "^5.3.8"
|
||||||
|
"@smithy/smithy-client" "^4.10.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/util-base64" "^4.3.0"
|
||||||
|
"@smithy/util-middleware" "^4.2.8"
|
||||||
|
"@smithy/util-utf8" "^4.2.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/credential-provider-env@3.970.0":
|
||||||
|
version "3.970.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.970.0.tgz"
|
||||||
|
integrity sha512-rtVzXzEtAfZBfh+lq3DAvRar4c3jyptweOAJR2DweyXx71QSMY+O879hjpMwES7jl07a3O1zlnFIDo4KP/96kQ==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/core" "3.970.0"
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@smithy/property-provider" "^4.2.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/credential-provider-http@3.970.0":
|
||||||
|
version "3.970.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.970.0.tgz"
|
||||||
|
integrity sha512-CjDbWL7JxjLc9ZxQilMusWSw05yRvUJKRpz59IxDpWUnSMHC9JMMUUkOy5Izk8UAtzi6gupRWArp4NG4labt9Q==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/core" "3.970.0"
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@smithy/fetch-http-handler" "^5.3.9"
|
||||||
|
"@smithy/node-http-handler" "^4.4.8"
|
||||||
|
"@smithy/property-provider" "^4.2.8"
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/smithy-client" "^4.10.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/util-stream" "^4.5.10"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/credential-provider-ini@3.971.0":
|
||||||
|
version "3.971.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.971.0.tgz"
|
||||||
|
integrity sha512-c0TGJG4xyfTZz3SInXfGU8i5iOFRrLmy4Bo7lMyH+IpngohYMYGYl61omXqf2zdwMbDv+YJ9AviQTcCaEUKi8w==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/core" "3.970.0"
|
||||||
|
"@aws-sdk/credential-provider-env" "3.970.0"
|
||||||
|
"@aws-sdk/credential-provider-http" "3.970.0"
|
||||||
|
"@aws-sdk/credential-provider-login" "3.971.0"
|
||||||
|
"@aws-sdk/credential-provider-process" "3.970.0"
|
||||||
|
"@aws-sdk/credential-provider-sso" "3.971.0"
|
||||||
|
"@aws-sdk/credential-provider-web-identity" "3.971.0"
|
||||||
|
"@aws-sdk/nested-clients" "3.971.0"
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@smithy/credential-provider-imds" "^4.2.8"
|
||||||
|
"@smithy/property-provider" "^4.2.8"
|
||||||
|
"@smithy/shared-ini-file-loader" "^4.4.3"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/credential-provider-login@3.971.0":
|
||||||
|
version "3.971.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.971.0.tgz"
|
||||||
|
integrity sha512-yhbzmDOsk0RXD3rTPhZra4AWVnVAC4nFWbTp+sUty1hrOPurUmhuz8bjpLqYTHGnlMbJp+UqkQONhS2+2LzW2g==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/core" "3.970.0"
|
||||||
|
"@aws-sdk/nested-clients" "3.971.0"
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@smithy/property-provider" "^4.2.8"
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/shared-ini-file-loader" "^4.4.3"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/credential-provider-node@3.971.0":
|
||||||
|
version "3.971.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.971.0.tgz"
|
||||||
|
integrity sha512-epUJBAKivtJqalnEBRsYIULKYV063o/5mXNJshZfyvkAgNIzc27CmmKRXTN4zaNOZg8g/UprFp25BGsi19x3nQ==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/credential-provider-env" "3.970.0"
|
||||||
|
"@aws-sdk/credential-provider-http" "3.970.0"
|
||||||
|
"@aws-sdk/credential-provider-ini" "3.971.0"
|
||||||
|
"@aws-sdk/credential-provider-process" "3.970.0"
|
||||||
|
"@aws-sdk/credential-provider-sso" "3.971.0"
|
||||||
|
"@aws-sdk/credential-provider-web-identity" "3.971.0"
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@smithy/credential-provider-imds" "^4.2.8"
|
||||||
|
"@smithy/property-provider" "^4.2.8"
|
||||||
|
"@smithy/shared-ini-file-loader" "^4.4.3"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/credential-provider-process@3.970.0":
|
||||||
|
version "3.970.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.970.0.tgz"
|
||||||
|
integrity sha512-0XeT8OaT9iMA62DFV9+m6mZfJhrD0WNKf4IvsIpj2Z7XbaYfz3CoDDvNoALf3rPY9NzyMHgDxOspmqdvXP00mw==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/core" "3.970.0"
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@smithy/property-provider" "^4.2.8"
|
||||||
|
"@smithy/shared-ini-file-loader" "^4.4.3"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/credential-provider-sso@3.971.0":
|
||||||
|
version "3.971.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.971.0.tgz"
|
||||||
|
integrity sha512-dY0hMQ7dLVPQNJ8GyqXADxa9w5wNfmukgQniLxGVn+dMRx3YLViMp5ZpTSQpFhCWNF0oKQrYAI5cHhUJU1hETw==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/client-sso" "3.971.0"
|
||||||
|
"@aws-sdk/core" "3.970.0"
|
||||||
|
"@aws-sdk/token-providers" "3.971.0"
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@smithy/property-provider" "^4.2.8"
|
||||||
|
"@smithy/shared-ini-file-loader" "^4.4.3"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/credential-provider-web-identity@3.971.0":
|
||||||
|
version "3.971.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.971.0.tgz"
|
||||||
|
integrity sha512-F1AwfNLr7H52T640LNON/h34YDiMuIqW/ZreGzhRR6vnFGaSPtNSKAKB2ssAMkLM8EVg8MjEAYD3NCUiEo+t/w==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/core" "3.970.0"
|
||||||
|
"@aws-sdk/nested-clients" "3.971.0"
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@smithy/property-provider" "^4.2.8"
|
||||||
|
"@smithy/shared-ini-file-loader" "^4.4.3"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/middleware-host-header@3.969.0":
|
||||||
|
version "3.969.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.969.0.tgz"
|
||||||
|
integrity sha512-AWa4rVsAfBR4xqm7pybQ8sUNJYnjyP/bJjfAw34qPuh3M9XrfGbAHG0aiAfQGrBnmS28jlO6Kz69o+c6PRw1dw==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/middleware-logger@3.969.0":
|
||||||
|
version "3.969.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.969.0.tgz"
|
||||||
|
integrity sha512-xwrxfip7Y2iTtCMJ+iifN1E1XMOuhxIHY9DreMCvgdl4r7+48x2S1bCYPWH3eNY85/7CapBWdJ8cerpEl12sQQ==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/middleware-recursion-detection@3.969.0":
|
||||||
|
version "3.969.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.969.0.tgz"
|
||||||
|
integrity sha512-2r3PuNquU3CcS1Am4vn/KHFwLi8QFjMdA/R+CRDXT4AFO/0qxevF/YStW3gAKntQIgWgQV8ZdEtKAoJvLI4UWg==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@aws/lambda-invoke-store" "^0.2.2"
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/middleware-sdk-s3@3.970.0":
|
||||||
|
version "3.970.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.970.0.tgz"
|
||||||
|
integrity sha512-v/Y5F1lbFFY7vMeG5yYxuhnn0CAshz6KMxkz1pDyPxejNE9HtA0w8R6OTBh/bVdIm44QpjhbI7qeLdOE/PLzXQ==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/core" "3.970.0"
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@aws-sdk/util-arn-parser" "3.968.0"
|
||||||
|
"@smithy/core" "^3.20.6"
|
||||||
|
"@smithy/node-config-provider" "^4.3.8"
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/signature-v4" "^5.3.8"
|
||||||
|
"@smithy/smithy-client" "^4.10.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/util-config-provider" "^4.2.0"
|
||||||
|
"@smithy/util-middleware" "^4.2.8"
|
||||||
|
"@smithy/util-stream" "^4.5.10"
|
||||||
|
"@smithy/util-utf8" "^4.2.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/middleware-user-agent@3.970.0":
|
||||||
|
version "3.970.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.970.0.tgz"
|
||||||
|
integrity sha512-dnSJGGUGSFGEX2NzvjwSefH+hmZQ347AwbLhAsi0cdnISSge+pcGfOFrJt2XfBIypwFe27chQhlfuf/gWdzpZg==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/core" "3.970.0"
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@aws-sdk/util-endpoints" "3.970.0"
|
||||||
|
"@smithy/core" "^3.20.6"
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/nested-clients@3.971.0":
|
||||||
|
version "3.971.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.971.0.tgz"
|
||||||
|
integrity sha512-TWaILL8GyYlhGrxxnmbkazM4QsXatwQgoWUvo251FXmUOsiXDFDVX3hoGIfB3CaJhV2pJPfebHUNJtY6TjZ11g==
|
||||||
|
dependencies:
|
||||||
|
"@aws-crypto/sha256-browser" "5.2.0"
|
||||||
|
"@aws-crypto/sha256-js" "5.2.0"
|
||||||
|
"@aws-sdk/core" "3.970.0"
|
||||||
|
"@aws-sdk/middleware-host-header" "3.969.0"
|
||||||
|
"@aws-sdk/middleware-logger" "3.969.0"
|
||||||
|
"@aws-sdk/middleware-recursion-detection" "3.969.0"
|
||||||
|
"@aws-sdk/middleware-user-agent" "3.970.0"
|
||||||
|
"@aws-sdk/region-config-resolver" "3.969.0"
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@aws-sdk/util-endpoints" "3.970.0"
|
||||||
|
"@aws-sdk/util-user-agent-browser" "3.969.0"
|
||||||
|
"@aws-sdk/util-user-agent-node" "3.971.0"
|
||||||
|
"@smithy/config-resolver" "^4.4.6"
|
||||||
|
"@smithy/core" "^3.20.6"
|
||||||
|
"@smithy/fetch-http-handler" "^5.3.9"
|
||||||
|
"@smithy/hash-node" "^4.2.8"
|
||||||
|
"@smithy/invalid-dependency" "^4.2.8"
|
||||||
|
"@smithy/middleware-content-length" "^4.2.8"
|
||||||
|
"@smithy/middleware-endpoint" "^4.4.7"
|
||||||
|
"@smithy/middleware-retry" "^4.4.23"
|
||||||
|
"@smithy/middleware-serde" "^4.2.9"
|
||||||
|
"@smithy/middleware-stack" "^4.2.8"
|
||||||
|
"@smithy/node-config-provider" "^4.3.8"
|
||||||
|
"@smithy/node-http-handler" "^4.4.8"
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/smithy-client" "^4.10.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/url-parser" "^4.2.8"
|
||||||
|
"@smithy/util-base64" "^4.3.0"
|
||||||
|
"@smithy/util-body-length-browser" "^4.2.0"
|
||||||
|
"@smithy/util-body-length-node" "^4.2.1"
|
||||||
|
"@smithy/util-defaults-mode-browser" "^4.3.22"
|
||||||
|
"@smithy/util-defaults-mode-node" "^4.2.25"
|
||||||
|
"@smithy/util-endpoints" "^3.2.8"
|
||||||
|
"@smithy/util-middleware" "^4.2.8"
|
||||||
|
"@smithy/util-retry" "^4.2.8"
|
||||||
|
"@smithy/util-utf8" "^4.2.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/region-config-resolver@3.969.0":
|
||||||
|
version "3.969.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.969.0.tgz"
|
||||||
|
integrity sha512-scj9OXqKpcjJ4jsFLtqYWz3IaNvNOQTFFvEY8XMJXTv+3qF5I7/x9SJtKzTRJEBF3spjzBUYPtGFbs9sj4fisQ==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@smithy/config-resolver" "^4.4.6"
|
||||||
|
"@smithy/node-config-provider" "^4.3.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/signature-v4-multi-region@3.970.0":
|
||||||
|
version "3.970.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.970.0.tgz"
|
||||||
|
integrity sha512-z3syXfuK/x/IsKf/AeYmgc2NT7fcJ+3fHaGO+fkghkV9WEba3fPyOwtTBX4KpFMNb2t50zDGZwbzW1/5ighcUQ==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/middleware-sdk-s3" "3.970.0"
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/signature-v4" "^5.3.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/token-providers@3.971.0":
|
||||||
|
version "3.971.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.971.0.tgz"
|
||||||
|
integrity sha512-4hKGWZbmuDdONMJV0HJ+9jwTDb0zLfKxcCLx2GEnBY31Gt9GeyIQ+DZ97Bb++0voawj6pnZToFikXTyrEq2x+w==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/core" "3.970.0"
|
||||||
|
"@aws-sdk/nested-clients" "3.971.0"
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@smithy/property-provider" "^4.2.8"
|
||||||
|
"@smithy/shared-ini-file-loader" "^4.4.3"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/types@^3.222.0", "@aws-sdk/types@3.969.0":
|
||||||
|
version "3.969.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/types/-/types-3.969.0.tgz"
|
||||||
|
integrity sha512-7IIzM5TdiXn+VtgPdVLjmE6uUBUtnga0f4RiSEI1WW10RPuNvZ9U+pL3SwDiRDAdoGrOF9tSLJOFZmfuwYuVYQ==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/util-arn-parser@3.968.0":
|
||||||
|
version "3.968.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.968.0.tgz"
|
||||||
|
integrity sha512-gqqvYcitIIM2K4lrDX9de9YvOfXBcVdxfT/iLnvHJd4YHvSXlt+gs+AsL4FfPCxG4IG9A+FyulP9Sb1MEA75vw==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/util-endpoints@3.970.0":
|
||||||
|
version "3.970.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.970.0.tgz"
|
||||||
|
integrity sha512-TZNZqFcMUtjvhZoZRtpEGQAdULYiy6rcGiXAbLU7e9LSpIYlRqpLa207oMNfgbzlL2PnHko+eVg8rajDiSOYCg==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/url-parser" "^4.2.8"
|
||||||
|
"@smithy/util-endpoints" "^3.2.8"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/util-locate-window@^3.0.0":
|
||||||
|
version "3.965.2"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.2.tgz"
|
||||||
|
integrity sha512-qKgO7wAYsXzhwCHhdbaKFyxd83Fgs8/1Ka+jjSPrv2Ll7mB55Wbwlo0kkfMLh993/yEc8aoDIAc1Fz9h4Spi4Q==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/util-user-agent-browser@3.969.0":
|
||||||
|
version "3.969.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.969.0.tgz"
|
||||||
|
integrity sha512-bpJGjuKmFr0rA6UKUCmN8D19HQFMLXMx5hKBXqBlPFdalMhxJSjcxzX9DbQh0Fn6bJtxCguFmRGOBdQqNOt49g==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
bowser "^2.11.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/util-user-agent-node@3.971.0":
|
||||||
|
version "3.971.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.971.0.tgz"
|
||||||
|
integrity sha512-Eygjo9mFzQYjbGY3MYO6CsIhnTwAMd3WmuFalCykqEmj2r5zf0leWrhPaqvA5P68V5JdGfPYgj7vhNOd6CtRBQ==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/middleware-user-agent" "3.970.0"
|
||||||
|
"@aws-sdk/types" "3.969.0"
|
||||||
|
"@smithy/node-config-provider" "^4.3.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws-sdk/xml-builder@3.969.0":
|
||||||
|
version "3.969.0"
|
||||||
|
resolved "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.969.0.tgz"
|
||||||
|
integrity sha512-BSe4Lx/qdRQQdX8cSSI7Et20vqBspzAjBy8ZmXVoyLkol3y4sXBXzn+BiLtR+oh60ExQn6o2DU4QjdOZbXaKIQ==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
fast-xml-parser "5.2.5"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@aws/lambda-invoke-store@^0.2.2":
|
||||||
|
version "0.2.3"
|
||||||
|
resolved "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz"
|
||||||
|
integrity sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==
|
||||||
|
|
||||||
"@babel/code-frame@^7.0.0":
|
"@babel/code-frame@^7.0.0":
|
||||||
version "7.27.1"
|
version "7.27.1"
|
||||||
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz"
|
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz"
|
||||||
@@ -686,6 +1162,409 @@
|
|||||||
resolved "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.13.0.tgz"
|
resolved "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.13.0.tgz"
|
||||||
integrity sha512-2ih5qGw5SZJ+2fLZxP6Lr6Na2NTIgPRL/7Kmyuw0uIyBQnuhQ8fi8fzUTd38eIQmqp+GYLC00cI6WgtqHxBwmw==
|
integrity sha512-2ih5qGw5SZJ+2fLZxP6Lr6Na2NTIgPRL/7Kmyuw0uIyBQnuhQ8fi8fzUTd38eIQmqp+GYLC00cI6WgtqHxBwmw==
|
||||||
|
|
||||||
|
"@smithy/abort-controller@^4.2.8":
|
||||||
|
version "4.2.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.8.tgz"
|
||||||
|
integrity sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/config-resolver@^4.4.6":
|
||||||
|
version "4.4.6"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.6.tgz"
|
||||||
|
integrity sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/node-config-provider" "^4.3.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/util-config-provider" "^4.2.0"
|
||||||
|
"@smithy/util-endpoints" "^3.2.8"
|
||||||
|
"@smithy/util-middleware" "^4.2.8"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/core@^3.20.6", "@smithy/core@^3.20.8":
|
||||||
|
version "3.20.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/core/-/core-3.20.8.tgz"
|
||||||
|
integrity sha512-ZBOJENmvM/IrKbxUxRVavCcVCjD5VcPI1MbOURjj3jZgV/t7UaBF8Z+lLSEzoQHH7Lq/efmOhBpV5xCfPjNRZw==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/middleware-serde" "^4.2.9"
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/util-base64" "^4.3.0"
|
||||||
|
"@smithy/util-body-length-browser" "^4.2.0"
|
||||||
|
"@smithy/util-middleware" "^4.2.8"
|
||||||
|
"@smithy/util-stream" "^4.5.10"
|
||||||
|
"@smithy/util-utf8" "^4.2.0"
|
||||||
|
"@smithy/uuid" "^1.1.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/credential-provider-imds@^4.2.8":
|
||||||
|
version "4.2.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.8.tgz"
|
||||||
|
integrity sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/node-config-provider" "^4.3.8"
|
||||||
|
"@smithy/property-provider" "^4.2.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/url-parser" "^4.2.8"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/fetch-http-handler@^5.3.9":
|
||||||
|
version "5.3.9"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.9.tgz"
|
||||||
|
integrity sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/querystring-builder" "^4.2.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/util-base64" "^4.3.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/hash-node@^4.2.8":
|
||||||
|
version "4.2.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.8.tgz"
|
||||||
|
integrity sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/util-buffer-from" "^4.2.0"
|
||||||
|
"@smithy/util-utf8" "^4.2.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/invalid-dependency@^4.2.8":
|
||||||
|
version "4.2.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.8.tgz"
|
||||||
|
integrity sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/is-array-buffer@^2.2.0":
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz"
|
||||||
|
integrity sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/is-array-buffer@^4.2.0":
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz"
|
||||||
|
integrity sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/middleware-content-length@^4.2.8":
|
||||||
|
version "4.2.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.8.tgz"
|
||||||
|
integrity sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/middleware-endpoint@^4.4.7", "@smithy/middleware-endpoint@^4.4.9":
|
||||||
|
version "4.4.9"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.9.tgz"
|
||||||
|
integrity sha512-+2Rxc3zzIT8BhclKEyj/ZzbOh95c55qjKA4XKx5eIGEEGPb4qFwYrNk2NZ1AGuRY71dxmpy4mar+GwurMOLT0w==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/core" "^3.20.8"
|
||||||
|
"@smithy/middleware-serde" "^4.2.9"
|
||||||
|
"@smithy/node-config-provider" "^4.3.8"
|
||||||
|
"@smithy/shared-ini-file-loader" "^4.4.3"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/url-parser" "^4.2.8"
|
||||||
|
"@smithy/util-middleware" "^4.2.8"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/middleware-retry@^4.4.23":
|
||||||
|
version "4.4.25"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.25.tgz"
|
||||||
|
integrity sha512-NZMnOw4jeNZ4O/bbqUWaLiK3xSpLzIq0QNJhxHZGvtLoywnKPnNJRCfyOE5w82tK3n2L3ZMIC5VtSVW6WvDBkQ==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/node-config-provider" "^4.3.8"
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/service-error-classification" "^4.2.8"
|
||||||
|
"@smithy/smithy-client" "^4.10.10"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/util-middleware" "^4.2.8"
|
||||||
|
"@smithy/util-retry" "^4.2.8"
|
||||||
|
"@smithy/uuid" "^1.1.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/middleware-serde@^4.2.9":
|
||||||
|
version "4.2.9"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.9.tgz"
|
||||||
|
integrity sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/middleware-stack@^4.2.8":
|
||||||
|
version "4.2.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.8.tgz"
|
||||||
|
integrity sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/node-config-provider@^4.3.8":
|
||||||
|
version "4.3.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.8.tgz"
|
||||||
|
integrity sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/property-provider" "^4.2.8"
|
||||||
|
"@smithy/shared-ini-file-loader" "^4.4.3"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/node-http-handler@^4.4.8":
|
||||||
|
version "4.4.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.8.tgz"
|
||||||
|
integrity sha512-q9u+MSbJVIJ1QmJ4+1u+cERXkrhuILCBDsJUBAW1MPE6sFonbCNaegFuwW9ll8kh5UdyY3jOkoOGlc7BesoLpg==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/abort-controller" "^4.2.8"
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/querystring-builder" "^4.2.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/property-provider@^4.2.8":
|
||||||
|
version "4.2.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.8.tgz"
|
||||||
|
integrity sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/protocol-http@^5.3.8":
|
||||||
|
version "5.3.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.8.tgz"
|
||||||
|
integrity sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/querystring-builder@^4.2.8":
|
||||||
|
version "4.2.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.8.tgz"
|
||||||
|
integrity sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/util-uri-escape" "^4.2.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/querystring-parser@^4.2.8":
|
||||||
|
version "4.2.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.8.tgz"
|
||||||
|
integrity sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/service-error-classification@^4.2.8":
|
||||||
|
version "4.2.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.8.tgz"
|
||||||
|
integrity sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
|
||||||
|
"@smithy/shared-ini-file-loader@^4.4.3":
|
||||||
|
version "4.4.3"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.3.tgz"
|
||||||
|
integrity sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/signature-v4@^5.3.8":
|
||||||
|
version "5.3.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.8.tgz"
|
||||||
|
integrity sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/is-array-buffer" "^4.2.0"
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/util-hex-encoding" "^4.2.0"
|
||||||
|
"@smithy/util-middleware" "^4.2.8"
|
||||||
|
"@smithy/util-uri-escape" "^4.2.0"
|
||||||
|
"@smithy/util-utf8" "^4.2.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/smithy-client@^4.10.10", "@smithy/smithy-client@^4.10.8":
|
||||||
|
version "4.10.10"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.10.10.tgz"
|
||||||
|
integrity sha512-yMT5cB4RdT3x+Etgc99aY/b4jmi++Bmwl7BtLAko+ma7zYZlSiFxbo58XgXouyrP8hDk2/peg55O3AyOm4GtEw==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/core" "^3.20.8"
|
||||||
|
"@smithy/middleware-endpoint" "^4.4.9"
|
||||||
|
"@smithy/middleware-stack" "^4.2.8"
|
||||||
|
"@smithy/protocol-http" "^5.3.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/util-stream" "^4.5.10"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/types@^4.12.0":
|
||||||
|
version "4.12.0"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/types/-/types-4.12.0.tgz"
|
||||||
|
integrity sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/url-parser@^4.2.8":
|
||||||
|
version "4.2.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.8.tgz"
|
||||||
|
integrity sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/querystring-parser" "^4.2.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/util-base64@^4.3.0":
|
||||||
|
version "4.3.0"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz"
|
||||||
|
integrity sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/util-buffer-from" "^4.2.0"
|
||||||
|
"@smithy/util-utf8" "^4.2.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/util-body-length-browser@^4.2.0":
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz"
|
||||||
|
integrity sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/util-body-length-node@^4.2.1":
|
||||||
|
version "4.2.1"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz"
|
||||||
|
integrity sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/util-buffer-from@^2.2.0":
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz"
|
||||||
|
integrity sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/is-array-buffer" "^2.2.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/util-buffer-from@^4.2.0":
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz"
|
||||||
|
integrity sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/is-array-buffer" "^4.2.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/util-config-provider@^4.2.0":
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz"
|
||||||
|
integrity sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/util-defaults-mode-browser@^4.3.22":
|
||||||
|
version "4.3.24"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.24.tgz"
|
||||||
|
integrity sha512-k2GUY1hGk5JfrlYh7O0PgrS5//IFqbQ3nAldQYfiam+J82h6wmuPkagF+kgF6ldKGHvi7iFpyCb4yXm+P5QD0Q==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/property-provider" "^4.2.8"
|
||||||
|
"@smithy/smithy-client" "^4.10.10"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/util-defaults-mode-node@^4.2.25":
|
||||||
|
version "4.2.27"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.27.tgz"
|
||||||
|
integrity sha512-Pam2VH8BSfd9uITrepxYq8QcJKmXVBCq/KCO/3Af/f5zugVioih2rWfTi0+hRzF75DhjgNnwfwdGebItQdO9Mw==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/config-resolver" "^4.4.6"
|
||||||
|
"@smithy/credential-provider-imds" "^4.2.8"
|
||||||
|
"@smithy/node-config-provider" "^4.3.8"
|
||||||
|
"@smithy/property-provider" "^4.2.8"
|
||||||
|
"@smithy/smithy-client" "^4.10.10"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/util-endpoints@^3.2.8":
|
||||||
|
version "3.2.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.8.tgz"
|
||||||
|
integrity sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/node-config-provider" "^4.3.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/util-hex-encoding@^4.2.0":
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz"
|
||||||
|
integrity sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/util-middleware@^4.2.8":
|
||||||
|
version "4.2.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.8.tgz"
|
||||||
|
integrity sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/util-retry@^4.2.8":
|
||||||
|
version "4.2.8"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.8.tgz"
|
||||||
|
integrity sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/service-error-classification" "^4.2.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/util-stream@^4.5.10":
|
||||||
|
version "4.5.10"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.10.tgz"
|
||||||
|
integrity sha512-jbqemy51UFSZSp2y0ZmRfckmrzuKww95zT9BYMmuJ8v3altGcqjwoV1tzpOwuHaKrwQrCjIzOib499ymr2f98g==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/fetch-http-handler" "^5.3.9"
|
||||||
|
"@smithy/node-http-handler" "^4.4.8"
|
||||||
|
"@smithy/types" "^4.12.0"
|
||||||
|
"@smithy/util-base64" "^4.3.0"
|
||||||
|
"@smithy/util-buffer-from" "^4.2.0"
|
||||||
|
"@smithy/util-hex-encoding" "^4.2.0"
|
||||||
|
"@smithy/util-utf8" "^4.2.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/util-uri-escape@^4.2.0":
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz"
|
||||||
|
integrity sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/util-utf8@^2.0.0":
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz"
|
||||||
|
integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/util-buffer-from" "^2.2.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/util-utf8@^4.2.0":
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz"
|
||||||
|
integrity sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==
|
||||||
|
dependencies:
|
||||||
|
"@smithy/util-buffer-from" "^4.2.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
|
"@smithy/uuid@^1.1.0":
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz"
|
||||||
|
integrity sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
"@swc/helpers@0.5.15":
|
"@swc/helpers@0.5.15":
|
||||||
version "0.5.15"
|
version "0.5.15"
|
||||||
resolved "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz"
|
resolved "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz"
|
||||||
@@ -737,6 +1616,14 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~5.26.4"
|
undici-types "~5.26.4"
|
||||||
|
|
||||||
|
"@types/nodemailer@^7.0.5":
|
||||||
|
version "7.0.5"
|
||||||
|
resolved "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-7.0.5.tgz"
|
||||||
|
integrity sha512-7WtR4MFJUNN2UFy0NIowBRJswj5KXjXDhlZY43Hmots5eGu5q/dTeFd/I6GgJA/qj3RqO6dDy4SvfcV3fOVeIA==
|
||||||
|
dependencies:
|
||||||
|
"@aws-sdk/client-sesv2" "^3.839.0"
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/normalize-package-data@^2.4.0":
|
"@types/normalize-package-data@^2.4.0":
|
||||||
version "2.4.4"
|
version "2.4.4"
|
||||||
resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz"
|
resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz"
|
||||||
@@ -958,6 +1845,11 @@
|
|||||||
resolved "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz"
|
resolved "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz"
|
||||||
integrity sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==
|
integrity sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==
|
||||||
|
|
||||||
|
"@vercel/analytics@^1.1.1":
|
||||||
|
version "1.5.0"
|
||||||
|
resolved "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.5.0.tgz"
|
||||||
|
integrity sha512-MYsBzfPki4gthY5HnYN7jgInhAZ7Ac1cYDoRWFomwGHWEX7odTEzbtg9kf/QSo7XEsEAqlQugA6gJ2WS2DEa3g==
|
||||||
|
|
||||||
abort-controller@^3.0.0:
|
abort-controller@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz"
|
resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz"
|
||||||
@@ -1276,6 +2168,11 @@ binary-extensions@^2.0.0:
|
|||||||
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz"
|
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz"
|
||||||
integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
|
integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
|
||||||
|
|
||||||
|
bowser@^2.11.0:
|
||||||
|
version "2.13.1"
|
||||||
|
resolved "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz"
|
||||||
|
integrity sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==
|
||||||
|
|
||||||
brace-expansion@^1.1.7:
|
brace-expansion@^1.1.7:
|
||||||
version "1.1.12"
|
version "1.1.12"
|
||||||
resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz"
|
resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz"
|
||||||
@@ -2211,6 +3108,13 @@ fast-uri@^3.0.1:
|
|||||||
resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz"
|
resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz"
|
||||||
integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==
|
integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==
|
||||||
|
|
||||||
|
fast-xml-parser@5.2.5:
|
||||||
|
version "5.2.5"
|
||||||
|
resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz"
|
||||||
|
integrity sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==
|
||||||
|
dependencies:
|
||||||
|
strnum "^2.1.0"
|
||||||
|
|
||||||
fastq@^1.6.0:
|
fastq@^1.6.0:
|
||||||
version "1.19.1"
|
version "1.19.1"
|
||||||
resolved "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz"
|
resolved "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz"
|
||||||
@@ -3459,7 +4363,7 @@ natural-compare@^1.4.0:
|
|||||||
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
|
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
|
||||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||||
|
|
||||||
next@^15.5.9:
|
next@^15.5.9, "next@>= 13":
|
||||||
version "15.5.9"
|
version "15.5.9"
|
||||||
resolved "https://registry.npmjs.org/next/-/next-15.5.9.tgz"
|
resolved "https://registry.npmjs.org/next/-/next-15.5.9.tgz"
|
||||||
integrity sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg==
|
integrity sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg==
|
||||||
@@ -3497,6 +4401,11 @@ node-releases@^2.0.21:
|
|||||||
resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz"
|
resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz"
|
||||||
integrity sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==
|
integrity sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==
|
||||||
|
|
||||||
|
nodemailer@^7.0.12:
|
||||||
|
version "7.0.12"
|
||||||
|
resolved "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.12.tgz"
|
||||||
|
integrity sha512-H+rnK5bX2Pi/6ms3sN4/jRQvYSMltV6vqup/0SFOrxYYY/qoNvhXPlYq3e+Pm9RFJRwrMGbMIwi81M4dxpomhA==
|
||||||
|
|
||||||
normalize-package-data@^2.5.0:
|
normalize-package-data@^2.5.0:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz"
|
resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz"
|
||||||
@@ -3962,7 +4871,7 @@ react-is@^16.13.1:
|
|||||||
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
||||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||||
|
|
||||||
"react@^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc", "react@^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react@^16.8.0 || ^17 || ^18 || ^19", react@^18, "react@^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", react@^18.3.1, "react@>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0":
|
"react@^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc", "react@^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react@^16.8.0 || ^17 || ^18 || ^19", react@^18, "react@^18 || ^19 || ^19.0.0-rc", "react@^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", react@^18.3.1, "react@>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0":
|
||||||
version "18.3.1"
|
version "18.3.1"
|
||||||
resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz"
|
resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz"
|
||||||
integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==
|
integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==
|
||||||
@@ -4627,6 +5536,11 @@ strip-json-comments@^3.1.1:
|
|||||||
resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz"
|
resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz"
|
||||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||||
|
|
||||||
|
strnum@^2.1.0:
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz"
|
||||||
|
integrity sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==
|
||||||
|
|
||||||
styled-jsx@5.1.6:
|
styled-jsx@5.1.6:
|
||||||
version "5.1.6"
|
version "5.1.6"
|
||||||
resolved "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz"
|
resolved "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz"
|
||||||
@@ -4814,7 +5728,7 @@ tsconfig-paths@^3.15.0:
|
|||||||
minimist "^1.2.6"
|
minimist "^1.2.6"
|
||||||
strip-bom "^3.0.0"
|
strip-bom "^3.0.0"
|
||||||
|
|
||||||
tslib@^2.4.0, tslib@^2.8.0:
|
tslib@^2.4.0, tslib@^2.6.2, tslib@^2.8.0:
|
||||||
version "2.8.1"
|
version "2.8.1"
|
||||||
resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz"
|
resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz"
|
||||||
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
||||||
|
|||||||