Commit caa6587a by PLN (Algolia)

feat: root KEY route rendering posts at the domain root

me.nech.pl/<KEY> resolves at build time to a post in one of its
languages, reusing PostArticle wholesale. generateStaticParams runs the
collision guard so a future slug can never silently shadow a route;
drafts mint nothing here in prod and the page re-checks.
parent c49d53d1
import { notFound } from "next/navigation";
import PostArticle from "../blog/PostArticle";
import { buildLanguageNav } from "../blog/language-nav";
import { blogCanonicalPath, getAllBlogKeys, getBlogPost, resolveBlogKey } from "@/lib/blog";
import { assertRootKeysAvailable } from "@/lib/keys";
import { draftsVisible } from "@/lib/content";
export const dynamicParams = false;
/**
* The root KEY namespace — me.nech.pl/<KEY>. Ratified in DESIGN.md: the root
* form is canonical, /blog/<slug> is the alias. A KEY resolves at build time
* to a post in one of its languages: the canonical folder slug renders the
* default language, a localized `slug:` renders its language. PostArticle is
* reused wholesale — title, TOC, margin notes, pager and switcher all ride
* along.
*
* Static routes always win over this segment (blog, parvagues, images…);
* the collision guard below makes that guarantee hold for the future too:
* a post slug that would shadow a real route fails the build loudly.
*/
export function generateStaticParams() {
const keys = getAllBlogKeys();
assertRootKeysAvailable(keys.map(({ key, slug }) => [key, `post ${slug}`]));
// Static export needs at least one prerendered variant per dynamic route;
// with no published posts in prod, emit a 404 sentinel (the page renders
// notFound() for it) so the export still ships.
return keys.length > 0 ? keys.map(({ key }) => ({ key })) : [{ key: "-" }];
}
export async function generateMetadata({ params }) {
const { key } = await params;
const resolved = resolveBlogKey(key);
if (!resolved) return {};
const post = await getBlogPost(resolved.slug, resolved.lang).catch(() => null);
if (!post) return {};
// The canonical is once per post: the root form of the canonical folder
// slug — even from a localized-slug alias rendering this very page.
const canonical = blogCanonicalPath(post.slug);
return {
title: post.title,
description: post.description,
alternates: { canonical },
openGraph: {
title: post.title,
description: post.description,
type: 'article',
url: canonical,
publishedTime: post.date,
images: ['/images/profile.png'],
},
};
}
export default async function RootKey({ params }) {
const { key } = await params;
const resolved = resolveBlogKey(key);
if (!resolved) notFound();
const post = await getBlogPost(resolved.slug, resolved.lang).catch(() => null);
if (!post) notFound();
// Drafts never mint here in prod (getAllBlogKeys filters them; the page
// re-checks so a dev-reachable key can't leak through either).
if (post.draft && !draftsVisible()) notFound();
const { switcher, banner } = buildLanguageNav(post);
return <PostArticle post={post} switcher={switcher} banner={banner} />;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment