made the mods vulnerabilities
This commit is contained in:
parent
a162f66b30
commit
deb62f9730
13 changed files with 628 additions and 109 deletions
|
@ -16,6 +16,7 @@ const EditPage = ({ params }: { params: { slug: string } }) => {
|
|||
const { toast } = useToast();
|
||||
const [title, setTitle] = useState("");
|
||||
const [content, setContent] = useState("");
|
||||
const [vulnerabilities, setVulnerabilities] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -25,6 +26,7 @@ const EditPage = ({ params }: { params: { slug: string } }) => {
|
|||
.then((data) => {
|
||||
setTitle(data.title);
|
||||
setContent(data.content);
|
||||
setVulnerabilities(data.vulnerabilities || "");
|
||||
})
|
||||
.catch((error) => console.error("Failed to load page", error));
|
||||
}
|
||||
|
@ -35,7 +37,7 @@ const EditPage = ({ params }: { params: { slug: string } }) => {
|
|||
const response = await fetch(`/api/mdx/pages/${slug}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ title, content }),
|
||||
body: JSON.stringify({ title, content, vulnerabilities }),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
|
@ -44,9 +46,7 @@ const EditPage = ({ params }: { params: { slug: string } }) => {
|
|||
router.push(`/admin/multi-logs/`);
|
||||
} else {
|
||||
setLoading(false);
|
||||
// TEMPERARORY ERROR
|
||||
router.push(`/admin/multi-logs/`);
|
||||
toast({ description: "Updated but cant return data" });
|
||||
toast({ description: "Page Updated" });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -57,7 +57,7 @@ const EditPage = ({ params }: { params: { slug: string } }) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<section id="edit-page" className="wrapper container">
|
||||
<section id="edit-page" className="wrapper container gap-4">
|
||||
<h1 className="text-3xl font-bold py-6">Edit Page: {slug}</h1>
|
||||
<Input
|
||||
value={title}
|
||||
|
@ -69,6 +69,12 @@ const EditPage = ({ params }: { params: { slug: string } }) => {
|
|||
onChange={handleEditorChange}
|
||||
height={560}
|
||||
/>
|
||||
<h1 className="text-3xl font-bold py-6">Vulnerabilities</h1>
|
||||
<MarkdownEditor
|
||||
value={vulnerabilities}
|
||||
onChange={(value) => setVulnerabilities(value || "")}
|
||||
height={200}
|
||||
/>
|
||||
<Button onClick={savePage} disabled={loading} className="mt-4">
|
||||
Save
|
||||
</Button>
|
||||
|
|
276
app/(auth)/admin/vulnerabilities/mods/page.tsx
Normal file
276
app/(auth)/admin/vulnerabilities/mods/page.tsx
Normal file
|
@ -0,0 +1,276 @@
|
|||
"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,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { z } from "zod";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import {
|
||||
ModsVulnerability,
|
||||
vulnerabilitiesSchema,
|
||||
} from "@/lib/validations/validation";
|
||||
|
||||
interface VulnerabilityEntry {
|
||||
_id: string;
|
||||
title: string;
|
||||
category: string;
|
||||
bullets: { point: string }[];
|
||||
}
|
||||
|
||||
type VulnerabilitiesForm = z.infer<typeof vulnerabilitiesSchema>;
|
||||
|
||||
const AdminLogPage = () => {
|
||||
const [logs, setLogs] = useState<VulnerabilityEntry[]>([]);
|
||||
const [categories, setCategories] = useState<
|
||||
{ title: string; _id: string }[]
|
||||
>([]);
|
||||
const [selectedCategory, setSelectedCategory] = useState<string>("");
|
||||
const [error, setError] = useState("");
|
||||
const { toast } = useToast();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const form = useForm<ModsVulnerability>({
|
||||
resolver: zodResolver(vulnerabilitiesSchema),
|
||||
defaultValues: {
|
||||
title: "",
|
||||
category: "",
|
||||
bullets: [{ point: "" }],
|
||||
},
|
||||
});
|
||||
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
control: form.control,
|
||||
name: "bullets",
|
||||
});
|
||||
|
||||
const fetchLogs = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/vulnerabilities", { method: "GET" });
|
||||
if (response.ok) {
|
||||
const data: VulnerabilityEntry[] = 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 fetchCategories = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/mdx/pages", { method: "GET" });
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
setCategories(data);
|
||||
} else {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
} catch (error: any) {
|
||||
setError(error.message || "Failed to fetch categories");
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit: SubmitHandler<ModsVulnerability> = async (data) => {
|
||||
setLoading(true);
|
||||
const response = await fetch("/api/vulnerability/mods", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
...data,
|
||||
category: selectedCategory,
|
||||
}),
|
||||
});
|
||||
|
||||
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/vulnerability/${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();
|
||||
fetchCategories();
|
||||
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 Vulnerabilities Form</h1>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Title</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="category"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Category</FormLabel>
|
||||
<Select
|
||||
value={selectedCategory}
|
||||
onValueChange={(value) => setSelectedCategory(field.value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={"Category"} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{categories.map((cat) => (
|
||||
<SelectItem key={cat._id} value={cat._id}>
|
||||
{cat.title}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{fields.map((field, index) => (
|
||||
<FormField
|
||||
key={field.id}
|
||||
control={form.control}
|
||||
name={`bullets.${index}.point`}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Bullet 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 Vulnerabilities
|
||||
</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">Title</TableHead>
|
||||
<TableHead className="border-b px-4 py-2">Category</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.title}
|
||||
</TableCell>
|
||||
<TableCell className="border-b px-4 py-2">
|
||||
{log.category}
|
||||
</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;
|
|
@ -22,30 +22,34 @@ import {
|
|||
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";
|
||||
import { vulnerabilitiesSchema } from "@/lib/validations/validation";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
} from "@/components/ui/select";
|
||||
|
||||
interface LogEntry {
|
||||
interface VulnerabiltyEntry {
|
||||
_id: string;
|
||||
version: string;
|
||||
date: string;
|
||||
bullets: { point: string }[];
|
||||
}
|
||||
|
||||
type LogsFormValues = z.infer<typeof logsSchema>;
|
||||
type VulnerabiltiesForm = z.infer<typeof vulnerabilitiesSchema>;
|
||||
|
||||
const AdminLogPage = () => {
|
||||
const [logs, setLogs] = useState<LogEntry[]>([]);
|
||||
const [logs, setLogs] = useState<VulnerabiltyEntry[]>([]);
|
||||
const [error, setError] = useState("");
|
||||
const { toast } = useToast();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const form = useForm<LogsFormValues>({
|
||||
resolver: zodResolver(logsSchema),
|
||||
const form = useForm<VulnerabiltiesForm>({
|
||||
resolver: zodResolver(vulnerabilitiesSchema),
|
||||
defaultValues: {
|
||||
version: "",
|
||||
date: "",
|
||||
bullets: [{ point: "" }],
|
||||
},
|
||||
});
|
||||
|
@ -59,7 +63,7 @@ const AdminLogPage = () => {
|
|||
try {
|
||||
const response = await fetch("/api/vulnerabilities", { method: "GET" });
|
||||
if (response.ok) {
|
||||
const data: LogEntry[] = await response.json();
|
||||
const data: VulnerabiltyEntry[] = await response.json();
|
||||
setLogs(data);
|
||||
} else {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
|
@ -69,7 +73,7 @@ const AdminLogPage = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const onSubmit: SubmitHandler<LogsFormValues> = async (data) => {
|
||||
const onSubmit: SubmitHandler<VulnerabiltiesForm> = async (data) => {
|
||||
setLoading(true);
|
||||
const response = await fetch("/api/uploadvulnerabilities", {
|
||||
method: "POST",
|
||||
|
@ -90,7 +94,7 @@ const AdminLogPage = () => {
|
|||
|
||||
const deleteLog = async (id: string) => {
|
||||
try {
|
||||
const response = await fetch(`/api/delete/logs/${id}`, {
|
||||
const response = await fetch(`/api/delete/vulnerability/${id}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
if (response.ok) {
|
||||
|
|
|
@ -74,7 +74,7 @@ const Page = ({ params }: { params: { slug: string } }) => {
|
|||
<>
|
||||
<section 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">
|
||||
{page.title}
|
||||
{page.title} Change Log
|
||||
</h1>
|
||||
<ReactMarkdown className="prose max-w-full md:prose-lg dark:prose-invert">
|
||||
{page.content}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import { vulnerabilities } from "@/constants/guidelines";
|
||||
import { VULNERABILITY } from "@/constants/guidelines";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
|
||||
|
@ -11,14 +11,22 @@ interface Bullet {
|
|||
|
||||
interface Vulnerabilities {
|
||||
_id: string;
|
||||
date: string;
|
||||
version: string;
|
||||
bullets?: Bullet[]; // Make bullets optional
|
||||
}
|
||||
|
||||
interface ModsVulnerability {
|
||||
_id: string;
|
||||
title: string;
|
||||
slug: string;
|
||||
content: string;
|
||||
vulnerabilities: string;
|
||||
}
|
||||
|
||||
const Vulnerabilities = () => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [downloads, setDownloads] = useState<Vulnerabilities[]>([]);
|
||||
const [mods, setMods] = useState<ModsVulnerability[]>([]);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const fetchData = async () => {
|
||||
|
@ -29,7 +37,7 @@ const Vulnerabilities = () => {
|
|||
if (response.ok) {
|
||||
const data: Vulnerabilities[] = await response.json();
|
||||
setDownloads(data);
|
||||
return (document.title = "Vulnerabilities | SVRJS");
|
||||
document.title = "Vulnerabilities | SVRJS";
|
||||
} else {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
@ -40,18 +48,43 @@ const Vulnerabilities = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const fetchMods = async () => {
|
||||
try {
|
||||
const response = await fetch(`/api/mdx/pages`, {
|
||||
method: "GET",
|
||||
});
|
||||
if (response.ok) {
|
||||
const data: ModsVulnerability[] = await response.json();
|
||||
// Filter out entries where vulnerabilities is undefined or an empty string
|
||||
const filteredMods = data.filter(
|
||||
(mod) => mod.vulnerabilities && mod.vulnerabilities.trim() !== ""
|
||||
);
|
||||
setMods(filteredMods);
|
||||
document.title = "Vulnerabilities | SVRJS";
|
||||
} else {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
} catch (error: any) {
|
||||
setError(error.message || "Failed to fetch vulnerabilities");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
|
||||
fetchMods();
|
||||
const interval = setInterval(() => {
|
||||
fetchData();
|
||||
fetchMods();
|
||||
}, 10000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
const reversedDownloads = [...downloads].reverse();
|
||||
|
||||
// initially loading = true
|
||||
const reversedDownloads = [...downloads].reverse();
|
||||
const reversedMods = [...mods].reverse();
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<section className="wrapper container py-24 md:py-28 gap-4 flex flex-col">
|
||||
|
@ -98,9 +131,27 @@ const Vulnerabilities = () => {
|
|||
</ul>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="prose max-w-full md:prose-lg dark:prose-invert">
|
||||
<ReactMarkdown>{vulnerabilities}</ReactMarkdown>
|
||||
<ReactMarkdown>{VULNERABILITY}</ReactMarkdown>
|
||||
</div>
|
||||
|
||||
{/* Section with MODS content */}
|
||||
{reversedMods.map((mod) => (
|
||||
<div
|
||||
key={mod._id}
|
||||
className="flex-start flex-col prose dark:prose-invert mb-4 gap-4"
|
||||
>
|
||||
<h2 className="text-3xl md:text-5xl py-1 md:py-2 font-bold text-black dark:bg-clip-text dark:text-transparent dark:bg-gradient-to-b dark:from-white dark:to-neutral-400 -mb-1">
|
||||
{mod.title}
|
||||
</h2>
|
||||
{mod.vulnerabilities && (
|
||||
<div className="prose max-w-full md:prose-lg dark:prose-invert">
|
||||
<ReactMarkdown>{mod.vulnerabilities}</ReactMarkdown>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
|
30
app/api/delete/vulnerability/[id]/route.ts
Normal file
30
app/api/delete/vulnerability/[id]/route.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
// app/api/delete/[id]/route.ts
|
||||
import clientPromise from "@/lib/db";
|
||||
import { ObjectId } from "mongodb";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function DELETE(
|
||||
request: Request,
|
||||
{ params }: { params: { id: string } }
|
||||
) {
|
||||
const { id } = params;
|
||||
|
||||
try {
|
||||
const client = await clientPromise;
|
||||
const db = client.db("downloadsDatabase");
|
||||
const collection = db.collection("vulnerabilities");
|
||||
|
||||
const result = await collection.deleteOne({ _id: new ObjectId(id) });
|
||||
|
||||
if (result.deletedCount === 1) {
|
||||
return NextResponse.json({ message: "Log deleted successfully" });
|
||||
} else {
|
||||
return NextResponse.json({ message: "Log not found" }, { status: 404 });
|
||||
}
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{ message: "Failed to delete log", error: error },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
32
app/api/delete/vulnerability/mods/route.ts
Normal file
32
app/api/delete/vulnerability/mods/route.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import clientPromise from "@/lib/db";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
if (req.method === "POST") {
|
||||
try {
|
||||
const client = await clientPromise;
|
||||
const db = client.db("modsVuln");
|
||||
const { title, category, bullets } = req.body;
|
||||
|
||||
if (!title || !category || !bullets || !Array.isArray(bullets)) {
|
||||
return res.status(400).json({ error: "Invalid data" });
|
||||
}
|
||||
|
||||
await db.collection("vulnerabilities").insertOne({
|
||||
title,
|
||||
category,
|
||||
bullets,
|
||||
});
|
||||
|
||||
res.status(200).json({ message: "Vulnerability added successfully" });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error || "Failed to add vulnerability" });
|
||||
}
|
||||
} else {
|
||||
res.setHeader("Allow", ["POST"]);
|
||||
res.status(405).end(`Method ${req.method} Not Allowed`);
|
||||
}
|
||||
}
|
|
@ -34,57 +34,35 @@ export const PUT = async (
|
|||
return NextResponse.json({ message: "Slug is required" }, { status: 400 });
|
||||
}
|
||||
|
||||
const { title, content } = await req.json();
|
||||
const { title, content, vulnerabilities } = await req.json();
|
||||
|
||||
if (typeof title !== "string" || typeof content !== "string") {
|
||||
if (
|
||||
typeof title !== "string" ||
|
||||
typeof content !== "string" ||
|
||||
typeof vulnerabilities !== "string"
|
||||
) {
|
||||
return NextResponse.json(
|
||||
{ message: "Invalid title or content" },
|
||||
{ message: "Invalid title, content, or vulnerabilities" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// it works here ig
|
||||
const result = await db
|
||||
.collection("pages")
|
||||
.findOneAndUpdate(
|
||||
{ slug },
|
||||
{ $set: { title, content } },
|
||||
{ returnDocument: "after" }
|
||||
);
|
||||
const result = await db.collection("pages").findOneAndUpdate(
|
||||
{ slug },
|
||||
{ $set: { title, content, vulnerabilities } },
|
||||
{ returnDocument: "after" } // Updated option
|
||||
);
|
||||
|
||||
// i hate my life fr fr
|
||||
console.log("Update Result:", result);
|
||||
// result returns like
|
||||
|
||||
// Update Result: {
|
||||
// _id: new ObjectId('66a2946b2b91eef505eef943'),
|
||||
// title: 'TEST PAGE',
|
||||
// slug: 'test-page',
|
||||
// content: 'asd]---\n' +
|
||||
// '---\n' +
|
||||
// '\n' +
|
||||
// 'this is basic heading ?\n' +
|
||||
// '\n' +
|
||||
// '**HELLO**\n' +
|
||||
// '\n' +
|
||||
// 'erw\n' +
|
||||
// '\n' +
|
||||
// 'trying another time for test'
|
||||
// }
|
||||
|
||||
// ERRROR : TypeError: Cannot read properties of undefined (reading '_id')
|
||||
// aposdjaoi sdio JUST WORK NIAWWWWWWWWW
|
||||
|
||||
// if (result && result.value) {
|
||||
const serializedResult = {
|
||||
...result?.value,
|
||||
_id: result?.value._id.toString(), // Convert ObjectId to string
|
||||
};
|
||||
return NextResponse.json(result?.value.content, { status: 200 });
|
||||
// } else {
|
||||
// return NextResponse.json({ message: "Page not found" }, { status: 404 });
|
||||
// }
|
||||
if (result?.value) {
|
||||
const serializedResult = {
|
||||
...result.value,
|
||||
_id: result.value._id.toString(), // Convert ObjectId to string
|
||||
};
|
||||
return NextResponse.json(serializedResult, { status: 200 });
|
||||
} else {
|
||||
return NextResponse.json({ message: "Page not found" }, { status: 404 });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating page:", error);
|
||||
return NextResponse.json(
|
||||
|
|
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
160
components/ui/select.tsx
Normal file
160
components/ui/select.tsx
Normal file
|
@ -0,0 +1,160 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as SelectPrimitive from "@radix-ui/react-select"
|
||||
import { Check, ChevronDown, ChevronUp } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Select = SelectPrimitive.Root
|
||||
|
||||
const SelectGroup = SelectPrimitive.Group
|
||||
|
||||
const SelectValue = SelectPrimitive.Value
|
||||
|
||||
const SelectTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SelectPrimitive.Icon asChild>
|
||||
<ChevronDown className="h-4 w-4 opacity-50" />
|
||||
</SelectPrimitive.Icon>
|
||||
</SelectPrimitive.Trigger>
|
||||
))
|
||||
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
||||
|
||||
const SelectScrollUpButton = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.ScrollUpButton
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronUp className="h-4 w-4" />
|
||||
</SelectPrimitive.ScrollUpButton>
|
||||
))
|
||||
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
|
||||
|
||||
const SelectScrollDownButton = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.ScrollDownButton
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
</SelectPrimitive.ScrollDownButton>
|
||||
))
|
||||
SelectScrollDownButton.displayName =
|
||||
SelectPrimitive.ScrollDownButton.displayName
|
||||
|
||||
const SelectContent = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||
>(({ className, children, position = "popper", ...props }, ref) => (
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
position === "popper" &&
|
||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||
className
|
||||
)}
|
||||
position={position}
|
||||
{...props}
|
||||
>
|
||||
<SelectScrollUpButton />
|
||||
<SelectPrimitive.Viewport
|
||||
className={cn(
|
||||
"p-1",
|
||||
position === "popper" &&
|
||||
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</SelectPrimitive.Viewport>
|
||||
<SelectScrollDownButton />
|
||||
</SelectPrimitive.Content>
|
||||
</SelectPrimitive.Portal>
|
||||
))
|
||||
SelectContent.displayName = SelectPrimitive.Content.displayName
|
||||
|
||||
const SelectLabel = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
SelectLabel.displayName = SelectPrimitive.Label.displayName
|
||||
|
||||
const SelectItem = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<SelectPrimitive.ItemIndicator>
|
||||
<Check className="h-4 w-4" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</span>
|
||||
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
</SelectPrimitive.Item>
|
||||
))
|
||||
SelectItem.displayName = SelectPrimitive.Item.displayName
|
||||
|
||||
const SelectSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
||||
|
||||
export {
|
||||
Select,
|
||||
SelectGroup,
|
||||
SelectValue,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectLabel,
|
||||
SelectItem,
|
||||
SelectSeparator,
|
||||
SelectScrollUpButton,
|
||||
SelectScrollDownButton,
|
||||
}
|
|
@ -357,7 +357,7 @@ By contributing to our project, you agree that your contributions will be licens
|
|||
Thank you for considering contributing to our project!
|
||||
`;
|
||||
|
||||
export const vulnerabilities = `
|
||||
export const VULNERABILITY = `
|
||||
|
||||
### Fixed in SVR.JS 3.15.0 and in SVR.JS 3.14.16 LTS
|
||||
- An attacker could send a HTTP forward proxy request with malformed URL not using CONNECT method to possibly crash the server.
|
||||
|
@ -419,46 +419,6 @@ export const vulnerabilities = `
|
|||
### Fixed in SVR.JS 2.1.1
|
||||
- An attacker could use directory listing to access secret files (through path traversal).
|
||||
|
||||
## easy-waf integration
|
||||
|
||||
### Fixed in easy-waf integration 1.2.4
|
||||
- An attacker could send a very small chunk of the POST request body (which will not trigger WAF) before the payload to bypass the WAF.
|
||||
|
||||
### Fixed in easy-waf integration 1.2.1
|
||||
- An attacker could access the resource on the server with poorly written SVR.JS mod or server-side JavaScript (that normally would invoke 500 Internal Server Error) to crash the server.
|
||||
|
||||
## RedBrick
|
||||
|
||||
### Fixed in RedBrick 2.5.4
|
||||
- An attacker could add HTTP authentication header to the HTTP request when not required to enable web application functionality normally disabled on unauthenticated requests. View the security advisory
|
||||
|
||||
### Fixed in RedBrick 2.3.3
|
||||
- An attacker could use “CGI-BIN” instead of “cgi-bin” to leak source code, while SVR.JS with RedBrick is running on Windows. View the security advisory
|
||||
- An attacker could leak RedBrick interpreter settings, while SVR.JS with RedBrick is running on Windows. View the security advisory
|
||||
|
||||
## reverse-proxy-mod
|
||||
|
||||
### Fixed in reverse-proxy-mod 1.1.2
|
||||
- An attacker could hack the upstream server, replace the web server or application with one that sends an invalid HTTP response code, and make a request to the hacked server through the reverse proxy to crash the reverse proxy server.
|
||||
|
||||
### Fixed in reverse-proxy-mod 1.0.4
|
||||
- An attacker could leak reverse proxy configuration file. View the security advisory
|
||||
|
||||
## OrangeCircle
|
||||
|
||||
### Fixed in OrangeCircle 1.1.2
|
||||
- An attacker could add HTTP authentication header to the HTTP request when not required to enable web application functionality normally disabled on unauthenticated requests. View the security advisory
|
||||
|
||||
### Fixed in OrangeCircle 1.0.2
|
||||
- An attacker could leak OrangeCircle configuration, while SVR.JS with YellowSquare is running on Windows. View the security advisory
|
||||
|
||||
## YellowSquare
|
||||
|
||||
### Fixed in YellowSquare 1.1.2
|
||||
- An attacker could add HTTP authentication header to the HTTP request when not required to enable web application functionality normally disabled on unauthenticated requests. View the security advisory
|
||||
|
||||
### Fixed in YellowSquare 1.0.1
|
||||
- An attacker could use “JSGI-BIN” instead of “jsgi-bin” to leak source code, while SVR.JS with YellowSquare is running on Windows. View the security advisory
|
||||
`;
|
||||
|
||||
export const CHANGE_LOGS = `
|
||||
|
|
|
@ -24,7 +24,28 @@ export const logsSchema = z.object({
|
|||
),
|
||||
});
|
||||
|
||||
export const vulnerabilitiesSchema = z.object({
|
||||
version: z.string(),
|
||||
bullets: z.array(
|
||||
z.object({
|
||||
point: z.string(),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
export const ModsVulnerabilities = z.object({
|
||||
title: z.string(),
|
||||
category: z.string(),
|
||||
bullets: z.array(
|
||||
z.object({
|
||||
point: z.string(),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
export type LogsFormValues = z.infer<typeof logsSchema>;
|
||||
export type VulnerabiltiesForm = z.infer<typeof vulnerabilitiesSchema>;
|
||||
export type ModsVulnerability = z.infer<typeof ModsVulnerabilities>;
|
||||
|
||||
// Contact Page
|
||||
|
||||
|
|
|
@ -13,9 +13,10 @@
|
|||
"@hookform/resolvers": "^3.6.0",
|
||||
"@radix-ui/react-accordion": "^1.1.2",
|
||||
"@radix-ui/react-dialog": "^1.1.1",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
"@radix-ui/react-navigation-menu": "^1.1.4",
|
||||
"@radix-ui/react-select": "^2.1.1",
|
||||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-toast": "^1.2.1",
|
||||
|
|
Loading…
Reference in a new issue