Initial commit - Route Commerce platform
This commit is contained in:
@@ -0,0 +1,264 @@
|
||||
import { createClient, type SupabaseClient } from "@supabase/supabase-js";
|
||||
import { getMockTableData } from "./mock-data";
|
||||
|
||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
||||
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
||||
|
||||
// Auto-enable mock mode when no Supabase URL is configured (demo/deployment without backend)
|
||||
const useMockData = process.env.NEXT_PUBLIC_USE_MOCK_DATA === "true" || !supabaseUrl || !supabaseUrl.includes("supabase.co");
|
||||
|
||||
// Mock query builder that supports all common Supabase methods
|
||||
class MockQueryBuilder {
|
||||
private data: unknown[];
|
||||
private tableName: string;
|
||||
private filters: { column: string; value: unknown; op: string }[] = [];
|
||||
private selectColumns: string = "*";
|
||||
private orderColumn?: string;
|
||||
private orderDirection?: "asc" | "desc";
|
||||
private limitValue?: number;
|
||||
|
||||
constructor(tableName: string) {
|
||||
this.tableName = tableName;
|
||||
this.data = [...(getMockTableData(tableName) || [])];
|
||||
}
|
||||
|
||||
select(columns: string = "*") {
|
||||
this.selectColumns = columns;
|
||||
return this;
|
||||
}
|
||||
|
||||
eq(column: string, value: unknown) {
|
||||
this.filters.push({ column, value, op: "eq" });
|
||||
return this;
|
||||
}
|
||||
|
||||
neq(column: string, value: unknown) {
|
||||
this.filters.push({ column, value, op: "neq" });
|
||||
return this;
|
||||
}
|
||||
|
||||
gt(column: string, value: unknown) {
|
||||
this.filters.push({ column, value, op: "gt" });
|
||||
return this;
|
||||
}
|
||||
|
||||
gte(column: string, value: unknown) {
|
||||
this.filters.push({ column, value, op: "gte" });
|
||||
return this;
|
||||
}
|
||||
|
||||
lt(column: string, value: unknown) {
|
||||
this.filters.push({ column, value, op: "lt" });
|
||||
return this;
|
||||
}
|
||||
|
||||
lte(column: string, value: unknown) {
|
||||
this.filters.push({ column, value, op: "lte" });
|
||||
return this;
|
||||
}
|
||||
|
||||
is(column: string, value: unknown) {
|
||||
this.filters.push({ column, value, op: "is" });
|
||||
return this;
|
||||
}
|
||||
|
||||
like(column: string, value: string) {
|
||||
this.filters.push({ column, value, op: "like" });
|
||||
return this;
|
||||
}
|
||||
|
||||
ilike(column: string, value: string) {
|
||||
this.filters.push({ column, value, op: "ilike" });
|
||||
return this;
|
||||
}
|
||||
|
||||
in(column: string, values: unknown[]) {
|
||||
this.filters.push({ column, value: values, op: "in" });
|
||||
return this;
|
||||
}
|
||||
|
||||
order(column: string, options?: { ascending?: boolean; nullsFirst?: boolean }) {
|
||||
this.orderColumn = column;
|
||||
this.orderDirection = options?.ascending === false ? "desc" : "asc";
|
||||
return this;
|
||||
}
|
||||
|
||||
limit(count: number) {
|
||||
this.limitValue = count;
|
||||
return this;
|
||||
}
|
||||
|
||||
range(min: number, max: number) {
|
||||
// For pagination mock
|
||||
return this;
|
||||
}
|
||||
|
||||
single() {
|
||||
return this.executeSingle();
|
||||
}
|
||||
|
||||
then(resolve: (value: unknown) => void, _reject?: (reason?: unknown) => void) {
|
||||
const result = this.execute();
|
||||
resolve(result);
|
||||
}
|
||||
|
||||
async executeSingle() {
|
||||
const result = this.execute();
|
||||
if (result.data && Array.isArray(result.data) && result.data.length > 0) {
|
||||
return { data: result.data[0], error: null };
|
||||
}
|
||||
return { data: null, error: null };
|
||||
}
|
||||
|
||||
execute() {
|
||||
let filtered = [...this.data];
|
||||
|
||||
// Apply filters
|
||||
for (const filter of this.filters) {
|
||||
filtered = filtered.filter((row: any) => {
|
||||
const rowValue = row[filter.column];
|
||||
switch (filter.op) {
|
||||
case "eq":
|
||||
return rowValue === filter.value;
|
||||
case "neq":
|
||||
return rowValue !== filter.value;
|
||||
case "gt":
|
||||
return (rowValue as number) > (filter.value as number);
|
||||
case "gte":
|
||||
return (rowValue as number) >= (filter.value as number);
|
||||
case "lt":
|
||||
return (rowValue as number) < (filter.value as number);
|
||||
case "lte":
|
||||
return (rowValue as number) <= (filter.value as number);
|
||||
case "is":
|
||||
if (filter.value === null) return rowValue === null;
|
||||
if (filter.value === undefined) return rowValue === undefined;
|
||||
return rowValue === filter.value;
|
||||
case "like":
|
||||
return typeof rowValue === "string" && rowValue.includes((filter.value as string).replace(/%/g, ""));
|
||||
case "ilike":
|
||||
return typeof rowValue === "string" && rowValue.toLowerCase().includes((filter.value as string).replace(/%/g, "").toLowerCase());
|
||||
case "in":
|
||||
return Array.isArray(filter.value) && filter.value.includes(rowValue);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Apply ordering
|
||||
if (this.orderColumn) {
|
||||
filtered.sort((a: any, b: any) => {
|
||||
const aVal = a[this.orderColumn!];
|
||||
const bVal = b[this.orderColumn!];
|
||||
if (aVal < bVal) return this.orderDirection === "desc" ? 1 : -1;
|
||||
if (aVal > bVal) return this.orderDirection === "desc" ? -1 : 1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
// Apply limit
|
||||
if (this.limitValue !== undefined) {
|
||||
filtered = filtered.slice(0, this.limitValue);
|
||||
}
|
||||
|
||||
return { data: filtered, error: null };
|
||||
}
|
||||
}
|
||||
|
||||
// Mock insert/update/delete builders
|
||||
class MockMutationBuilder {
|
||||
private tableName: string;
|
||||
private data: Record<string, unknown> | Record<string, unknown>[];
|
||||
|
||||
constructor(tableName: string, data: Record<string, unknown> | Record<string, unknown>[]) {
|
||||
this.tableName = tableName;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
select() {
|
||||
return new MockQueryBuilder(this.tableName);
|
||||
}
|
||||
|
||||
then(resolve: (value: unknown) => void) {
|
||||
const items = Array.isArray(this.data) ? this.data : [this.data];
|
||||
const returning = items.map((item, i) => ({
|
||||
...item,
|
||||
id: item.id || `generated-${Date.now()}-${i}`,
|
||||
}));
|
||||
resolve({ data: returning, error: null });
|
||||
}
|
||||
}
|
||||
|
||||
// Mock storage builder
|
||||
class MockStorageBuilder {
|
||||
from(bucket: string) {
|
||||
return {
|
||||
upload: async (path: string, _file: unknown) => {
|
||||
return { data: { path }, error: null };
|
||||
},
|
||||
download: async (path: string) => {
|
||||
return { data: new Blob(), error: null };
|
||||
},
|
||||
remove: async (paths: string[]) => {
|
||||
return { data: { paths }, error: null };
|
||||
},
|
||||
list: async () => {
|
||||
return { data: [], error: null };
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Create mock client
|
||||
function createMockClient() {
|
||||
return {
|
||||
from: (table: string) => new MockQueryBuilder(table),
|
||||
insert: (data: Record<string, unknown> | Record<string, unknown>[]) => new MockMutationBuilder("unknown", data),
|
||||
update: (data: Record<string, unknown>) => new MockMutationBuilder("unknown", data),
|
||||
delete: () => ({
|
||||
eq: () => ({ then: (resolve: (value: unknown) => void) => resolve({ data: null, error: null }) }),
|
||||
in: () => ({ then: (resolve: (value: unknown) => void) => resolve({ data: null, error: null }) }),
|
||||
}),
|
||||
storage: new MockStorageBuilder(),
|
||||
auth: {
|
||||
getSession: async () => ({ data: { session: null }, error: null }),
|
||||
getUser: async () => ({ data: { user: null }, error: null }),
|
||||
signInWithPassword: async () => ({ data: { user: null, session: null }, error: null }),
|
||||
signOut: async () => ({ error: null }),
|
||||
onAuthStateChange: () => ({ data: { subscription: { unsubscribe: () => {} } } }),
|
||||
},
|
||||
channel: () => ({
|
||||
on: () => ({ subscribe: () => ({}) }),
|
||||
subscribe: () => ({}),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
// Real Supabase client creation
|
||||
function getSupabase(): SupabaseClient {
|
||||
if (!supabaseUrl || !supabaseAnonKey) {
|
||||
throw new Error(
|
||||
`Missing Supabase env vars: ${[!supabaseUrl && "NEXT_PUBLIC_SUPABASE_URL", !supabaseAnonKey && "NEXT_PUBLIC_SUPABASE_ANON_KEY"].filter(Boolean).join(", ")}. ` +
|
||||
"Check Vercel environment variables for Production environment. " +
|
||||
"Node env: " + (process.env.NODE_ENV ?? "unknown")
|
||||
);
|
||||
}
|
||||
return createClient(supabaseUrl, supabaseAnonKey);
|
||||
}
|
||||
|
||||
// Create proxy that routes to real or mock client
|
||||
let realSupabase: SupabaseClient | null = null;
|
||||
if (!useMockData) {
|
||||
try {
|
||||
realSupabase = getSupabase();
|
||||
} catch {
|
||||
// Will use mock below
|
||||
}
|
||||
}
|
||||
|
||||
export const supabase: SupabaseClient = useMockData || !realSupabase
|
||||
? createMockClient() as unknown as SupabaseClient
|
||||
: realSupabase;
|
||||
|
||||
export { supabaseUrl, supabaseAnonKey, useMockData };
|
||||
Reference in New Issue
Block a user