newsletter done
This commit is contained in:
parent
803afe55d1
commit
b157cc932f
16 changed files with 655 additions and 216 deletions
|
@ -14,4 +14,4 @@ EMAIL_PASS=
|
||||||
SANITY_PROJECT_ID=
|
SANITY_PROJECT_ID=
|
||||||
|
|
||||||
NEXT_PUBLIC_HCAPTCHA_SITE_KEY=
|
NEXT_PUBLIC_HCAPTCHA_SITE_KEY=
|
||||||
|
HCAPTCHA_SECRET=
|
||||||
|
|
133
app/(auth)/admin/email/page.tsx
Normal file
133
app/(auth)/admin/email/page.tsx
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import {
|
||||||
|
Pagination,
|
||||||
|
PaginationContent,
|
||||||
|
PaginationItem,
|
||||||
|
PaginationLink,
|
||||||
|
PaginationNext,
|
||||||
|
PaginationPrevious,
|
||||||
|
} from "@/components/ui/pagination";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
interface Subscriber {
|
||||||
|
email: string;
|
||||||
|
subscribedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EmailPage = () => {
|
||||||
|
const [subscribers, setSubscribers] = useState<Subscriber[]>([]);
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
const [totalPages, setTotalPages] = useState(0);
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Function to fetch subscribers data
|
||||||
|
const fetchSubscribers = async () => {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`/api/newsletter/subscriber?page=${currentPage}`
|
||||||
|
);
|
||||||
|
const data = await res.json();
|
||||||
|
setSubscribers(data.subscribers);
|
||||||
|
setTotalPages(data.totalPages);
|
||||||
|
} catch (error) {
|
||||||
|
toast({
|
||||||
|
title: "Error fetching subscribers",
|
||||||
|
description: `${error}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fetch data initially
|
||||||
|
fetchSubscribers();
|
||||||
|
|
||||||
|
// Set up interval to fetch data every 10 seconds
|
||||||
|
const intervalId = setInterval(fetchSubscribers, 10000);
|
||||||
|
|
||||||
|
// Clear interval on component unmount
|
||||||
|
return () => clearInterval(intervalId);
|
||||||
|
}, [currentPage, toast]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section id="downloads-page" className="wrapper container">
|
||||||
|
<h1 className="text-3xl md:text-4xl font-bold py-6">Newsletter Emails</h1>
|
||||||
|
<Link href="/email-editor">
|
||||||
|
<Button>Create a new email</Button>
|
||||||
|
</Link>
|
||||||
|
<section id="downloads-list" className="py-8">
|
||||||
|
<h2 className="text-2xl font-semibold">Newsletter Subscribers</h2>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Total subscribers: {subscribers.length}
|
||||||
|
</p>
|
||||||
|
<Table className="w-full mt-4 border-muted">
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead className="border-b px-4 py-2">Email</TableHead>
|
||||||
|
<TableHead className="border-b px-4 py-2">
|
||||||
|
Subscribed At
|
||||||
|
</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{subscribers.map((subscriber, idx) => (
|
||||||
|
<TableRow key={idx}>
|
||||||
|
<TableCell className="border-b px-4 py-2">
|
||||||
|
{subscriber.email}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="border-b px-4 py-2">
|
||||||
|
{new Date(subscriber.subscribedAt).toLocaleDateString()}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<div className="flex-center mt-12">
|
||||||
|
{totalPages > 1 && (
|
||||||
|
<Pagination>
|
||||||
|
<PaginationContent>
|
||||||
|
<PaginationItem>
|
||||||
|
{currentPage > 1 && (
|
||||||
|
<PaginationPrevious
|
||||||
|
onClick={() => setCurrentPage(currentPage - 1)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</PaginationItem>
|
||||||
|
{Array.from({ length: totalPages }).map((_, i) => (
|
||||||
|
<PaginationItem key={i}>
|
||||||
|
<PaginationLink
|
||||||
|
isActive={currentPage === i + 1}
|
||||||
|
onClick={() => setCurrentPage(i + 1)}
|
||||||
|
>
|
||||||
|
{i + 1}
|
||||||
|
</PaginationLink>
|
||||||
|
</PaginationItem>
|
||||||
|
))}
|
||||||
|
<PaginationItem>
|
||||||
|
{currentPage < totalPages && (
|
||||||
|
<PaginationNext
|
||||||
|
onClick={() => setCurrentPage(currentPage + 1)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</PaginationItem>
|
||||||
|
</PaginationContent>
|
||||||
|
</Pagination>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EmailPage;
|
|
@ -1,5 +1,5 @@
|
||||||
import MobileNav from "./_components/Mobilenav";
|
import MobileNav from "../_components/Mobilenav";
|
||||||
import Sidebar from "./_components/Sidebar";
|
import Sidebar from "../_components/Sidebar";
|
||||||
|
|
||||||
export default function PageLayout({
|
export default function PageLayout({
|
||||||
children,
|
children,
|
147
app/(auth)/email-editor/page.tsx
Normal file
147
app/(auth)/email-editor/page.tsx
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import CodeEditor from "@/components/cards/MonacoEditor";
|
||||||
|
import { EXAMPLE_A1 } from "@/constants";
|
||||||
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
|
|
||||||
|
const EmailEditor = () => {
|
||||||
|
const { toast } = useToast();
|
||||||
|
const [subject, setSubject] = useState("");
|
||||||
|
const [previewContent, setPreviewContent] = useState<string>(EXAMPLE_A1);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const validateInputs = () => {
|
||||||
|
if (!subject.trim() || !previewContent.trim()) {
|
||||||
|
toast({
|
||||||
|
title: "Validation Error",
|
||||||
|
description: "Subject and content cannot be empty.",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSendAll = async () => {
|
||||||
|
if (!validateInputs()) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/newsletter/send", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
subject: subject,
|
||||||
|
html: previewContent,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Network response was not ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
toast({
|
||||||
|
title: "Success!",
|
||||||
|
description: result.message || "Emails sent successfully",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error:", error);
|
||||||
|
toast({
|
||||||
|
title: "Uh oh!",
|
||||||
|
description: `Failed to send emails: ${error}`,
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSendTest = async () => {
|
||||||
|
if (!validateInputs()) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/newsletter/test", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
subject: subject,
|
||||||
|
html: previewContent,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Network response was not ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
toast({
|
||||||
|
title: "Success!",
|
||||||
|
description: result.message || "Test email sent successfully",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error:", error);
|
||||||
|
toast({
|
||||||
|
title: "Uh oh!",
|
||||||
|
description: `Failed to send test email: ${error}`,
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEditorChange = (value: string) => {
|
||||||
|
setPreviewContent(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col lg:flex-row h-screen">
|
||||||
|
<div className="w-full lg:w-1/2 p-4 flex flex-col space-y-4">
|
||||||
|
<Link href="/admin/email" className="text-blue-500 underline">
|
||||||
|
Back
|
||||||
|
</Link>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Subject"
|
||||||
|
value={subject}
|
||||||
|
onChange={(e) => setSubject(e.target.value)}
|
||||||
|
className="border rounded-md p-2"
|
||||||
|
/>
|
||||||
|
<CodeEditor onChange={handleEditorChange} />
|
||||||
|
<div className="flex space-x-2 mt-4">
|
||||||
|
<Button
|
||||||
|
variant={"secondary"}
|
||||||
|
onClick={handleSendTest}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading ? "Sending..." : "Send Test"}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleSendAll} disabled={loading}>
|
||||||
|
{loading ? "Sending..." : "Send All"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full lg:w-1/2 p-4 overflow-auto">
|
||||||
|
<h2 className="text-2xl font-bold mb-4 text-secondary-foreground">
|
||||||
|
Email Preview
|
||||||
|
</h2>
|
||||||
|
<div
|
||||||
|
className="border rounded-md p-4"
|
||||||
|
dangerouslySetInnerHTML={{ __html: previewContent }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EmailEditor;
|
|
@ -45,10 +45,10 @@ export async function generateMetadata({
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: data.title,
|
title: `${data.title} - SVRJS`,
|
||||||
description: data.smallDescription,
|
description: data.smallDescription,
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: data.title,
|
title: `${data.title} - SVRJS`,
|
||||||
description: data.smallDescription,
|
description: data.smallDescription,
|
||||||
url: `https://svrjs.org/blog/${data.currentSlug}`,
|
url: `https://svrjs.org/blog/${data.currentSlug}`,
|
||||||
type: "website",
|
type: "website",
|
||||||
|
@ -57,14 +57,14 @@ export async function generateMetadata({
|
||||||
url: urlFor(data.titleImage).url(),
|
url: urlFor(data.titleImage).url(),
|
||||||
width: 800,
|
width: 800,
|
||||||
height: 600,
|
height: 600,
|
||||||
alt: data.title,
|
alt: `${data.title} - SVRJS`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
card: "summary_large_image",
|
card: "summary_large_image",
|
||||||
site: "@SVR_JS",
|
site: "@SVR_JS",
|
||||||
title: data.title,
|
title: `${data.title} - SVRJS`,
|
||||||
description: data.smallDescription,
|
description: data.smallDescription,
|
||||||
images: [urlFor(data.titleImage).url()],
|
images: [urlFor(data.titleImage).url()],
|
||||||
creator: "@SVR_JS",
|
creator: "@SVR_JS",
|
||||||
|
|
|
@ -21,7 +21,7 @@ interface Download {
|
||||||
fileName: string;
|
fileName: string;
|
||||||
version: string;
|
version: string;
|
||||||
fileSize: string;
|
fileSize: string;
|
||||||
downloadLink: string;
|
downloadLink?: string; // Optional
|
||||||
}
|
}
|
||||||
|
|
||||||
const DownloadPage: React.FC = () => {
|
const DownloadPage: React.FC = () => {
|
||||||
|
@ -40,7 +40,7 @@ const DownloadPage: React.FC = () => {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
setError(error);
|
setError(error.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,12 +88,19 @@ const DownloadPage: React.FC = () => {
|
||||||
<TableCell>{download.version}</TableCell>
|
<TableCell>{download.version}</TableCell>
|
||||||
<TableCell className="text-left">{download.fileSize}</TableCell>
|
<TableCell className="text-left">{download.fileSize}</TableCell>
|
||||||
<TableCell className="flex items-center justify-end">
|
<TableCell className="flex items-center justify-end">
|
||||||
<Link href={download.downloadLink}>
|
{download.downloadLink ? (
|
||||||
<Button variant={"ghost"} className="">
|
<Link href={download.downloadLink}>
|
||||||
|
<Button variant={"ghost"} className="">
|
||||||
|
<Download className="w-4 h-4 mr-2" />
|
||||||
|
Download
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<Button variant={"ghost"} disabled>
|
||||||
<Download className="w-4 h-4 mr-2" />
|
<Download className="w-4 h-4 mr-2" />
|
||||||
Download
|
Unavailable
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
|
|
65
app/api/newsletter/send/route.ts
Normal file
65
app/api/newsletter/send/route.ts
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import nodemailer from "nodemailer";
|
||||||
|
import clientPromise from "@/lib/db";
|
||||||
|
|
||||||
|
const transporter = nodemailer.createTransport({
|
||||||
|
host: "smtp.ethereal.email", //replace this also comment this if u not using etheral
|
||||||
|
// service: "gmail", // uncomment if u using gmail
|
||||||
|
port: 587,
|
||||||
|
auth: {
|
||||||
|
user: process.env.EMAIL,
|
||||||
|
pass: process.env.EMAIL_PASS,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const sendEmail = async (to: string[], subject: string, html: string) => {
|
||||||
|
try {
|
||||||
|
await transporter.sendMail({
|
||||||
|
from: process.env.EMAIL_USER,
|
||||||
|
to: to.join(", "),
|
||||||
|
subject: subject,
|
||||||
|
html: html,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error sending email:", error);
|
||||||
|
throw new Error("Failed to send email");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function POST(req: NextRequest) {
|
||||||
|
try {
|
||||||
|
const { subject, html } = await req.json();
|
||||||
|
|
||||||
|
const client = await clientPromise;
|
||||||
|
const db = client.db("newsletter");
|
||||||
|
const collection = db.collection("subscribers");
|
||||||
|
|
||||||
|
const subscribers = await collection
|
||||||
|
.find({}, { projection: { email: 1 } })
|
||||||
|
.toArray();
|
||||||
|
|
||||||
|
if (subscribers.length === 0) {
|
||||||
|
console.error("No subscribers found in the database.");
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: "No subscribers found." },
|
||||||
|
{ status: 404 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const emails = subscribers.map((subscriber) => subscriber.email);
|
||||||
|
|
||||||
|
if (emails.length === 0) {
|
||||||
|
console.error("No email addresses found.");
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: "No email addresses found." },
|
||||||
|
{ status: 404 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await sendEmail(emails, subject, html);
|
||||||
|
return NextResponse.json({ message: "Emails sent successfully" });
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error handling POST request:", error);
|
||||||
|
return NextResponse.error();
|
||||||
|
}
|
||||||
|
}
|
42
app/api/newsletter/subscriber/route.ts
Normal file
42
app/api/newsletter/subscriber/route.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import clientPromise from "@/lib/db"; // Adjust the path to where your db.ts is located
|
||||||
|
|
||||||
|
interface Subscriber {
|
||||||
|
email: string;
|
||||||
|
subscribedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GET(req: Request) {
|
||||||
|
try {
|
||||||
|
const url = new URL(req.url);
|
||||||
|
const page = parseInt(url.searchParams.get("page") || "1", 10);
|
||||||
|
const limit = 10;
|
||||||
|
const skip = (page - 1) * limit;
|
||||||
|
|
||||||
|
const client = await clientPromise;
|
||||||
|
const db = client.db("newsletter");
|
||||||
|
const collection = db.collection("subscribers");
|
||||||
|
|
||||||
|
// pagination
|
||||||
|
const documents = await collection.find().skip(skip).limit(limit).toArray();
|
||||||
|
|
||||||
|
const subscribers: Subscriber[] = documents.map((doc) => ({
|
||||||
|
email: doc.email,
|
||||||
|
subscribedAt:
|
||||||
|
doc.subscribedAt instanceof Date
|
||||||
|
? doc.subscribedAt
|
||||||
|
: new Date(doc.subscribedAt),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const totalSubscribers = await collection.countDocuments();
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
subscribers,
|
||||||
|
totalSubscribers,
|
||||||
|
totalPages: Math.ceil(totalSubscribers / limit),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching subscribers:", error);
|
||||||
|
return new NextResponse("Internal Server Error", { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
52
app/api/newsletter/test/route.ts
Normal file
52
app/api/newsletter/test/route.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import nodemailer from "nodemailer";
|
||||||
|
|
||||||
|
const transporter = nodemailer.createTransport({
|
||||||
|
host: "smtp.ethereal.email", // Replace with your SMTP host
|
||||||
|
// service: "gmail", // Uncomment if using Gmail
|
||||||
|
port: 587,
|
||||||
|
auth: {
|
||||||
|
user: process.env.EMAIL,
|
||||||
|
pass: process.env.EMAIL_PASS,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const sendEmail = async (to: string[], subject: string, html: string) => {
|
||||||
|
try {
|
||||||
|
await transporter.sendMail({
|
||||||
|
from: process.env.EMAIL_USER,
|
||||||
|
to: to.join(", "),
|
||||||
|
subject: subject,
|
||||||
|
html: html,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error sending email:", error);
|
||||||
|
throw new Error("Failed to send email");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function POST(req: NextRequest) {
|
||||||
|
try {
|
||||||
|
const { subject, html } = await req.json();
|
||||||
|
|
||||||
|
// add ur email here
|
||||||
|
const testEmails = [
|
||||||
|
"abhijitbhattacharjee333@gmail.com",
|
||||||
|
"test2@example.com",
|
||||||
|
];
|
||||||
|
|
||||||
|
if (testEmails.length === 0) {
|
||||||
|
console.error("No email addresses provided.");
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: "No email addresses provided." },
|
||||||
|
{ status: 404 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await sendEmail(testEmails, subject, html);
|
||||||
|
return NextResponse.json({ message: "Emails sent successfully" });
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error handling POST request:", error);
|
||||||
|
return NextResponse.error();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,25 +1,63 @@
|
||||||
import mailchimp from "@mailchimp/mailchimp_marketing";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import clientPromise from "@/lib/db";
|
||||||
mailchimp.setConfig({
|
|
||||||
apiKey: process.env.MAILCHIP_API_KEY,
|
|
||||||
server: process.env.MAILCHIP_API_SERVER,
|
|
||||||
});
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const { email } = await request.json();
|
|
||||||
|
|
||||||
if (!email) new Response(JSON.stringify({ error: "Email not found" }));
|
|
||||||
|
|
||||||
|
export async function POST(req: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const res = await mailchimp.lists.addListMember(
|
const { email, captchaToken } = await req.json();
|
||||||
process.env.MAILCHIP_AUDIENCE_ID!,
|
|
||||||
{ email_address: email, status: "subscribed" }
|
if (!email || !captchaToken) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: "Email and captcha token are required." },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify hCaptcha token
|
||||||
|
const hcaptchaResponse = await fetch(
|
||||||
|
`https://api.hcaptcha.com/siteverify`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
body: `secret=${process.env.HCAPTCHA_SECRET}&response=${captchaToken}`,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return new Response(JSON.stringify(res));
|
const hcaptchaData = await hcaptchaResponse.json();
|
||||||
} catch (error: any) {
|
|
||||||
return new Response(
|
if (!hcaptchaData.success) {
|
||||||
JSON.stringify({ error: JSON.parse(error.response.text) })
|
return NextResponse.json(
|
||||||
|
{ message: "Captcha verification failed." },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = await clientPromise;
|
||||||
|
const db = client.db("newsletter");
|
||||||
|
const collection = db.collection("subscribers");
|
||||||
|
|
||||||
|
// checking if email alr exists
|
||||||
|
const existingSubscriber = await collection.findOne({ email });
|
||||||
|
if (existingSubscriber) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: "This email is already subscribed." },
|
||||||
|
{ status: 409 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// saves the email in the db
|
||||||
|
await collection.insertOne({ email, subscribedAt: new Date() });
|
||||||
|
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: "Successfully subscribed!" },
|
||||||
|
{ status: 201 }
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error subscribing:", error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: "Internal Server Error" },
|
||||||
|
{ status: 500 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
30
components/cards/MonacoEditor.tsx
Normal file
30
components/cards/MonacoEditor.tsx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Editor } from "@monaco-editor/react";
|
||||||
|
import { EXAMPLE_A1 } from "@/constants";
|
||||||
|
|
||||||
|
interface CodeEditorProps {
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CodeEditor = ({ onChange }: CodeEditorProps) => {
|
||||||
|
return (
|
||||||
|
<div className="bg-white w-full max-w-full">
|
||||||
|
<Editor
|
||||||
|
options={{
|
||||||
|
minimap: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
height="75vh"
|
||||||
|
theme="vs-dark"
|
||||||
|
defaultValue={EXAMPLE_A1}
|
||||||
|
language={"html"}
|
||||||
|
onChange={(newValue) => onChange(newValue || "")}
|
||||||
|
className="bg-zinc-950 text-white"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CodeEditor;
|
|
@ -6,6 +6,7 @@ import { Input } from "../ui/input";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { Happy_Monkey } from "next/font/google";
|
import { Happy_Monkey } from "next/font/google";
|
||||||
import { Mail } from "lucide-react";
|
import { Mail } from "lucide-react";
|
||||||
|
import HCaptcha from "@hcaptcha/react-hcaptcha";
|
||||||
|
|
||||||
const happyMonkey = Happy_Monkey({
|
const happyMonkey = Happy_Monkey({
|
||||||
preload: true,
|
preload: true,
|
||||||
|
@ -18,16 +19,22 @@ const Newsletter = () => {
|
||||||
"idle" | "loading" | "success" | "error"
|
"idle" | "loading" | "success" | "error"
|
||||||
>("idle");
|
>("idle");
|
||||||
const [input, setInput] = useState<string>("");
|
const [input, setInput] = useState<string>("");
|
||||||
|
const [captchaToken, setCaptchaToken] = useState<string | null>(null);
|
||||||
|
const [showCaptcha, setShowCaptcha] = useState<boolean>(false);
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState<boolean>(false); // Added this line
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||||
|
const hcaptchaRef = useRef<HCaptcha>(null);
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
const handleCaptcha = async (token: string) => {
|
||||||
e.preventDefault();
|
setCaptchaToken(token);
|
||||||
|
setShowCaptcha(false);
|
||||||
|
await handleSubmit(token);
|
||||||
|
};
|
||||||
|
|
||||||
const email = input;
|
const handleSubmit = async (token: string | null) => {
|
||||||
const button = buttonRef.current;
|
if (!input || !token || isSubmitting) return;
|
||||||
|
|
||||||
if (!button || !email) return;
|
|
||||||
|
|
||||||
|
setIsSubmitting(true);
|
||||||
setSubmission("loading");
|
setSubmission("loading");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -36,20 +43,28 @@ const Newsletter = () => {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ email }),
|
body: JSON.stringify({ email: input, captchaToken: token }),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
setSubmission("success");
|
setSubmission("success");
|
||||||
|
setInput("");
|
||||||
} else {
|
} else {
|
||||||
setSubmission("error");
|
setSubmission("error");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error subscribing:", error);
|
console.error("Error subscribing:", error);
|
||||||
setSubmission("error");
|
setSubmission("error");
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSubscribeClick = () => {
|
||||||
|
if (!input) return;
|
||||||
|
setShowCaptcha(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="newsletter">
|
<section id="newsletter">
|
||||||
<hr className="w-11/12 mx-auto" />
|
<hr className="w-11/12 mx-auto" />
|
||||||
|
@ -58,13 +73,13 @@ const Newsletter = () => {
|
||||||
Join The Newsletter!
|
Join The Newsletter!
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-lg text-muted-foreground text-center mt-4 md:mt-2 mb-8">
|
<p className="text-lg text-muted-foreground text-center mt-4 md:mt-2 mb-8">
|
||||||
Subscribe to our newsletter for updates. we promise no spam emails
|
Subscribe to our newsletter for updates. We promise no spam emails
|
||||||
will be sent
|
will be sent
|
||||||
</p>
|
</p>
|
||||||
<form
|
<form
|
||||||
className="relative flex flex-col w-full md:flex-row md:w-6/12 lg:w-4/12 mx-auto gap-4 md:gap-2"
|
className="relative flex flex-col w-full md:flex-row md:w-6/12 lg:w-4/12 mx-auto gap-4 md:gap-2"
|
||||||
aria-label="Email Information"
|
aria-label="Email Information"
|
||||||
onSubmit={handleSubmit}
|
onSubmit={(e) => e.preventDefault()}
|
||||||
>
|
>
|
||||||
<div className="group flex items-center gap-x-4 py-1 pl-4 pr-1 rounded-[9px] bg-[#090D11] hover:bg-[#15141B] shadow-outline-gray hover:shadow-transparent focus-within:bg-[#15141B] focus-within:!shadow-outline-gray-focus transition-all duration-300">
|
<div className="group flex items-center gap-x-4 py-1 pl-4 pr-1 rounded-[9px] bg-[#090D11] hover:bg-[#15141B] shadow-outline-gray hover:shadow-transparent focus-within:bg-[#15141B] focus-within:!shadow-outline-gray-focus transition-all duration-300">
|
||||||
<Mail className="hidden sm:inline w-6 h-6 text-[#4B4C52] group-focus-within:text-white group-hover:text-white transition-colors duration-300" />
|
<Mail className="hidden sm:inline w-6 h-6 text-[#4B4C52] group-focus-within:text-white group-hover:text-white transition-colors duration-300" />
|
||||||
|
@ -77,9 +92,14 @@ const Newsletter = () => {
|
||||||
className="flex-1 text-white text-sm sm:text-base outline-none placeholder-[#4B4C52] group-focus-within:placeholder-white bg-transparent placeholder:transition-colors placeholder:duration-300 border-none"
|
className="flex-1 text-white text-sm sm:text-base outline-none placeholder-[#4B4C52] group-focus-within:placeholder-white bg-transparent placeholder:transition-colors placeholder:duration-300 border-none"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button ref={buttonRef} disabled={submission === "loading" || !input}>
|
<Button
|
||||||
|
ref={buttonRef}
|
||||||
|
onClick={handleSubscribeClick}
|
||||||
|
disabled={submission === "loading" || !input || isSubmitting}
|
||||||
|
>
|
||||||
Subscribe
|
Subscribe
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<div className="pointer-events-none dark:invert -scale-x-100 absolute -bottom-14 right-1/2 md:right-14 inline-flex justify-center items-center gap-1">
|
<div className="pointer-events-none dark:invert -scale-x-100 absolute -bottom-14 right-1/2 md:right-14 inline-flex justify-center items-center gap-1">
|
||||||
<Image
|
<Image
|
||||||
src="/curly-arrow.png"
|
src="/curly-arrow.png"
|
||||||
|
@ -87,6 +107,7 @@ const Newsletter = () => {
|
||||||
width={35}
|
width={35}
|
||||||
height={35}
|
height={35}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
className={`mt-10 font-bold text-black -scale-x-100 text-[15px] ${happyMonkey.className}`}
|
className={`mt-10 font-bold text-black -scale-x-100 text-[15px] ${happyMonkey.className}`}
|
||||||
>
|
>
|
||||||
|
@ -107,6 +128,15 @@ const Newsletter = () => {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
{showCaptcha && (
|
||||||
|
<div className="flex-center">
|
||||||
|
<HCaptcha
|
||||||
|
sitekey={process.env.NEXT_PUBLIC_HCAPTCHA_SITE_KEY!}
|
||||||
|
onVerify={handleCaptcha}
|
||||||
|
ref={hcaptchaRef}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<hr className="w-11/12 mx-auto" />
|
<hr className="w-11/12 mx-auto" />
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -233,6 +233,10 @@ export const AdminDashboardLINKS = [
|
||||||
label: "Vulnerabilities",
|
label: "Vulnerabilities",
|
||||||
url: "/admin/vulnerabilities",
|
url: "/admin/vulnerabilities",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Emails",
|
||||||
|
url: "/admin/email",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// contact page emails
|
// contact page emails
|
||||||
|
@ -258,3 +262,10 @@ export const emails = [
|
||||||
url: "mailto:vulnerability-reports@svrjs.org",
|
url: "mailto:vulnerability-reports@svrjs.org",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const EXAMPLE_A1 = `
|
||||||
|
<div>
|
||||||
|
<h1>Test Email Preview</h1>
|
||||||
|
<p>This is a simple email preview test.</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
|
@ -37,5 +37,6 @@ export const config = {
|
||||||
"/api/uploadmods",
|
"/api/uploadmods",
|
||||||
"/api/uploadthing",
|
"/api/uploadthing",
|
||||||
"/api/uploadvulnerabilities",
|
"/api/uploadvulnerabilities",
|
||||||
|
"/email-editor",
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
230
package-lock.json
generated
230
package-lock.json
generated
|
@ -10,8 +10,8 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hcaptcha/react-hcaptcha": "^1.11.0",
|
"@hcaptcha/react-hcaptcha": "^1.11.0",
|
||||||
"@hookform/resolvers": "^3.6.0",
|
"@hookform/resolvers": "^3.6.0",
|
||||||
"@mailchimp/mailchimp_marketing": "^3.0.80",
|
|
||||||
"@mdx-js/mdx": "^3.0.1",
|
"@mdx-js/mdx": "^3.0.1",
|
||||||
|
"@monaco-editor/react": "^4.6.0",
|
||||||
"@portabletext/react": "^3.1.0",
|
"@portabletext/react": "^3.1.0",
|
||||||
"@portabletext/to-html": "^2.0.13",
|
"@portabletext/to-html": "^2.0.13",
|
||||||
"@radix-ui/react-accordion": "^1.1.2",
|
"@radix-ui/react-accordion": "^1.1.2",
|
||||||
|
@ -29,7 +29,6 @@
|
||||||
"@tailwindcss/typography": "^0.5.13",
|
"@tailwindcss/typography": "^0.5.13",
|
||||||
"@types/bcrypt": "^5.0.2",
|
"@types/bcrypt": "^5.0.2",
|
||||||
"@types/cookie": "^0.6.0",
|
"@types/cookie": "^0.6.0",
|
||||||
"@types/mailchimp__mailchimp_marketing": "^3.0.20",
|
|
||||||
"@types/mdx": "^2.0.13",
|
"@types/mdx": "^2.0.13",
|
||||||
"@types/nodemailer": "^6.4.15",
|
"@types/nodemailer": "^6.4.15",
|
||||||
"@uiw/react-md-editor": "^4.0.4",
|
"@uiw/react-md-editor": "^4.0.4",
|
||||||
|
@ -3236,19 +3235,6 @@
|
||||||
"@lezer/common": "^1.0.0"
|
"@lezer/common": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mailchimp/mailchimp_marketing": {
|
|
||||||
"version": "3.0.80",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mailchimp/mailchimp_marketing/-/mailchimp_marketing-3.0.80.tgz",
|
|
||||||
"integrity": "sha512-Cgz0xPb+1DUjmrl5whAsmqfAChBko+Wf4/PLQE4RvwfPlcq2agfHr1QFiXEhZ8e+GQwQ3hZQn9iLGXwIXwxUCg==",
|
|
||||||
"license": "Apache 2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"dotenv": "^8.2.0",
|
|
||||||
"superagent": "3.8.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mapbox/node-pre-gyp": {
|
"node_modules/@mapbox/node-pre-gyp": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
|
||||||
|
@ -3404,6 +3390,32 @@
|
||||||
"react": ">=16"
|
"react": ">=16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@monaco-editor/loader": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"state-local": "^1.0.6"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"monaco-editor": ">= 0.21.0 < 1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@monaco-editor/react": {
|
||||||
|
"version": "4.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.6.0.tgz",
|
||||||
|
"integrity": "sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@monaco-editor/loader": "^1.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"monaco-editor": ">= 0.25.0 < 1",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@mongodb-js/saslprep": {
|
"node_modules/@mongodb-js/saslprep": {
|
||||||
"version": "1.1.7",
|
"version": "1.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.7.tgz",
|
||||||
|
@ -8407,12 +8419,6 @@
|
||||||
"@types/lodash": "*"
|
"@types/lodash": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/mailchimp__mailchimp_marketing": {
|
|
||||||
"version": "3.0.20",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/mailchimp__mailchimp_marketing/-/mailchimp__mailchimp_marketing-3.0.20.tgz",
|
|
||||||
"integrity": "sha512-fg7iKnnbfBxyVjh6WZy39sXscuhaYv9K5DRAok/ykHMJeh3la4qSv+v4i5x0IgE3fGWTRZpixhCfkkzEDUImhw==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@types/mdast": {
|
"node_modules/@types/mdast": {
|
||||||
"version": "3.0.15",
|
"version": "3.0.15",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz",
|
||||||
|
@ -10269,7 +10275,8 @@
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/available-typed-arrays": {
|
"node_modules/available-typed-arrays": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
|
@ -10629,6 +10636,7 @@
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
|
||||||
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
|
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-define-property": "^1.0.0",
|
"es-define-property": "^1.0.0",
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
|
@ -11048,6 +11056,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"delayed-stream": "~1.0.0"
|
"delayed-stream": "~1.0.0"
|
||||||
},
|
},
|
||||||
|
@ -11079,15 +11088,6 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/component-emitter": {
|
|
||||||
"version": "1.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz",
|
|
||||||
"integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/compress-commons": {
|
"node_modules/compress-commons": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz",
|
||||||
|
@ -11254,12 +11254,6 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cookiejar": {
|
|
||||||
"version": "2.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz",
|
|
||||||
"integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/copy-to-clipboard": {
|
"node_modules/copy-to-clipboard": {
|
||||||
"version": "3.3.3",
|
"version": "3.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
|
||||||
|
@ -12441,6 +12435,7 @@
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||||
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-define-property": "^1.0.0",
|
"es-define-property": "^1.0.0",
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
|
@ -12493,6 +12488,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
|
@ -12692,15 +12688,6 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dotenv": {
|
|
||||||
"version": "8.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz",
|
|
||||||
"integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==",
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/duplexify": {
|
"node_modules/duplexify": {
|
||||||
"version": "4.1.3",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
|
||||||
|
@ -12850,6 +12837,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
|
||||||
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
|
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"get-intrinsic": "^1.2.4"
|
"get-intrinsic": "^1.2.4"
|
||||||
},
|
},
|
||||||
|
@ -12861,6 +12849,7 @@
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||||
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
|
@ -14078,16 +14067,6 @@
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/formidable": {
|
|
||||||
"version": "1.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz",
|
|
||||||
"integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==",
|
|
||||||
"deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"url": "https://ko-fi.com/tunnckoCore/commissions"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/framer-motion": {
|
"node_modules/framer-motion": {
|
||||||
"version": "11.2.10",
|
"version": "11.2.10",
|
||||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.2.10.tgz",
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.2.10.tgz",
|
||||||
|
@ -14365,6 +14344,7 @@
|
||||||
"version": "1.2.4",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
|
||||||
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
|
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
"function-bind": "^1.1.2",
|
"function-bind": "^1.1.2",
|
||||||
|
@ -14677,6 +14657,7 @@
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||||
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
|
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"get-intrinsic": "^1.1.3"
|
"get-intrinsic": "^1.1.3"
|
||||||
},
|
},
|
||||||
|
@ -14800,6 +14781,7 @@
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||||
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-define-property": "^1.0.0"
|
"es-define-property": "^1.0.0"
|
||||||
},
|
},
|
||||||
|
@ -14811,6 +14793,7 @@
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
|
||||||
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
|
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
|
||||||
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
|
@ -14822,6 +14805,7 @@
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||||
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
|
@ -20033,15 +20017,6 @@
|
||||||
"web-worker": "^1.2.0"
|
"web-worker": "^1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/methods": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/micromark": {
|
"node_modules/micromark": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz",
|
||||||
|
@ -21401,23 +21376,12 @@
|
||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mime": {
|
|
||||||
"version": "1.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
|
||||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"mime": "cli.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mime-db": {
|
"node_modules/mime-db": {
|
||||||
"version": "1.52.0",
|
"version": "1.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
|
@ -21427,6 +21391,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mime-db": "1.52.0"
|
"mime-db": "1.52.0"
|
||||||
},
|
},
|
||||||
|
@ -21646,6 +21611,13 @@
|
||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/monaco-editor": {
|
||||||
|
"version": "0.51.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.51.0.tgz",
|
||||||
|
"integrity": "sha512-xaGwVV1fq343cM7aOYB6lVE4Ugf0UyimdD/x5PWcWBMKENwectaEu77FAN7c5sFiyumqeJdX1RPTh1ocioyDjw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/mongodb": {
|
"node_modules/mongodb": {
|
||||||
"version": "6.6.2",
|
"version": "6.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.6.2.tgz",
|
||||||
|
@ -26462,21 +26434,6 @@
|
||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/qs": {
|
|
||||||
"version": "6.13.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
|
||||||
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"side-channel": "^1.0.6"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.6"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/querystringify": {
|
"node_modules/querystringify": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||||
|
@ -30072,6 +30029,7 @@
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||||
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"define-data-property": "^1.1.4",
|
"define-data-property": "^1.1.4",
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
|
@ -30160,6 +30118,7 @@
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
|
||||||
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
|
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind": "^1.0.7",
|
"call-bind": "^1.0.7",
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
|
@ -30427,6 +30386,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
|
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
|
||||||
},
|
},
|
||||||
|
"node_modules/state-local": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/std-env": {
|
"node_modules/std-env": {
|
||||||
"version": "3.7.0",
|
"version": "3.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz",
|
||||||
|
@ -30824,87 +30789,6 @@
|
||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/superagent": {
|
|
||||||
"version": "3.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.1.tgz",
|
|
||||||
"integrity": "sha512-VMBFLYgFuRdfeNQSMLbxGSLfmXL/xc+OO+BZp41Za/NRDBet/BNbkRJrYzCUu0u4GU0i/ml2dtT8b9qgkw9z6Q==",
|
|
||||||
"deprecated": "Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"component-emitter": "^1.2.0",
|
|
||||||
"cookiejar": "^2.1.0",
|
|
||||||
"debug": "^3.1.0",
|
|
||||||
"extend": "^3.0.0",
|
|
||||||
"form-data": "^2.3.1",
|
|
||||||
"formidable": "^1.1.1",
|
|
||||||
"methods": "^1.1.1",
|
|
||||||
"mime": "^1.4.1",
|
|
||||||
"qs": "^6.5.1",
|
|
||||||
"readable-stream": "^2.0.5"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/superagent/node_modules/debug": {
|
|
||||||
"version": "3.2.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
|
||||||
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ms": "^2.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/superagent/node_modules/form-data": {
|
|
||||||
"version": "2.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
|
|
||||||
"integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"asynckit": "^0.4.0",
|
|
||||||
"combined-stream": "^1.0.6",
|
|
||||||
"mime-types": "^2.1.12"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/superagent/node_modules/isarray": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/superagent/node_modules/readable-stream": {
|
|
||||||
"version": "2.3.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
|
||||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"core-util-is": "~1.0.0",
|
|
||||||
"inherits": "~2.0.3",
|
|
||||||
"isarray": "~1.0.0",
|
|
||||||
"process-nextick-args": "~2.0.0",
|
|
||||||
"safe-buffer": "~5.1.1",
|
|
||||||
"string_decoder": "~1.1.1",
|
|
||||||
"util-deprecate": "~1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/superagent/node_modules/safe-buffer": {
|
|
||||||
"version": "5.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
|
||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/superagent/node_modules/string_decoder": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"safe-buffer": "~5.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/supports-color": {
|
"node_modules/supports-color": {
|
||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hcaptcha/react-hcaptcha": "^1.11.0",
|
"@hcaptcha/react-hcaptcha": "^1.11.0",
|
||||||
"@hookform/resolvers": "^3.6.0",
|
"@hookform/resolvers": "^3.6.0",
|
||||||
"@mailchimp/mailchimp_marketing": "^3.0.80",
|
|
||||||
"@mdx-js/mdx": "^3.0.1",
|
"@mdx-js/mdx": "^3.0.1",
|
||||||
|
"@monaco-editor/react": "^4.6.0",
|
||||||
"@portabletext/react": "^3.1.0",
|
"@portabletext/react": "^3.1.0",
|
||||||
"@portabletext/to-html": "^2.0.13",
|
"@portabletext/to-html": "^2.0.13",
|
||||||
"@radix-ui/react-accordion": "^1.1.2",
|
"@radix-ui/react-accordion": "^1.1.2",
|
||||||
|
@ -31,7 +31,6 @@
|
||||||
"@tailwindcss/typography": "^0.5.13",
|
"@tailwindcss/typography": "^0.5.13",
|
||||||
"@types/bcrypt": "^5.0.2",
|
"@types/bcrypt": "^5.0.2",
|
||||||
"@types/cookie": "^0.6.0",
|
"@types/cookie": "^0.6.0",
|
||||||
"@types/mailchimp__mailchimp_marketing": "^3.0.20",
|
|
||||||
"@types/mdx": "^2.0.13",
|
"@types/mdx": "^2.0.13",
|
||||||
"@types/nodemailer": "^6.4.15",
|
"@types/nodemailer": "^6.4.15",
|
||||||
"@uiw/react-md-editor": "^4.0.4",
|
"@uiw/react-md-editor": "^4.0.4",
|
||||||
|
|
Loading…
Reference in a new issue