feat: update dependencies and enhance privacy policy

Replace "@vercel/analytics" with "@tryghost/content-api" and add 
"node-fetch" to dependencies. Remove "@vercel/speed-insights" to 
streamline the package. Update robots.txt to dis access to 
"/legal-notice" and "/privacy-policy". Change <p> tags to <div> in 
the Privacy Policy for better structure. Update the last modified 
date in the Legal Notice. Add a new API route for fetching images 
with error handling for missing URL parameters.
This commit is contained in:
2025-02-12 12:59:15 +01:00
parent 433a3c6d58
commit 635c244c06
14 changed files with 665 additions and 329 deletions

View File

@@ -1,28 +1,36 @@
// app/Projects/[slug]/page.tsx
"use client";
import {useParams, useRouter} from "next/navigation";
import {
useRouter,
useSearchParams,
useParams,
usePathname,
} from "next/navigation";
import { useEffect, useState } from "react";
import Header from "../../components/Header";
import Footer_Back from "../../components/Footer_Back";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import rehypeRaw from "rehype-raw";
import matter from "gray-matter";
import Footer_Back from "@/app/components/Footer_Back";
import Header from "@/app/components/Header";
import Image from "next/image";
import "@/app/styles/ghostContent.css"; // Import the global styles
interface Project {
slug: string;
id: string;
title: string;
description: string;
text: string;
slug: string;
image: string;
feature_image: string;
visibility: string;
published_at: string;
updated_at: string;
html: string;
reading_time: number;
meta_description: string;
}
export default function ProjectDetail() {
const params = useParams();
const ProjectDetails = () => {
const router = useRouter();
const {slug} = params as { slug: string };
const searchParams = useSearchParams();
const params = useParams();
const pathname = usePathname();
const [project, setProject] = useState<Project | null>(null);
const [isVisible, setIsVisible] = useState(false);
@@ -33,57 +41,87 @@ export default function ProjectDetail() {
}, []);
useEffect(() => {
if (slug) {
fetch(`/projects/${slug}.md`)
.then((res) => res.text())
.then((content) => {
const {data, content: markdownContent} = matter(content);
setProject({
id: data.id,
title: data.title,
description: data.description,
text: markdownContent,
slug: slug,
image: data.image,
});
// Log the project view
fetch("/api/stats", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
type: "project_view",
projectId: data.id,
}),
}).catch((err) => console.error("Failed to log project view", err));
})
.catch(() => {
// Redirect to 404 if project not found
router.replace("/not-found");
});
const projectData = searchParams.get("project");
if (projectData) {
setProject(JSON.parse(projectData));
// Remove the project data from the URL without reloading the page
const url = new URL(window.location.href);
url.searchParams.delete("project");
window.history.replaceState({}, "", url.toString());
} else {
// Fetch project data based on slug from URL
const slug = params.slug as string;
fetchProjectData(slug);
}
}, [slug, router]);
}, [searchParams, router, params, pathname]);
const fetchProjectData = async (slug: string) => {
try {
const response = await fetch(`/api/fetchProject?slug=${slug}`);
if (!response.ok) {
throw new Error("Failed to fetch project data");
}
const projectData = await response.json();
setProject(projectData.posts[0]); // Assuming the API returns an array of posts
} catch (error) {
console.error("Failed to fetch project data:", error);
}
};
if (!project) {
return <div className="p-10 text-center">Loading...</div>;
return (
<div className="min-h-screen flex flex-col bg-radiant">
<Header />
<div className="flex-grow flex items-center justify-center">
<div className="loader ease-linear rounded-full border-8 border-t-8 border-gray-200 h-32 w-32"></div>
</div>
<Footer_Back />
</div>
);
}
const featureImageUrl = `/api/fetchImage?url=${encodeURIComponent(project.feature_image)}`;
return (
<div className={`min-h-screen flex flex-col bg-radiant ${isVisible ? 'animate-fly-in' : 'opacity-0'}`}>
<Header/>
<div className="flex-grow p-10 pt-24">
<div
className="flex flex-col p-8 bg-gradient-to-br from-white/60 to-white/30 backdrop-blur-lg rounded-2xl shadow-xl">
<div className="mt-4 text-gray-600 dark:text-gray-300 markdown">
<ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
{project.text}
</ReactMarkdown>
className={`min-h-screen flex flex-col bg-radiant ${isVisible ? "animate-fly-in" : "opacity-0"}`}
>
<Header />
<div className="flex-grow">
{/* Hero Section */}
<div className="flex justify-center md:mt-28 px-4 md:px-0">
<div className="relative w-full max-w-4xl h-0 pb-[56.25%] rounded-2xl overflow-hidden">
{" "}
{/* 16:9 Aspect Ratio */}
<Image
src={featureImageUrl}
alt={project.title}
fill
style={{ objectFit: "cover" }}
className="rounded-2xl"
priority={true}
/>
</div>
</div>
<div className="flex items-center justify-center mt-4">
<h1 className="text-4xl md:text-6xl font-bold text-white">
{project.title}
</h1>
</div>
{/* Project Content */}
<div className="p-10 pt-12">
<div className="flex flex-col p-8 bg-gradient-to-br from-white/60 to-white/30 backdrop-blur-lg rounded-2xl shadow-xl">
<div
className="content mt-4 text-gray-600 text-lg leading-relaxed"
dangerouslySetInnerHTML={{ __html: project.html }}
></div>
</div>
</div>
</div>
<Footer_Back />
</div>
);
}
};
export default ProjectDetails;

