← back to Wine Finder Next
app/api/membership/bottles/route.ts
124 lines
import { NextRequest, NextResponse } from 'next/server';
import { bottleDb } 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';
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/bottles'));
if (!rateLimit.success) {
logAudit({
action: 'RATE_LIMIT_EXCEEDED',
resource: 'bottles/list',
ipAddress: identifier,
userAgent: request.headers.get('user-agent') || 'unknown',
success: false
});
return NextResponse.json({
success: false,
error: 'Rate limit exceeded. Please try again later.'
} as ApiResponse<null>, {
status: 429,
headers: {
'X-RateLimit-Limit': rateLimit.limit.toString(),
'X-RateLimit-Remaining': '0',
'X-RateLimit-Reset': new Date(rateLimit.reset).toISOString()
}
});
}
const bottles = bottleDb.getAll();
logAudit({
action: 'BOTTLES_LIST',
resource: 'bottles',
ipAddress: identifier,
userAgent: request.headers.get('user-agent') || 'unknown',
success: true,
metadata: { count: bottles.length }
});
return NextResponse.json({
success: true,
data: bottles
} as ApiResponse<typeof bottles>, {
headers: {
'X-RateLimit-Limit': rateLimit.limit.toString(),
'X-RateLimit-Remaining': rateLimit.remaining.toString(),
'X-RateLimit-Reset': new Date(rateLimit.reset).toISOString()
}
});
} catch (error) {
return NextResponse.json({
success: false,
error: 'Failed to fetch bottles'
} 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/bottles'));
if (!rateLimit.success) {
return NextResponse.json({
success: false,
error: 'Rate limit exceeded. Please try again later.'
} as ApiResponse<null>, { status: 429 });
}
// Threat detection
const analysis = analyzeRequest({
body: await request.json(),
headers: Object.fromEntries(request.headers),
ip: identifier
});
if (!analysis.safe) {
return NextResponse.json({
success: false,
error: 'Security threat detected'
} as ApiResponse<null>, { status: 403 });
}
const body = await request.json();
// Input validation and sanitization
const sanitizedName = sanitizeString(body.name, 200);
const newBottle = bottleDb.create({
...body,
name: sanitizedName
});
logAudit({
action: 'BOTTLE_CREATE',
resource: `bottle/${newBottle.id}`,
ipAddress: identifier,
userAgent: request.headers.get('user-agent') || 'unknown',
success: true,
metadata: { bottleId: newBottle.id, name: sanitizedName }
});
return NextResponse.json({
success: true,
data: newBottle,
message: 'Bottle created successfully'
} as ApiResponse<typeof newBottle>);
} catch (error) {
return NextResponse.json({
success: false,
error: 'Failed to create bottle'
} as ApiResponse<null>, { status: 500 });
}
}