← back to Handbag Auth Nextjs

src/app/api/price-alerts/route.ts

168 lines

import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { priceAlertGetSchema, priceAlertCreateSchema, priceAlertDeleteSchema, validateInput } from '@/lib/validation'
import { rateLimit, getClientIp } from '@/lib/rateLimit'
import { addSecurityHeaders } from '@/lib/securityHeaders'

export async function GET(request: NextRequest) {
  // Apply rate limiting
  const clientIp = getClientIp(request)
  const rateLimitResult = rateLimit(clientIp)

  if (!rateLimitResult.allowed) {
    const response = NextResponse.json(
      { success: false, error: 'Rate limit exceeded' },
      { status: 429 }
    )
    addSecurityHeaders(response)
    return response
  }

  try {
    // Validate input
    const searchParams = Object.fromEntries(request.nextUrl.searchParams.entries())
    const validation = validateInput(priceAlertGetSchema, searchParams)

    if (!validation.success) {
      return NextResponse.json(
        { success: false, error: 'Invalid parameters', details: validation.errors.format() },
        { status: 400 }
      )
    }

    const { listingId, externalId, email } = validation.data

    const where: any = {}
    if (listingId) where.listingId = listingId
    if (externalId) where.externalId = externalId
    if (email) where.userEmail = email

    const alerts = await prisma.priceAlert.findMany({
      where,
      orderBy: { createdAt: 'desc' },
    })

    return NextResponse.json({
      success: true,
      count: alerts.length,
      alerts,
    })
  } catch (error) {
    console.error('Price alerts GET error:', error)
    return NextResponse.json(
      { success: false, error: 'Failed to fetch price alerts' },
      { status: 500 }
    )
  }
}

export async function POST(request: NextRequest) {
  // Apply rate limiting
  const clientIp = getClientIp(request)
  const rateLimitResult = rateLimit(clientIp, {
    windowMs: 60 * 1000,
    maxRequests: 20, // Limit alert creation
  })

  if (!rateLimitResult.allowed) {
    const response = NextResponse.json(
      { success: false, error: 'Rate limit exceeded' },
      { status: 429 }
    )
    addSecurityHeaders(response)
    return response
  }

  try {
    const body = await request.json()

    // Validate input
    const validation = validateInput(priceAlertCreateSchema, body)
    if (!validation.success) {
      return NextResponse.json(
        { success: false, error: 'Invalid input', details: validation.errors.format() },
        { status: 400 }
      )
    }

    const {
      listingId,
      externalId,
      userEmail,
      targetPrice,
      alertType,
    } = validation.data

    const now = new Date().toISOString().replace('T', ' ').split('.')[0]

    const alert = await prisma.priceAlert.create({
      data: {
        listingId,
        externalId,
        userEmail,
        targetPrice,
        alertType,
        isActive: 1,
        createdAt: now,
        notified: 0,
      },
    })

    return NextResponse.json({
      success: true,
      alert,
    })
  } catch (error) {
    console.error('Price alert POST error:', error)
    return NextResponse.json(
      { success: false, error: 'Failed to create price alert' },
      { status: 500 }
    )
  }
}

export async function DELETE(request: NextRequest) {
  // Apply rate limiting
  const clientIp = getClientIp(request)
  const rateLimitResult = rateLimit(clientIp)

  if (!rateLimitResult.allowed) {
    const response = NextResponse.json(
      { success: false, error: 'Rate limit exceeded' },
      { status: 429 }
    )
    addSecurityHeaders(response)
    return response
  }

  try {
    // Validate input
    const searchParams = Object.fromEntries(request.nextUrl.searchParams.entries())
    const validation = validateInput(priceAlertDeleteSchema, searchParams)

    if (!validation.success) {
      return NextResponse.json(
        { success: false, error: 'Invalid parameters', details: validation.errors.format() },
        { status: 400 }
      )
    }

    const { id } = validation.data

    await prisma.priceAlert.delete({
      where: { id },
    })

    return NextResponse.json({
      success: true,
      message: 'Price alert deleted',
    })
  } catch (error) {
    console.error('Price alert DELETE error:', error)
    return NextResponse.json(
      { success: false, error: 'Failed to delete price alert' },
      { status: 500 }
    )
  }
}