feat: Docker部署与CI/CD集成, 搜索栏修复, 上传目录改为data

This commit is contained in:
xiner
2025-11-28 18:42:30 +08:00
commit 8351d6bbfc
243 changed files with 13192 additions and 0 deletions

View 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;
}

View 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;
}