← back to Wine Finder Next
app/api/membership/samples/route.ts
65 lines
import { NextRequest, NextResponse } from 'next/server';
import { sampleUnitDb, sampleRightDb } from '@/lib/membershipDatabase';
import type { ApiResponse } from '@/src/types/membership';
import { checkRateLimit, getRateLimitConfig, getClientIdentifier } from '@/lib/rateLimit';
import { logAudit } from '@/lib/auth';
import { sanitizeString, validateBottleId } from '@/lib/inputValidation';
export async function GET(request: NextRequest) {
try {
// Rate limiting
const identifier = getClientIdentifier(request);
const rateLimit = checkRateLimit(identifier, getRateLimitConfig('/api/membership/samples'));
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 bottleId = searchParams.get('bottleId');
const availableOnly = searchParams.get('available') === 'true';
// Validate bottleId if provided
if (bottleId) {
const sanitizedBottleId = sanitizeString(bottleId, 100);
if (!validateBottleId(sanitizedBottleId)) {
return NextResponse.json({
success: false,
error: 'Invalid bottle ID format'
} as ApiResponse<null>, { status: 400 });
}
}
let samples;
if (availableOnly) {
samples = sampleUnitDb.getAvailable(bottleId ? sanitizeString(bottleId, 100) : undefined);
} else if (bottleId) {
samples = sampleUnitDb.getByBottleId(sanitizeString(bottleId, 100));
} else {
samples = sampleUnitDb.getAll();
}
logAudit({
action: 'SAMPLES_LIST',
resource: 'samples',
ipAddress: identifier,
userAgent: request.headers.get('user-agent') || 'unknown',
success: true,
metadata: { count: samples.length, bottleId, availableOnly }
});
return NextResponse.json({
success: true,
data: samples
} as ApiResponse<typeof samples>);
} catch (error) {
return NextResponse.json({
success: false,
error: 'Failed to fetch samples'
} as ApiResponse<null>, { status: 500 });
}
}