Files
QR-master/src/app/(main)/api/qrs/[id]/feedback/route.ts
Timo Knuth 30c1e57eab Shema
2026-01-25 14:59:25 +01:00

123 lines
4.4 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import { db } from '@/lib/db';
import { cookies } from 'next/headers';
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
let userId: string | undefined;
// Try NextAuth session first
const session = await getServerSession(authOptions);
if (session?.user?.id) {
userId = session.user.id;
} else {
// Fallback: Check raw userId cookie (like /api/user does)
const cookieStore = await cookies();
userId = cookieStore.get('userId')?.value;
}
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { id } = await params;
const { searchParams } = new URL(request.url);
const page = parseInt(searchParams.get('page') || '1');
const limit = parseInt(searchParams.get('limit') || '20');
const skip = (page - 1) * limit;
// Verify QR ownership and type
const qrCode = await db.qRCode.findUnique({
where: { id, userId: userId },
select: { id: true, contentType: true },
});
if (!qrCode) {
return NextResponse.json({ error: 'QR code not found' }, { status: 404 });
}
// Check if consistent with schema (Prisma enum mismatch fix)
// @ts-ignore - Temporary ignore until client regeneration catches up fully in all envs
if (qrCode.contentType !== 'FEEDBACK') {
return NextResponse.json({ error: 'Not a feedback QR code' }, { status: 400 });
}
// Fetch feedback entries (stored as QRScans with ipHash='feedback')
const [feedbackEntries, totalCount] = await Promise.all([
db.qRScan.findMany({
where: { qrId: id, ipHash: 'feedback' },
orderBy: { ts: 'desc' },
skip,
take: limit,
select: { id: true, userAgent: true, ts: true },
}),
db.qRScan.count({
where: { qrId: id, ipHash: 'feedback' },
}),
]);
// Parse feedback data from userAgent field
const feedbacks = feedbackEntries.map((entry) => {
const parsed = parseFeedback(entry.userAgent || '');
return {
id: entry.id,
rating: parsed.rating,
comment: parsed.comment,
date: entry.ts,
};
});
// Calculate stats
const allRatings = await db.qRScan.findMany({
where: { qrId: id, ipHash: 'feedback' },
select: { userAgent: true },
});
const ratings = allRatings.map((e) => parseFeedback(e.userAgent || '').rating).filter((r) => r > 0);
const avgRating = ratings.length > 0 ? ratings.reduce((a, b) => a + b, 0) / ratings.length : 0;
// Rating distribution
const distribution = {
5: ratings.filter((r) => r === 5).length,
4: ratings.filter((r) => r === 4).length,
3: ratings.filter((r) => r === 3).length,
2: ratings.filter((r) => r === 2).length,
1: ratings.filter((r) => r === 1).length,
};
return NextResponse.json({
feedbacks,
stats: {
total: totalCount,
avgRating: Math.round(avgRating * 10) / 10,
distribution,
},
pagination: {
page,
limit,
totalPages: Math.ceil(totalCount / limit),
hasMore: skip + limit < totalCount,
},
});
} catch (error) {
console.error('Error fetching feedback:', error);
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
}
}
function parseFeedback(userAgent: string): { rating: number; comment: string } {
// Format: "rating:4|comment:Great service!"
const ratingMatch = userAgent.match(/rating:(\d)/);
const commentMatch = userAgent.match(/comment:(.+)/);
return {
rating: ratingMatch ? parseInt(ratingMatch[1]) : 0,
comment: commentMatch ? commentMatch[1] : '',
};
}