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 72 73 74 75 76 77 78 79 80 81 82 83 | 1x 1x 1x 1x 1x 123x 123x 123x 123x 3x 3x 3x 123x 1x 1x 119x 119x 119x 21x 21x 21x 123x 123x 123x 123x 1x 123x 1x 1x 122x 23x 123x 1x 1x 22x 123x 123x 123x 21x 21x 21x 123x 123x 123x 123x 123x 123x 1x 1x 1x 20x 123x 100x 100x 100x 123x | import { NextRequest, NextResponse } from 'next/server';
import { createClient } from '@/lib/supabase/server';
import { trackEventSchema } from '@/lib/validations/analytics';
import { createHash } from 'crypto';
// Rate limit: 100 requests per minute per IP
const rateLimitMap = new Map<string, { count: number; timestamp: number }>();
const RATE_LIMIT_WINDOW = 60 * 1000; // 1 minute
const RATE_LIMIT_MAX = 100;
function isRateLimited(ip: string): boolean {
const now = Date.now();
const record = rateLimitMap.get(ip);
if (!record || now - record.timestamp > RATE_LIMIT_WINDOW) {
rateLimitMap.set(ip, { count: 1, timestamp: now });
return false;
}
if (record.count >= RATE_LIMIT_MAX) {
return true;
}
record.count++;
return false;
}
function hashIP(ip: string): string {
return createHash('sha256').update(ip).digest('hex').slice(0, 16);
}
export async function POST(request: NextRequest) {
try {
// Get IP for rate limiting and hashing
const ip =
request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ||
request.headers.get('x-real-ip') ||
'unknown';
// Rate limit check
if (isRateLimited(ip)) {
return NextResponse.json({ success: false, error: 'Rate limit exceeded' }, { status: 429 });
}
// Parse and validate body
const body = await request.json();
const validation = trackEventSchema.safeParse(body);
if (!validation.success) {
return NextResponse.json({ success: false, error: 'Invalid request body' }, { status: 400 });
}
const { profile_id, event_type, meta } = validation.data;
// Get user agent and referrer
const userAgent = request.headers.get('user-agent') || null;
const referrer = (meta?.referrer as string) || request.headers.get('referer') || null;
// Insert analytics event
const supabase = await createClient();
const { error } = await supabase.from('analytics').insert({
profile_id,
event_type,
meta: meta || {},
ip_hash: hashIP(ip),
user_agent: userAgent,
referrer: referrer,
});
if (error) {
console.error('[Analytics] Insert error:', error);
// Don't expose internal errors
return NextResponse.json({ success: true });
}
return NextResponse.json({ success: true });
} catch (error) {
console.error('[Analytics] Error:', error);
// Always return success to not break the client
return NextResponse.json({ success: true });
}
}
|