Enable ISR on list pages; cache reader + search DB queries
Stable image URLs (post-signing removal) make page-level caching safe again. Homepage, genre page, sitemap, and detail page now revalidate on an interval instead of running Prisma on every hit. Reader and search keep dynamic rendering (searchParams) but wrap their Prisma queries in unstable_cache. TTLs: home/genre/detail 5m, reader manga 5m, reader page meta 1h (immutable), search 1m, sitemap 1h. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0923dc1dce
commit
90f8f50166
@ -3,7 +3,7 @@ import { collectGenres } from "@/lib/genres";
|
||||
import { GenreTabs } from "@/components/GenreTabs";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
export const revalidate = 300;
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Genres",
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { notFound } from "next/navigation";
|
||||
import { unstable_cache } from "next/cache";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { PageReader } from "@/components/PageReader";
|
||||
import { encodeId } from "@/lib/hashids";
|
||||
@ -9,6 +10,32 @@ type Props = {
|
||||
searchParams: Promise<{ resume?: string }>;
|
||||
};
|
||||
|
||||
const getMangaForReader = unstable_cache(
|
||||
async (slug: string) =>
|
||||
prisma.manga.findUnique({
|
||||
where: { slug },
|
||||
include: {
|
||||
chapters: {
|
||||
orderBy: { number: "asc" },
|
||||
include: { _count: { select: { pages: true } } },
|
||||
},
|
||||
},
|
||||
}),
|
||||
["reader-manga"],
|
||||
{ revalidate: 300 }
|
||||
);
|
||||
|
||||
const getChapterPageMeta = unstable_cache(
|
||||
async (slug: string, chapterNum: number) =>
|
||||
prisma.page.findMany({
|
||||
where: { chapter: { number: chapterNum, manga: { slug } } },
|
||||
orderBy: { number: "asc" },
|
||||
select: { number: true, width: true, height: true },
|
||||
}),
|
||||
["reader-chapter-meta"],
|
||||
{ revalidate: 3600 }
|
||||
);
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
@ -18,7 +45,7 @@ export async function generateMetadata({
|
||||
const chapterNum = parseInt(chapter, 10);
|
||||
if (isNaN(chapterNum)) return { title: "Not Found" };
|
||||
|
||||
const manga = await prisma.manga.findUnique({ where: { slug } });
|
||||
const manga = await getMangaForReader(slug);
|
||||
if (!manga) return { title: "Not Found" };
|
||||
|
||||
return {
|
||||
@ -37,22 +64,8 @@ export default async function ChapterReaderPage({
|
||||
if (isNaN(chapterNum)) notFound();
|
||||
|
||||
const [manga, initialChapterMeta] = await Promise.all([
|
||||
prisma.manga.findUnique({
|
||||
where: { slug },
|
||||
include: {
|
||||
chapters: {
|
||||
orderBy: { number: "asc" },
|
||||
include: {
|
||||
_count: { select: { pages: true } },
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.page.findMany({
|
||||
where: { chapter: { number: chapterNum, manga: { slug } } },
|
||||
orderBy: { number: "asc" },
|
||||
select: { number: true, width: true, height: true },
|
||||
}),
|
||||
getMangaForReader(slug),
|
||||
getChapterPageMeta(slug, chapterNum),
|
||||
]);
|
||||
|
||||
if (!manga) notFound();
|
||||
|
||||
@ -5,6 +5,8 @@ import { ChapterList } from "@/components/ChapterList";
|
||||
import { ReadingProgressButton } from "@/components/ReadingProgressButton";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const revalidate = 300;
|
||||
|
||||
type Props = {
|
||||
params: Promise<{ slug: string }>;
|
||||
};
|
||||
|
||||
@ -3,7 +3,7 @@ import { collectGenres } from "@/lib/genres";
|
||||
import { TrendingCarousel } from "@/components/TrendingCarousel";
|
||||
import { GenreTabs } from "@/components/GenreTabs";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
export const revalidate = 300;
|
||||
|
||||
export default async function Home() {
|
||||
const manga = await prisma.manga.findMany({
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { unstable_cache } from "next/cache";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { MangaGrid } from "@/components/MangaGrid";
|
||||
import type { Metadata } from "next";
|
||||
@ -10,19 +11,23 @@ type Props = {
|
||||
searchParams: Promise<{ q?: string }>;
|
||||
};
|
||||
|
||||
export default async function SearchPage({ searchParams }: Props) {
|
||||
const { q } = await searchParams;
|
||||
|
||||
const manga = q
|
||||
? await prisma.manga.findMany({
|
||||
const searchManga = unstable_cache(
|
||||
async (q: string) =>
|
||||
prisma.manga.findMany({
|
||||
where: {
|
||||
status: "PUBLISHED",
|
||||
title: { contains: q, mode: "insensitive" },
|
||||
},
|
||||
orderBy: { title: "asc" },
|
||||
include: { _count: { select: { chapters: true } } },
|
||||
})
|
||||
: [];
|
||||
}),
|
||||
["search-manga"],
|
||||
{ revalidate: 60 }
|
||||
);
|
||||
|
||||
export default async function SearchPage({ searchParams }: Props) {
|
||||
const { q } = await searchParams;
|
||||
const manga = q ? await searchManga(q) : [];
|
||||
|
||||
return (
|
||||
<div className="max-w-7xl mx-auto px-4 py-6">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { MetadataRoute } from "next";
|
||||
import { prisma } from "@/lib/db";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
export const revalidate = 3600;
|
||||
|
||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
const manga = await prisma.manga.findMany({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user