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:
@@ -1,134 +0,0 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { useNews } from './use-news';
|
||||
import { contentService } from '@/lib/api/services';
|
||||
import { NewsItem } from '@/lib/api/types';
|
||||
|
||||
jest.mock('@/lib/api/services');
|
||||
|
||||
describe('useNews', () => {
|
||||
const mockNewsData: NewsItem[] = [
|
||||
{
|
||||
id: '1',
|
||||
title: '测试新闻1',
|
||||
excerpt: '测试摘要1',
|
||||
content: '测试内容1',
|
||||
date: '2024-01-01',
|
||||
category: '公司新闻',
|
||||
slug: 'test-news-1',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: '测试新闻2',
|
||||
excerpt: '测试摘要2',
|
||||
content: '测试内容2',
|
||||
date: '2024-01-02',
|
||||
category: '产品发布',
|
||||
slug: 'test-news-2',
|
||||
},
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('应该成功获取新闻数据', async () => {
|
||||
(contentService.getNews as jest.Mock).mockResolvedValue(mockNewsData);
|
||||
|
||||
const { result } = renderHook(() => useNews());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.news).toEqual(mockNewsData);
|
||||
expect(result.current.error).toBeNull();
|
||||
expect(contentService.getNews).toHaveBeenCalledWith(undefined, undefined, 'desc');
|
||||
});
|
||||
|
||||
it('应该支持分类筛选', async () => {
|
||||
const categories = ['公司新闻'];
|
||||
(contentService.getNews as jest.Mock).mockResolvedValue([mockNewsData[0]]);
|
||||
|
||||
const { result } = renderHook(() => useNews(categories));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.news).toEqual([mockNewsData[0]]);
|
||||
expect(contentService.getNews).toHaveBeenCalledWith(categories, undefined, 'desc');
|
||||
});
|
||||
|
||||
it('应该支持数量限制', async () => {
|
||||
const limit = 1;
|
||||
(contentService.getNews as jest.Mock).mockResolvedValue([mockNewsData[0]]);
|
||||
|
||||
const { result } = renderHook(() => useNews(undefined, limit));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.news).toEqual([mockNewsData[0]]);
|
||||
expect(contentService.getNews).toHaveBeenCalledWith(undefined, limit, 'desc');
|
||||
});
|
||||
|
||||
it('应该支持排序', async () => {
|
||||
(contentService.getNews as jest.Mock).mockResolvedValue(mockNewsData);
|
||||
|
||||
const { result } = renderHook(() => useNews(undefined, undefined, 'asc'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(contentService.getNews).toHaveBeenCalledWith(undefined, undefined, 'asc');
|
||||
});
|
||||
|
||||
it('应该处理获取失败的情况', async () => {
|
||||
const error = new Error('获取新闻失败');
|
||||
(contentService.getNews as jest.Mock).mockRejectedValue(error);
|
||||
|
||||
const { result } = renderHook(() => useNews());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.news).toEqual([]);
|
||||
expect(result.current.error).toEqual(error);
|
||||
});
|
||||
|
||||
it('应该在加载时设置loading状态', () => {
|
||||
(contentService.getNews as jest.Mock).mockImplementation(
|
||||
() => new Promise(() => {})
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useNews());
|
||||
|
||||
expect(result.current.loading).toBe(true);
|
||||
expect(result.current.news).toEqual([]);
|
||||
expect(result.current.error).toBeNull();
|
||||
});
|
||||
|
||||
it('应该在参数变化时重新获取数据', async () => {
|
||||
(contentService.getNews as jest.Mock).mockResolvedValue(mockNewsData);
|
||||
|
||||
const { result, rerender } = renderHook(
|
||||
({ categories }) => useNews(categories),
|
||||
{ initialProps: { categories: ['公司新闻'] } }
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(contentService.getNews).toHaveBeenCalledTimes(1);
|
||||
|
||||
rerender({ categories: ['产品发布'] });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(contentService.getNews).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,32 +0,0 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { contentService } from '@/lib/api/services';
|
||||
import { NewsItem } from '@/lib/api/types';
|
||||
|
||||
export function useNews(
|
||||
categories?: string[],
|
||||
limit?: number,
|
||||
sortOrder: 'asc' | 'desc' = 'desc'
|
||||
) {
|
||||
const [news, setNews] = useState<NewsItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchNews() {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const data = await contentService.getNews(categories, limit, sortOrder);
|
||||
setNews(data);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err : new Error('Failed to fetch news'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
fetchNews();
|
||||
}, [categories, limit, sortOrder]);
|
||||
|
||||
return { news, loading, error };
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { useProducts } from './use-products';
|
||||
import { contentService } from '@/lib/api/services';
|
||||
import { Product } from '@/lib/api/types';
|
||||
|
||||
jest.mock('@/lib/api/services');
|
||||
|
||||
describe('useProducts', () => {
|
||||
const mockProductsData: Product[] = [
|
||||
{
|
||||
id: '1',
|
||||
title: '测试产品1',
|
||||
description: '测试描述1',
|
||||
category: '软件产品',
|
||||
features: ['功能1', '功能2'],
|
||||
benefits: ['优势1', '优势2'],
|
||||
slug: 'test-product-1',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: '测试产品2',
|
||||
description: '测试描述2',
|
||||
category: '云服务',
|
||||
features: ['功能3', '功能4'],
|
||||
benefits: ['优势3', '优势4'],
|
||||
slug: 'test-product-2',
|
||||
},
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('应该成功获取产品数据', async () => {
|
||||
(contentService.getProducts as jest.Mock).mockResolvedValue(mockProductsData);
|
||||
|
||||
const { result } = renderHook(() => useProducts());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.products).toEqual(mockProductsData);
|
||||
expect(result.current.error).toBeNull();
|
||||
expect(contentService.getProducts).toHaveBeenCalledWith(undefined);
|
||||
});
|
||||
|
||||
it('应该支持精选产品筛选', async () => {
|
||||
const featuredIds = ['1'];
|
||||
(contentService.getProducts as jest.Mock).mockResolvedValue([mockProductsData[0]]);
|
||||
|
||||
const { result } = renderHook(() => useProducts(featuredIds));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.products).toEqual([mockProductsData[0]]);
|
||||
expect(contentService.getProducts).toHaveBeenCalledWith(featuredIds);
|
||||
});
|
||||
|
||||
it('应该处理获取失败的情况', async () => {
|
||||
const error = new Error('获取产品失败');
|
||||
(contentService.getProducts as jest.Mock).mockRejectedValue(error);
|
||||
|
||||
const { result } = renderHook(() => useProducts());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.products).toEqual([]);
|
||||
expect(result.current.error).toEqual(error);
|
||||
});
|
||||
|
||||
it('应该在加载时设置loading状态', () => {
|
||||
(contentService.getProducts as jest.Mock).mockImplementation(
|
||||
() => new Promise(() => {})
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useProducts());
|
||||
|
||||
expect(result.current.loading).toBe(true);
|
||||
expect(result.current.products).toEqual([]);
|
||||
expect(result.current.error).toBeNull();
|
||||
});
|
||||
|
||||
it('应该在featuredIds变化时重新获取数据', async () => {
|
||||
(contentService.getProducts as jest.Mock).mockResolvedValue(mockProductsData);
|
||||
|
||||
const { result, rerender } = renderHook(
|
||||
({ featuredIds }) => useProducts(featuredIds),
|
||||
{ initialProps: { featuredIds: ['1'] } }
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(contentService.getProducts).toHaveBeenCalledTimes(1);
|
||||
|
||||
rerender({ featuredIds: ['2'] });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(contentService.getProducts).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('应该处理空数组featuredIds', async () => {
|
||||
(contentService.getProducts as jest.Mock).mockResolvedValue(mockProductsData);
|
||||
|
||||
const { result } = renderHook(() => useProducts([]));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.products).toEqual(mockProductsData);
|
||||
expect(contentService.getProducts).toHaveBeenCalledWith([]);
|
||||
});
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { contentService } from '@/lib/api/services';
|
||||
import { Product } from '@/lib/api/types';
|
||||
|
||||
export function useProducts(featuredIds?: string[]) {
|
||||
const [products, setProducts] = useState<Product[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchProducts() {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const data = await contentService.getProducts(featuredIds);
|
||||
setProducts(data);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err : new Error('Failed to fetch products'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
fetchProducts();
|
||||
}, [featuredIds]);
|
||||
|
||||
return { products, loading, error };
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { useServices } from './use-services';
|
||||
import { contentService } from '@/lib/api/services';
|
||||
import { Service } from '@/lib/api/types';
|
||||
|
||||
jest.mock('@/lib/api/services');
|
||||
|
||||
describe('useServices', () => {
|
||||
const mockServicesData: Service[] = [
|
||||
{
|
||||
id: '1',
|
||||
title: '测试服务1',
|
||||
description: '测试描述1',
|
||||
icon: 'Code',
|
||||
features: ['功能1', '功能2'],
|
||||
benefits: ['优势1', '优势2'],
|
||||
slug: 'test-service-1',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: '测试服务2',
|
||||
description: '测试描述2',
|
||||
icon: 'Cloud',
|
||||
features: ['功能3', '功能4'],
|
||||
benefits: ['优势3', '优势4'],
|
||||
slug: 'test-service-2',
|
||||
},
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('应该成功获取服务数据', async () => {
|
||||
(contentService.getServices as jest.Mock).mockResolvedValue(mockServicesData);
|
||||
|
||||
const { result } = renderHook(() => useServices());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.services).toEqual(mockServicesData);
|
||||
expect(result.current.error).toBeNull();
|
||||
expect(contentService.getServices).toHaveBeenCalledWith(undefined);
|
||||
});
|
||||
|
||||
it('应该支持服务ID筛选', async () => {
|
||||
const ids = ['1'];
|
||||
(contentService.getServices as jest.Mock).mockResolvedValue([mockServicesData[0]]);
|
||||
|
||||
const { result } = renderHook(() => useServices(ids));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.services).toEqual([mockServicesData[0]]);
|
||||
expect(contentService.getServices).toHaveBeenCalledWith(ids);
|
||||
});
|
||||
|
||||
it('应该处理获取失败的情况', async () => {
|
||||
const error = new Error('获取服务失败');
|
||||
(contentService.getServices as jest.Mock).mockRejectedValue(error);
|
||||
|
||||
const { result } = renderHook(() => useServices());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.services).toEqual([]);
|
||||
expect(result.current.error).toEqual(error);
|
||||
});
|
||||
|
||||
it('应该在加载时设置loading状态', () => {
|
||||
(contentService.getServices as jest.Mock).mockImplementation(
|
||||
() => new Promise(() => {})
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useServices());
|
||||
|
||||
expect(result.current.loading).toBe(true);
|
||||
expect(result.current.services).toEqual([]);
|
||||
expect(result.current.error).toBeNull();
|
||||
});
|
||||
|
||||
it('应该在ids变化时重新获取数据', async () => {
|
||||
(contentService.getServices as jest.Mock).mockResolvedValue(mockServicesData);
|
||||
|
||||
const { result, rerender } = renderHook(
|
||||
({ ids }) => useServices(ids),
|
||||
{ initialProps: { ids: ['1'] } }
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(contentService.getServices).toHaveBeenCalledTimes(1);
|
||||
|
||||
rerender({ ids: ['2'] });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(contentService.getServices).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('应该处理空数组ids', async () => {
|
||||
(contentService.getServices as jest.Mock).mockResolvedValue(mockServicesData);
|
||||
|
||||
const { result } = renderHook(() => useServices([]));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(result.current.services).toEqual(mockServicesData);
|
||||
expect(contentService.getServices).toHaveBeenCalledWith([]);
|
||||
});
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { contentService } from '@/lib/api/services';
|
||||
import { Service } from '@/lib/api/types';
|
||||
|
||||
export function useServices(ids?: string[]) {
|
||||
const [services, setServices] = useState<Service[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchServices() {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const data = await contentService.getServices(ids);
|
||||
setServices(data);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err : new Error('Failed to fetch services'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
fetchServices();
|
||||
}, [ids]);
|
||||
|
||||
return { services, loading, error };
|
||||
}
|
||||
Reference in New Issue
Block a user