- Fixed authentication system (removed HTTP Basic Auth popup) - Added session-based authentication with proper logout - Updated rate limiting (20 req/s for login, 5 req/m for admin) - Created production deployment scripts and configs - Updated nginx configuration for dk0.dev domain - Added comprehensive production deployment guide - Fixed logout button functionality - Optimized for production with proper resource limits
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 { requireSessionAuth, 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 = requireSessionAuth(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 }
|
|
);
|
|
}
|
|
}
|