✅ Updated Middleware Logic: - Enhanced admin route protection with Basic Auth for legacy routes and session-based auth for `/manage` and `/editor`. ✅ Improved Admin Panel Styles: - Added glassmorphism styles for admin components to enhance UI aesthetics. ✅ Refined Rate Limiting: - Adjusted rate limits for admin dashboard requests to allow more generous access. ✅ Introduced Analytics Reset API: - Added a new endpoint for resetting analytics data with rate limiting and admin authentication. 🎯 Overall Improvements: - Strengthened security and user experience for admin functionalities. - Enhanced visual design for better usability. - Streamlined analytics management processes.
75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
import { NextRequest } from 'next/server';
|
|
|
|
// Server-side authentication utilities
|
|
export function verifyAdminAuth(request: NextRequest): boolean {
|
|
// Check for basic auth header
|
|
const authHeader = request.headers.get('authorization');
|
|
|
|
if (!authHeader || !authHeader.startsWith('Basic ')) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
const base64Credentials = authHeader.split(' ')[1];
|
|
const credentials = atob(base64Credentials);
|
|
const [username, password] = credentials.split(':');
|
|
|
|
// Get admin credentials from environment
|
|
const adminAuth = process.env.ADMIN_BASIC_AUTH || 'admin:default_password_change_me';
|
|
const [expectedUsername, expectedPassword] = adminAuth.split(':');
|
|
|
|
return username === expectedUsername && password === expectedPassword;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function requireAdminAuth(request: NextRequest): Response | null {
|
|
if (!verifyAdminAuth(request)) {
|
|
return new Response(
|
|
JSON.stringify({ error: 'Unauthorized' }),
|
|
{
|
|
status: 401,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'WWW-Authenticate': 'Basic realm="Admin Access"'
|
|
}
|
|
}
|
|
);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Rate limiting for admin endpoints
|
|
const rateLimitMap = new Map<string, { count: number; resetTime: number }>();
|
|
|
|
export function checkRateLimit(ip: string, maxRequests: number = 10, windowMs: number = 60000): boolean {
|
|
const now = Date.now();
|
|
const key = `admin_${ip}`;
|
|
|
|
const current = rateLimitMap.get(key);
|
|
|
|
if (!current || now > current.resetTime) {
|
|
rateLimitMap.set(key, { count: 1, resetTime: now + windowMs });
|
|
return true;
|
|
}
|
|
|
|
if (current.count >= maxRequests) {
|
|
return false;
|
|
}
|
|
|
|
current.count++;
|
|
return true;
|
|
}
|
|
|
|
export function getRateLimitHeaders(ip: string, maxRequests: number = 10, windowMs: number = 60000): Record<string, string> {
|
|
const current = rateLimitMap.get(`admin_${ip}`);
|
|
const remaining = current ? Math.max(0, maxRequests - current.count) : maxRequests;
|
|
|
|
return {
|
|
'X-RateLimit-Limit': maxRequests.toString(),
|
|
'X-RateLimit-Remaining': remaining.toString(),
|
|
'X-RateLimit-Reset': current ? Math.ceil(current.resetTime / 1000).toString() : Math.ceil((Date.now() + windowMs) / 1000).toString()
|
|
};
|
|
}
|