All files / src/app/api/analytics/pageview route.ts

0% Statements 0/70
100% Branches 0/0
0% Functions 0/1
0% Lines 0/70

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71                                                                                                                                             
/**
 * Analytics Page View API Route
 *
 * Tracks individual page views.
 * POST - Record a page view
 */

import { NextRequest, NextResponse } from 'next/server';
import { z } from "zod";
import { prisma } from "@/lib/prisma";
import { shouldExcludePath } from "@/lib/analytics/utils";
import { Prisma } from "@prisma/client";
import {
  withErrorHandling,
  successResponse,
  ApiSuccessResponse,
  ApiErrorResponse } from "@/lib/api";

// Schema for page view
const pageViewSchema = z.object({
  sessionId: z.string().min(1),
  visitorId: z.string().min(1).max(50),
  userId: z.number().optional(),
  path: z.string().max(500),
  title: z.string().max(200).optional(),
  referrer: z.string().max(500).optional(),
  queryParams: z.record(z.string(), z.string()).optional()});

/**
 * POST - Record a page view
 */
async function handlePost(
  request: NextRequest
): Promise<NextResponse<ApiSuccessResponse<{ skipped?: boolean }> | ApiErrorResponse>> {
  const body = await request.json();
  const data = pageViewSchema.parse(body);

  // Skip excluded paths (API routes, static files, etc.)
  if (shouldExcludePath(data.path)) {
    return successResponse({ skipped: true });
  }

  // Create page view record and update session in parallel
  await Promise.all([
    // Create page view
    prisma.analyticsPageView.create({
      data: {
        sessionId: data.sessionId,
        visitorId: data.visitorId,
        userId: data.userId,
        path: data.path,
        title: data.title,
        referrer: data.referrer,
        queryParams: data.queryParams as Prisma.InputJsonValue | undefined}}),

    // Update session
    prisma.analyticsSession.update({
      where: { id: data.sessionId },
      data: {
        pageViews: { increment: 1 },
        lastSeenAt: new Date(),
        exitPage: data.path}}).catch(() => {
      // Session might not exist yet (race condition), log but don't fail
    }),
  ]);

  return successResponse({});
}

export const POST = withErrorHandling(handlePost);