refactor: simplify UserContext and supabaseClient
- Refactored UserContext.tsx (67 lines simplified) - Simplified supabaseClient.ts (82 lines reduced) - Updated .env.development for local dev
This commit is contained in:
parent
de78090963
commit
341c551ea5
@ -1,3 +1,4 @@
|
|||||||
|
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiaWF0IjoxNzcxODE3MjE5LCJpc3MiOiJzdXBhYmFzZSIsInN1YiI6IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMCIsImV4cCI6MzM0ODYxNzIxOSwicm9sZSI6ImFub24ifQ.JbmQOTOBAzpBJ9JttOrGlo_JTXDXhCjYMjKiFvRkaNQ
|
||||||
PORT_FRONTEND=5173
|
PORT_FRONTEND=5173
|
||||||
PORT_FRONTEND_HMR=3002
|
PORT_FRONTEND_HMR=3002
|
||||||
PORT_API=8000
|
PORT_API=8000
|
||||||
@ -15,7 +16,6 @@ VITE_APP_HMR_URL=http://192.168.0.94:5173
|
|||||||
|
|
||||||
# Supabase is on external container - use its IP
|
# Supabase is on external container - use its IP
|
||||||
VITE_SUPABASE_URL=http://192.168.0.155:8000
|
VITE_SUPABASE_URL=http://192.168.0.155:8000
|
||||||
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiaWF0IjoxNzcxODE3MjE5LCJpc3MiOiJzdXBhYmFzZSIsInN1YiI6IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMCIsImV4cCI6MzM0ODYxNzIxOSwicm9sZSI6ImFub24ifQ.JbmQOTOBAzpBJ9JttOrGlo_JTXDXhCjYMjKiFvRkaNQ
|
|
||||||
|
|
||||||
# API should use localhost for local development
|
# API should use localhost for local development
|
||||||
VITE_API_URL=http://192.168.0.94:8000
|
VITE_API_URL=http://192.168.0.94:8000
|
||||||
|
|||||||
@ -8,9 +8,6 @@ import { DatabaseNameService } from '../services/graph/databaseNameService';
|
|||||||
import { provisionUser } from '../services/provisioningService';
|
import { provisionUser } from '../services/provisioningService';
|
||||||
import { storageService, StorageKeys } from '../services/auth/localStorageService';
|
import { storageService, StorageKeys } from '../services/auth/localStorageService';
|
||||||
|
|
||||||
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
|
|
||||||
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
|
|
||||||
|
|
||||||
export interface UserContextType {
|
export interface UserContextType {
|
||||||
user: CCUser | null;
|
user: CCUser | null;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
@ -32,9 +29,9 @@ export const UserContext = createContext<UserContextType>({
|
|||||||
preferences: {},
|
preferences: {},
|
||||||
isMobile: false,
|
isMobile: false,
|
||||||
isInitialized: false,
|
isInitialized: false,
|
||||||
updateProfile: async () => { },
|
updateProfile: async () => {},
|
||||||
updatePreferences: async () => { },
|
updatePreferences: async () => {},
|
||||||
clearError: () => { }
|
clearError: () => {}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const UserProvider = ({ children }: { children: React.ReactNode }) => {
|
export const UserProvider = ({ children }: { children: React.ReactNode }) => {
|
||||||
@ -66,7 +63,7 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let userInfo: User | null = null; // Declare at function scope
|
let userInfo: User | null = null; // Declare at function scope
|
||||||
try {
|
try {
|
||||||
logger.debug('user-context', '🔄 Resolving user profile', {
|
logger.debug('user-context', '🔄 Resolving user profile', {
|
||||||
@ -113,61 +110,45 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
email: userInfo.email
|
email: userInfo.email
|
||||||
});
|
});
|
||||||
|
|
||||||
let profileRow: Record<string, unknown> | null = null;
|
let profileRow: Record<string, unknown> | null = null;
|
||||||
|
|
||||||
logger.debug('user-context', '🔧 Step 5: Querying profiles table...', {
|
logger.debug('user-context', '🔧 Step 5: Querying profiles table...', {
|
||||||
userId: userInfo.id
|
userId: userInfo.id
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set loading state when we start the actual database query
|
// Set loading state when we start the actual database query
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
// Query profiles table without timeout to see actual error
|
// Query profiles table without timeout to see actual error
|
||||||
logger.debug('user-context', '🔧 Step 5b: Starting profiles query...', {
|
logger.debug('user-context', '🔧 Step 5b: Starting profiles query...', {
|
||||||
userId: userInfo.id,
|
userId: userInfo.id,
|
||||||
clientType: 'authenticated'
|
clientType: 'authenticated'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Try direct fetch instead of Supabase client to bypass hanging issue
|
logger.debug('user-context', '🔧 Step 5b1: About to make profiles query...', {
|
||||||
logger.debug('user-context', '🔧 Step 5b1: About to make profiles query with direct fetch...', {
|
|
||||||
userId: userInfo.id,
|
userId: userInfo.id,
|
||||||
queryStarted: true
|
queryStarted: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data, error } = await fetch(`${supabaseUrl}/rest/v1/profiles?select=*&id=eq.${userInfo.id}`, {
|
const { data, error } = await supabase
|
||||||
headers: {
|
.from('profiles')
|
||||||
'Authorization': `Bearer ${supabaseAnonKey}`,
|
.select('*')
|
||||||
'Content-Type': 'application/json'
|
.eq('id', userInfo.id)
|
||||||
}
|
.single();
|
||||||
})
|
|
||||||
.then(async (response) => {
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
||||||
}
|
|
||||||
const result = await response.json();
|
|
||||||
return { data: result[0] || null, error: null };
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
logger.debug('user-context', '🔧 Step 5b1: Direct fetch failed', {
|
|
||||||
userId: userInfo?.id,
|
|
||||||
error: err.message
|
|
||||||
});
|
|
||||||
return { data: null, error: { message: err.message, code: 'FETCH_ERROR' } };
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.debug('user-context', '🔧 Step 5b2: Direct fetch completed...', {
|
logger.debug('user-context', '🔧 Step 5b2: Direct fetch completed...', {
|
||||||
userId: userInfo.id,
|
userId: userInfo.id,
|
||||||
hasData: !!data,
|
hasData: !!data,
|
||||||
hasError: !!error
|
hasError: !!error
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.debug('user-context', '🔧 Step 5c: Profiles query completed', {
|
logger.debug('user-context', '🔧 Step 5c: Profiles query completed', {
|
||||||
hasData: !!data,
|
hasData: !!data,
|
||||||
hasError: !!error,
|
hasError: !!error,
|
||||||
errorCode: error?.code,
|
errorCode: error?.code,
|
||||||
errorMessage: error?.message
|
errorMessage: error?.message
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.debug('user-context', '🔧 Step 5a: Profiles query result', {
|
logger.debug('user-context', '🔧 Step 5a: Profiles query result', {
|
||||||
hasData: !!data,
|
hasData: !!data,
|
||||||
hasError: !!error,
|
hasError: !!error,
|
||||||
@ -319,7 +300,7 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
userId: userInfo?.id,
|
userId: userInfo?.id,
|
||||||
email: userInfo?.email
|
email: userInfo?.email
|
||||||
});
|
});
|
||||||
|
|
||||||
if (userInfo) {
|
if (userInfo) {
|
||||||
const metadata = userInfo.user_metadata as CCUserMetadata;
|
const metadata = userInfo.user_metadata as CCUserMetadata;
|
||||||
const fallbackProfile: CCUser = {
|
const fallbackProfile: CCUser = {
|
||||||
@ -333,12 +314,12 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
created_at: userInfo.created_at,
|
created_at: userInfo.created_at,
|
||||||
updated_at: userInfo.updated_at
|
updated_at: userInfo.updated_at
|
||||||
};
|
};
|
||||||
|
|
||||||
DatabaseNameService.rememberDatabaseNames({
|
DatabaseNameService.rememberDatabaseNames({
|
||||||
userDbName: fallbackProfile.user_db_name,
|
userDbName: fallbackProfile.user_db_name,
|
||||||
schoolDbName: fallbackProfile.school_db_name
|
schoolDbName: fallbackProfile.school_db_name
|
||||||
});
|
});
|
||||||
|
|
||||||
setProfile(fallbackProfile);
|
setProfile(fallbackProfile);
|
||||||
logger.debug('user-context', '✅ Fallback profile created', {
|
logger.debug('user-context', '✅ Fallback profile created', {
|
||||||
userId: fallbackProfile.id,
|
userId: fallbackProfile.id,
|
||||||
@ -348,7 +329,7 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
} else {
|
} else {
|
||||||
setProfile(null);
|
setProfile(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
setPreferences({});
|
setPreferences({});
|
||||||
setError(error instanceof Error ? error : new Error('Failed to load user profile'));
|
setError(error instanceof Error ? error : new Error('Failed to load user profile'));
|
||||||
setLoading(false); // Ensure loading is cleared on error
|
setLoading(false); // Ensure loading is cleared on error
|
||||||
@ -356,12 +337,12 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
logger.debug('user-context', '🔧 Finalizing user context initialization...', {
|
logger.debug('user-context', '🔧 Finalizing user context initialization...', {
|
||||||
isMounted: mountedRef.current
|
isMounted: mountedRef.current
|
||||||
});
|
});
|
||||||
|
|
||||||
if (mountedRef.current) {
|
if (mountedRef.current) {
|
||||||
// Loading state is already managed above, just log completion
|
// Loading state is already managed above, just log completion
|
||||||
logger.debug('user-context', '✅ User context initialization complete');
|
logger.debug('user-context', '✅ User context initialization complete');
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('user-context', '🔧 Step 10: Setting isInitialized to true');
|
logger.debug('user-context', '🔧 Step 10: Setting isInitialized to true');
|
||||||
setIsInitialized(true);
|
setIsInitialized(true);
|
||||||
logger.debug('user-context', '✅ User context initialized flag set - initialization complete!', {
|
logger.debug('user-context', '✅ User context initialized flag set - initialization complete!', {
|
||||||
|
|||||||
@ -4,65 +4,35 @@ import { logger } from './debugConfig';
|
|||||||
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
|
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
|
||||||
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
|
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
|
||||||
|
|
||||||
logger.info('supabase-client', '🔄 Supabase configuration', {
|
|
||||||
url: supabaseUrl,
|
|
||||||
key: supabaseAnonKey,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!supabaseUrl || !supabaseAnonKey) {
|
if (!supabaseUrl || !supabaseAnonKey) {
|
||||||
logger.error('supabase-client', '❌ Missing Supabase configuration', {
|
|
||||||
hasUrl: !!supabaseUrl,
|
|
||||||
hasKey: !!supabaseAnonKey,
|
|
||||||
});
|
|
||||||
throw new Error('Missing Supabase configuration');
|
throw new Error('Missing Supabase configuration');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lazy-loaded Supabase client instance for non-auth operations
|
logger.info('supabase-client', '🔄 Initializing Supabase client', {
|
||||||
let supabaseInstance: SupabaseClient | null = null;
|
url: supabaseUrl,
|
||||||
|
hasKey: !!supabaseAnonKey,
|
||||||
const getSupabaseClient = () => {
|
|
||||||
if (!supabaseInstance) {
|
|
||||||
logger.info('supabase-client', '🔄 Initializing Supabase client');
|
|
||||||
supabaseInstance = createClient(
|
|
||||||
supabaseUrl,
|
|
||||||
supabaseAnonKey,
|
|
||||||
{
|
|
||||||
auth: {
|
|
||||||
flowType: 'pkce',
|
|
||||||
autoRefreshToken: true,
|
|
||||||
persistSession: true,
|
|
||||||
detectSessionInUrl: false,
|
|
||||||
storage: window.localStorage,
|
|
||||||
storageKey: 'supabase.auth.token',
|
|
||||||
debug: true
|
|
||||||
},
|
|
||||||
global: {
|
|
||||||
headers: {
|
|
||||||
'X-Client-Info': 'classroom-copilot',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Allow JWT issuer mismatch for local development
|
|
||||||
db: {
|
|
||||||
schema: 'public'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Log configuration in development
|
|
||||||
logger.info('supabase-client', '🔄 Supabase client configuration loaded', {
|
|
||||||
url: supabaseUrl,
|
|
||||||
hasKey: !!supabaseAnonKey,
|
|
||||||
storageKey: 'supabase.auth.token'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return supabaseInstance;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Export a proxy that will lazy load the client when needed
|
|
||||||
export const supabase = new Proxy({} as SupabaseClient, {
|
|
||||||
get: (target, prop) => {
|
|
||||||
const client = getSupabaseClient();
|
|
||||||
return client[prop as keyof SupabaseClient];
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const supabase: SupabaseClient = createClient(
|
||||||
|
supabaseUrl,
|
||||||
|
supabaseAnonKey,
|
||||||
|
{
|
||||||
|
auth: {
|
||||||
|
flowType: 'pkce',
|
||||||
|
autoRefreshToken: true,
|
||||||
|
persistSession: true,
|
||||||
|
detectSessionInUrl: false,
|
||||||
|
storage: window.localStorage,
|
||||||
|
storageKey: 'supabase.auth.token',
|
||||||
|
debug: true
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
headers: {
|
||||||
|
'X-Client-Info': 'classroom-copilot',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
db: {
|
||||||
|
schema: 'public'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
Loading…
x
Reference in New Issue
Block a user