From 1978e0224e5f57174bd81379b97b3268ea91ba8c Mon Sep 17 00:00:00 2001 From: William Tso Date: Tue, 1 Apr 2025 17:45:12 +0800 Subject: [PATCH] supabase client tool --- app/components/ui/TeamSelector.tsx | 17 ++++---- app/utils/supabase.ts | 59 +++++++++++++++++++++++++++ types/supabase.ts | 65 ++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 7 deletions(-) create mode 100644 app/utils/supabase.ts diff --git a/app/components/ui/TeamSelector.tsx b/app/components/ui/TeamSelector.tsx index dd031a4..d33d79d 100644 --- a/app/components/ui/TeamSelector.tsx +++ b/app/components/ui/TeamSelector.tsx @@ -1,11 +1,12 @@ "use client"; import { useEffect, useState } from 'react'; -import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'; import type { Database } from '@/types/supabase'; import { Select } from './Select'; +import { getSupabaseClient } from '../../utils/supabase'; +import { AuthChangeEvent, Session } from '@supabase/supabase-js'; -type Team = Database['public']['Tables']['teams']['Row']; +type Team = Database['limq']['Tables']['teams']['Row']; interface TeamSelectorProps { value?: string; @@ -17,8 +18,6 @@ export function TeamSelector({ value, onChange, className }: TeamSelectorProps) const [teams, setTeams] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - - const supabase = createClientComponentClient(); useEffect(() => { let isMounted = true; @@ -31,6 +30,8 @@ export function TeamSelector({ value, onChange, className }: TeamSelectorProps) setError(null); try { + const supabase = getSupabaseClient(); + // 获取用户的团队成员关系 console.log('Fetching team memberships for user:', userId); const { data: memberships, error: membershipError } = await supabase @@ -84,8 +85,10 @@ export function TeamSelector({ value, onChange, className }: TeamSelectorProps) } }; + const supabase = getSupabaseClient(); + // 设置认证状态监听器 - const { data: { subscription } } = supabase.auth.onAuthStateChange((event, session) => { + const { data: { subscription } } = supabase.auth.onAuthStateChange((event: AuthChangeEvent, session: Session | null) => { console.log('Auth state changed:', event, session?.user); if (event === 'SIGNED_IN' && session?.user?.id) { fetchTeams(session.user.id); @@ -96,7 +99,7 @@ export function TeamSelector({ value, onChange, className }: TeamSelectorProps) }); // 初始检查session - supabase.auth.getSession().then(({ data: { session } }) => { + supabase.auth.getSession().then(({ data: { session } }: { data: { session: Session | null } }) => { console.log('Initial session check:', session?.user); if (session?.user?.id) { fetchTeams(session.user.id); @@ -107,7 +110,7 @@ export function TeamSelector({ value, onChange, className }: TeamSelectorProps) isMounted = false; subscription.unsubscribe(); }; - }, [supabase]); + }, []); if (loading) { return
; diff --git a/app/utils/supabase.ts b/app/utils/supabase.ts new file mode 100644 index 0000000..ab645b7 --- /dev/null +++ b/app/utils/supabase.ts @@ -0,0 +1,59 @@ +import { createClient, SupabaseClient } from "@supabase/supabase-js"; +import type { Database } from "@/types/supabase"; + +let supabase: SupabaseClient | null = null; + +// 简单的存储适配器,使用localStorage +const storageAdapter = { + getItem: async (key: string) => { + try { + const item = localStorage.getItem(key); + return item; + } catch (error) { + console.error("Storage get error:", error); + return null; + } + }, + + setItem: async (key: string, value: string) => { + try { + localStorage.setItem(key, value); + } catch (error) { + console.error("Storage set error:", error); + } + }, + + removeItem: async (key: string) => { + try { + localStorage.removeItem(key); + } catch (error) { + console.error("Storage remove error:", error); + } + }, +}; + +export const getSupabaseClient = (): SupabaseClient => { + if (!supabase) { + if (!process.env.NEXT_PUBLIC_SUPABASE_URL || !process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY) { + throw new Error('Missing Supabase environment variables'); + } + + supabase = createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, + { + db: { schema: "limq" }, + auth: { + storage: storageAdapter, + persistSession: true, + autoRefreshToken: true, + }, + } + ); + } + return supabase; +}; + +export const clearSupabaseInstance = () => { + supabase = null; +}; \ No newline at end of file diff --git a/types/supabase.ts b/types/supabase.ts index ea99551..257b2cb 100644 --- a/types/supabase.ts +++ b/types/supabase.ts @@ -78,4 +78,69 @@ export interface Database { [_ in never]: never } } + limq: { + Tables: { + teams: { + Row: { + id: string + name: string + description: string | null + avatar_url: string | null + attributes: Json | null + created_at: string + updated_at: string + deleted_at: string | null + } + Insert: { + id?: string + name: string + description?: string | null + avatar_url?: string | null + attributes?: Json | null + created_at?: string + updated_at?: string + deleted_at?: string | null + } + Update: { + id?: string + name?: string + description?: string | null + avatar_url?: string | null + attributes?: Json | null + created_at?: string + updated_at?: string + deleted_at?: string | null + } + } + team_membership: { + Row: { + id: string + team_id: string + user_id: string + role: string + created_at: string + updated_at: string + deleted_at: string | null + } + Insert: { + id?: string + team_id: string + user_id: string + role: string + created_at?: string + updated_at?: string + deleted_at?: string | null + } + Update: { + id?: string + team_id?: string + user_id?: string + role?: string + created_at?: string + updated_at?: string + deleted_at?: string | null + } + } + } + } } \ No newline at end of file