← back to Wine Finder Next

app/api/membership/marketplace/route.ts

137 lines

import { NextRequest, NextResponse } from 'next/server';
import { marketplaceDb } from '@/lib/membershipDatabase';
import type { ApiResponse } from '@/src/types/membership';
import { checkRateLimit, getRateLimitConfig, getClientIdentifier } from '@/lib/rateLimit';
import { logAudit } from '@/lib/auth';
import { sanitizeString, validateUserId, validatePrice, validateQuantity } from '@/lib/inputValidation';
import { analyzeRequest } from '@/lib/securityMonitoring';

export async function GET(request: NextRequest) {
  try {
    // Rate limiting
    const identifier = getClientIdentifier(request);
    const rateLimit = checkRateLimit(identifier, getRateLimitConfig('/api/membership/marketplace'));

    if (!rateLimit.success) {
      return NextResponse.json({
        success: false,
        error: 'Rate limit exceeded. Please try again later.'
      } as ApiResponse<null>, { status: 429 });
    }

    const { searchParams } = new URL(request.url);
    const activeOnly = searchParams.get('active') === 'true';
    const sellerId = searchParams.get('sellerId');

    // Validate sellerId if provided
    if (sellerId) {
      const sanitizedSellerId = sanitizeString(sellerId, 100);
      if (!validateUserId(sanitizedSellerId)) {
        return NextResponse.json({
          success: false,
          error: 'Invalid seller ID format'
        } as ApiResponse<null>, { status: 400 });
      }
    }

    let listings;
    if (activeOnly) {
      listings = marketplaceDb.getActive();
    } else if (sellerId) {
      listings = marketplaceDb.getBySellerId(sanitizeString(sellerId, 100));
    } else {
      listings = marketplaceDb.getAll();
    }

    logAudit({
      action: 'MARKETPLACE_LIST',
      resource: 'marketplace',
      ipAddress: identifier,
      userAgent: request.headers.get('user-agent') || 'unknown',
      success: true,
      metadata: { count: listings.length, sellerId, activeOnly }
    });

    return NextResponse.json({
      success: true,
      data: listings
    } as ApiResponse<typeof listings>);
  } catch (error) {
    return NextResponse.json({
      success: false,
      error: 'Failed to fetch marketplace listings'
    } as ApiResponse<null>, { status: 500 });
  }
}

export async function POST(request: NextRequest) {
  try {
    // Rate limiting
    const identifier = getClientIdentifier(request);
    const rateLimit = checkRateLimit(identifier, getRateLimitConfig('/api/membership/marketplace'));

    if (!rateLimit.success) {
      return NextResponse.json({
        success: false,
        error: 'Rate limit exceeded. Please try again later.'
      } as ApiResponse<null>, { status: 429 });
    }

    // Threat detection
    const body = await request.json();
    const analysis = analyzeRequest({
      body,
      headers: Object.fromEntries(request.headers),
      ip: identifier
    });

    if (!analysis.safe) {
      return NextResponse.json({
        success: false,
        error: 'Security threat detected'
      } as ApiResponse<null>, { status: 403 });
    }

    // Validate and sanitize
    if (body.sellerId && !validateUserId(body.sellerId)) {
      return NextResponse.json({
        success: false,
        error: 'Invalid seller ID format'
      } as ApiResponse<null>, { status: 400 });
    }

    if (body.price && !validatePrice(body.price)) {
      return NextResponse.json({
        success: false,
        error: 'Invalid price format'
      } as ApiResponse<null>, { status: 400 });
    }

    const newListing = marketplaceDb.create(body);

    logAudit({
      action: 'MARKETPLACE_CREATE',
      resource: `marketplace/${newListing.id}`,
      ipAddress: identifier,
      userAgent: request.headers.get('user-agent') || 'unknown',
      success: true,
      metadata: {
        listingId: newListing.id,
        sellerId: newListing.sellerId,
        price: newListing.price
      }
    });

    return NextResponse.json({
      success: true,
      data: newListing,
      message: 'Listing created successfully'
    } as ApiResponse<typeof newListing>);
  } catch (error) {
    return NextResponse.json({
      success: false,
      error: 'Failed to create listing'
    } as ApiResponse<null>, { status: 500 });
  }
}