feat: implement frontend-backend configuration linkage

- Create public config API for frontend consumption
- Add configuration fetching to homepage
- Implement module show/hide logic based on config
- Add support for Services items filtering
- Add support for Products featured products and pricing display
- Add support for News display count, categories, and sort order
- Fix table name from 'configs' to 'siteConfig' in API route
- Update type definitions for proper TypeScript support
This commit is contained in:
张翔
2026-03-13 13:11:20 +08:00
parent f93f802427
commit 4fdfc2d8b4
100 changed files with 894 additions and 316 deletions
+70 -60
View File
@@ -9,18 +9,24 @@ jest.mock('@/lib/auth/permissions', () => ({
hasPermission: jest.fn(),
}));
jest.mock('@/db', () => ({
db: {
select: jest.fn().mockReturnValue({
from: jest.fn().mockReturnValue({
where: jest.fn().mockReturnValue({
limit: jest.fn().mockResolvedValue([]),
orderBy: jest.fn().mockResolvedValue([]),
}),
jest.mock('@/lib/auth/check-permission', () => ({
checkIsAdmin: jest.fn(),
getAdminUserId: jest.fn(),
}));
jest.mock('@/db', () => {
const mockSelect = jest.fn().mockReturnValue({
from: jest.fn().mockReturnValue({
where: jest.fn().mockReturnValue({
limit: jest.fn().mockResolvedValue([]),
orderBy: jest.fn().mockResolvedValue([]),
}),
}),
insert: jest.fn().mockReturnValue({
values: jest.fn().mockReturnValue({
});
const mockUpdate = jest.fn().mockReturnValue({
set: jest.fn().mockReturnValue({
where: jest.fn().mockReturnValue({
returning: jest.fn().mockResolvedValue([{
id: 'test-id',
key: 'test_key',
@@ -29,8 +35,27 @@ jest.mock('@/db', () => ({
}]),
}),
}),
},
}));
});
return {
db: {
select: mockSelect,
update: mockUpdate,
insert: jest.fn().mockReturnValue({
values: jest.fn().mockReturnValue({
returning: jest.fn().mockResolvedValue([{
id: 'test-id',
key: 'test_key',
value: 'test_value',
category: 'general',
}]),
}),
}),
},
};
});
const { checkIsAdmin: mockCheckIsAdmin, getAdminUserId: mockGetAdminUserId } = require('@/lib/auth/check-permission');
describe('/api/admin/config', () => {
beforeEach(() => {
@@ -39,35 +64,29 @@ describe('/api/admin/config', () => {
describe('GET', () => {
it('should return 401 if not authenticated', async () => {
mockCheckIsAdmin.mockResolvedValueOnce({ isAdmin: false });
const request = new NextRequest('http://localhost/api/admin/config');
const response = await GET(request);
const data = await response.json();
expect(response.status).toBe(401);
expect(data.error).toBe('未授权');
expect(response.status).toBe(403);
expect(data.error).toBe('无权限执行此操作');
});
it('should return 403 if no permission', async () => {
const { auth } = require('@/lib/auth');
const { hasPermission } = require('@/lib/auth/permissions');
auth.mockResolvedValue({ user: { role: 'viewer' } });
hasPermission.mockReturnValue(false);
mockCheckIsAdmin.mockResolvedValueOnce({ isAdmin: false });
const request = new NextRequest('http://localhost/api/admin/config');
const response = await GET(request);
const data = await response.json();
expect(response.status).toBe(403);
expect(data.error).toBe('无权限');
expect(data.error).toBe('无权限执行此操作');
});
it('should return configs if authenticated and has permission', async () => {
const { auth } = require('@/lib/auth');
const { hasPermission } = require('@/lib/auth/permissions');
auth.mockResolvedValue({ user: { role: 'admin' } });
hasPermission.mockReturnValue(true);
mockCheckIsAdmin.mockResolvedValueOnce({ isAdmin: true, userId: '1' });
const request = new NextRequest('http://localhost/api/admin/config');
const response = await GET(request);
@@ -81,8 +100,8 @@ describe('/api/admin/config', () => {
describe('POST', () => {
it('should return 401 if not authenticated', async () => {
const { auth } = require('@/lib/auth');
auth.mockResolvedValue(null);
mockCheckIsAdmin.mockResolvedValueOnce({ isAdmin: false });
mockGetAdminUserId.mockResolvedValueOnce(null);
const request = new NextRequest('http://localhost/api/admin/config', {
method: 'POST',
@@ -91,16 +110,13 @@ describe('/api/admin/config', () => {
const response = await POST(request);
const data = await response.json();
expect(response.status).toBe(401);
expect(data.error).toBe('未授权');
expect(response.status).toBe(403);
expect(data.error).toBe('无权限执行此操作');
});
it('should return 400 if missing required fields', async () => {
const { auth } = require('@/lib/auth');
const { hasPermission } = require('@/lib/auth/permissions');
auth.mockResolvedValue({ user: { role: 'admin' } });
hasPermission.mockReturnValue(true);
mockCheckIsAdmin.mockResolvedValueOnce({ isAdmin: true, userId: '1' });
mockGetAdminUserId.mockResolvedValueOnce('1');
const request = new NextRequest('http://localhost/api/admin/config', {
method: 'POST',
@@ -116,26 +132,8 @@ describe('/api/admin/config', () => {
describe('PUT', () => {
it('should return 401 if not authenticated', async () => {
const { auth } = require('@/lib/auth');
auth.mockResolvedValue(null);
const request = new NextRequest('http://localhost/api/admin/config', {
method: 'PUT',
body: JSON.stringify({ configs: [] }),
});
const response = await PUT(request);
const data = await response.json();
expect(response.status).toBe(401);
expect(data.error).toBe('未授权');
});
it('should return 403 if no permission', async () => {
const { auth } = require('@/lib/auth');
const { hasPermission } = require('@/lib/auth/permissions');
auth.mockResolvedValue({ user: { role: 'viewer' } });
hasPermission.mockReturnValue(false);
mockCheckIsAdmin.mockResolvedValueOnce({ isAdmin: false });
mockGetAdminUserId.mockResolvedValueOnce(null);
const request = new NextRequest('http://localhost/api/admin/config', {
method: 'PUT',
@@ -145,15 +143,27 @@ describe('/api/admin/config', () => {
const data = await response.json();
expect(response.status).toBe(403);
expect(data.error).toBe('无权限');
expect(data.error).toBe('无权限执行此操作');
});
it('should return 403 if no permission', async () => {
mockCheckIsAdmin.mockResolvedValueOnce({ isAdmin: false });
mockGetAdminUserId.mockResolvedValueOnce(null);
const request = new NextRequest('http://localhost/api/admin/config', {
method: 'PUT',
body: JSON.stringify({ configs: [] }),
});
const response = await PUT(request);
const data = await response.json();
expect(response.status).toBe(403);
expect(data.error).toBe('无权限执行此操作');
});
it('should return 400 if configs is not an array', async () => {
const { auth } = require('@/lib/auth');
const { hasPermission } = require('@/lib/auth/permissions');
auth.mockResolvedValue({ user: { role: 'admin' } });
hasPermission.mockReturnValue(true);
mockCheckIsAdmin.mockResolvedValueOnce({ isAdmin: true, userId: '1' });
mockGetAdminUserId.mockResolvedValueOnce('1');
const request = new NextRequest('http://localhost/api/admin/config', {
method: 'PUT',