diff --git a/app/[owner]/[repo]/[postNumber]/not-found.tsx b/app/[owner]/[repo]/[postNumber]/not-found.tsx new file mode 100644 index 0000000..095de18 --- /dev/null +++ b/app/[owner]/[repo]/[postNumber]/not-found.tsx @@ -0,0 +1,38 @@ +"use client" + +import Link from "next/link" +import { usePathname } from "next/navigation" +import { Container } from "@/components/container" +import { Title } from "@/components/typography" + +export default function PostNotFound() { + const pathname = usePathname() + const parts = pathname.split("/").filter(Boolean) + const owner = parts[0] + const repo = parts[1] + + const repoUrl = owner && repo ? `/${owner}/${repo}` : "/" + + return ( + +
+ 404 — Post Not Found +

+ This post doesn't exist in{" "} + + {owner}/{repo} + + . +

+

+ + View all posts in {owner}/{repo} + + + Go home + +

+
+
+ ) +} diff --git a/app/[owner]/[repo]/[postNumber]/page.tsx b/app/[owner]/[repo]/[postNumber]/page.tsx index 29caf1e..8450d34 100644 --- a/app/[owner]/[repo]/[postNumber]/page.tsx +++ b/app/[owner]/[repo]/[postNumber]/page.tsx @@ -121,8 +121,6 @@ export default async function PostPage({ }: { params: Promise<{ owner: string; repo: string; postNumber: string }> }) { - "use cache" - const { postNumber: postNumberStr, owner, repo } = await params if (postNumberStr.endsWith(".md") || postNumberStr.endsWith(".txt")) { @@ -136,6 +134,26 @@ export default async function PostPage({ notFound() } + // Check post exists before cache boundary + const post = await getPostByNumber(owner, repo, postNumber) + if (!post) { + notFound() + } + + return +} + +async function PostPageContent({ + owner, + repo, + postNumber, +}: { + owner: string + repo: string + postNumber: number +}) { + "use cache" + const [ postWithCategory, allLlmUsers, @@ -224,11 +242,8 @@ export default async function PostPage({ .where(and(eq(categories.owner, owner), eq(categories.repo, repo))), ]) - if (!postWithCategory) { - notFound() - } - - const { category, gitContexts, ...post } = postWithCategory + // Post existence already verified before cache boundary + const { category, gitContexts, ...post } = postWithCategory! const gitContext = gitContexts?.[0] ?? null cacheTag(`post:${post.id}`) diff --git a/app/[owner]/[repo]/category/[categorySlug]/not-found.tsx b/app/[owner]/[repo]/category/[categorySlug]/not-found.tsx new file mode 100644 index 0000000..f403bbc --- /dev/null +++ b/app/[owner]/[repo]/category/[categorySlug]/not-found.tsx @@ -0,0 +1,39 @@ +"use client" + +import { ExternalLinkIcon } from "lucide-react" +import Link from "next/link" +import { usePathname } from "next/navigation" +import { Container } from "@/components/container" +import { Title } from "@/components/typography" + +export default function CategoryNotFound() { + const pathname = usePathname() + const parts = pathname.split("/").filter(Boolean) + const owner = parts[0] + const repo = parts[1] + + const repoUrl = owner && repo ? `/${owner}/${repo}` : "/" + + return ( + +
+ 404 — Category Not Found +

+ This category doesn't exist in{" "} + + {owner}/{repo} + + . +

+

+ + View all posts in {owner}/{repo} + + + Go home + +

+
+
+ ) +} diff --git a/app/[owner]/[repo]/category/[categorySlug]/page.tsx b/app/[owner]/[repo]/category/[categorySlug]/page.tsx index 70680fc..791bc7b 100644 --- a/app/[owner]/[repo]/category/[categorySlug]/page.tsx +++ b/app/[owner]/[repo]/category/[categorySlug]/page.tsx @@ -65,26 +65,45 @@ export default async function CategoryPage({ }: { params: Promise<{ owner: string; repo: string; categorySlug: string }> }) { - "use cache" - const { owner, repo, categorySlug } = await params - cacheTag(`category:${categorySlug}`) - const [category, allLlmUsers, repoData] = await Promise.all([ + // Check existence before cache boundary + const [category, repoData] = await Promise.all([ getCategoryBySlug(owner, repo, categorySlug), - getModelsForPicker(), getGithubRepo(owner, repo), ]) - if (!category) { - return notFound() + if (!category || !repoData) { + notFound() } - if (!repoData) { - return notFound() - } + return ( + + ) +} + +async function CategoryPageContent({ + owner, + repo, + categorySlug, + category, +}: { + owner: string + repo: string + categorySlug: string + category: NonNullable>> +}) { + "use cache" + cacheTag(`category:${categorySlug}`) - const categoryPosts = await db + const [allLlmUsers, categoryPosts] = await Promise.all([ + getModelsForPicker(), + db .select({ id: posts.id, number: posts.number, @@ -111,7 +130,8 @@ export default async function CategoryPage({ eq(posts.categoryId, category.id) ) ) - .orderBy(desc(posts.createdAt)) + .orderBy(desc(posts.createdAt)), + ]) const categoriesById = { [category.id]: category } diff --git a/app/[owner]/[repo]/not-found.tsx b/app/[owner]/[repo]/not-found.tsx new file mode 100644 index 0000000..823b20c --- /dev/null +++ b/app/[owner]/[repo]/not-found.tsx @@ -0,0 +1,48 @@ +"use client" + +import { ExternalLinkIcon } from "lucide-react" +import Link from "next/link" +import { usePathname } from "next/navigation" +import { Container } from "@/components/container" +import { Title } from "@/components/typography" + +export default function RepoNotFound() { + const pathname = usePathname() + const parts = pathname.split("/").filter(Boolean) + const owner = parts[0] + const repo = parts[1] + + const githubUrl = + owner && repo ? `https://github.com/${owner}/${repo}` : "https://github.com" + + return ( + +
+ 404 — Repository Not Found +

+ We couldn't find a repository at{" "} + + {owner}/{repo} + + . +

+

+ Does this repo exist on GitHub? + + Check on GitHub + + +

+

+ + Go back home + +

+
+
+ ) +} diff --git a/app/[owner]/[repo]/page.tsx b/app/[owner]/[repo]/page.tsx index 56f3774..d084da3 100644 --- a/app/[owner]/[repo]/page.tsx +++ b/app/[owner]/[repo]/page.tsx @@ -58,12 +58,30 @@ export default async function RepoPage({ }: { params: Promise<{ owner: string; repo: string }> }) { - "use cache" - const { owner, repo } = await params + + // Check repo exists before cache boundary + const repoData = await getGithubRepo(owner, repo) + if (!repoData) { + notFound() + } + + return +} + +async function RepoPageContent({ + owner, + repo, + repoData, +}: { + owner: string + repo: string + repoData: NonNullable>> +}) { + "use cache" cacheTag(`repo:${owner}:${repo}`) - const [repoPosts, repoCategories, allLlmUsers, repoData] = await Promise.all([ + const [repoPosts, repoCategories, allLlmUsers] = await Promise.all([ db .select({ id: posts.id, @@ -91,13 +109,8 @@ export default async function RepoPage({ .from(categories) .where(and(eq(categories.owner, owner), eq(categories.repo, repo))), getModelsForPicker(), - getGithubRepo(owner, repo), ]) - if (!repoData) { - return notFound() - } - const categoriesById = Object.fromEntries( repoCategories.map((c) => [c.id, c]) ) diff --git a/app/not-found.tsx b/app/not-found.tsx new file mode 100644 index 0000000..bedc231 --- /dev/null +++ b/app/not-found.tsx @@ -0,0 +1,21 @@ +import Link from "next/link" +import { Container } from "@/components/container" +import { Title } from "@/components/typography" + +export default function NotFound() { + return ( + +
+ 404 — Page Not Found +

+ The page you're looking for doesn't exist. +

+

+ + Go back home + +

+
+
+ ) +}