Initial commit

This commit is contained in:
twotalesanimation
2026-06-11 10:46:09 +02:00
commit 81ad7e4ea9
223 changed files with 39530 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
// app/api/admin/stats/route.ts
import { NextResponse } from 'next/server';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth-options';
import { prisma } from '@/lib/prisma';
import { Prisma } from '@prisma/client';
export async function GET(req: Request) {
try {
const session = await getServerSession(authOptions);
if (!session?.user?.email)
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
const role = (session as any)?.user?.role ?? null;
if (!(role === 'admin' || role === 'superadmin')) {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}
// Fetch all videos with aggregated stats using raw queries for better performance
const videos = await prisma.video.findMany({
include: {
playlist: {
select: {
title: true,
},
},
uploader: {
select: {
id: true,
name: true,
},
},
_count: {
select: {
likes: true,
comments: true,
unlocks: true,
},
},
},
orderBy: { createdAt: 'desc' },
});
// Fetch detailed stats for each video using raw queries for better performance
const statsData = await prisma.$queryRaw<
Array<{
videoId: string;
totalViewers: number;
totalSecondsWatched: bigint;
avgPercentWatched: number | null;
totalSegments: number;
completionCount: number;
}>
>`
SELECT
p."videoId",
COUNT(DISTINCT p."userId") as "totalViewers",
CAST(COALESCE(SUM(p."watchedSec"), 0) AS BIGINT) as "totalSecondsWatched",
ROUND(CAST(AVG(p."percent") AS NUMERIC), 2) as "avgPercentWatched",
(SELECT COUNT(*) FROM "VideoWatchSegment" ws WHERE ws."videoId" = p."videoId") as "totalSegments",
COUNT(CASE WHEN p."completed" = true THEN 1 END) as "completionCount"
FROM "VideoProgress" p
GROUP BY p."videoId"
`;
// Create a map for quick lookup
const statsMap = new Map(statsData.map(item => [item.videoId, item]));
// Combine video data with stats
const videosWithStats = videos.map(video => {
const stats = statsMap.get(video.id);
const views = video._count.unlocks || 0;
const totalViewers = stats ? Number(stats.totalViewers) : 0;
const completions = stats ? Number(stats.completionCount) : 0;
const engagement = (video._count.likes || 0) + (video._count.comments || 0);
const avgWatched = stats?.avgPercentWatched ? Number(stats.avgPercentWatched) : 0;
return {
id: video.id,
title: video.title,
playlistTitle: video.playlist.title,
uploaderName: video.uploader?.name || 'Unknown',
views,
totalViewers,
completions,
completionRate: totalViewers > 0 ? ((completions / totalViewers) * 100).toFixed(2) : '0.00',
likes: video._count.likes,
comments: video._count.comments,
engagement,
avgPercentWatched: avgWatched,
totalSecondsWatched: stats ? Number(stats.totalSecondsWatched) : 0,
totalSegments: stats ? Number(stats.totalSegments) : 0,
durationSec: video.durationSec,
createdAt: video.createdAt,
};
});
return NextResponse.json(videosWithStats);
} catch (err: any) {
console.error('get stats error', err);
return NextResponse.json(
{ error: err?.message ?? 'server error' },
{ status: 500 }
);
}
}