feat: Docker部署与CI/CD集成, 搜索栏修复, 上传目录改为data
This commit is contained in:
26
lib/middleware/adminMiddleware.ts
Normal file
26
lib/middleware/adminMiddleware.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { NextApiResponse } from 'next';
|
||||
import { AuthenticatedRequest, requireAuth } from './authMiddleware';
|
||||
import { UserRole } from '../../types';
|
||||
|
||||
/**
|
||||
* Middleware to require admin authentication
|
||||
* Returns 403 if user is not an admin
|
||||
*/
|
||||
export async function requireAdmin(
|
||||
req: AuthenticatedRequest,
|
||||
res: NextApiResponse
|
||||
): Promise<boolean> {
|
||||
// First check if user is authenticated
|
||||
const isAuthenticated = await requireAuth(req, res);
|
||||
if (!isAuthenticated) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if user has admin role
|
||||
if (req.user?.role !== UserRole.ADMIN) {
|
||||
res.status(403).json({ success: false, error: 'Admin access required' });
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
89
lib/middleware/authMiddleware.ts
Normal file
89
lib/middleware/authMiddleware.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { verifyToken } from '../auth';
|
||||
import { UserService } from '../../backend/services/userService';
|
||||
import { UserDTO } from '../../types';
|
||||
|
||||
// Extend NextApiRequest to include user property
|
||||
export interface AuthenticatedRequest extends NextApiRequest {
|
||||
user?: UserDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware to require authentication
|
||||
* Returns 401 if no valid token is found
|
||||
*/
|
||||
export async function requireAuth(
|
||||
req: AuthenticatedRequest,
|
||||
res: NextApiResponse
|
||||
): Promise<boolean> {
|
||||
const token = getTokenFromRequest(req);
|
||||
|
||||
if (!token) {
|
||||
res.status(401).json({ success: false, error: 'Authentication required' });
|
||||
return false;
|
||||
}
|
||||
|
||||
const decoded = verifyToken(token);
|
||||
if (!decoded) {
|
||||
res.status(401).json({ success: false, error: 'Invalid or expired token' });
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load user from database
|
||||
const user = await UserService.getUserById(decoded.userId);
|
||||
if (!user) {
|
||||
res.status(401).json({ success: false, error: 'User not found' });
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if user is banned
|
||||
if (user.status === 'BANNED') {
|
||||
res.status(403).json({ success: false, error: 'Account has been banned' });
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attach user to request
|
||||
req.user = user;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware for optional authentication
|
||||
* Attaches user to request if valid token exists, but doesn't fail if not
|
||||
*/
|
||||
export async function optionalAuth(req: AuthenticatedRequest): Promise<void> {
|
||||
const token = getTokenFromRequest(req);
|
||||
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
|
||||
const decoded = verifyToken(token);
|
||||
if (!decoded) {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await UserService.getUserById(decoded.userId);
|
||||
if (user && user.status !== 'BANNED') {
|
||||
req.user = user;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract JWT token from request
|
||||
* Checks both cookies and Authorization header
|
||||
*/
|
||||
function getTokenFromRequest(req: NextApiRequest): string | null {
|
||||
// Check cookie first
|
||||
if (req.cookies.token) {
|
||||
return req.cookies.token;
|
||||
}
|
||||
|
||||
// Check Authorization header
|
||||
const authHeader = req.headers.authorization;
|
||||
if (authHeader && authHeader.startsWith('Bearer ')) {
|
||||
return authHeader.substring(7);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user