Files
CrazeAnalytix/services/supabase.ts
T
Christian Vidal WolfandQwen-Coder 8d05173b40 Fix Supabase LockManager timeout error
- Disable auth persistence in Supabase client
- Set autoRefreshToken: false, persistSession: false
- Prevents 'Acquiring an exclusive Navigator LockManager lock' timeout
- Applied to both services/supabase.ts and api/experiments.ts

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-02-21 09:29:22 +01:00

136 lines
3.6 KiB
TypeScript

import { createClient, SupabaseClient } from '@supabase/supabase-js';
import { VendorDailyRow } from '../types';
export type { VendorDailyRow };
const supabaseUrl = process.env.SUPABASE_URL || '';
const supabaseAnonKey = process.env.SUPABASE_ANON_KEY || '';
let _client: SupabaseClient | null = null;
const getClient = (): SupabaseClient => {
if (!_client) {
if (!supabaseUrl || !supabaseAnonKey) {
throw new Error('Supabase credentials not configured. Add SUPABASE_URL and SUPABASE_ANON_KEY to environment variables.');
}
// Disable auth persistence to avoid LockManager timeout issues
_client = createClient(supabaseUrl, supabaseAnonKey, {
auth: {
autoRefreshToken: false,
persistSession: false,
detectSessionInUrl: false,
storage: false as any
}
});
}
return _client;
};
export interface VendorFilters {
markets?: string[];
tags?: string[];
asins?: string[];
dateFrom?: string;
dateTo?: string;
years?: number[];
}
export const fetchVendorData = async (filters: VendorFilters): Promise<VendorDailyRow[]> => {
const allRows: VendorDailyRow[] = [];
let offset = 0;
const pageSize = 1000;
let hasMore = true;
while (hasMore) {
let query = getClient()
.from('vendor_daily_data')
.select('*')
.order('date', { ascending: true })
.range(offset, offset + pageSize - 1);
if (filters.markets?.length) {
query = query.in('market', filters.markets);
}
if (filters.tags?.length) {
// Use ilike for partial match on tags
query = query.or(filters.tags.map(t => `tags.ilike.%${t}%`).join(','));
}
if (filters.asins?.length) {
query = query.in('asin', filters.asins);
}
if (filters.dateFrom) {
query = query.gte('date', filters.dateFrom);
}
if (filters.dateTo) {
query = query.lte('date', filters.dateTo);
}
if (filters.years?.length) {
// Filter by year using date range
const yearFilters = filters.years.map(y => ({
from: `${y}-01-01`,
to: `${y}-12-31`
}));
const dateConditions = yearFilters.flatMap(f => [
`date.gte.${f.from}`,
`date.lte.${f.to}`
]);
// Build OR condition for multiple years
query = query.or(
filters.years.map(y => `and(date.gte.${y}-01-01,date.lte.${y}-12-31)`).join(',')
);
}
const { data, error } = await query;
if (error) throw error;
if (data) {
allRows.push(...data);
hasMore = data.length === pageSize;
offset += pageSize;
} else {
hasMore = false;
}
}
return allRows;
};
export const fetchVendorFilterOptions = async (): Promise<{
markets: string[];
tags: string[];
dateRange: { min: string; max: string } | null;
}> => {
const client = getClient();
const { data: marketData } = await client
.from('vendor_daily_data')
.select('market');
const { data: tagData } = await client
.from('vendor_daily_data')
.select('tags');
const { data: dateMin } = await client
.from('vendor_daily_data')
.select('date')
.order('date', { ascending: true })
.limit(1);
const { data: dateMax } = await client
.from('vendor_daily_data')
.select('date')
.order('date', { ascending: false })
.limit(1);
const markets = [...new Set((marketData || []).map((r: any) => r.market as string))].sort();
const tags = [...new Set((tagData || []).map((r: any) => r.tags as string).filter(Boolean))].sort();
return {
markets,
tags,
dateRange: dateMin?.[0] && dateMax?.[0]
? { min: dateMin[0].date, max: dateMax[0].date }
: null,
};
};