refactor: 完成静态网站转换,移除所有 CMS 和动态功能

- 删除数据库相关代码 (src/db/)
- 删除 API 路由 (src/app/api/)
- 删除认证相关代码 (src/lib/auth/, src/providers/)
- 删除监控和安全中间件 (src/lib/security/, src/lib/monitoring/)
- 删除 hooks (use-news, use-products, use-services)
- 更新组件为静态数据源
- 添加 nginx 静态配置和部署脚本
- 添加 static-link 组件
This commit is contained in:
张翔
2026-04-21 07:53:56 +08:00
parent cd1d6aa28a
commit 6403489954
197 changed files with 654 additions and 24762 deletions
+7 -4
View File
@@ -1,18 +1,21 @@
'use client';
import { useRouter } from 'next/navigation';
import { ArrowLeft } from 'lucide-react';
import { Button } from '@/components/ui/button';
/**
* BackButton - 统一的返回按钮组件
*
* 在纯静态导出模式下使用 window.history.back() 替代 Next.js 的 router.back()
* 确保在无服务端路由的环境下正常工作。
*/
export function BackButton() {
const router = useRouter();
return (
<Button
variant="ghost"
size="sm"
className="text-[#5C5C5C] hover:text-[#C41E3A] hover:bg-transparent h-auto py-2 px-3"
onClick={() => router.back()}
onClick={() => window.history.back()}
>
<ArrowLeft className="w-4 h-4 mr-2" />
+76
View File
@@ -0,0 +1,76 @@
'use client';
import { useCallback, type AnchorHTMLAttributes, type MouseEventHandler, type ReactNode } from 'react';
/**
* StaticLink - 纯静态站点专用链接组件
*
* 在 output: 'export' 模式下,Next.js 的客户端路由会拦截所有站内 <a> 标签的点击,
* 尝试发送 RSC 请求,导致 "Failed to fetch RSC payload" 错误。
*
* 本组件通过 e.preventDefault() 阻止 Next.js 拦截,然后根据情况导航:
* - 有外部 onClick:只阻止拦截,由外部 onClick 控制导航
* - 外部链接 / 新窗口:不拦截,保持默认行为
* - Hash 链接:平滑滚动
* - 站内链接:window.location.href 完整页面导航
*
* @see https://github.com/vercel/next.js/issues/85374
*/
interface StaticLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
children: ReactNode;
href: string;
}
function isExternalLink(href: string): boolean {
return href.startsWith('http://') || href.startsWith('https://') || href.startsWith('mailto:') || href.startsWith('tel:');
}
export function StaticLink({ children, href, onClick, target, rel, ...props }: StaticLinkProps) {
const handleClick: MouseEventHandler<HTMLAnchorElement> = useCallback(
(e) => {
// 外部链接或新窗口打开:不拦截,保持默认行为
if (isExternalLink(href) || target === '_blank') {
onClick?.(e);
return;
}
// 阻止 Next.js 客户端路由拦截
e.preventDefault();
// 如果有外部 onClick,由它完全控制导航行为
if (onClick) {
onClick(e);
return;
}
// Hash 链接:平滑滚动
if (href.includes('#')) {
const [path, hash] = href.split('#');
if (path && path !== window.location.pathname) {
window.location.href = href;
} else if (hash) {
const el = document.getElementById(hash);
if (el) {
el.scrollIntoView({ behavior: 'smooth' });
}
}
return;
}
// 站内页面链接:完整页面导航
window.location.href = href;
},
[href, onClick, target]
);
// 外部链接自动添加安全属性
const linkRel = isExternalLink(href)
? 'noopener noreferrer'
: rel;
return (
<a href={href} onClick={handleClick} target={target} rel={linkRel} {...props}>
{children}
</a>
);
}