Skip to main content
Glama
raza10006

ElevenLabs MCP Backend

by raza10006
supabase.ts11.5 kB
/** * Supabase client and order query functions */ import { createClient, SupabaseClient } from "@supabase/supabase-js"; import { LookupOrderResponse, OrderStatus } from "./types.js"; import { logger } from "./logger.js"; // Lazy initialization - read env vars at runtime, not module load time let supabaseClient: SupabaseClient | null = null; function getSupabaseClient(): SupabaseClient { if (supabaseClient) { return supabaseClient; } const SUPABASE_URL = process.env.SUPABASE_URL; const SUPABASE_SERVICE_ROLE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY; if (!SUPABASE_URL) { throw new Error("SUPABASE_URL environment variable is required"); } if (!SUPABASE_SERVICE_ROLE_KEY) { throw new Error("SUPABASE_SERVICE_ROLE_KEY environment variable is required"); } // Initialize Supabase client with service role key (bypasses RLS) supabaseClient = createClient( SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, { auth: { autoRefreshToken: false, persistSession: false, }, } ); return supabaseClient; } // Export a getter that initializes on first access export const supabase: SupabaseClient = new Proxy({} as SupabaseClient, { get(_target, prop) { return getSupabaseClient()[prop as keyof SupabaseClient]; }, }); /** * Looks up an order by order_id * Returns null if order not found */ export async function lookupOrder( orderId: string, logContext?: { requestId?: string; orderId?: string } ): Promise<LookupOrderResponse | null> { const contextLogger = logger.withContext({ ...logContext, orderId, }); try { contextLogger.debug("Querying Supabase for order", { orderId }); const { data, error } = await supabase .from("orders") .select("*") .eq("order_id", orderId) .single(); if (error) { // Handle "not found" case (Supabase returns PGRST116 when no rows found) if (error.code === "PGRST116") { contextLogger.info("Order not found", { orderId }); return null; } // Log other Supabase errors safely (no secrets) contextLogger.error("Supabase query error", error, { errorCode: error.code, errorMessage: error.message, // Do not log error.details or error.hint as they might contain sensitive info }); throw new Error(`Database query failed: ${error.message}`); } if (!data) { contextLogger.info("Order not found (null data)", { orderId }); return null; } // Debug: Log the raw data structure to help diagnose issues contextLogger.debug("Raw order data from Supabase", { orderId, dataKeys: Object.keys(data), dataSample: { order_id: (data as any).order_id, status: (data as any).status, order_status: (data as any).order_status, hasStatus: !!(data as any).status, hasOrderStatus: !!(data as any).order_status, }, }); const rawData = data as any; // Map existing columns to expected format // Support both new schema (status) and existing schema (order_status) const statusValue = rawData.status || rawData.order_status; // Map status values to our expected format let mappedStatus: OrderStatus; if (statusValue) { const upperStatus = String(statusValue).toUpperCase(); // Map common status values if (upperStatus.includes('SHIPPED') || upperStatus.includes('SHIPPING')) { mappedStatus = 'SHIPPED'; } else if (upperStatus.includes('DELIVERED') || upperStatus.includes('DELIVERY')) { mappedStatus = 'DELIVERED'; } else if (upperStatus.includes('PROCESSING') || upperStatus.includes('PROCESS')) { mappedStatus = 'PROCESSING'; } else if (upperStatus.includes('CANCELED') || upperStatus.includes('CANCELLED')) { mappedStatus = 'CANCELED'; } else if (upperStatus.includes('RETURNED') || upperStatus.includes('RETURN')) { mappedStatus = 'RETURNED'; } else if (upperStatus.includes('HOLD') || upperStatus.includes('ON_HOLD')) { mappedStatus = 'ON_HOLD'; } else { // Try to use as-is if it matches our enum mappedStatus = upperStatus as OrderStatus; } } else { // Default to PROCESSING if no status found mappedStatus = 'PROCESSING'; contextLogger.warn("No status found, defaulting to PROCESSING", { orderId }); } // Map other fields - support both new and existing column names const eta = rawData.eta || rawData.estimated_delivery_date; const carrier = rawData.carrier || rawData.delivery_partner; const tracking_number = rawData.tracking_number || rawData.tracking_id || rawData.tracking; const last_update = rawData.last_update || rawData.updated_at || rawData.order_date || rawData.created_at || new Date().toISOString(); const issue_flag = rawData.issue_flag || rawData.issue_type; const notes = rawData.notes || rawData.order_notes; // Validate required fields if (!rawData.order_id) { contextLogger.error("Order data missing order_id", { data }); throw new Error("Invalid order data: missing order_id"); } contextLogger.info("Order found", { orderId: rawData.order_id, status: mappedStatus, originalStatus: statusValue, }); // Helper function to safely convert to string or null const toStr = (val: unknown): string | null => val ? String(val) : null; const toDateStr = (val: unknown): string | null => { if (!val) return null; if (typeof val === 'string') { // If it's already a date string, try to parse it try { const date = new Date(val); if (!isNaN(date.getTime())) { return date.toISOString().split('T')[0]; } } catch { return null; } return null; } if (val instanceof Date) { return val.toISOString().split('T')[0]; } if (typeof val === 'number') { try { return new Date(val).toISOString().split('T')[0]; } catch { return null; } } return null; }; const toTimestampStr = (val: unknown): string => { if (!val) return new Date().toISOString(); if (typeof val === 'string') { try { const date = new Date(val); if (!isNaN(date.getTime())) { return date.toISOString(); } } catch { return new Date().toISOString(); } return new Date().toISOString(); } if (val instanceof Date) { return val.toISOString(); } if (typeof val === 'number') { try { return new Date(val).toISOString(); } catch { return new Date().toISOString(); } } return new Date().toISOString(); }; // Transform to response format with ALL available fields const response: LookupOrderResponse = { // Core order info order_id: String(rawData.order_id), status: mappedStatus, order_status: toStr(rawData.order_status), // Customer info customer_name: toStr(rawData.customer_name), customer_phone: toStr(rawData.customer_phone), // Product info product_id: toStr(rawData.product_id), product_name: toStr(rawData.product_name), category: toStr(rawData.category), // Delivery info eta: toDateStr(eta), estimated_delivery_date: toDateStr(rawData.estimated_delivery_date), carrier: toStr(carrier), delivery_partner: toStr(rawData.delivery_partner), delivery_address: toStr(rawData.delivery_address), tracking_number: toStr(tracking_number), // Payment info payment_method: toStr(rawData.payment_method), // Dates order_date: toTimestampStr(rawData.order_date), created_at: toTimestampStr(rawData.created_at), last_update: toTimestampStr(last_update), // Issues and returns issue_flag: toStr(issue_flag), issue_type: toStr(rawData.issue_type), return_eligible: rawData.return_eligible !== undefined ? Boolean(rawData.return_eligible) : null, refund_amount: rawData.refund_amount !== undefined ? Number(rawData.refund_amount) : null, // Additional notes notes: toStr(notes), }; return response; } catch (error) { contextLogger.error("Unexpected error in lookupOrder", error); throw error; } } /** * Formats order data into a human-readable text for voice agents * Includes all available order information */ export function formatOrderForAgent(order: LookupOrderResponse): string { const parts: string[] = []; // Order ID and Status parts.push(`Order ${order.order_id} is ${order.status}`); // Customer Information if (order.customer_name) { parts.push(`Customer: ${order.customer_name}`); } if (order.customer_phone) { parts.push(`Phone: ${order.customer_phone}`); } // Product Information if (order.product_name) { parts.push(`Product: ${order.product_name}`); } if (order.product_id) { parts.push(`Product ID: ${order.product_id}`); } if (order.category) { parts.push(`Category: ${order.category}`); } // Delivery Information const etaDate = order.eta || order.estimated_delivery_date; if (etaDate) { try { const formattedDate = new Date(etaDate).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", }); parts.push(`Estimated delivery: ${formattedDate}`); } catch { parts.push(`Estimated delivery: ${etaDate}`); } } if (order.tracking_number) { const carrier = order.carrier || order.delivery_partner; if (carrier) { parts.push(`Tracking: ${order.tracking_number} via ${carrier}`); } else { parts.push(`Tracking number: ${order.tracking_number}`); } } else if (order.carrier || order.delivery_partner) { parts.push(`Carrier: ${order.carrier || order.delivery_partner}`); } if (order.delivery_address) { parts.push(`Delivery address: ${order.delivery_address}`); } // Payment Information if (order.payment_method) { parts.push(`Payment method: ${order.payment_method}`); } // Order Date if (order.order_date) { try { const orderDate = new Date(order.order_date).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", }); parts.push(`Order date: ${orderDate}`); } catch { // Keep as-is if date parsing fails } } // Issues and Returns const issueInfo = order.issue_flag || order.issue_type; if (issueInfo) { parts.push(`Issue: ${issueInfo}`); } if (order.return_eligible !== null && order.return_eligible !== undefined) { parts.push(`Return eligible: ${order.return_eligible ? 'Yes' : 'No'}`); } if (order.refund_amount !== null && order.refund_amount !== undefined) { parts.push(`Refund amount: $${order.refund_amount.toFixed(2)}`); } // Additional Notes if (order.notes) { parts.push(`Notes: ${order.notes}`); } // Last Update try { const lastUpdate = new Date(order.last_update).toLocaleString("en-US", { year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "2-digit", }); parts.push(`Last updated: ${lastUpdate}`); } catch { // Keep as-is if date parsing fails } return parts.join(". ") + "."; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/raza10006/elevenlabs-mcp-backend'

If you have feedback or need assistance with the MCP directory API, please join our Discord server