feat: 服务器迁移后的全量演进(依赖升级/安全审计/设计统一/截图/SEO 底座化)

在原仓库历史之上同步本服务器上的全部演进:

- 迁移至新服务器并重建 PG 数据库 + ddnc 开发域名绑定
- 全部依赖升级至最新(Next 16.3.5 / React 19.3 / better-auth 1.7.5 / Drizzle 0.45 / TS 7),
  overrides 消除已知 CVE,安全审计 19 项发现全部修复闭环
- 设计统一:各站宽度 token(--container + site-container)、classic hero 重设计、
  头部换行修复、页面过渡与 View Transitions
- 截图功能:api.adx9.com 截图服务接入(ow=800 本地化 webp、cf=1、去重/限流/占位图)
- SEO 底座化:canonical/hreflang/sitemap/JSON-LD/robots 全部收进 core,新站自动继承
- 加载体验:五站五形态路由骨架(data-theme 切换、shimmer 微光、延迟淡入)+
  过滤 useTransition 交叉淡入淡出(card-enter 错峰入场)
- 构建:experimental.cpus 限流防 PG 连接打满;清理死组件链
This commit is contained in:
bigcode
2026-09-15 18:19:53 +08:00
parent 0eed5183b2
commit 05440ffb5b
144 changed files with 3055 additions and 12937 deletions
@@ -1,2 +1,2 @@
// 自动生成:[locale]/(admin)/layout.tsx 的薄路由
export { default } from "@aidir/core/pages/[locale]/(admin)/layout";
export { default, metadata } from "@aidir/core/pages/[locale]/(admin)/layout";
@@ -1,2 +1,2 @@
// 自动生成:[locale]/(auth)/layout.tsx 的薄路由
export { default } from "@aidir/core/pages/[locale]/(auth)/layout";
export { default, metadata } from "@aidir/core/pages/[locale]/(auth)/layout";
@@ -1,2 +1,2 @@
// 自动生成:[locale]/(protected)/layout.tsx 的薄路由
export { default } from "@aidir/core/pages/[locale]/(protected)/layout";
export { default, metadata } from "@aidir/core/pages/[locale]/(protected)/layout";
+1 -1
View File
@@ -1,2 +1,2 @@
// 自动生成:[locale]/layout.tsx 的薄路由
export { default, generateStaticParams } from "@aidir/core/pages/[locale]/layout";
export { default, generateMetadata, generateStaticParams } from "@aidir/core/pages/[locale]/layout";
@@ -0,0 +1,2 @@
// 自动生成:api/screenshots/[slug]/route.ts 的薄路由
export { GET } from "@aidir/core/pages/api/screenshots/[slug]/route";
+2
View File
@@ -0,0 +1,2 @@
// 自动生成:robots.ts 的薄路由
export { default } from "@aidir/core/pages/robots";
+12 -54
View File
@@ -1,6 +1,6 @@
// 自动生成:站点首页组件(源自底座 product 形态,品牌来自 @/site/brand)
import { siteConfig } from "@/site/brand";
import { Flame, Trophy, Rocket, ChevronRight, MessageSquare } from "lucide-react";
import { Rocket, ChevronRight, MessageSquare } from "lucide-react";
import { getTranslations } from "next-intl/server";
import { VoteButton } from "@/features/voting/components/vote-button";
import { LocaleLink } from "@/components/locale-link";
@@ -8,45 +8,6 @@ import { ToolLogo } from "@/components/tool-logo";
import { SectionHeader } from "@/kit/section";
import { getHomeDataSafe } from "@/kit/home-data";
/** Product:AI 产品社区(Daily Launch / Upvote / Maker) */
const navItems = [
{ label: "Today", href: "/" },
{ label: "Leaderboard", href: "/rankings" },
{ label: "Categories", href: "/tools" },
{ label: "Collections", href: "/best" },
{ label: "Blog", href: "/blog" },
{ label: "Submit", href: "/submit" },
];
const footerGroups = [
{
title: "Community",
links: [
{ label: "Submit a Product", href: "/submit" },
{ label: "Leaderboard", href: "/rankings" },
{ label: "Dashboard", href: "/dashboard" },
],
},
{
title: "Explore",
links: [
{ label: "All Products", href: "/tools" },
{ label: "Use Cases", href: "/ai-tools-for" },
{ label: "Collections", href: "/best" },
],
},
{
title: "About",
links: [
{ label: "Blog", href: "/blog" },
{ label: "Terms", href: "/terms" },
{ label: "Privacy", href: "/privacy" },
],
},
];
/** 单条 Launch 卡片:PH 风格(大投票块 + 产品信息 + 互动) */
function LaunchRow({
tool,
@@ -110,18 +71,18 @@ async function SiteHome({ locale }: { locale: string }) {
return (
<div className="min-h-screen bg-background">
{/* Hero:今日发布 */}
<section className="hero-launch border-b border-border bg-gradient-to-b from-primary/5 to-background">
<div className="mx-auto max-w-5xl px-4 py-12 text-center sm:px-6 md:py-16">
<h1 className="text-2xl font-extrabold tracking-tight text-foreground sm:text-4xl">
{/* Hero:亮底排版,橙色只留给主按钮 */}
<section className="border-b border-border">
<div className="site-container px-4 py-12 text-center sm:px-6 md:py-16">
<h1 className="text-balance text-2xl font-extrabold tracking-tight text-foreground sm:text-4xl">
{th("title")}
</h1>
<p className="mt-3 text-sm text-muted-foreground sm:text-base">
<p className="mx-auto mt-3 max-w-xl text-sm leading-relaxed text-muted-foreground sm:text-base">
{th("description")}
</p>
<LocaleLink
href="/submit"
className="mt-6 inline-flex items-center gap-1.5 rounded-full bg-primary px-6 py-2.5 text-sm font-semibold text-primary-foreground shadow transition-all hover:opacity-90"
className="mt-6 inline-flex items-center gap-1.5 rounded-lg bg-primary px-6 py-2.5 text-sm font-semibold text-primary-foreground transition-opacity hover:opacity-90"
>
<Rocket className="h-4 w-4" />
{t("launchButton")}
@@ -129,14 +90,13 @@ async function SiteHome({ locale }: { locale: string }) {
</div>
</section>
<div className="mx-auto grid max-w-5xl gap-8 px-4 py-10 sm:px-6 lg:grid-cols-[1fr_280px]">
<div className="site-container grid gap-8 px-4 py-10 sm:px-6 lg:grid-cols-[1fr_280px]">
{/* 产品 Feed */}
<div>
<div className="overflow-hidden rounded-2xl border border-border bg-background">
<div className="flex items-center gap-2 border-b border-border bg-secondary/30 px-4 py-3 sm:px-6">
<Flame className="h-4 w-4 text-primary" />
<h2 className="text-sm font-bold text-foreground">Today's feed</h2>
</div>
<div className="flex items-center justify-between border-b border-border px-4 py-3 sm:px-6">
<h2 className="text-sm font-bold text-foreground">Today's feed</h2>
</div>
{data.trending.length > 0 ? (
data.trending.map((tool, i) => (
<LaunchRow key={tool.id} tool={tool} locale={locale} rank={i + 1} />
@@ -172,8 +132,7 @@ async function SiteHome({ locale }: { locale: string }) {
<aside className="space-y-6">
{data.popular.length > 0 && (
<div className="rounded-2xl border border-border bg-background p-5">
<div className="flex items-center gap-2">
<Trophy className="h-4 w-4 text-primary" />
<div className="flex items-center justify-between">
<h3 className="text-sm font-bold text-foreground">Leaderboard</h3>
</div>
<ol className="mt-4 space-y-3">
@@ -255,5 +214,4 @@ async function SiteHome({ locale }: { locale: string }) {
</div>
);
}
export { SiteHome };