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,119 +0,0 @@
|
||||
import { GET, PUT, DELETE } from './route';
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
jest.mock('@/lib/auth', () => ({
|
||||
auth: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@/lib/auth/permissions', () => ({
|
||||
hasPermission: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@/lib/auth/check-permission', () => ({
|
||||
checkIsAdmin: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@/db', () => ({
|
||||
db: {
|
||||
select: jest.fn().mockReturnValue({
|
||||
from: jest.fn().mockReturnValue({
|
||||
where: jest.fn().mockReturnValue({
|
||||
limit: jest.fn().mockResolvedValue([{
|
||||
id: 'test-user-id',
|
||||
email: 'test@example.com',
|
||||
name: 'Test User',
|
||||
isAdmin: true,
|
||||
}]),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
update: jest.fn().mockReturnValue({
|
||||
set: jest.fn().mockReturnValue({
|
||||
where: jest.fn().mockReturnValue({
|
||||
returning: jest.fn().mockResolvedValue([{
|
||||
id: 'test-user-id',
|
||||
email: 'updated@example.com',
|
||||
name: 'Updated User',
|
||||
}]),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
delete: jest.fn().mockReturnValue({
|
||||
where: jest.fn().mockResolvedValue(undefined),
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
const { checkIsAdmin: mockCheckIsAdmin } = require('@/lib/auth/check-permission');
|
||||
|
||||
describe('/api/admin/users/[id]', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('GET', () => {
|
||||
it('should return 401 if not authenticated', async () => {
|
||||
mockCheckIsAdmin.mockResolvedValueOnce({ isAdmin: false });
|
||||
|
||||
const request = new NextRequest('http://localhost/api/admin/users/test-id');
|
||||
const response = await GET(request, { params: Promise.resolve({ id: 'test-id' }) });
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(403);
|
||||
expect(data.error).toBe('无权限执行此操作');
|
||||
});
|
||||
|
||||
it('should return 403 if no permission', async () => {
|
||||
mockCheckIsAdmin.mockResolvedValueOnce({ isAdmin: false });
|
||||
|
||||
const request = new NextRequest('http://localhost/api/admin/users/test-id');
|
||||
const response = await GET(request, { params: Promise.resolve({ id: 'test-id' }) });
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(403);
|
||||
expect(data.error).toBe('无权限执行此操作');
|
||||
});
|
||||
|
||||
it('should return user if authenticated and has permission', async () => {
|
||||
mockCheckIsAdmin.mockResolvedValueOnce({ isAdmin: true, userId: '1' });
|
||||
|
||||
const request = new NextRequest('http://localhost/api/admin/users/test-id');
|
||||
const response = await GET(request, { params: Promise.resolve({ id: 'test-id' }) });
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(data.user).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT', () => {
|
||||
it('should return 401 if not authenticated', async () => {
|
||||
mockCheckIsAdmin.mockResolvedValueOnce({ isAdmin: false });
|
||||
|
||||
const request = new NextRequest('http://localhost/api/admin/users/test-id', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ name: 'Updated User' }),
|
||||
});
|
||||
const response = await PUT(request, { params: Promise.resolve({ id: 'test-id' }) });
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(403);
|
||||
expect(data.error).toBe('无权限执行此操作');
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE', () => {
|
||||
it('should return 401 if not authenticated', async () => {
|
||||
mockCheckIsAdmin.mockResolvedValueOnce({ isAdmin: false });
|
||||
|
||||
const request = new NextRequest('http://localhost/api/admin/users/test-id', {
|
||||
method: 'DELETE',
|
||||
});
|
||||
const response = await DELETE(request, { params: Promise.resolve({ id: 'test-id' }) });
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(403);
|
||||
expect(data.error).toBe('无权限执行此操作');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,121 +0,0 @@
|
||||
import { NextRequest } from 'next/server';
|
||||
import { db } from '@/db';
|
||||
import { users } from '@/db/schema';
|
||||
import { checkIsAdmin } from '@/lib/auth/check-permission';
|
||||
import { forbidden, notFound, success, handleApiError } from '@/lib/api-response';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import bcrypt from 'bcryptjs';
|
||||
|
||||
export async function GET(
|
||||
_request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { isAdmin } = await checkIsAdmin();
|
||||
|
||||
if (!isAdmin) {
|
||||
return forbidden();
|
||||
}
|
||||
|
||||
const { id } = await params;
|
||||
|
||||
const user = await db
|
||||
.select({
|
||||
id: users.id,
|
||||
email: users.email,
|
||||
name: users.name,
|
||||
isAdmin: users.isAdmin,
|
||||
createdAt: users.createdAt,
|
||||
updatedAt: users.updatedAt,
|
||||
})
|
||||
.from(users)
|
||||
.where(eq(users.id, id))
|
||||
.limit(1);
|
||||
|
||||
if (user.length === 0) {
|
||||
return notFound('用户不存在');
|
||||
}
|
||||
|
||||
return success({ user: user[0] });
|
||||
} catch (error) {
|
||||
return handleApiError(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function PUT(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { isAdmin } = await checkIsAdmin();
|
||||
|
||||
if (!isAdmin) {
|
||||
return forbidden();
|
||||
}
|
||||
|
||||
const { id } = await params;
|
||||
|
||||
const body = await request.json();
|
||||
const { email, name, password } = body;
|
||||
|
||||
const existingUser = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.id, id))
|
||||
.limit(1);
|
||||
|
||||
if (existingUser.length === 0) {
|
||||
return notFound('用户不存在');
|
||||
}
|
||||
|
||||
const updateData: Record<string, unknown> = {
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
if (email) updateData.email = email;
|
||||
if (name) updateData.name = name;
|
||||
if (password) {
|
||||
updateData.passwordHash = await bcrypt.hash(password, 10);
|
||||
}
|
||||
|
||||
const updated = await db
|
||||
.update(users)
|
||||
.set(updateData)
|
||||
.where(eq(users.id, id))
|
||||
.returning();
|
||||
|
||||
return success({
|
||||
user: {
|
||||
id: updated[0]!.id,
|
||||
email: updated[0]!.email,
|
||||
name: updated[0]!.name,
|
||||
isAdmin: updated[0]!.isAdmin,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
return handleApiError(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function DELETE(
|
||||
_request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { isAdmin } = await checkIsAdmin();
|
||||
|
||||
if (!isAdmin) {
|
||||
return forbidden();
|
||||
}
|
||||
|
||||
const { id } = await params;
|
||||
|
||||
await db
|
||||
.delete(users)
|
||||
.where(eq(users.id, id));
|
||||
|
||||
return success({ success: true });
|
||||
} catch (error) {
|
||||
return handleApiError(error);
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
import { describe, it, expect, jest, beforeAll, beforeEach } from '@jest/globals';
|
||||
import { NextRequest } from 'next/server';
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
const mockAuth = jest.fn();
|
||||
const mockHasPermission = jest.fn();
|
||||
const mockCheckIsAdmin = jest.fn();
|
||||
const mockDbSelect = jest.fn();
|
||||
const mockDbInsert = jest.fn();
|
||||
|
||||
jest.mock('@/lib/auth', () => ({
|
||||
auth: mockAuth,
|
||||
}));
|
||||
|
||||
jest.mock('@/lib/auth/check-permission', () => ({
|
||||
checkIsAdmin: mockCheckIsAdmin,
|
||||
}));
|
||||
|
||||
jest.mock('@/lib/auth/permissions', () => ({
|
||||
hasPermission: mockHasPermission,
|
||||
}));
|
||||
|
||||
jest.mock('@/db', () => ({
|
||||
db: {
|
||||
select: () => ({
|
||||
from: () => ({
|
||||
where: () => ({
|
||||
limit: mockDbSelect,
|
||||
}),
|
||||
orderBy: () => mockDbSelect(),
|
||||
}),
|
||||
}),
|
||||
insert: () => ({
|
||||
values: () => ({
|
||||
returning: mockDbInsert,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('drizzle-orm', () => ({
|
||||
eq: jest.fn(),
|
||||
desc: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('nanoid', () => ({
|
||||
nanoid: () => 'test-id-123',
|
||||
}));
|
||||
|
||||
jest.mock('bcryptjs', () => ({
|
||||
hash: jest.fn().mockResolvedValue('hashed-password'),
|
||||
}));
|
||||
|
||||
jest.mock('@/db/schema', () => ({
|
||||
users: {},
|
||||
}));
|
||||
|
||||
import { GET } from './route';
|
||||
|
||||
describe('/api/admin/users', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('GET', () => {
|
||||
it('should return 401 when not authenticated', async () => {
|
||||
mockCheckIsAdmin.mockResolvedValueOnce({ isAdmin: false });
|
||||
|
||||
const request = new NextRequest('http://localhost/api/admin/users');
|
||||
const response = await GET(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(403);
|
||||
expect(data.error).toBe('无权限执行此操作');
|
||||
});
|
||||
|
||||
it('should return 403 when user lacks permission', async () => {
|
||||
mockCheckIsAdmin.mockResolvedValueOnce({ isAdmin: false, userId: '1' });
|
||||
|
||||
const request = new NextRequest('http://localhost/api/admin/users');
|
||||
const response = await GET(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(403);
|
||||
expect(data.error).toBe('无权限执行此操作');
|
||||
});
|
||||
|
||||
it('should return users list when authorized', async () => {
|
||||
mockCheckIsAdmin.mockResolvedValueOnce({ isAdmin: true, userId: '1' });
|
||||
mockDbSelect.mockResolvedValueOnce([
|
||||
{ id: '1', email: 'admin@example.com', name: 'Admin', isAdmin: true },
|
||||
]);
|
||||
|
||||
const request = new NextRequest('http://localhost/api/admin/users');
|
||||
const response = await GET(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(data.users).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,33 +0,0 @@
|
||||
import { NextRequest } from 'next/server';
|
||||
import { db } from '@/db';
|
||||
import { users } from '@/db/schema';
|
||||
import { checkIsAdmin } from '@/lib/auth/check-permission';
|
||||
import { forbidden, success, handleApiError } from '@/lib/api-response';
|
||||
import { desc } from 'drizzle-orm';
|
||||
|
||||
export async function GET(_request: NextRequest) {
|
||||
try {
|
||||
const { isAdmin } = await checkIsAdmin();
|
||||
|
||||
if (!isAdmin) {
|
||||
return forbidden();
|
||||
}
|
||||
|
||||
const allUsers = await db
|
||||
.select({
|
||||
id: users.id,
|
||||
email: users.email,
|
||||
name: users.name,
|
||||
isAdmin: users.isAdmin,
|
||||
createdAt: users.createdAt,
|
||||
updatedAt: users.updatedAt,
|
||||
})
|
||||
.from(users)
|
||||
.orderBy(desc(users.createdAt));
|
||||
|
||||
return success({ users: allUsers });
|
||||
} catch (error) {
|
||||
console.error('获取用户列表失败:', error);
|
||||
return handleApiError(error);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user