fix: 移除 build 期 generateStaticParams,改为 force-dynamic 避免构建时连接数据库
This commit is contained in:
+1
-2
@@ -42,9 +42,8 @@ COPY . .
|
|||||||
# 同时注入 OPENAI_API_KEY / DEEPSEEK_API_KEY 占位值,避免 openai SDK v6+ 构造时抛错
|
# 同时注入 OPENAI_API_KEY / DEEPSEEK_API_KEY 占位值,避免 openai SDK v6+ 构造时抛错
|
||||||
# (build 期不会真正调用 AI,仅为通过模块求值)
|
# (build 期不会真正调用 AI,仅为通过模块求值)
|
||||||
ARG DATABASE_URL
|
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}" \
|
&& export DATABASE_URL="${DATABASE_URL:-placeholder}" \
|
||||||
&& echo "=== FINAL DATABASE_URL ===" && echo "$DATABASE_URL" \
|
|
||||||
&& export DEEPSEEK_API_KEY="${DEEPSEEK_API_KEY:-sk-build-placeholder}" \
|
&& export DEEPSEEK_API_KEY="${DEEPSEEK_API_KEY:-sk-build-placeholder}" \
|
||||||
&& export OPENAI_API_KEY="${OPENAI_API_KEY:-sk-build-placeholder}" \
|
&& export OPENAI_API_KEY="${OPENAI_API_KEY:-sk-build-placeholder}" \
|
||||||
&& npx prisma generate \
|
&& npx prisma generate \
|
||||||
|
|||||||
@@ -23,16 +23,7 @@ interface Props {
|
|||||||
searchParams: Promise<{ page?: string }>;
|
searchParams: Promise<{ page?: string }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
export const dynamic = "force-dynamic";
|
||||||
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 async function generateMetadata({ params }: Props): Promise<Metadata> {
|
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { prisma } from "@/lib/prisma";
|
import { prisma } from "@/lib/prisma";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import dynamic from "next/dynamic";
|
import nextDynamic from "next/dynamic";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
@@ -11,15 +11,15 @@ import { CompareButton } from "@/components/common/CompareButton";
|
|||||||
import { BreadcrumbJsonLd } from "@/components/seo/JsonLd";
|
import { BreadcrumbJsonLd } from "@/components/seo/JsonLd";
|
||||||
import PersonalizedRecommend from "@/components/common/PersonalizedRecommend";
|
import PersonalizedRecommend from "@/components/common/PersonalizedRecommend";
|
||||||
|
|
||||||
const SkillReviewCard = dynamic(
|
const SkillReviewCard = nextDynamic(
|
||||||
() => import("@/components/common/SkillReviewCard"),
|
() => import("@/components/common/SkillReviewCard"),
|
||||||
{ ssr: false }
|
{ ssr: false }
|
||||||
);
|
);
|
||||||
const ShareButton = dynamic(() => import("@/components/common/ShareButton"), { ssr: false });
|
const ShareButton = nextDynamic(() => import("@/components/common/ShareButton"), { ssr: false });
|
||||||
const SkillInstallButton = dynamic(() => import("@/components/common/SkillInstallButton").then((mod) => ({ default: mod.SkillInstallButton })), { ssr: false });
|
const SkillInstallButton = nextDynamic(() => 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 ReviewSection = nextDynamic(() => import("@/components/common/ReviewSection").then((mod) => ({ default: mod.ReviewSection })), { ssr: false });
|
||||||
const SimilarSkills = dynamic(() => import("@/components/common/SimilarSkills"), { ssr: false });
|
const SimilarSkills = nextDynamic(() => import("@/components/common/SimilarSkills"), { ssr: false });
|
||||||
const FeedbackButton = dynamic(() => import("@/components/common/FeedbackButton"), { ssr: false });
|
const FeedbackButton = nextDynamic(() => import("@/components/common/FeedbackButton"), { ssr: false });
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
params: { slug: string };
|
params: { slug: string };
|
||||||
@@ -64,18 +64,7 @@ function formatRelativeTime(date: Date): string {
|
|||||||
return `${Math.floor(days / 365)}年前`;
|
return `${Math.floor(days / 365)}年前`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
export const dynamic = "force-dynamic";
|
||||||
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 async function generateMetadata({ params }: Props): Promise<Metadata> {
|
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||||
const skill = await prisma.skill.findUnique({
|
const skill = await prisma.skill.findUnique({
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { prisma } from "@/lib/prisma";
|
import { prisma } from "@/lib/prisma";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import dynamic from "next/dynamic";
|
import nextDynamic from "next/dynamic";
|
||||||
import { getServerSession } from "next-auth";
|
import { getServerSession } from "next-auth";
|
||||||
import { authOptions } from "@/lib/auth";
|
import { authOptions } from "@/lib/auth";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
@@ -14,13 +14,13 @@ import { PRICING_LABEL, PRICING_COLOR } from "@/lib/constants";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
const ShareButton = dynamic(() => import("@/components/common/ShareButton"), { ssr: false });
|
const ShareButton = nextDynamic(() => import("@/components/common/ShareButton"), { ssr: false });
|
||||||
const ReviewSection = dynamic(() => import("@/components/common/ReviewSection").then((mod) => ({ default: mod.ReviewSection })), { ssr: false });
|
const ReviewSection = nextDynamic(() => import("@/components/common/ReviewSection").then((mod) => ({ default: mod.ReviewSection })), { ssr: false });
|
||||||
const SimilarTools = dynamic(() => import("@/components/common/SimilarTools"), { ssr: false });
|
const SimilarTools = nextDynamic(() => import("@/components/common/SimilarTools"), { ssr: false });
|
||||||
const FeedbackButton = dynamic(() => import("@/components/common/FeedbackButton"), { ssr: false });
|
const FeedbackButton = nextDynamic(() => import("@/components/common/FeedbackButton"), { ssr: false });
|
||||||
const ScreenshotGallery = dynamic(() => import("@/components/common/ScreenshotGallery"), { ssr: false });
|
const ScreenshotGallery = nextDynamic(() => import("@/components/common/ScreenshotGallery"), { ssr: false });
|
||||||
const ToolLivePreview = dynamic(() => import("@/components/common/ToolLivePreview"), { ssr: false });
|
const ToolLivePreview = nextDynamic(() => import("@/components/common/ToolLivePreview"), { ssr: false });
|
||||||
const ApiPlayground = dynamic(() => import("@/components/common/ApiPlayground"), { ssr: false });
|
const ApiPlayground = nextDynamic(() => import("@/components/common/ApiPlayground"), { ssr: false });
|
||||||
|
|
||||||
const VERIFIED_TOOLS_KEY = "verified_tools";
|
const VERIFIED_TOOLS_KEY = "verified_tools";
|
||||||
|
|
||||||
@@ -58,18 +58,7 @@ interface PageProps {
|
|||||||
params: { slug: string };
|
params: { slug: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
export const dynamic = "force-dynamic";
|
||||||
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 async function generateMetadata({ params }: PageProps) {
|
export async function generateMetadata({ params }: PageProps) {
|
||||||
const tool = await prisma.tool.findUnique({
|
const tool = await prisma.tool.findUnique({
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import { notFound } from "next/navigation";
|
|||||||
import { getLevelInfo } from "@/lib/level-config";
|
import { getLevelInfo } from "@/lib/level-config";
|
||||||
import UserPublicProfile from "./UserPublicProfile";
|
import UserPublicProfile from "./UserPublicProfile";
|
||||||
|
|
||||||
export const revalidate = 3600;
|
export const dynamic = "force-dynamic";
|
||||||
export const dynamicParams = true;
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
params: { id: string };
|
params: { id: string };
|
||||||
|
|||||||
Reference in New Issue
Block a user