import { createClient, SupabaseClient } from '@supabase/supabase-js';
import { randomBytes } from 'crypto';
// Initialize Supabase client
const supabaseUrl = process.env.SUPABASE_URL || '';
const supabaseKey = process.env.SUPABASE_KEY || '';
if (!supabaseUrl || !supabaseKey) {
throw new Error('Missing SUPABASE_URL or SUPABASE_KEY environment variables');
}
export const supabase: SupabaseClient = createClient(supabaseUrl, supabaseKey);
// Types
export interface Customer {
id: string;
email: string;
hubspot_contact_id: string | null;
access_token: string;
subscription_status: 'active' | 'cancelled' | 'past_due' | 'trialing';
created_at: string;
updated_at: string;
}
export interface WebhookLog {
id?: string;
source: string;
event_type: string | null;
payload: any;
processed: boolean;
error: string | null;
created_at?: string;
}
// Generate a secure random access token
export function generateAccessToken(): string {
return `bridge_${randomBytes(32).toString('hex')}`;
}
// Validate access token format
export function isValidTokenFormat(token: string): boolean {
return /^bridge_[a-f0-9]{64}$/.test(token);
}
// Customer operations
export async function createCustomer(
email: string,
hubspotContactId?: string
): Promise<Customer | null> {
const accessToken = generateAccessToken();
const { data, error } = await supabase
.from('customers')
.insert({
email,
hubspot_contact_id: hubspotContactId || null,
access_token: accessToken,
subscription_status: 'active',
})
.select()
.single();
if (error) {
console.error('Error creating customer:', error);
return null;
}
return data;
}
export async function getCustomerByToken(
token: string
): Promise<Customer | null> {
const { data, error } = await supabase
.from('customers')
.select('*')
.eq('access_token', token)
.single();
if (error) {
return null;
}
return data;
}
export async function getCustomerByEmail(
email: string
): Promise<Customer | null> {
const { data, error } = await supabase
.from('customers')
.select('*')
.eq('email', email)
.single();
if (error) {
return null;
}
return data;
}
export async function getCustomerByHubSpotId(
hubspotContactId: string
): Promise<Customer | null> {
const { data, error } = await supabase
.from('customers')
.select('*')
.eq('hubspot_contact_id', hubspotContactId)
.single();
if (error) {
return null;
}
return data;
}
export async function updateCustomerStatus(
id: string,
status: 'active' | 'cancelled' | 'past_due' | 'trialing'
): Promise<boolean> {
const { error } = await supabase
.from('customers')
.update({ subscription_status: status })
.eq('id', id);
return !error;
}
export async function regenerateAccessToken(id: string): Promise<string | null> {
const newToken = generateAccessToken();
const { error } = await supabase
.from('customers')
.update({ access_token: newToken })
.eq('id', id);
if (error) {
console.error('Error regenerating token:', error);
return null;
}
return newToken;
}
// Webhook logging
export async function logWebhook(log: WebhookLog): Promise<void> {
await supabase.from('webhook_logs').insert(log);
}
export async function markWebhookProcessed(
id: string,
error?: string
): Promise<void> {
await supabase
.from('webhook_logs')
.update({
processed: true,
error: error || null,
})
.eq('id', id);
}