supabase client tool
This commit is contained in:
@@ -1,11 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
|
|
||||||
import type { Database } from '@/types/supabase';
|
import type { Database } from '@/types/supabase';
|
||||||
import { Select } from './Select';
|
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 {
|
interface TeamSelectorProps {
|
||||||
value?: string;
|
value?: string;
|
||||||
@@ -17,8 +18,6 @@ export function TeamSelector({ value, onChange, className }: TeamSelectorProps)
|
|||||||
const [teams, setTeams] = useState<Team[]>([]);
|
const [teams, setTeams] = useState<Team[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
const supabase = createClientComponentClient<Database>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isMounted = true;
|
let isMounted = true;
|
||||||
@@ -31,6 +30,8 @@ export function TeamSelector({ value, onChange, className }: TeamSelectorProps)
|
|||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
|
||||||
// 获取用户的团队成员关系
|
// 获取用户的团队成员关系
|
||||||
console.log('Fetching team memberships for user:', userId);
|
console.log('Fetching team memberships for user:', userId);
|
||||||
const { data: memberships, error: membershipError } = await supabase
|
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);
|
console.log('Auth state changed:', event, session?.user);
|
||||||
if (event === 'SIGNED_IN' && session?.user?.id) {
|
if (event === 'SIGNED_IN' && session?.user?.id) {
|
||||||
fetchTeams(session.user.id);
|
fetchTeams(session.user.id);
|
||||||
@@ -96,7 +99,7 @@ export function TeamSelector({ value, onChange, className }: TeamSelectorProps)
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 初始检查session
|
// 初始检查session
|
||||||
supabase.auth.getSession().then(({ data: { session } }) => {
|
supabase.auth.getSession().then(({ data: { session } }: { data: { session: Session | null } }) => {
|
||||||
console.log('Initial session check:', session?.user);
|
console.log('Initial session check:', session?.user);
|
||||||
if (session?.user?.id) {
|
if (session?.user?.id) {
|
||||||
fetchTeams(session.user.id);
|
fetchTeams(session.user.id);
|
||||||
@@ -107,7 +110,7 @@ export function TeamSelector({ value, onChange, className }: TeamSelectorProps)
|
|||||||
isMounted = false;
|
isMounted = false;
|
||||||
subscription.unsubscribe();
|
subscription.unsubscribe();
|
||||||
};
|
};
|
||||||
}, [supabase]);
|
}, []);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <div className="animate-pulse h-10 bg-gray-200 rounded-md dark:bg-gray-700" />;
|
return <div className="animate-pulse h-10 bg-gray-200 rounded-md dark:bg-gray-700" />;
|
||||||
|
|||||||
59
app/utils/supabase.ts
Normal file
59
app/utils/supabase.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { createClient, SupabaseClient } from "@supabase/supabase-js";
|
||||||
|
import type { Database } from "@/types/supabase";
|
||||||
|
|
||||||
|
let supabase: SupabaseClient<Database> | 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<Database> => {
|
||||||
|
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<Database>(
|
||||||
|
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;
|
||||||
|
};
|
||||||
@@ -78,4 +78,69 @@ export interface Database {
|
|||||||
[_ in never]: never
|
[_ 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user