diff --git a/lib/supabase/product-files.ts b/lib/supabase/product-files.ts new file mode 100644 index 0000000..01eda78 --- /dev/null +++ b/lib/supabase/product-files.ts @@ -0,0 +1,84 @@ +import type { SupabaseClient } from '@supabase/supabase-js'; +import { expandProductAccess } from '@/lib/product-access'; + +export interface ProductRow { + id: string; + name: string; + description: string | null; + description_long: string | null; + price: number; + category: string; + is_active: boolean; + is_listed: boolean; + sort_order: number; + features: string[] | null; + pay_method: string; +} + +export interface ProductFile { + id: string; + product_id: string | null; + label: string; + file_path: string; + filename: string; + size_bytes: number; + sort_order: number; + uploaded_at: string; + deleted_at: string | null; + min_tier: string; // 레거시 컬럼 — 신규 로직에서는 미사용 +} + +/** 카탈로그 노출 제품 (is_listed && is_active, sort_order 순) */ +export async function getListedProducts(supabase: SupabaseClient): Promise { + const { data, error } = await supabase + .from('products') + .select('*') + .eq('is_listed', true) + .eq('is_active', true) + .order('sort_order') + .order('id'); + if (error) throw error; + return (data ?? []) as ProductRow[]; +} + +export async function getProductById(supabase: SupabaseClient, id: string): Promise { + const { data, error } = await supabase.from('products').select('*').eq('id', id).maybeSingle(); + if (error) throw error; + return (data as ProductRow) ?? null; +} + +/** 사용자의 결제 완료 product_id 목록 (orders 단일 소스) */ +export async function getUserPaidProductIds(supabase: SupabaseClient, userId: string): Promise { + const { data, error } = await supabase + .from('orders') + .select('product_id') + .eq('user_id', userId) + .eq('status', 'paid') + .not('product_id', 'is', null); + if (error) throw error; + return Array.from(new Set((data ?? []).map((r) => r.product_id as string))); +} + +/** 접근 확장 포함 — 사용자가 다운로드 가능한 product_id 집합 */ +export async function getUserAccessibleProductIds(supabase: SupabaseClient, userId: string): Promise { + return expandProductAccess(await getUserPaidProductIds(supabase, userId)); +} + +export async function getFilesByProductIds(supabase: SupabaseClient, productIds: string[]): Promise { + if (productIds.length === 0) return []; + const { data, error } = await supabase + .from('pack_files') + .select('*') + .in('product_id', productIds) + .is('deleted_at', null) + .order('product_id') + .order('sort_order'); + if (error) throw error; + return (data ?? []) as ProductFile[]; +} + +export async function getFileById(supabase: SupabaseClient, id: string): Promise { + const { data, error } = await supabase.from('pack_files').select('*').eq('id', id).maybeSingle(); + if (error) throw error; + return (data as ProductFile) ?? null; +}