Merge pull request #11 from Denshooter/d-branch-1

feat: update dependencies and enhance privacy policy
This commit is contained in:
Denshooter
2025-02-12 12:59:56 +01:00
committed by GitHub
14 changed files with 665 additions and 329 deletions

View File

@@ -1,28 +1,36 @@
// app/Projects/[slug]/page.tsx
"use client"; "use client";
import {useParams, useRouter} from "next/navigation"; import {
useRouter,
useSearchParams,
useParams,
usePathname,
} from "next/navigation";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Header from "../../components/Header";
import Footer_Back from "../../components/Footer_Back"; import Footer_Back from "@/app/components/Footer_Back";
import ReactMarkdown from "react-markdown"; import Header from "@/app/components/Header";
import remarkGfm from "remark-gfm"; import Image from "next/image";
import rehypeRaw from "rehype-raw"; import "@/app/styles/ghostContent.css"; // Import the global styles
import matter from "gray-matter";
interface Project { interface Project {
slug: string;
id: string; id: string;
title: string; title: string;
description: string; feature_image: string;
text: string; visibility: string;
slug: string; published_at: string;
image: string; updated_at: string;
html: string;
reading_time: number;
meta_description: string;
} }
export default function ProjectDetail() { const ProjectDetails = () => {
const params = useParams();
const router = useRouter(); 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 [project, setProject] = useState<Project | null>(null);
const [isVisible, setIsVisible] = useState(false); const [isVisible, setIsVisible] = useState(false);
@@ -33,57 +41,87 @@ export default function ProjectDetail() {
}, []); }, []);
useEffect(() => { useEffect(() => {
if (slug) { const projectData = searchParams.get("project");
fetch(`/projects/${slug}.md`) if (projectData) {
.then((res) => res.text()) setProject(JSON.parse(projectData));
.then((content) => { // Remove the project data from the URL without reloading the page
const {data, content: markdownContent} = matter(content); const url = new URL(window.location.href);
setProject({ url.searchParams.delete("project");
id: data.id, window.history.replaceState({}, "", url.toString());
title: data.title, } else {
description: data.description, // Fetch project data based on slug from URL
text: markdownContent, const slug = params.slug as string;
slug: slug, fetchProjectData(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");
});
} }
}, [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) { 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 ( 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 <div
className="flex flex-col p-8 bg-gradient-to-br from-white/60 to-white/30 backdrop-blur-lg rounded-2xl shadow-xl"> className={`min-h-screen flex flex-col bg-radiant ${isVisible ? "animate-fly-in" : "opacity-0"}`}
<div className="mt-4 text-gray-600 dark:text-gray-300 markdown"> >
<ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}> <Header />
{project.text} <div className="flex-grow">
</ReactMarkdown> {/* 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> </div>
</div> </div>
<Footer_Back /> <Footer_Back />
</div> </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 ( 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 <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"> <h1 className="text-4xl md:text-5xl font-extrabold text-gray-900">
Hi, Im Dennis Hi, Im Dennis
</h1> </h1>
@@ -27,11 +28,12 @@ export default function Hero() {
</h3> </h3>
<p className="mt-6 text-gray-800 text-lg leading-relaxed"> <p className="mt-6 text-gray-800 text-lg leading-relaxed">
Passionate about technology, coding, and solving real-world problems. 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>
<p className="mt-4 text-gray-700 text-base"> <p className="mt-4 text-gray-700 text-base">
Currently working on exciting projects that merge creativity with functionality. Currently working on exciting projects that merge creativity with
Always eager to learn and collaborate! functionality. Always eager to learn and collaborate!
</p> </p>
</div> </div>
<div className="flex mt-8 md:mt-0 md:ml-12"> <div className="flex mt-8 md:mt-0 md:ml-12">
@@ -41,6 +43,8 @@ export default function Hero() {
width={400} width={400}
height={400} height={400}
className="rounded-2xl shadow-lg shadow-gray-700 object-cover" className="rounded-2xl shadow-lg shadow-gray-700 object-cover"
priority={true}
style={{ width: "auto", height: "400px" }}
/> />
</div> </div>
</div> </div>

View File

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

View File

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

View File

@@ -27,11 +27,6 @@ export default function LegalNotice() {
info@dki.one info@dki.one
</Link>{" "} </Link>{" "}
<br /> <br />
<strong>Telefon:</strong>{" "}
<Link href={"tel:+4917612669990"} className="transition-underline">
+49 176 12669990
</Link>
<br />
<strong>Website:</strong>{" "} <strong>Website:</strong>{" "}
<Link href={"https://www.dki.one"} className="transition-underline"> <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 Meine Website enthält Links auf externe Websites. Ich habe keinen
Einfluss auf die Inhalte dieser Websites und kann daher keine Gewähr Einfluss auf die Inhalte dieser Websites und kann daher keine Gewähr
übernehmen. Für die Inhalte der verlinkten Seiten ist stets der ü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> </p>
<h2 className="text-2xl font-semibold mt-6">Urheberrecht</h2> <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 Diensteanbieter kann ich keine Gewähr übernehmen für Schäden, die
entstehen können, durch den Zugriff oder die Nutzung dieser Website. entstehen können, durch den Zugriff oder die Nutzung dieser Website.
</p> </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> </main>
<Footer_Back /> <Footer_Back />
</div> </div>

View File

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

View File

@@ -39,11 +39,6 @@ export default function PrivacyPolicy() {
info@dki.one info@dki.one
</Link>{" "} </Link>{" "}
<br /> <br />
<strong>Telefon:</strong>{" "}
<Link className="transition-underline" href={"tel:+4917612669990"}>
+49 176 12669990
</Link>
<br />
<strong>Website:</strong>{" "} <strong>Website:</strong>{" "}
<Link className="transition-underline" href={"https://www.dki.one"}> <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"> <h2 className="text-2xl font-semibold mt-6">
Erfassung allgemeiner Informationen beim Besuch meiner Website Erfassung allgemeiner Informationen beim Besuch meiner Website
</h2> </h2>
<p className="mt-2"> <div className="mt-2">
Beim Zugriff auf meiner Website werden automatisch Informationen Beim Zugriff auf meiner Website werden automatisch Informationen
allgemeiner Natur erfasst. Diese beinhalten unter anderem: allgemeiner Natur erfasst. Diese beinhalten unter anderem:
<ul className="list-disc list-inside mt-2"> <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 Inhalte meiner Website zu optimieren,</li>
<li>die Systemsicherheit und -stabilität zu analysiern.</li> <li>die Systemsicherheit und -stabilität zu analysiern.</li>
</ul> </ul>
</p> </div>
<h2 className="text-2xl font-semibold mt-6">Cookies</h2> <h2 className="text-2xl font-semibold mt-6">Cookies</h2>
<p className="mt-2"> <p className="mt-2">
Meine Website verwendet keine Cookies. Daher ist kein Meine Website verwendet keine Cookies. Daher ist kein
@@ -129,7 +124,7 @@ export default function PrivacyPolicy() {
jeweiligen Anbieter. jeweiligen Anbieter.
</p> </p>
<h2 className="text-2xl font-semibold mt-6">Weitergabe von Daten</h2> <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: Eine Weitergabe Ihrer personenbezogenen Daten erfolgt nur, wenn:
<ul className="list-disc list-inside mt-2"> <ul className="list-disc list-inside mt-2">
<li> <li>
@@ -149,7 +144,7 @@ export default function PrivacyPolicy() {
berechtigter Interessen erforderlich ist. berechtigter Interessen erforderlich ist.
</li> </li>
</ul> </ul>
</p> </div>
<h2 className="text-2xl font-semibold mt-6"> <h2 className="text-2xl font-semibold mt-6">
Speicherdauer und Löschung Speicherdauer und Löschung
</h2> </h2>
@@ -159,7 +154,7 @@ export default function PrivacyPolicy() {
werden Ihre Daten gelöscht. werden Ihre Daten gelöscht.
</p> </p>
<h2 className="text-2xl font-semibold mt-6">Ihre Rechte</h2> <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: Sie haben gemäß DSGVO folgende Rechte:
<ul className="list-disc list-inside mt-2"> <ul className="list-disc list-inside mt-2">
<li> <li>
@@ -198,7 +193,7 @@ export default function PrivacyPolicy() {
> >
https://www.bfdi.bund.de/ https://www.bfdi.bund.de/
</Link> </Link>
</p> </div>
<h2 className="text-2xl font-semibold mt-6">Datensicherheit</h2> <h2 className="text-2xl font-semibold mt-6">Datensicherheit</h2>
<p className="mt-2"> <p className="mt-2">
Ich setze technische und organisatorische Maßnahmen ein, um Ihre Daten 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 berücksichtigen. Die jeweils aktuelle Datenschutzerklärung finden Sie
auf meiner Website. auf meiner Website.
</p> </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> </main>
<Footer_Back /> <Footer_Back />
</div> </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", "version": "0.1.0",
"dependencies": { "dependencies": {
"@prisma/client": "^6.1.0", "@prisma/client": "^6.1.0",
"@vercel/analytics": "^1.4.1", "@tryghost/content-api": "^1.11.21",
"@vercel/og": "^0.6.5", "@vercel/og": "^0.6.5",
"@vercel/speed-insights": "^1.1.0",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"next": "15.1.3", "next": "15.1.3",
"node-fetch": "^3.3.2",
"nodemailer": "^6.10.0", "nodemailer": "^6.10.0",
"prisma": "^6.1.0", "prisma": "^6.1.0",
"react": "^19.0.0", "react": "^19.0.0",
@@ -1010,6 +1010,15 @@
"tslib": "^2.8.0" "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": { "node_modules/@types/debug": {
"version": "4.1.12", "version": "4.1.12",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
@@ -1359,44 +1368,6 @@
"integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
"license": "ISC" "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": { "node_modules/@vercel/og": {
"version": "0.6.5", "version": "0.6.5",
"resolved": "https://registry.npmjs.org/@vercel/og/-/og-0.6.5.tgz", "resolved": "https://registry.npmjs.org/@vercel/og/-/og-0.6.5.tgz",
@@ -1411,41 +1382,6 @@
"node": ">=16" "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": { "node_modules/acorn": {
"version": "8.14.0", "version": "8.14.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
@@ -1734,6 +1670,12 @@
"node": ">= 0.4" "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": { "node_modules/available-typed-arrays": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@@ -1760,6 +1702,17 @@
"node": ">=4" "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": { "node_modules/axobject-query": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@@ -2098,6 +2051,18 @@
"simple-swizzle": "^0.2.2" "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": { "node_modules/comma-separated-tokens": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
@@ -2207,6 +2172,15 @@
"dev": true, "dev": true,
"license": "BSD-2-Clause" "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": { "node_modules/data-view-buffer": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", "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" "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": { "node_modules/dequal": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -3208,6 +3191,29 @@
"reusify": "^1.0.4" "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": { "node_modules/fflate": {
"version": "0.7.4", "version": "0.7.4",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz",
@@ -3278,6 +3284,26 @@
"dev": true, "dev": true,
"license": "ISC" "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": { "node_modules/for-each": {
"version": "0.3.4", "version": "0.3.4",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz",
@@ -3311,6 +3337,32 @@
"url": "https://github.com/sponsors/isaacs" "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": { "node_modules/fsevents": {
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -5550,6 +5602,27 @@
"node": ">=8.6" "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": { "node_modules/minimatch": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -5708,6 +5781,43 @@
"node": "^10 || ^12 || >=14" "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": { "node_modules/nodemailer": {
"version": "6.10.0", "version": "6.10.0",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz",
@@ -6301,6 +6411,12 @@
"url": "https://github.com/sponsors/wooorm" "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": { "node_modules/punycode": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -7817,6 +7933,15 @@
"url": "https://github.com/sponsors/wooorm" "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": { "node_modules/which": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

View File

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

View File

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