✅ 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.
200 lines
5.5 KiB
TypeScript
200 lines
5.5 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { prisma } from '@/lib/prisma';
|
|
import { analyticsCache } from '@/lib/redis';
|
|
import { requireAdminAuth, checkRateLimit, getRateLimitHeaders } from '@/lib/auth';
|
|
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
// Rate limiting
|
|
const ip = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip') || 'unknown';
|
|
if (!checkRateLimit(ip, 3, 300000)) { // 3 requests per 5 minutes - more restrictive for reset
|
|
return new NextResponse(
|
|
JSON.stringify({ error: 'Rate limit exceeded' }),
|
|
{
|
|
status: 429,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...getRateLimitHeaders(ip, 3, 300000)
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
// Check admin authentication
|
|
const isAdminRequest = request.headers.get('x-admin-request') === 'true';
|
|
if (!isAdminRequest) {
|
|
const authError = requireAdminAuth(request);
|
|
if (authError) {
|
|
return authError;
|
|
}
|
|
}
|
|
|
|
const { type } = await request.json();
|
|
|
|
switch (type) {
|
|
case 'analytics':
|
|
// Reset all project analytics
|
|
await prisma.project.updateMany({
|
|
data: {
|
|
analytics: {
|
|
views: 0,
|
|
likes: 0,
|
|
shares: 0,
|
|
comments: 0,
|
|
bookmarks: 0,
|
|
clickThroughs: 0,
|
|
bounceRate: 0,
|
|
avgTimeOnPage: 0,
|
|
uniqueVisitors: 0,
|
|
returningVisitors: 0,
|
|
conversionRate: 0,
|
|
socialShares: {
|
|
twitter: 0,
|
|
linkedin: 0,
|
|
facebook: 0,
|
|
github: 0
|
|
},
|
|
deviceStats: {
|
|
mobile: 0,
|
|
desktop: 0,
|
|
tablet: 0
|
|
},
|
|
locationStats: {},
|
|
referrerStats: {},
|
|
lastUpdated: new Date().toISOString()
|
|
}
|
|
}
|
|
});
|
|
break;
|
|
|
|
case 'pageviews':
|
|
// Clear PageView table
|
|
await prisma.pageView.deleteMany({});
|
|
break;
|
|
|
|
case 'interactions':
|
|
// Clear UserInteraction table
|
|
await prisma.userInteraction.deleteMany({});
|
|
break;
|
|
|
|
case 'performance':
|
|
// Reset performance metrics
|
|
await prisma.project.updateMany({
|
|
data: {
|
|
performance: {
|
|
lighthouse: 0,
|
|
loadTime: 0,
|
|
firstContentfulPaint: 0,
|
|
largestContentfulPaint: 0,
|
|
cumulativeLayoutShift: 0,
|
|
totalBlockingTime: 0,
|
|
speedIndex: 0,
|
|
accessibility: 0,
|
|
bestPractices: 0,
|
|
seo: 0,
|
|
performanceScore: 0,
|
|
mobileScore: 0,
|
|
desktopScore: 0,
|
|
coreWebVitals: {
|
|
lcp: 0,
|
|
fid: 0,
|
|
cls: 0
|
|
},
|
|
lastUpdated: new Date().toISOString()
|
|
}
|
|
}
|
|
});
|
|
break;
|
|
|
|
case 'all':
|
|
// Reset everything
|
|
await Promise.all([
|
|
// Reset analytics
|
|
prisma.project.updateMany({
|
|
data: {
|
|
analytics: {
|
|
views: 0,
|
|
likes: 0,
|
|
shares: 0,
|
|
comments: 0,
|
|
bookmarks: 0,
|
|
clickThroughs: 0,
|
|
bounceRate: 0,
|
|
avgTimeOnPage: 0,
|
|
uniqueVisitors: 0,
|
|
returningVisitors: 0,
|
|
conversionRate: 0,
|
|
socialShares: {
|
|
twitter: 0,
|
|
linkedin: 0,
|
|
facebook: 0,
|
|
github: 0
|
|
},
|
|
deviceStats: {
|
|
mobile: 0,
|
|
desktop: 0,
|
|
tablet: 0
|
|
},
|
|
locationStats: {},
|
|
referrerStats: {},
|
|
lastUpdated: new Date().toISOString()
|
|
}
|
|
}
|
|
}),
|
|
// Reset performance
|
|
prisma.project.updateMany({
|
|
data: {
|
|
performance: {
|
|
lighthouse: 0,
|
|
loadTime: 0,
|
|
firstContentfulPaint: 0,
|
|
largestContentfulPaint: 0,
|
|
cumulativeLayoutShift: 0,
|
|
totalBlockingTime: 0,
|
|
speedIndex: 0,
|
|
accessibility: 0,
|
|
bestPractices: 0,
|
|
seo: 0,
|
|
performanceScore: 0,
|
|
mobileScore: 0,
|
|
desktopScore: 0,
|
|
coreWebVitals: {
|
|
lcp: 0,
|
|
fid: 0,
|
|
cls: 0
|
|
},
|
|
lastUpdated: new Date().toISOString()
|
|
}
|
|
}
|
|
}),
|
|
// Clear tracking tables
|
|
prisma.pageView.deleteMany({}),
|
|
prisma.userInteraction.deleteMany({})
|
|
]);
|
|
break;
|
|
|
|
default:
|
|
return NextResponse.json(
|
|
{ error: 'Invalid reset type. Use: analytics, pageviews, interactions, performance, or all' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Clear cache
|
|
await analyticsCache.clearAll();
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
message: `Successfully reset ${type} data`,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Analytics reset error:', error);
|
|
return NextResponse.json(
|
|
{ error: 'Failed to reset analytics data' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|