added vulnerabilitiess

This commit is contained in:
Cypro Freelance 2024-07-31 01:04:39 +05:30
parent d37443d365
commit 9b8261be2f
7 changed files with 433 additions and 89 deletions

View file

@ -12,6 +12,7 @@ const AdminPage = () => {
<Card title="Mods" url="/admin/mods" /> <Card title="Mods" url="/admin/mods" />
<Card title="Logs" url="/admin/changelogs" /> <Card title="Logs" url="/admin/changelogs" />
<Card title="MultiLogs" url="/admin/multi-logs" /> <Card title="MultiLogs" url="/admin/multi-logs" />
<Card title="Vulnerabilities" url="/admin/vulnerabilities" />
</div> </div>
</section> </section>
</> </>

View file

@ -0,0 +1,9 @@
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Admin // Vulnerabilities",
};
export default function logPages({ children }: { children: React.ReactNode }) {
return <>{children}</>;
}

View file

@ -0,0 +1,217 @@
"use client";
import React, { useEffect, useState } from "react";
import { useForm, SubmitHandler, useFieldArray } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Input } from "@/components/ui/input";
import { logsSchema } from "@/lib/validations/validation";
import { z } from "zod";
import { useToast } from "@/components/ui/use-toast";
interface LogEntry {
_id: string;
version: string;
date: string;
bullets: { point: string }[];
}
type LogsFormValues = z.infer<typeof logsSchema>;
const AdminLogPage = () => {
const [logs, setLogs] = useState<LogEntry[]>([]);
const [error, setError] = useState("");
const { toast } = useToast();
const [loading, setLoading] = useState(false);
const form = useForm<LogsFormValues>({
resolver: zodResolver(logsSchema),
defaultValues: {
version: "",
date: "",
bullets: [{ point: "" }],
},
});
const { fields, append, remove } = useFieldArray({
control: form.control,
name: "bullets",
});
const fetchLogs = async () => {
try {
const response = await fetch("/api/vulnerabilties", { method: "GET" });
if (response.ok) {
const data: LogEntry[] = await response.json();
setLogs(data);
} else {
throw new Error(`HTTP error! status: ${response.status}`);
}
} catch (error: any) {
setError(error.message || "Failed to fetch logs");
}
};
const onSubmit: SubmitHandler<LogsFormValues> = async (data) => {
setLoading(true);
const response = await fetch("/api/uploadvulnerabilities", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
if (response.ok) {
form.reset();
fetchLogs();
setLoading(false);
toast({ description: "Logs successfully added" });
} else {
setLoading(false);
toast({ description: "Upload Failed", variant: "destructive" });
}
};
const deleteLog = async (id: string) => {
try {
const response = await fetch(`/api/delete/logs/${id}`, {
method: "DELETE",
});
if (response.ok) {
fetchLogs();
} else {
throw new Error(`HTTP error! status: ${response.status}`);
}
} catch (error: any) {
setError(error.message || "Failed to delete log");
}
};
useEffect(() => {
fetchLogs();
const interval = setInterval(() => {
fetchLogs();
}, 10000);
return () => clearInterval(interval);
}, []);
return (
<section id="logs-page" className="wrapper container">
<h1 className="text-3xl font-bold py-6">Server Vulnerabilties Form</h1>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
control={form.control}
name="version"
render={({ field }) => (
<FormItem>
<FormLabel>Version Name</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{fields.map((field, index) => (
<FormField
key={field.id}
control={form.control}
name={`bullets.${index}.point`}
render={({ field }) => (
<FormItem>
<FormLabel>Key Point {index + 1}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
<Button
type="button"
className="mt-2"
variant={"secondary"}
onClick={() => remove(index)}
>
Remove
</Button>
</FormItem>
)}
/>
))}
<Button
type="button"
className="mb-4"
size={"icon"}
variant={"outline"}
onClick={() => append({ point: "" })}
>
+
</Button>
<Button
type="submit"
className="w-full text-lg rounded-full"
disabled={loading}
size={"lg"}
>
Submit
</Button>
</form>
</Form>
{/* Section to list and delete logs */}
<section id="logs-list" className="py-16 md:py-24">
<h2 className="text-3xl md:text-4xl font-bold">
Existing Vulnerabilties
</h2>
{error && <p className="text-red-500">{error}</p>}
<Table className="w-full mt-4 border-muted">
<TableHeader>
<TableRow>
<TableHead className="border-b px-4 py-2">Version</TableHead>
<TableHead className="border-b px-4 py-2">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{logs
.slice()
.reverse()
.map((log) => (
<TableRow key={log._id}>
<TableCell className="border-b px-4 py-2">
{log.version}
</TableCell>
<TableCell className="border-b px-4 py-2">
<Button
variant={"destructive"}
onClick={() => deleteLog(log._id)}
>
Delete
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</section>
</section>
);
};
export default AdminLogPage;

View file

@ -3,103 +3,104 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Table, Table,
TableBody, TableBody,
TableCaption, TableCaption,
TableCell, TableCell,
TableHead, TableHead,
TableHeader, TableHeader,
TableRow, TableRow,
} from "@/components/ui/table"; } from "@/components/ui/table";
import { Download } from "lucide-react"; import { Download } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { Skeleton } from "@/components/ui/skeleton";
interface Download { interface Download {
_id: string; _id: string;
date: string; date: string;
fileName: string; fileName: string;
version: string; version: string;
fileSize: string; fileSize: string;
downloadLink: string; downloadLink: string;
} }
const DownloadPage: React.FC = () => { const DownloadPage: React.FC = () => {
const [downloads, setDownloads] = useState<Download[]>([]); const [downloads, setDownloads] = useState<Download[]>([]);
const [error, setError] = useState(""); const [error, setError] = useState("");
const fetchDownloads = async () => { const fetchDownloads = async () => {
try { try {
const response = await fetch("/api/downloads", { const response = await fetch("/api/downloads", {
method: "GET", method: "GET",
}); });
if (response.ok) { if (response.ok) {
const data: Download[] = await response.json(); const data: Download[] = await response.json();
setDownloads(data); setDownloads(data);
} else { } else {
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);
} }
}; };
useEffect(() => { useEffect(() => {
fetchDownloads(); fetchDownloads();
const interval = setInterval(() => { const interval = setInterval(() => {
fetchDownloads(); fetchDownloads();
}, 10000); }, 10000);
return () => clearInterval(interval); return () => clearInterval(interval);
}, []); }, []);
return ( return (
<section <section
id="download" id="download"
className="wrapper container py-24 md:py-28 gap-2 flex flex-col" className="wrapper container py-24 md:py-28 gap-2 flex flex-col"
> >
<h1 className="text-3xl md:text-5xl pb-1 md:pb-2 font-bold text-black dark:bg-clip-text dark:text-transparent dark:bg-gradient-to-b dark:from-white dark:to-neutral-400"> <h1 className="text-3xl md:text-5xl pb-1 md:pb-2 font-bold text-black dark:bg-clip-text dark:text-transparent dark:bg-gradient-to-b dark:from-white dark:to-neutral-400">
Downloads Downloads
</h1> </h1>
<p className="md:text-lg text-muted-foreground text-start mb-6"> <p className="md:text-lg text-muted-foreground text-start mb-6">
Get all the latest version of SVRJS download and compiled Files here! Get all the latest version of SVRJS download and compiled Files here!
</p> </p>
{error && <p className="text-red-500">{error}</p>} {error && <p className="text-red-500">{error}</p>}
<Table> <Table>
<TableCaption>A list of all available downloads.</TableCaption> <TableCaption>A list of all available downloads.</TableCaption>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead className="w-[150px]">Date</TableHead> <TableHead className="w-[150px]">Date</TableHead>
<TableHead>File Name</TableHead> <TableHead>File Name</TableHead>
<TableHead>Version</TableHead> <TableHead>Version</TableHead>
<TableHead>File Size</TableHead> <TableHead>File Size</TableHead>
<TableHead className="text-right">Download Link</TableHead> <TableHead className="text-right">Download Link</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{downloads {downloads
.slice(0, 10) .slice(0, 10)
.reverse() .reverse()
.map((download) => ( .map((download) => (
<TableRow key={download._id}> <TableRow key={download._id}>
<TableCell className="font-medium">{download.date}</TableCell> <TableCell className="font-medium">{download.date}</TableCell>
<TableCell>{download.fileName}</TableCell> <TableCell>{download.fileName}</TableCell>
<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}> <Link href={download.downloadLink}>
<Button variant={"ghost"} className=""> <Button variant={"ghost"} className="">
<Download className="w-4 h-4 mr-2" /> <Download className="w-4 h-4 mr-2" />
Download Download
</Button> </Button>
</Link> </Link>
</TableCell> </TableCell>
</TableRow> </TableRow>
))} ))}
</TableBody> </TableBody>
</Table> </Table>
</section> </section>
); );
}; };
export default DownloadPage; export default DownloadPage;

View file

@ -1,15 +1,76 @@
"use client";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import { vulnerabilities } from "@/constants/guidelines"; import { vulnerabilities } from "@/constants/guidelines";
import { Metadata } from "next"; import { useEffect, useState } from "react";
import { Skeleton } from "@/components/ui/skeleton";
export const metadata: Metadata = { interface Bullet {
title: "Vulnerabilities - SVRJS", point: string;
}; }
interface Vulnerabilities {
_id: string;
date: string;
version: string;
bullets?: Bullet[]; // Make bullets optional
}
const Vulnerabilities = () => { const Vulnerabilities = () => {
const [loading, setLoading] = useState(true);
const [downloads, setDownloads] = useState<Vulnerabilities[]>([]);
const [error, setError] = useState("");
const fetchData = async () => {
try {
const response = await fetch("/api/vulnerabilities", {
method: "GET",
});
if (response.ok) {
const data: Vulnerabilities[] = await response.json();
setDownloads(data);
return (document.title = "Vulnerabilities | SVRJS");
} else {
throw new Error(`HTTP error! status: ${response.status}`);
}
} catch (error: any) {
setError(error.message || "Failed to fetch downloads");
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
const interval = setInterval(() => {
fetchData();
}, 10000);
return () => clearInterval(interval);
}, []);
const reversedDownloads = [...downloads].reverse();
// initially loading = true
if (loading) {
return (
<section className="wrapper container py-24 md:py-28 gap-4 flex flex-col">
<div className="mb-3">
<Skeleton className="w-[400px] h-[50px] rounded-md" />
</div>
<div className="flex flex-col gap-4">
<Skeleton className="w-[300px] h-[30px] rounded-md" />
<Skeleton className="w-[200px] h-[20px] rounded-md" />
<Skeleton className="w-[200px] h-[20px] rounded-md" />
<Skeleton className="w-[200px] h-[20px] rounded-md" />
</div>
</section>
);
}
return ( return (
<section <section
id="tos" id="vulnerabilities"
className="wrapper container py-24 md:py-28 gap-2 flex flex-col" className="wrapper container py-24 md:py-28 gap-2 flex flex-col"
> >
<h1 className="text-3xl md:text-5xl pb-1 md:pb-2 font-bold text-black dark:bg-clip-text dark:text-transparent dark:bg-gradient-to-b dark:from-white dark:to-neutral-400"> <h1 className="text-3xl md:text-5xl pb-1 md:pb-2 font-bold text-black dark:bg-clip-text dark:text-transparent dark:bg-gradient-to-b dark:from-white dark:to-neutral-400">
@ -22,6 +83,21 @@ const Vulnerabilities = () => {
vulnerability-reports[at]svrjs[dot]org. We&apos;ll mitigate that vulnerability-reports[at]svrjs[dot]org. We&apos;ll mitigate that
vulnerability if it is possible. vulnerability if it is possible.
</p> </p>
{error && <p className="text-red-500">{error}</p>}
{reversedDownloads.map((download) => (
<div
key={download._id}
className="flex-start flex-col prose dark:prose-invert mb-4 gap-4"
>
<h2 className="font-semibold text-3xl -mb-2">{download.version}</h2>
<ul className="list-disc pl-5">
{(download.bullets ?? []).map((bullet, index) => (
<li key={index}>{bullet.point}</li>
))}
</ul>
</div>
))}
<div className="prose max-w-full md:prose-lg dark:prose-invert"> <div className="prose max-w-full md:prose-lg dark:prose-invert">
<ReactMarkdown>{vulnerabilities}</ReactMarkdown> <ReactMarkdown>{vulnerabilities}</ReactMarkdown>
</div> </div>

View file

@ -0,0 +1,20 @@
import { NextResponse } from "next/server";
import clientPromise from "@/lib/db";
// Force the API to use SSR instead of static generation
export const dynamic = "force-dynamic";
export async function POST(request: Request) {
const body = await request.json();
const { version, date, bullets } = body;
const client = await clientPromise;
const db = client.db("downloadsDatabase");
const result = await db.collection("vulnerabilities").insertOne({
version,
bullets,
});
return NextResponse.json({ success: true, id: result.insertedId });
}

View file

@ -0,0 +1,20 @@
import { NextRequest, NextResponse } from "next/server";
import clientPromise from "@/lib/db";
// Force the API to use SSR instead of static generation
export const dynamic = "force-dynamic";
// Handler for GET requests
export async function GET(req: NextRequest) {
try {
const client = await clientPromise;
const db = client.db("downloadsDatabase");
const downloads = await db.collection("vulnerabilities").find().toArray();
return NextResponse.json(downloads, { status: 200 });
} catch (error) {
return NextResponse.json(
{ error: "Failed to fetch logs" },
{ status: 500 }
);
}
}