import { NextRequest, NextResponse } from 'next/server'; import { Event } from '../../types'; import { v4 as uuid } from 'uuid'; import clickhouse from '@/lib/clickhouse'; // 将时间格式化为ClickHouse兼容的格式:YYYY-MM-DD HH:MM:SS.SSS const formatDateTime = (date: Date) => { return date.toISOString().replace('T', ' ').replace('Z', ''); }; // Handler for POST request to track events export async function POST(req: NextRequest) { try { // Parse request body const eventData = await req.json(); // Validate required fields if (!eventData.event_type) { return NextResponse.json( { error: 'Missing required field: event_type' }, { status: 400 } ); } // 获取当前时间并格式化 const currentTime = formatDateTime(new Date()); // Set default values for required fields if missing const event: Event = { // Core event fields event_id: eventData.event_id || uuid(), event_time: eventData.event_time ? formatDateTime(new Date(eventData.event_time)) : currentTime, event_type: eventData.event_type, event_attributes: eventData.event_attributes || '{}', // Link information link_id: eventData.link_id || '', link_slug: eventData.link_slug || '', link_label: eventData.link_label || '', link_title: eventData.link_title || '', link_original_url: eventData.link_original_url || '', link_attributes: eventData.link_attributes || '{}', link_created_at: eventData.link_created_at ? formatDateTime(new Date(eventData.link_created_at)) : currentTime, link_expires_at: eventData.link_expires_at ? formatDateTime(new Date(eventData.link_expires_at)) : null, link_tags: eventData.link_tags || '[]', // User information user_id: eventData.user_id || '', user_name: eventData.user_name || '', user_email: eventData.user_email || '', user_attributes: eventData.user_attributes || '{}', // Team information team_id: eventData.team_id || '', team_name: eventData.team_name || '', team_attributes: eventData.team_attributes || '{}', // Project information project_id: eventData.project_id || '', project_name: eventData.project_name || '', project_attributes: eventData.project_attributes || '{}', // QR code information qr_code_id: eventData.qr_code_id || '', qr_code_name: eventData.qr_code_name || '', qr_code_attributes: eventData.qr_code_attributes || '{}', // Visitor information visitor_id: eventData.visitor_id || uuid(), session_id: eventData.session_id || uuid(), ip_address: eventData.ip_address || req.headers.get('x-forwarded-for')?.toString() || '', country: eventData.country || '', city: eventData.city || '', device_type: eventData.device_type || '', browser: eventData.browser || '', os: eventData.os || '', user_agent: eventData.user_agent || req.headers.get('user-agent')?.toString() || '', // Referrer information referrer: eventData.referrer || req.headers.get('referer')?.toString() || '', utm_source: eventData.utm_source || '', utm_medium: eventData.utm_medium || '', utm_campaign: eventData.utm_campaign || '', // Interaction information time_spent_sec: eventData.time_spent_sec || 0, is_bounce: eventData.is_bounce !== undefined ? eventData.is_bounce : true, is_qr_scan: eventData.is_qr_scan !== undefined ? eventData.is_qr_scan : false, conversion_type: eventData.conversion_type || '', conversion_value: eventData.conversion_value || 0, }; // 确保JSON字符串字段的正确处理 if (typeof event.event_attributes === 'object') { event.event_attributes = JSON.stringify(event.event_attributes); } if (typeof event.link_attributes === 'object') { event.link_attributes = JSON.stringify(event.link_attributes); } if (typeof event.user_attributes === 'object') { event.user_attributes = JSON.stringify(event.user_attributes); } if (typeof event.team_attributes === 'object') { event.team_attributes = JSON.stringify(event.team_attributes); } if (typeof event.project_attributes === 'object') { event.project_attributes = JSON.stringify(event.project_attributes); } if (typeof event.qr_code_attributes === 'object') { event.qr_code_attributes = JSON.stringify(event.qr_code_attributes); } if (typeof event.link_tags === 'object') { event.link_tags = JSON.stringify(event.link_tags); } // Insert event into ClickHouse await clickhouse.insert({ table: 'events', values: [event], format: 'JSONEachRow', }); // Return success response return NextResponse.json({ success: true, message: 'Event tracked successfully', event_id: event.event_id }, { status: 201 }); } catch (error) { console.error('Error tracking event:', error); return NextResponse.json( { error: 'Failed to track event', details: (error as Error).message }, { status: 500 } ); } }