diff --git a/Dockerfile b/Dockerfile index d7f793d..55c0203 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,9 +42,8 @@ COPY . . # 同时注入 OPENAI_API_KEY / DEEPSEEK_API_KEY 占位值,避免 openai SDK v6+ 构造时抛错 # (build 期不会真正调用 AI,仅为通过模块求值) ARG DATABASE_URL -RUN echo "=== ARG raw ===" && echo "${DATABASE_URL}" && echo "=== env dump ===" && env | grep DATABASE && echo "=== .env file ===" && cat .env | grep DATABASE 2>/dev/null && echo "=== .env.example ===" && cat .env.example | grep DATABASE 2>/dev/null && if [ ! -f .env ]; then cp .env.example .env 2>/dev/null || echo "DATABASE_URL=placeholder" > .env; fi \ +RUN if [ ! -f .env ]; then cp .env.example .env 2>/dev/null || echo "DATABASE_URL=placeholder" > .env; fi \ && export DATABASE_URL="${DATABASE_URL:-placeholder}" \ - && echo "=== FINAL DATABASE_URL ===" && echo "$DATABASE_URL" \ && export DEEPSEEK_API_KEY="${DEEPSEEK_API_KEY:-sk-build-placeholder}" \ && export OPENAI_API_KEY="${OPENAI_API_KEY:-sk-build-placeholder}" \ && npx prisma generate \ diff --git a/src/app/categories/[slug]/page.tsx b/src/app/categories/[slug]/page.tsx index 66f9f9e..f3b4500 100644 --- a/src/app/categories/[slug]/page.tsx +++ b/src/app/categories/[slug]/page.tsx @@ -23,16 +23,7 @@ interface Props { searchParams: Promise<{ page?: string }>; } -export async function generateStaticParams() { - const categories = await prisma.category.findMany({ - select: { slug: true }, - orderBy: { sortOrder: "asc" }, - }); - return categories.map((c) => ({ slug: c.slug })); -} - -export const dynamicParams = true; -export const revalidate = 3600; // 分类页1小时 ISR +export const dynamic = "force-dynamic"; export async function generateMetadata({ params }: Props): Promise { const { slug } = await params; diff --git a/src/app/skills/[slug]/page.tsx b/src/app/skills/[slug]/page.tsx index 607a2f2..cf2727c 100644 --- a/src/app/skills/[slug]/page.tsx +++ b/src/app/skills/[slug]/page.tsx @@ -1,7 +1,7 @@ import { prisma } from "@/lib/prisma"; import { notFound } from "next/navigation"; import type { Metadata } from "next"; -import dynamic from "next/dynamic"; +import nextDynamic from "next/dynamic"; import Link from "next/link"; import Image from "next/image"; import { Button } from "@/components/ui/button"; @@ -11,15 +11,15 @@ import { CompareButton } from "@/components/common/CompareButton"; import { BreadcrumbJsonLd } from "@/components/seo/JsonLd"; import PersonalizedRecommend from "@/components/common/PersonalizedRecommend"; -const SkillReviewCard = dynamic( +const SkillReviewCard = nextDynamic( () => import("@/components/common/SkillReviewCard"), { ssr: false } ); -const ShareButton = dynamic(() => import("@/components/common/ShareButton"), { ssr: false }); -const SkillInstallButton = dynamic(() => import("@/components/common/SkillInstallButton").then((mod) => ({ default: mod.SkillInstallButton })), { ssr: false }); -const ReviewSection = dynamic(() => import("@/components/common/ReviewSection").then((mod) => ({ default: mod.ReviewSection })), { ssr: false }); -const SimilarSkills = dynamic(() => import("@/components/common/SimilarSkills"), { ssr: false }); -const FeedbackButton = dynamic(() => import("@/components/common/FeedbackButton"), { ssr: false }); +const ShareButton = nextDynamic(() => import("@/components/common/ShareButton"), { ssr: false }); +const SkillInstallButton = nextDynamic(() => import("@/components/common/SkillInstallButton").then((mod) => ({ default: mod.SkillInstallButton })), { ssr: false }); +const ReviewSection = nextDynamic(() => import("@/components/common/ReviewSection").then((mod) => ({ default: mod.ReviewSection })), { ssr: false }); +const SimilarSkills = nextDynamic(() => import("@/components/common/SimilarSkills"), { ssr: false }); +const FeedbackButton = nextDynamic(() => import("@/components/common/FeedbackButton"), { ssr: false }); interface Props { params: { slug: string }; @@ -64,18 +64,7 @@ function formatRelativeTime(date: Date): string { return `${Math.floor(days / 365)}年前`; } -export async function generateStaticParams() { - const skills = await prisma.skill.findMany({ - where: { status: "published" }, - select: { slug: true }, - orderBy: { viewCount: "desc" }, - take: 100, - }); - return skills.map((s) => ({ slug: s.slug })); -} - -export const dynamicParams = true; -export const revalidate = 3600; // 技能详情页1小时 ISR,减少 RDS 压力 +export const dynamic = "force-dynamic"; export async function generateMetadata({ params }: Props): Promise { const skill = await prisma.skill.findUnique({ diff --git a/src/app/tools/[slug]/page.tsx b/src/app/tools/[slug]/page.tsx index 2bae4f1..a7919f4 100644 --- a/src/app/tools/[slug]/page.tsx +++ b/src/app/tools/[slug]/page.tsx @@ -1,6 +1,6 @@ import { prisma } from "@/lib/prisma"; import { notFound } from "next/navigation"; -import dynamic from "next/dynamic"; +import nextDynamic from "next/dynamic"; import { getServerSession } from "next-auth"; import { authOptions } from "@/lib/auth"; import { Button } from "@/components/ui/button"; @@ -14,13 +14,13 @@ import { PRICING_LABEL, PRICING_COLOR } from "@/lib/constants"; import Link from "next/link"; import Image from "next/image"; -const ShareButton = dynamic(() => import("@/components/common/ShareButton"), { ssr: false }); -const ReviewSection = dynamic(() => import("@/components/common/ReviewSection").then((mod) => ({ default: mod.ReviewSection })), { ssr: false }); -const SimilarTools = dynamic(() => import("@/components/common/SimilarTools"), { ssr: false }); -const FeedbackButton = dynamic(() => import("@/components/common/FeedbackButton"), { ssr: false }); -const ScreenshotGallery = dynamic(() => import("@/components/common/ScreenshotGallery"), { ssr: false }); -const ToolLivePreview = dynamic(() => import("@/components/common/ToolLivePreview"), { ssr: false }); -const ApiPlayground = dynamic(() => import("@/components/common/ApiPlayground"), { ssr: false }); +const ShareButton = nextDynamic(() => import("@/components/common/ShareButton"), { ssr: false }); +const ReviewSection = nextDynamic(() => import("@/components/common/ReviewSection").then((mod) => ({ default: mod.ReviewSection })), { ssr: false }); +const SimilarTools = nextDynamic(() => import("@/components/common/SimilarTools"), { ssr: false }); +const FeedbackButton = nextDynamic(() => import("@/components/common/FeedbackButton"), { ssr: false }); +const ScreenshotGallery = nextDynamic(() => import("@/components/common/ScreenshotGallery"), { ssr: false }); +const ToolLivePreview = nextDynamic(() => import("@/components/common/ToolLivePreview"), { ssr: false }); +const ApiPlayground = nextDynamic(() => import("@/components/common/ApiPlayground"), { ssr: false }); const VERIFIED_TOOLS_KEY = "verified_tools"; @@ -58,18 +58,7 @@ interface PageProps { params: { slug: string }; } -export async function generateStaticParams() { - const tools = await prisma.tool.findMany({ - where: { status: "published" }, - select: { slug: true }, - orderBy: { viewCount: "desc" }, - take: 100, - }); - return tools.map((t) => ({ slug: t.slug })); -} - -export const dynamicParams = true; -export const revalidate = 3600; // 工具详情页1小时 ISR,减少 RDS 压力 +export const dynamic = "force-dynamic"; export async function generateMetadata({ params }: PageProps) { const tool = await prisma.tool.findUnique({ diff --git a/src/app/user/[id]/page.tsx b/src/app/user/[id]/page.tsx index ba10e7b..eb9d2fa 100644 --- a/src/app/user/[id]/page.tsx +++ b/src/app/user/[id]/page.tsx @@ -4,8 +4,7 @@ import { notFound } from "next/navigation"; import { getLevelInfo } from "@/lib/level-config"; import UserPublicProfile from "./UserPublicProfile"; -export const revalidate = 3600; -export const dynamicParams = true; +export const dynamic = "force-dynamic"; interface Props { params: { id: string };