From ec9f4bbf02e6d7dcb32be745048ea4c862003dfd Mon Sep 17 00:00:00 2001 From: hjdave Date: Sat, 4 Apr 2026 16:59:37 +0800 Subject: [PATCH] Fix SSR hydration issues and finalize deployment config --- package-lock.json | 13 + package.json | 1 + src/app/page.tsx | 331 +------------------- src/app/page.tsx.backup | 517 +++++++++++++++++++++++++++++++ src/components/ThemeProvider.tsx | 6 +- src/lib/db.ts | 7 +- 6 files changed, 540 insertions(+), 335 deletions(-) create mode 100644 src/app/page.tsx.backup diff --git a/package-lock.json b/package-lock.json index 8d90bca..45dabcd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "devDependencies": { "@tailwindcss/postcss": "^4", "@types/node": "^20", + "@types/pg": "^8.20.0", "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", @@ -1629,6 +1630,18 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/pg": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.20.0.tgz", + "integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, "node_modules/@types/react": { "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", diff --git a/package.json b/package.json index 1e0740f..5480aa2 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "devDependencies": { "@tailwindcss/postcss": "^4", "@types/node": "^20", + "@types/pg": "^8.20.0", "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", diff --git a/src/app/page.tsx b/src/app/page.tsx index be8a5ca..5f971da 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -507,331 +507,12 @@ function HomeInner() { ); } - const [activeSection, setActiveSection] = useState(""); - const [sidebarOpen, setSidebarOpen] = useState(false); - const [searchQuery, setSearchQuery] = useState(""); - const [copied, setCopied] = useState(false); - const currentDoc = documentation.find((doc) => doc.slug === activeDoc); - const sections = currentDoc?.sections || []; - - const handleCopy = () => { - navigator.clipboard.writeText("npm install docs-site"); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }; - - const handleNavClick = (id: string) => { - setActiveSection(id); - setSidebarOpen(false); - const element = document.getElementById(id); - element?.scrollIntoView({ behavior: "smooth", block: "start" }); - }; - - const filteredDocs = documentation.filter( - (doc) => - doc.title.includes(searchQuery) || - doc.description.includes(searchQuery) - ); - - return ( -
-
- {/* 移动端遮罩 */} - {sidebarOpen && ( -
setSidebarOpen(false)} - /> - )} - - {/* 左侧导航栏 */} - - - {/* 主内容区 */} -
- {/* 顶部导航栏 */} -
-
-
- -

{currentDoc?.title}