View File

@@ -0,0 +1,25 @@
import { NextResponse } from "next/server";
export const runtime = "nodejs"; // Force Node runtime
const GHOST_API_URL = "http://192.168.179.31:2368";
const GHOST_API_KEY = "067b8434f2e7f2a771dfcc45a7"; // Replace with your actual key
export async function GET() {
try {
const response = await fetch(
`${GHOST_API_URL}/ghost/api/content/posts/?key=${GHOST_API_KEY}&limit=all`,
);
if (!response.ok) {
throw new Error(`Failed to fetch posts: ${response.statusText}`);
}
const posts = await response.json();
return NextResponse.json(posts);
} catch (error) {
console.error("Failed to fetch posts from Ghost:", error);
return NextResponse.json(
{ error: "Failed to fetch projects" },
{ status: 500 },
);
}
}

View File

@@ -0,0 +1,35 @@
import { NextRequest, NextResponse } from "next/server";
export async function GET(req: NextRequest) {
const { searchParams } = new URL(req.url);
const url = searchParams.get("url");
if (!url) {
return NextResponse.json(
{ error: "Missing URL parameter" },
{ status: 400 },
);
}
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch image: ${response.statusText}`);
}
const contentType = response.headers.get("content-type");
const buffer = await response.arrayBuffer();
return new NextResponse(buffer, {
headers: {
"Content-Type": contentType || "application/octet-stream",
},
});
} catch (error) {
console.error("Failed to fetch image:", error);
return NextResponse.json(
{ error: "Failed to fetch image" },
{ status: 500 },
);
}
}

View File

@@ -0,0 +1,32 @@
import { NextResponse } from "next/server";
export const runtime = "nodejs"; // Force Node runtime
const GHOST_API_URL = "http://192.168.179.31:2368";
const GHOST_API_KEY = "067b8434f2e7f2a771dfcc45a7"; // Replace with your actual key
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const slug = searchParams.get("slug");
if (!slug) {
return NextResponse.json({ error: "Slug is required" }, { status: 400 });
}
try {
const response = await fetch(
`${GHOST_API_URL}/ghost/api/content/posts/slug/${slug}/?key=${GHOST_API_KEY}`,
);
if (!response.ok) {
throw new Error(`Failed to fetch post: ${response.statusText}`);
}
const post = await response.json();
return NextResponse.json(post);
} catch (error) {
console.error("Failed to fetch post from Ghost:", error);
return NextResponse.json(
{ error: "Failed to fetch project" },
{ status: 500 },
);
}
}

View File

@@ -12,10 +12,11 @@ export default function Hero() {
}, []);
return (
<div id="about"
className={`flex flex-col md:flex-row items-center justify-center pt-16 pb-16 px-6 text-gray-700 ${isVisible ? 'animate-fly-in' : 'opacity-0'}`}>
<div
className="flex flex-col items-center p-8 bg-gradient-to-br from-white/60 to-white/30 backdrop-blur-lg rounded-2xl shadow-xl max-w-lg text-center">
id="about"
className={`flex flex-col md:flex-row items-center justify-center pt-16 pb-16 px-6 text-gray-700 ${isVisible ? "animate-fly-in" : "opacity-0"}`}
>
<div className="flex flex-col items-center p-8 bg-gradient-to-br from-white/60 to-white/30 backdrop-blur-lg rounded-2xl shadow-xl max-w-lg text-center">
<h1 className="text-4xl md:text-5xl font-extrabold text-gray-900">
Hi, Im Dennis
</h1>
@@ -27,11 +28,12 @@ export default function Hero() {
</h3>
<p className="mt-6 text-gray-800 text-lg leading-relaxed">
Passionate about technology, coding, and solving real-world problems.
I enjoy building innovative solutions and continuously expanding my knowledge.
I enjoy building innovative solutions and continuously expanding my
knowledge.
</p>
<p className="mt-4 text-gray-700 text-base">
Currently working on exciting projects that merge creativity with functionality.
Always eager to learn and collaborate!
Currently working on exciting projects that merge creativity with
functionality. Always eager to learn and collaborate!
</p>
</div>
<div className="flex mt-8 md:mt-0 md:ml-12">
@@ -41,6 +43,8 @@ export default function Hero() {
width={400}
height={400}
className="rounded-2xl shadow-lg shadow-gray-700 object-cover"
priority={true}
style={{ width: "auto", height: "400px" }}
/>
</div>
</div>

View File

@@ -2,10 +2,16 @@ import React, {useEffect, useState} from "react";
import Link from "next/link";
interface Project {
slug: string;
id: string;
title: string;
description: string;
slug: string;
feature_image: string;
visibility: string;
published_at: string;
updated_at: string;
html: string;
reading_time: number;
meta_description: string;
}
export default function Projects() {
@@ -15,16 +21,13 @@ export default function Projects() {
useEffect(() => {
const fetchProjects = async () => {
try {
const response = await fetch('/api/projects');
try {
const response = await fetch("/api/fetchAllProjects");
if (!response.ok) {
throw new Error("Failed to fetch projects");
}
} catch (error) {
console.error("Failed to fetch projects:", error);
throw new Error("Failed to fetch projects from Ghost");
}
const projectsData = await response.json();
setProjects(projectsData);
setProjects(projectsData.posts);
setTimeout(() => {
setIsVisible(true);
}, 250); // Delay to start the animation after Hero
@@ -32,37 +35,45 @@ export default function Projects() {
console.error("Failed to fetch projects:", error);
}
};
fetchProjects().then(r => r);
fetchProjects();
}, []);
const numberOfProjects = projects.length;
console.log(projects.at(0)?.feature_image);
const numberOfProjects = projects.length;
return (
<section id="projects" className={`p-10 ${isVisible ? 'animate-fly-in' : 'opacity-0'}`}>
<h2 className="text-3xl font-bold text-center text-gray-800">
Projects
</h2>
<section
id="projects"
className={`p-10 ${isVisible ? "animate-fly-in" : "opacity-0"}`}
>
<h2 className="text-3xl font-bold text-center text-gray-800">Projects</h2>
<div className="mt-6 grid grid-cols-1 md:grid-cols-2 gap-6">
{projects.map((project, index) => (
<Link key={project.id} href={`/Projects/${project.slug}`} className="cursor-pointer">
<div className={`p-4 border shadow-lg bg-white/45 rounded-2xl animate-fly-in`}
style={{animationDelay: `${index * 0.1}s`}}>
<Link
key={project.id}
href={{
pathname: `/Projects/${project.slug}`,
query: { project: JSON.stringify(project) },
}}
className="cursor-pointer"
>
<div
className={`p-4 border shadow-lg bg-white/45 rounded-2xl animate-fly-in`}
style={{ animationDelay: `${index * 0.1}s` }}
>
<h3 className="text-2xl font-bold text-gray-800">
{project.title}
</h3>
<p className="mt-2 text-gray-500">
{project.description}
</p>
<p className="mt-2 text-gray-500">{project.meta_description}</p>
</div>
</Link>
))}
<div className={`p-4 border shadow-lg bg-white/45 rounded-2xl animate-fly-in`}
style={{animationDelay: `${(numberOfProjects + 1) * 0.1}s`}}>
<h3 className="text-2xl font-bold text-gray-800">
More to come
</h3>
<p className="mt-2 text-gray-500">
...
</p>
<div
className={`p-4 border shadow-lg bg-white/45 rounded-2xl animate-fly-in`}
style={{ animationDelay: `${(numberOfProjects + 1) * 0.1}s` }}
>
<h3 className="text-2xl font-bold text-gray-800">More to come</h3>
<p className="mt-2 text-gray-500">...</p>
</div>
</div>
</section>

View File

@@ -4,15 +4,14 @@
import "./globals.css";
import {Roboto} from 'next/font/google';
import { Roboto } from "next/font/google";
import React from "react";
//import ClientCookieConsentBanner from "./components/ClientCookieConsentBanner";
const roboto = Roboto({
variable: '--font-roboto',
weight: '400',
subsets: ['latin'],
variable: "--font-roboto",
weight: "400",
subsets: ["latin"],
});
export default function RootLayout({
@@ -20,13 +19,17 @@ export default function RootLayout({
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<script defer src="https://umami.denshooter.de/script.js" data-website-id="1f213877-deef-4238-8df1-71a5a3bcd142"></script>
<body className={roboto.variable}>
{children}
</body>
<head>
<script
defer
src="https://umami.denshooter.de/script.js"
data-website-id="1f213877-deef-4238-8df1-71a5a3bcd142"
></script>
<meta charSet="utf-8" />
</head>
<body className={roboto.variable}>{children}</body>
</html>
);
}

View File

@@ -27,11 +27,6 @@ export default function LegalNotice() {
info@dki.one
</Link>{" "}
<br />
<strong>Telefon:</strong>{" "}
<Link href={"tel:+4917612669990"} className="transition-underline">
+49 176 12669990
</Link>
<br />
<strong>Website:</strong>{" "}
<Link href={"https://www.dki.one"} className="transition-underline">
{" "}
@@ -44,7 +39,10 @@ export default function LegalNotice() {
Meine Website enthält Links auf externe Websites. Ich habe keinen
Einfluss auf die Inhalte dieser Websites und kann daher keine Gewähr
übernehmen. Für die Inhalte der verlinkten Seiten ist stets der
Betreiber oder Anbieter der Seiten verantwortlich.
Betreiber oder Anbieter der Seiten verantwortlich. Jedoch überprüfe
ich die verlinkten Seiten zum Zeitpunkt der Verlinkung auf mögliche
Rechtsverstöße. Bei Bekanntwerden von Rechtsverletzungen werde ich
derartige Links umgehend entfernen.
</p>
<h2 className="text-2xl font-semibold mt-6">Urheberrecht</h2>
@@ -60,7 +58,7 @@ export default function LegalNotice() {
Diensteanbieter kann ich keine Gewähr übernehmen für Schäden, die
entstehen können, durch den Zugriff oder die Nutzung dieser Website.
</p>
<p className="font-semibold mt-6">Letzte Aktualisierung: 11.02.2025</p>
<p className="font-semibold mt-6">Letzte Aktualisierung: 12.02.2025</p>
</main>
<Footer_Back />
</div>

View File

@@ -10,7 +10,6 @@ import Script from "next/script";
export default function Home() {
return (
<div className="min-h-screen flex flex-col bg-radiant-animated">
<Script
id={"structured-data"}
@@ -19,15 +18,15 @@ export default function Home() {
__html: JSON.stringify({
"@context": "https://schema.org",
"@type": "Person",
"name": "Dennis Konkol",
"url": "https://dki.one",
"jobTitle": "Software Engineer",
"address": {
name: "Dennis Konkol",
url: "https://dki.one",
jobTitle: "Software Engineer",
address: {
"@type": "PostalAddress",
"addressLocality": "Osnabrück",
"addressCountry": "Germany",
addressLocality: "Osnabrück",
addressCountry: "Germany",
},
"sameAs": [
sameAs: [
"https://github.com/Denshooter",
"https://linkedin.com/in/dkonkol",
],

View File

@@ -39,11 +39,6 @@ export default function PrivacyPolicy() {
info@dki.one
</Link>{" "}
<br />
<strong>Telefon:</strong>{" "}
<Link className="transition-underline" href={"tel:+4917612669990"}>
+49 176 12669990
</Link>
<br />
<strong>Website:</strong>{" "}
<Link className="transition-underline" href={"https://www.dki.one"}>
{" "}
@@ -57,7 +52,7 @@ export default function PrivacyPolicy() {
<h2 className="text-2xl font-semibold mt-6">
Erfassung allgemeiner Informationen beim Besuch meiner Website
</h2>
<p className="mt-2">
<div className="mt-2">
Beim Zugriff auf meiner Website werden automatisch Informationen
allgemeiner Natur erfasst. Diese beinhalten unter anderem:
<ul className="list-disc list-inside mt-2">
@@ -76,7 +71,7 @@ export default function PrivacyPolicy() {
<li>die Inhalte meiner Website zu optimieren,</li>
<li>die Systemsicherheit und -stabilität zu analysiern.</li>
</ul>
</p>
</div>
<h2 className="text-2xl font-semibold mt-6">Cookies</h2>
<p className="mt-2">
Meine Website verwendet keine Cookies. Daher ist kein
@@ -129,7 +124,7 @@ export default function PrivacyPolicy() {
jeweiligen Anbieter.
</p>
<h2 className="text-2xl font-semibold mt-6">Weitergabe von Daten</h2>
<p className="mt-2">
<div className="mt-2">
Eine Weitergabe Ihrer personenbezogenen Daten erfolgt nur, wenn:
<ul className="list-disc list-inside mt-2">
<li>
@@ -149,7 +144,7 @@ export default function PrivacyPolicy() {
berechtigter Interessen erforderlich ist.
</li>
</ul>
</p>
</div>
<h2 className="text-2xl font-semibold mt-6">
Speicherdauer und Löschung
</h2>
@@ -159,7 +154,7 @@ export default function PrivacyPolicy() {
werden Ihre Daten gelöscht.
</p>
<h2 className="text-2xl font-semibold mt-6">Ihre Rechte</h2>
<p className="mt-2">
<div className="mt-2">
Sie haben gemäß DSGVO folgende Rechte:
<ul className="list-disc list-inside mt-2">
<li>
@@ -198,7 +193,7 @@ export default function PrivacyPolicy() {
>
https://www.bfdi.bund.de/
</Link>
</p>
</div>
<h2 className="text-2xl font-semibold mt-6">Datensicherheit</h2>
<p className="mt-2">
Ich setze technische und organisatorische Maßnahmen ein, um Ihre Daten
@@ -226,7 +221,7 @@ export default function PrivacyPolicy() {
berücksichtigen. Die jeweils aktuelle Datenschutzerklärung finden Sie
auf meiner Website.
</p>
<p className="mt-6 font-bold">Letzte Aktualisierung: 11.02.2025</p>
<p className="mt-6 font-bold">Letzte Aktualisierung: 12.02.2025</p>
</main>
<Footer_Back />
</div>

View File

@@ -0,0 +1,70 @@
/* ghostContent.css */
.content {
font-family: "Arial", sans-serif;
line-height: 1.6;
color: #333;
}
.content h1,
.content h2,
.content h3,
.content h4,
.content h5,
.content h6 {
color: #222;
margin-top: 1.5em;
margin-bottom: 0.5em;
}
.content p {
margin-bottom: 1em;
}
.content img {
max-width: 100%;
height: auto;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
margin: 1em 0;
}
.content ul,
.content ol {
margin: 1em 0;
padding-left: 1.5em;
}
.content a {
color: #0070f3;
text-decoration: none;
}
.content a:hover {
text-decoration: underline;
}
.content blockquote {
border-left: 4px solid #ddd;
padding-left: 1em;
color: #666;
margin: 1em 0;
}
.content pre {
background: #f5f5f5;
padding: 1em;
border-radius: 8px;
overflow-x: auto;
}
.loader {
border-top-color: #3498db;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}

275
package-lock.json generated
View File

@@ -9,12 +9,12 @@
"version": "0.1.0",
"dependencies": {
"@prisma/client": "^6.1.0",
"@vercel/analytics": "^1.4.1",
"@tryghost/content-api": "^1.11.21",
"@vercel/og": "^0.6.5",
"@vercel/speed-insights": "^1.1.0",
"dotenv": "^16.4.7",
"gray-matter": "^4.0.3",
"next": "15.1.3",
"node-fetch": "^3.3.2",
"nodemailer": "^6.10.0",
"prisma": "^6.1.0",
"react": "^19.0.0",
@@ -1010,6 +1010,15 @@
"tslib": "^2.8.0"
}
},
"node_modules/@tryghost/content-api": {
"version": "1.11.21",
"resolved": "https://registry.npmjs.org/@tryghost/content-api/-/content-api-1.11.21.tgz",
"integrity": "sha512-ozJqEMHDUO7D0SGxPbUnG+RvwBbzC3zmdGOW8cFvkcKzrhe7uOAmVKyq7/J3kRAM2QthTlmiDpqp7NEo9ZLlKg==",
"license": "MIT",
"dependencies": {
"axios": "^1.0.0"
}
},
"node_modules/@types/debug": {
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
@@ -1359,44 +1368,6 @@
"integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
"license": "ISC"
},
"node_modules/@vercel/analytics": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.4.1.tgz",
"integrity": "sha512-ekpL4ReX2TH3LnrRZTUKjHHNpNy9S1I7QmS+g/RQXoSUQ8ienzosuX7T9djZ/s8zPhBx1mpHP/Rw5875N+zQIQ==",
"license": "MPL-2.0",
"peerDependencies": {
"@remix-run/react": "^2",
"@sveltejs/kit": "^1 || ^2",
"next": ">= 13",
"react": "^18 || ^19 || ^19.0.0-rc",
"svelte": ">= 4",
"vue": "^3",
"vue-router": "^4"
},
"peerDependenciesMeta": {
"@remix-run/react": {
"optional": true
},
"@sveltejs/kit": {
"optional": true
},
"next": {
"optional": true
},
"react": {
"optional": true
},
"svelte": {
"optional": true
},
"vue": {
"optional": true
},
"vue-router": {
"optional": true
}
}
},
"node_modules/@vercel/og": {
"version": "0.6.5",
"resolved": "https://registry.npmjs.org/@vercel/og/-/og-0.6.5.tgz",
@@ -1411,41 +1382,6 @@
"node": ">=16"
}
},
"node_modules/@vercel/speed-insights": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@vercel/speed-insights/-/speed-insights-1.1.0.tgz",
"integrity": "sha512-rAXxuhhO4mlRGC9noa5F7HLMtGg8YF1zAN6Pjd1Ny4pII4cerhtwSG4vympbCl+pWkH7nBS9kVXRD4FAn54dlg==",
"hasInstallScript": true,
"license": "Apache-2.0",
"peerDependencies": {
"@sveltejs/kit": "^1 || ^2",
"next": ">= 13",
"react": "^18 || ^19 || ^19.0.0-rc",
"svelte": ">= 4",
"vue": "^3",
"vue-router": "^4"
},
"peerDependenciesMeta": {
"@sveltejs/kit": {
"optional": true
},
"next": {
"optional": true
},
"react": {
"optional": true
},
"svelte": {
"optional": true
},
"vue": {
"optional": true
},
"vue-router": {
"optional": true
}
}
},
"node_modules/acorn": {
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
@@ -1734,6 +1670,12 @@
"node": ">= 0.4"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@@ -1760,6 +1702,17 @@
"node": ">=4"
}
},
"node_modules/axios": {
"version": "1.7.9",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axobject-query": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@@ -2098,6 +2051,18 @@
"simple-swizzle": "^0.2.2"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/comma-separated-tokens": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
@@ -2207,6 +2172,15 @@
"dev": true,
"license": "BSD-2-Clause"
},
"node_modules/data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
"license": "MIT",
"engines": {
"node": ">= 12"
}
},
"node_modules/data-view-buffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
@@ -2334,6 +2308,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -3208,6 +3191,29 @@
"reusify": "^1.0.4"
}
},
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"license": "MIT",
"dependencies": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"node_modules/fflate": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz",
@@ -3278,6 +3284,26 @@
"dev": true,
"license": "ISC"
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/for-each": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz",
@@ -3311,6 +3337,32 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/form-data": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"license": "MIT",
"dependencies": {
"fetch-blob": "^3.1.2"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -5550,6 +5602,27 @@
"node": ">=8.6"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -5708,6 +5781,43 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"license": "MIT",
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
"license": "MIT",
"dependencies": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/nodemailer": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz",
@@ -6301,6 +6411,12 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -7817,6 +7933,15 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/web-streams-polyfill": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
"license": "MIT",
"engines": {
"node": ">= 8"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

View File

@@ -10,12 +10,12 @@
},
"dependencies": {
"@prisma/client": "^6.1.0",
"@vercel/analytics": "^1.4.1",
"@tryghost/content-api": "^1.11.21",
"@vercel/og": "^0.6.5",
"@vercel/speed-insights": "^1.1.0",
"dotenv": "^16.4.7",
"gray-matter": "^4.0.3",
"next": "15.1.3",
"node-fetch": "^3.3.2",
"nodemailer": "^6.10.0",
"prisma": "^6.1.0",
"react": "^19.0.0",

View File

@@ -1,3 +1,4 @@
User-agent: *
Allow: /
Disallow: ['/legal-notice', '/privacy-policy']
Sitemap: https://dki.one/sitemap.xml