"use server"; import { OpenPanel } from "@openpanel/nextjs"; // Server-side OpenPanel instance const op = new OpenPanel({ clientId: process.env.NEXT_PUBLIC_OPENPANEL_CLIENT_ID || "", clientSecret: process.env.OPENPANEL_CLIENT_SECRET || "", apiUrl: process.env.OPENPANEL_API_URL || "https://op.nodecrew.me/api", }); // Rybbit server-side tracking const RYBBIT_HOST = process.env.NEXT_PUBLIC_RYBBIT_HOST || "https://rybbit.nodecrew.me"; const RYBBIT_API_KEY = process.env.RYBBIT_API_KEY; const RYBBIT_SITE_ID = process.env.NEXT_PUBLIC_RYBBIT_SITE_ID || "1"; export interface ServerOrderData { orderId: string; orderNumber: string; total: number; currency: string; itemCount: number; customerEmail?: string; paymentMethod?: string; shippingCost?: number; couponCode?: string; } export interface ServerEventData { event: string; properties?: Record; } async function trackRybbitServer(eventName: string, properties?: Record) { try { const headers: Record = { "Content-Type": "application/json", }; if (RYBBIT_API_KEY) { headers["Authorization"] = `Bearer ${RYBBIT_API_KEY}`; } const response = await fetch(`${RYBBIT_HOST}/api/track`, { method: "POST", headers, body: JSON.stringify({ site_id: RYBBIT_SITE_ID, type: "custom_event", event_name: eventName, properties: JSON.stringify(properties || {}), }), }); if (!response.ok) { console.warn("[Rybbit Server] Track failed:", await response.text()); } } catch (error) { console.warn("[Rybbit Server] Track error:", error); } } /** * Server-side analytics tracking * Called from API routes or Server Components */ export async function trackOrderCompletedServer(data: ServerOrderData) { try { console.log("[Server Analytics] Tracking order:", data.orderNumber, "Total:", data.total); // Track order event with OpenPanel await op.track("order_completed", { order_id: data.orderId, order_number: data.orderNumber, total: data.total, currency: data.currency, item_count: data.itemCount, customer_email: data.customerEmail, payment_method: data.paymentMethod, shipping_cost: data.shippingCost, coupon_code: data.couponCode, source: "server", }); // Track revenue with OpenPanel await op.revenue(data.total, { currency: data.currency, transaction_id: data.orderNumber, order_id: data.orderId, source: "server", }); // Track conversion/revenue with Rybbit await trackRybbitServer("order_completed", { order_id: data.orderId, order_number: data.orderNumber, total: data.total, currency: data.currency, item_count: data.itemCount, customer_email: data.customerEmail, payment_method: data.paymentMethod, shipping_cost: data.shippingCost, coupon_code: data.couponCode, revenue: data.total, source: "server", }); console.log("[Server Analytics] Order tracked successfully"); return { success: true }; } catch (error) { console.error("[Server Analytics] Failed to track order:", error); // Don't throw - analytics shouldn't break the app return { success: false, error: String(error) }; } } /** * Track any server-side event */ export async function trackServerEvent(data: ServerEventData) { try { await op.track(data.event, { ...data.properties, source: "server", }); // Also track to Rybbit await trackRybbitServer(data.event, data.properties); return { success: true }; } catch (error) { console.error("[Server Analytics] Event tracking failed:", error); return { success: false, error: String(error) }; } } /** * Identify user server-side */ export async function identifyUserServer(profileId: string, properties?: Record) { try { await op.identify({ profileId, ...properties, }); return { success: true }; } catch (error) { console.error("[Server Analytics] Identify failed:", error); return { success: false, error: String(error) }; } }