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:
Agent Zero 2026-02-23 17:48:14 +00:00
parent de78090963
commit 341c551ea5
3 changed files with 51 additions and 100 deletions

View File

@ -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

View File

@ -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!', {

View File

@ -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'
}
}
);