219 lines
5.0 KiB
TypeScript
219 lines
5.0 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { cookies } from 'next/headers';
|
|
import { db } from '@/lib/db';
|
|
|
|
export const dynamic = 'force-dynamic';
|
|
|
|
export async function GET(request: NextRequest) {
|
|
try {
|
|
// Check newsletter-admin cookie authentication
|
|
const cookieStore = cookies();
|
|
const adminCookie = cookieStore.get('newsletter-admin');
|
|
|
|
if (!adminCookie || adminCookie.value !== 'authenticated') {
|
|
return NextResponse.json(
|
|
{ error: 'Unauthorized - Admin login required' },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
// Get 30 days ago date
|
|
const thirtyDaysAgo = new Date();
|
|
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
|
|
|
|
// Get 7 days ago date
|
|
const sevenDaysAgo = new Date();
|
|
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
|
|
|
|
// Get start of current month
|
|
const startOfMonth = new Date();
|
|
startOfMonth.setDate(1);
|
|
startOfMonth.setHours(0, 0, 0, 0);
|
|
|
|
// Fetch all statistics in parallel
|
|
const [
|
|
totalUsers,
|
|
premiumUsers,
|
|
newUsersThisWeek,
|
|
newUsersThisMonth,
|
|
totalQRCodes,
|
|
dynamicQRCodes,
|
|
staticQRCodes,
|
|
totalScans,
|
|
dynamicQRCodesWithScans,
|
|
activeQRCodes,
|
|
newsletterSubscribers,
|
|
] = await Promise.all([
|
|
// Total users
|
|
db.user.count(),
|
|
|
|
// Premium users (PRO or BUSINESS)
|
|
db.user.count({
|
|
where: {
|
|
plan: {
|
|
in: ['PRO', 'BUSINESS'],
|
|
},
|
|
},
|
|
}),
|
|
|
|
// New users this week
|
|
db.user.count({
|
|
where: {
|
|
createdAt: {
|
|
gte: sevenDaysAgo,
|
|
},
|
|
},
|
|
}),
|
|
|
|
// New users this month
|
|
db.user.count({
|
|
where: {
|
|
createdAt: {
|
|
gte: startOfMonth,
|
|
},
|
|
},
|
|
}),
|
|
|
|
// Total QR codes
|
|
db.qRCode.count(),
|
|
|
|
// Dynamic QR codes
|
|
db.qRCode.count({
|
|
where: {
|
|
type: 'DYNAMIC',
|
|
},
|
|
}),
|
|
|
|
// Static QR codes
|
|
db.qRCode.count({
|
|
where: {
|
|
type: 'STATIC',
|
|
},
|
|
}),
|
|
|
|
// Total scans
|
|
db.qRScan.count(),
|
|
|
|
// Get all dynamic QR codes with their scan counts
|
|
db.qRCode.findMany({
|
|
where: {
|
|
type: 'DYNAMIC',
|
|
},
|
|
include: {
|
|
_count: {
|
|
select: {
|
|
scans: true,
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
|
|
// Active QR codes (scanned in last 30 days)
|
|
db.qRCode.findMany({
|
|
where: {
|
|
scans: {
|
|
some: {
|
|
ts: {
|
|
gte: thirtyDaysAgo,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
distinct: ['id'],
|
|
}),
|
|
|
|
// Newsletter subscribers
|
|
db.newsletterSubscription.count({
|
|
where: {
|
|
status: 'subscribed',
|
|
},
|
|
}),
|
|
]);
|
|
|
|
// Calculate dynamic QR scans
|
|
const dynamicQRScans = dynamicQRCodesWithScans.reduce(
|
|
(total, qr) => total + qr._count.scans,
|
|
0
|
|
);
|
|
|
|
// Calculate average scans per dynamic QR
|
|
const avgScansPerDynamicQR =
|
|
dynamicQRCodes > 0 ? (dynamicQRScans / dynamicQRCodes).toFixed(1) : '0';
|
|
|
|
// Get top 5 most scanned QR codes
|
|
const topQRCodes = await db.qRCode.findMany({
|
|
take: 5,
|
|
include: {
|
|
_count: {
|
|
select: {
|
|
scans: true,
|
|
},
|
|
},
|
|
user: {
|
|
select: {
|
|
email: true,
|
|
name: true,
|
|
},
|
|
},
|
|
},
|
|
orderBy: {
|
|
scans: {
|
|
_count: 'desc',
|
|
},
|
|
},
|
|
});
|
|
|
|
// Get recent users
|
|
const recentUsers = await db.user.findMany({
|
|
take: 5,
|
|
orderBy: {
|
|
createdAt: 'desc',
|
|
},
|
|
select: {
|
|
email: true,
|
|
name: true,
|
|
plan: true,
|
|
createdAt: true,
|
|
},
|
|
});
|
|
|
|
return NextResponse.json({
|
|
users: {
|
|
total: totalUsers,
|
|
premium: premiumUsers,
|
|
newThisWeek: newUsersThisWeek,
|
|
newThisMonth: newUsersThisMonth,
|
|
recent: recentUsers,
|
|
},
|
|
qrCodes: {
|
|
total: totalQRCodes,
|
|
dynamic: dynamicQRCodes,
|
|
static: staticQRCodes,
|
|
active: activeQRCodes.length,
|
|
},
|
|
scans: {
|
|
total: totalScans,
|
|
dynamicOnly: dynamicQRScans,
|
|
avgPerDynamicQR: avgScansPerDynamicQR,
|
|
},
|
|
newsletter: {
|
|
subscribers: newsletterSubscribers,
|
|
},
|
|
topQRCodes: topQRCodes.map((qr) => ({
|
|
id: qr.id,
|
|
title: qr.title,
|
|
type: qr.type,
|
|
scans: qr._count.scans,
|
|
owner: qr.user.name || qr.user.email,
|
|
createdAt: qr.createdAt,
|
|
})),
|
|
});
|
|
} catch (error) {
|
|
console.error('Error fetching admin stats:', error);
|
|
return NextResponse.json(
|
|
{ error: 'Failed to fetch statistics' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|