Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 63 additions & 18 deletions app/[locale]/news/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import type { Metadata } from "next";

import Date from "@/components/Date";
import ShareButtons from "@/components/shareButtons/ShareButtons";

import { checkIfSlugIsValid, getPostData } from "@/lib/news";
import { safeJsonLdStringify } from "@/utils/jsonLd";
import { notFound } from "next/navigation";

export type Params = {
Expand All @@ -17,9 +20,10 @@ export type PostData = {
date: string;
author?: string;
contentHtml: string;
excerpt: string;
};

export async function generateMetadata({ params }: Props) {
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params;

if (!(await checkIfSlugIsValid(slug))) {
Expand All @@ -29,9 +33,22 @@ export async function generateMetadata({ params }: Props) {
}

const postData: PostData = await getPostData(slug);
const url = `https://rockylinux.org/news/${slug}`;
const author = postData.author || "Rocky Linux Team";

return {
title: `${postData.title} - Rocky Linux`,
description: postData.excerpt,
openGraph: {
title: postData.title,
description: postData.excerpt,
url,
siteName: "Rocky Linux",
locale: "en_US",
type: "article",
publishedTime: postData.date,
authors: [author],
},
};
}

Expand All @@ -43,25 +60,53 @@ export default async function Post({ params }: Props) {
}

const postData: PostData = await getPostData(slug);
const author = postData.author || "Rocky Linux Team";

const jsonLd = {
"@context": "https://schema.org",
"@type": "NewsArticle",
headline: postData.title,
description: postData.excerpt,
datePublished: postData.date,
author: {
"@type": "Person",
name: author,
},
publisher: {
"@type": "Organization",
name: "Rocky Linux",
url: "https://rockylinux.org",
},
mainEntityOfPage: {
"@type": "WebPage",
"@id": `https://rockylinux.org/news/${slug}`,
},
};

return (
<div className="py-24 sm:py-32">
<div className="mx-auto max-w-3xl text-base leading-7">
<p className="text-base font-semibold leading-7 text-primary text-center uppercase font-display">
<Date dateString={postData.date} />
</p>
<h1 className="mt-2 text-3xl font-bold tracking-tight sm:text-4xl mb-2 text-center font-display">
{postData.title}
</h1>
<p className="text-base leading-7 text-center mb-12 italic">
{postData.author ? postData.author : "Rocky Linux Team"}
</p>
<div
className="prose dark:prose-invert prose-headings:font-display prose-a:text-primary prose-pre:bg-muted prose-pre:py-3 prose-pre:px-4 prose-pre:rounded prose-pre:text-black dark:prose-pre:text-white prose-img:rounded-md max-w-none mb-12"
dangerouslySetInnerHTML={{ __html: postData.contentHtml }}
/>
<ShareButtons url={`https://rockylinux.org/news/${slug}`} />
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: safeJsonLdStringify(jsonLd) }}
/>
<div className="py-24 sm:py-32">
<div className="mx-auto max-w-3xl text-base leading-7">
<p className="text-base font-semibold leading-7 text-primary text-center uppercase font-display">
<Date dateString={postData.date} />
</p>
<h1 className="mt-2 text-3xl font-bold tracking-tight sm:text-4xl mb-2 text-center font-display">
{postData.title}
</h1>
<p className="text-base leading-7 text-center mb-12 italic">
{author}
</p>
<div
className="prose dark:prose-invert prose-headings:font-display prose-a:text-primary prose-pre:bg-muted prose-pre:py-3 prose-pre:px-4 prose-pre:rounded prose-pre:text-black dark:prose-pre:text-white prose-img:rounded-md max-w-none mb-12"
dangerouslySetInnerHTML={{ __html: postData.contentHtml }}
/>
<ShareButtons url={`https://rockylinux.org/news/${slug}`} />
</div>
</div>
</div>
</>
);
}
18 changes: 14 additions & 4 deletions app/[locale]/news/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { NextPage, Route } from "next";
import type { Metadata, NextPage, Route } from "next";

import { getTranslations } from "next-intl/server";
import Link from "next/link";
Expand All @@ -14,12 +14,22 @@ import {
CardTitle,
} from "@/components/ui/card";

export async function generateMetadata() {
export async function generateMetadata(): Promise<Metadata> {
const t = await getTranslations("news");
const title = t("title");
const description = t("description");

return {
title: `${t("title")} - Rocky Linux`,
description: `${t("description")}`,
title: `${title} - Rocky Linux`,
description,
openGraph: {
title,
description,
url: "https://rockylinux.org/news",
siteName: "Rocky Linux",
locale: "en_US",
type: "website",
},
};
}

Expand Down
7 changes: 7 additions & 0 deletions utils/jsonLd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Sanitizes and stringifies an object for safe use in JSON-LD script tags.
* Prevents XSS by escaping characters that could break out of the script context.
*/
export function safeJsonLdStringify(data: Record<string, unknown>): string {
return JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e");
}