-
- -
- {!authenticated && ( - - 登录 - - )} - {/* 深色模式切换 */} - - - {/* 复制链接 */} - -
-
-
- - {/* 文档内容 */} -
- {sections.map((section, index) => ( -
-
-

{section.title}

- - - - - -
- - {section.content && ( -

- {section.content} -

- )} - - {section.features && ( -
- {(section.features as string[] | FeatureItem[]).map((feature, i) => { - if (typeof feature === "string") { - return ( -
- - - - {feature} -
- ); - } - return ( - - {feature.items && ( -
    - {feature.items.map((item, j) => ( -
  • - - {item} -
  • - ))} -
- )} -
- ); - })} -
- )} - - {section.code && ( - - {section.code} - - )} - - {section.instructions && section.code && ( -

- {section.instructions} -

- )} -
- ))} -
- - {/* 便签 */} -
- -
- - {/* 留言板 */} -
- -
- - {/* 底部 */} - -
-
-
- ); +function Home() { + const [mounted, setMounted] = useState(false); + useEffect(() => setMounted(true), []); + if (!mounted) return
; + return ; } -export default HomeInner; +export default Home; diff --git a/src/app/page.tsx.backup b/src/app/page.tsx.backup new file mode 100644 index 0000000..6821d6e --- /dev/null +++ b/src/app/page.tsx.backup @@ -0,0 +1,517 @@ +"use client"; + +import { useState, useEffect } from "react"; +import CodeBlock from "@/components/CodeBlock"; +import NotesList from "@/components/note/NotesList"; +import MessageBoard from "@/components/message/MessageBoard"; +import { useTheme } from "@/components/ThemeProvider"; +import { useAuth } from "@/components/AuthProvider"; + +const documentation = [ + { + title: "快速开始", + slug: "quick-start", + description: "了解如何开始使用我们的平台", + sections: [ + { + title: "简介", + content: "欢迎使用我们的在线文档系统!这是一个现代、响应式、功能丰富的文档平台,专为技术文档设计。", + features: [ + "响应式布局,支持移动端和桌面端", + "平滑滚动和锚点导航", + "代码高亮和复制功能", + "深色模式支持", + "内置搜索功能", + ], + }, + { + title: "安装", + content: "开始之前,请确保你已经安装了以下工具:", + requirements: ["Node.js (v18 或更高版本)", "npm 或 yarn 包管理器"], + code: "npm install\ngit pull origin main", + instructions: "然后运行以下命令:\n\nnpm install\n\n然后:\n\nnpm run dev\n\n访问 http://localhost:3000 即可查看。", + }, + { + title: "项目结构", + content: "项目采用以下目录结构:", + code: `docs-site/ +├── src/ +│ ├── app/ +│ │ ├── page.tsx # 主文档页面 +│ │ ├── layout.tsx # 根布局 +│ │ └── globals.css # 全局样式 +│ └── components/ +│ ├── Sidebar.tsx # 侧边栏组件 +│ ├── Navbar.tsx # 导航栏组件 +│ └── CodeBlock.tsx # 代码块组件 +├── public/ +└── package.json`, + }, + ], + }, + { + title: "核心功能", + slug: "features", + description: "探索平台的各项功能", + sections: [ + { + title: "响应式布局", + content: "平台采用响应式设计,自动适配各种屏幕尺寸。", + features: [ + { + title: "移动端", + items: ["侧边栏可折叠", "字体大小自动调整", "导航栏简化"], + }, + { + title: "桌面端", + items: ["固定侧边栏", "更多内容展示", "优化排版"], + }, + ], + }, + { + title: "交互功能", + content: "平台提供丰富的交互功能:", + features: [ + { + title: "代码复制", + description: "点击代码块右上角的复制按钮即可快速复制代码", + }, + { + title: "平滑滚动", + description: "点击导航链接自动滚动到对应位置", + }, + { + title: "深色模式", + description: "点击右上角切换按钮即可切换主题", + }, + { + title: "锚点导航", + description: "每个章节都有独立的锚点链接", + }, + ], + }, + { + title: "搜索功能", + content: "内置搜索功能帮助你快速找到所需内容。", + code: "1. 点击右上角的搜索图标\n2. 输入关键词\n3. 查看搜索结果\n4. 点击结果跳转到对应章节", + instructions: "搜索支持标题、描述和代码片段。", + }, + ], + }, + { + title: "API 参考", + slug: "api-reference", + description: "API 接口文档", + sections: [ + { + title: "获取文档列表", + content: "获取所有可用文档的列表。", + code: `// GET /api/docs + +Response: +{ + "docs": [ + { + "title": "文档标题", + "slug": "文档_slug", + "description": "文档描述" + } + ] +}`, + }, + { + title: "获取文档内容", + content: "获取指定文档的详细内容。", + code: `// GET /api/docs/:slug + +Response: +{ + "title": "文档标题", + "sections": [ + { + "title": "章节标题", + "content": "章节内容" + } + ] +}`, + }, + ], + }, +]; + +interface FeatureItem { + title: string; + items?: string[]; + description?: string; +} + +function FeatureCard({ title, description, children }: { + title: string; + description?: string; + children?: React.ReactNode; +}) { + return ( +
+

{title}

+ {description &&

{description}

} + {children} +
+ ); +} + +function HomeInner() { + const { theme, toggleTheme } = useTheme(); + const { authenticated, user, checkAuth } = useAuth(); + const [activeDoc, setActiveDoc] = useState("quick-start"); + const [activeSection, setActiveSection] = useState(""); + const [sidebarOpen, setSidebarOpen] = useState(false); + const [searchQuery, setSearchQuery] = useState(""); + const [copied, setCopied] = useState(false); + + useEffect(() => { + checkAuth(); + }, []); + + const currentDoc = documentation.find((doc) => doc.slug === activeDoc); + const sections = currentDoc?.sections || []; + + const handleCopy = () => { + navigator.clipboard.writeText("npm install docs-site"); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + const handleNavClick = (id: string) => { + setActiveSection(id); + setSidebarOpen(false); + const element = document.getElementById(id); + element?.scrollIntoView({ behavior: "smooth", block: "start" }); + }; + + const filteredDocs = documentation.filter( + (doc) => + doc.title.includes(searchQuery) || + doc.description.includes(searchQuery) + ); + + return ( +
+
+ {/* 移动端遮罩 */} + {sidebarOpen && ( +
setSidebarOpen(false)} + /> + )} + + {/* 左侧导航栏 */} + + + {/* 主内容区 */} +
+ {/* 顶部导航栏 */} +
+
+
+ +

{currentDoc?.title}

+ {!authenticated && ( + + 登录 + + )} + {authenticated && user && ( +
+ {user.username} + +
+ )} +
+ +
+ {/* 深色模式切换 */} + + + {/* 复制链接 */} + +
+
+
+ + {/* 文档内容 */} +
+ {sections.map((section, index) => ( +
+
+

{section.title}

+ + + + + +
+ + {section.content && ( +

+ {section.content} +

+ )} + + {section.features && ( +
+ {(section.features as string[] | FeatureItem[]).map((feature, i) => { + if (typeof feature === "string") { + return ( +
+ + + + {feature} +
+ ); + } + return ( + + {feature.items && ( +
    + {feature.items.map((item, j) => ( +
  • + + {item} +
  • + ))} +
+ )} +
+ ); + })} +
+ )} + + {section.code && ( + + {section.code} + + )} + + {section.instructions && section.code && ( +

+ {section.instructions} +

+ )} +
+ ))} + + {/* 便签 */} +
+ +
+ + {/* 留言板 */} +
+ +
+
+ + {/* 底部 */} + +
+
+
+ ); + +function Home() { + const [mounted, setMounted] = useState(false); + useEffect(() => setMounted(true), []); + if (!mounted) return
; + return ; +} + +export default Home; diff --git a/src/components/ThemeProvider.tsx b/src/components/ThemeProvider.tsx index 8241769..f1ff39f 100644 --- a/src/components/ThemeProvider.tsx +++ b/src/components/ThemeProvider.tsx @@ -42,13 +42,9 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) { }); }; - if (!mounted) { - return <>{children}; - } - return ( - {children} + {mounted ? children :
{children}
}
); } diff --git a/src/lib/db.ts b/src/lib/db.ts index 1f47ab7..06bc37b 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -42,10 +42,7 @@ export async function getClient(): Promise { const client = await getPool().connect(); const release = () => client.release(); - return { - ...client, - release, - }; + return client as PoolClient & { release: () => void }; } // 初始化数据库表 @@ -213,7 +210,7 @@ export async function getDocs(): Promise { ORDER BY d.created_at DESC ` ); - return result.rows.map((row) => ({ + return result.rows.map((row: any) => ({ title: row.title, slug: row.slug, description: row.description,