diff --git a/Dockerfile b/Dockerfile index b25a9e1..e323d01 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20 as builder +FROM node:20 AS builder WORKDIR /app COPY package*.json ./ # TODO: Remove this or review embedded variables @@ -20,19 +20,24 @@ RUN echo 'server { \ root /usr/share/nginx/html; \ index index.html; \ location / { \ - try_files $uri $uri/ /index.html; \ - expires 30d; \ - add_header Cache-Control "public, no-transform"; \ + try_files $uri $uri/ /index.html; \ + expires 30d; \ + add_header Cache-Control "public, no-transform"; \ } \ location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ { \ - expires 30d; \ - add_header Cache-Control "public, no-transform"; \ + expires 30d; \ + add_header Cache-Control "public, no-transform"; \ + } \ + location /searxng-api/ { \ + proxy_pass https://search.kevlarai.com/; \ + proxy_ssl_server_name on; \ + proxy_set_header Host search.kevlarai.com; \ } \ location ~ /\. { \ - deny all; \ + deny all; \ } \ error_page 404 /index.html; \ -}' > /etc/nginx/conf.d/default.conf + }' > /etc/nginx/conf.d/default.conf # Set up permissions RUN chown -R nginx:nginx /usr/share/nginx/html \ diff --git a/package.json b/package.json index fde0142..c6fa9af 100644 --- a/package.json +++ b/package.json @@ -57,14 +57,6 @@ }, "devDependencies": { "@eslint/js": "^9.15.0", - "@storybook/addon-actions": "^8.6.12", - "@storybook/addon-essentials": "^8.6.12", - "@storybook/addon-interactions": "^8.6.12", - "@storybook/addon-links": "^8.6.12", - "@storybook/addon-onboarding": "^8.6.12", - "@storybook/react": "^8.6.12", - "@storybook/react-vite": "^8.6.12", - "@storybook/testing-library": "^0.2.2", "@testing-library/jest-dom": "^6.4.5", "@testing-library/react": "^15.0.7", "@types/react": "^18.2.66", @@ -90,10 +82,9 @@ "postcss": "^8.4.38", "prettier": "^3.2.5", "react-refresh": "^0.14.2", - "storybook": "^8.6.12", "tailwindcss": "^3.4.3", "typescript": "^5.2.2", "vite-plugin-pwa": "^0.21.1", "vitest": "^1.6.0" } -} +} \ No newline at end of file diff --git a/src/contexts/UserContext.tsx b/src/contexts/UserContext.tsx index 0fa60f9..e1791ca 100644 --- a/src/contexts/UserContext.tsx +++ b/src/contexts/UserContext.tsx @@ -8,6 +8,9 @@ import { DatabaseNameService } from '../services/graph/databaseNameService'; import { provisionUser } from '../services/provisioningService'; 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 { user: CCUser | null; loading: boolean; @@ -29,9 +32,9 @@ export const UserContext = createContext({ preferences: {}, isMobile: false, isInitialized: false, - updateProfile: async () => {}, - updatePreferences: async () => {}, - clearError: () => {} + updateProfile: async () => { }, + updatePreferences: async () => { }, + clearError: () => { } }); export const UserProvider = ({ children }: { children: React.ReactNode }) => { @@ -63,7 +66,7 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => { return; } } - + let userInfo: User | null = null; // Declare at function scope try { logger.debug('user-context', '๐Ÿ”„ Resolving user profile', { @@ -110,30 +113,30 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => { email: userInfo.email }); - let profileRow: Record | null = null; + let profileRow: Record | null = null; logger.debug('user-context', '๐Ÿ”ง Step 5: Querying profiles table...', { userId: userInfo.id }); - + // Set loading state when we start the actual database query setLoading(true); - + // Query profiles table without timeout to see actual error logger.debug('user-context', '๐Ÿ”ง Step 5b: Starting profiles query...', { userId: userInfo.id, clientType: 'authenticated' }); - + // Try direct fetch instead of Supabase client to bypass hanging issue logger.debug('user-context', '๐Ÿ”ง Step 5b1: About to make profiles query with direct fetch...', { userId: userInfo.id, queryStarted: true }); - - const { data, error } = await fetch(`http://localhost:8000/rest/v1/profiles?select=*&id=eq.${userInfo.id}`, { + + const { data, error } = await fetch(`${supabaseUrl}/rest/v1/profiles?select=*&id=eq.${userInfo.id}`, { headers: { - 'Authorization': `Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0`, + 'Authorization': `Bearer ${supabaseAnonKey}`, 'Content-Type': 'application/json' } }) @@ -151,20 +154,20 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => { }); return { data: null, error: { message: err.message, code: 'FETCH_ERROR' } }; }); - + logger.debug('user-context', '๐Ÿ”ง Step 5b2: Direct fetch completed...', { userId: userInfo.id, hasData: !!data, hasError: !!error }); - + logger.debug('user-context', '๐Ÿ”ง Step 5c: Profiles query completed', { hasData: !!data, hasError: !!error, errorCode: error?.code, errorMessage: error?.message }); - + logger.debug('user-context', '๐Ÿ”ง Step 5a: Profiles query result', { hasData: !!data, hasError: !!error, @@ -316,7 +319,7 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => { userId: userInfo?.id, email: userInfo?.email }); - + if (userInfo) { const metadata = userInfo.user_metadata as CCUserMetadata; const fallbackProfile: CCUser = { @@ -330,12 +333,12 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => { created_at: userInfo.created_at, updated_at: userInfo.updated_at }; - + DatabaseNameService.rememberDatabaseNames({ userDbName: fallbackProfile.user_db_name, schoolDbName: fallbackProfile.school_db_name }); - + setProfile(fallbackProfile); logger.debug('user-context', 'โœ… Fallback profile created', { userId: fallbackProfile.id, @@ -345,7 +348,7 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => { } else { setProfile(null); } - + setPreferences({}); setError(error instanceof Error ? error : new Error('Failed to load user profile')); setLoading(false); // Ensure loading is cleared on error @@ -353,12 +356,12 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => { logger.debug('user-context', '๐Ÿ”ง Finalizing user context initialization...', { isMounted: mountedRef.current }); - + if (mountedRef.current) { // Loading state is already managed above, just log completion logger.debug('user-context', 'โœ… User context initialization complete'); } - + logger.debug('user-context', '๐Ÿ”ง Step 10: Setting isInitialized to true'); setIsInitialized(true); logger.debug('user-context', 'โœ… User context initialized flag set - initialization complete!', { diff --git a/src/debugConfig.ts b/src/debugConfig.ts index 59f1a17..b3d50b7 100644 --- a/src/debugConfig.ts +++ b/src/debugConfig.ts @@ -110,7 +110,8 @@ export type LogCategory = | 'auth-service' | 'user-context' | 'neo-user-context' - | 'neo-institute-context'; + | 'neo-institute-context' + | 'search-service'; interface LogConfig { enabled: boolean; // Master switch to turn logging on/off @@ -338,6 +339,7 @@ logger.setConfig({ 'user-context', 'neo-user-context', 'neo-institute-context', + 'search-service' ], }); diff --git a/src/pages/dev/SimpleUploadTest.tsx b/src/pages/dev/SimpleUploadTest.tsx index d3ed7aa..baf26b1 100644 --- a/src/pages/dev/SimpleUploadTest.tsx +++ b/src/pages/dev/SimpleUploadTest.tsx @@ -42,10 +42,10 @@ import { Info as InfoIcon } from '@mui/icons-material'; import { supabase } from '../../supabaseClient'; -import { - pickDirectory, - processDirectoryFiles, - calculateDirectoryStats, +import { + pickDirectory, + processDirectoryFiles, + calculateDirectoryStats, formatFileSize, isDirectoryPickerSupported, FileWithPath @@ -104,7 +104,7 @@ const SimpleUploadTest: React.FC = () => { const [files, setFiles] = useState([]); const [pagination, setPagination] = useState(null); const [loading, setLoading] = useState(false); - + // Pagination and filtering state const [currentPage, setCurrentPage] = useState(1); const [itemsPerPage, setItemsPerPage] = useState(10); @@ -128,7 +128,7 @@ const SimpleUploadTest: React.FC = () => { const apiFetch = useCallback(async (url: string, init?: { method?: string; body?: FormData | string; headers?: Record }) => { const session = await supabase.auth.getSession(); const token = session?.data?.session?.access_token; - + if (!token) { throw new Error('No authentication token available'); } @@ -140,12 +140,12 @@ const SimpleUploadTest: React.FC = () => { const fullUrl = url.startsWith('http') ? url : `${API_BASE}${url}`; const res = await fetch(fullUrl, { ...init, headers }); - + if (!res.ok) { const errorText = await res.text(); throw new Error(`HTTP ${res.status}: ${errorText}`); } - + return res.json(); }, [API_BASE]); @@ -171,7 +171,7 @@ const SimpleUploadTest: React.FC = () => { const loadFiles = useCallback(async (cabinetId: string, page: number = currentPage) => { if (!cabinetId) return; - + setLoading(true); try { // Build query parameters for pagination, search, and sorting @@ -183,20 +183,20 @@ const SimpleUploadTest: React.FC = () => { sort_order: sortOrder, include_directories: 'true' }); - + if (searchTerm) { params.append('search', searchTerm); } - + // Use the new simple upload endpoint for listing files with pagination const data: FileListResponse = await apiFetch(`/simple-upload/files?${params.toString()}`); - + setFiles(data.files || []); setPagination(data.pagination); - - setMessage({ - type: 'success', - text: `Loaded ${data.files?.length || 0} files (${data.pagination.total_count} total)` + + setMessage({ + type: 'success', + text: `Loaded ${data.files?.length || 0} files (${data.pagination.total_count} total)` }); } catch (error: unknown) { console.error('Failed to load files:', error); @@ -240,7 +240,7 @@ const SimpleUploadTest: React.FC = () => { // Single file upload const handleSingleUpload = async (e: React.ChangeEvent) => { if (!e.target.files || !selectedCabinet) return; - + const file = e.target.files[0]; const formData = new FormData(); formData.append('cabinet_id', selectedCabinet); @@ -250,22 +250,22 @@ const SimpleUploadTest: React.FC = () => { try { setLoading(true); - + // Choose endpoint based on upload type const endpoint = uploadType === 'new' ? '/simple-upload/files/upload' : '/database/files/upload'; - + const result = await apiFetch(endpoint, { method: 'POST', body: formData }); console.log('Upload result:', result); - - setMessage({ - type: 'success', - text: `File uploaded successfully using ${uploadType === 'new' ? 'NEW' : 'OLD'} endpoint: ${file.name}` + + setMessage({ + type: 'success', + text: `File uploaded successfully using ${uploadType === 'new' ? 'NEW' : 'OLD'} endpoint: ${file.name}` }); - + await loadFiles(selectedCabinet); e.target.value = ''; } catch (error: unknown) { @@ -311,14 +311,14 @@ const SimpleUploadTest: React.FC = () => { setSelectedFiles(files); setDirectoryStats(calculateDirectoryStats(files)); - + const progress: UploadProgress[] = files.map(file => ({ path: file.relativePath, size: file.size, status: 'queued', progress: 0 })); - + setUploadProgress(progress); setShowUploadDialog(true); }; @@ -327,61 +327,61 @@ const SimpleUploadTest: React.FC = () => { if (!selectedCabinet || selectedFiles.length === 0) return; setIsUploading(true); - + try { const firstFilePath = selectedFiles[0].relativePath; const directoryName = firstFilePath.split('/')[0] || 'uploaded-folder'; - + const formData = new FormData(); formData.append('cabinet_id', selectedCabinet); formData.append('scope', 'teacher'); formData.append('directory_name', directoryName); - + selectedFiles.forEach(file => { formData.append('files', file); }); - + const relativePaths = selectedFiles.map(f => f.relativePath); formData.append('file_paths', JSON.stringify(relativePaths)); - - const result = await apiFetch('/simple-upload/files/upload-directory', { - method: 'POST', - body: formData + + const result = await apiFetch('/simple-upload/files/upload-directory', { + method: 'POST', + body: formData }); - + console.log('Directory upload result:', result); - + setUploadProgress(prev => prev.map(item => ({ ...item, status: 'done', progress: 100 }))); - - setMessage({ - type: 'success', - text: `Directory uploaded successfully: ${directoryName} (${selectedFiles.length} files)` + + setMessage({ + type: 'success', + text: `Directory uploaded successfully: ${directoryName} (${selectedFiles.length} files)` }); - + await loadFiles(selectedCabinet); - + setTimeout(() => { setShowUploadDialog(false); setIsUploading(false); setSelectedFiles([]); setUploadProgress([]); }, 2000); - + } catch (error: unknown) { console.error('Directory upload failed:', error); const errorMessage = error instanceof Error ? error.message : 'Unknown error'; setMessage({ type: 'error', text: `Directory upload failed: ${errorMessage}` }); - + setUploadProgress(prev => prev.map(item => ({ ...item, status: 'error', error: String(error) }))); - + setIsUploading(false); } }; @@ -404,12 +404,12 @@ const SimpleUploadTest: React.FC = () => { try { const formData = new FormData(); formData.append('processing_type', 'basic'); - + const result = await apiFetch(`/simple-upload/files/${fileId}/process-manual`, { method: 'POST', body: formData }); - + console.log('Manual processing result:', result); setMessage({ type: 'info', text: 'Manual processing triggered (not yet implemented)' }); } catch (error: unknown) { @@ -434,7 +434,7 @@ const SimpleUploadTest: React.FC = () => { ๐Ÿงช Simple Upload Test Page - + This page tests the NEW simple upload system (no auto-processing) vs the OLD system (with auto-processing). @@ -452,8 +452,8 @@ const SimpleUploadTest: React.FC = () => { {/* Upload Controls */} - } /> @@ -504,15 +504,15 @@ const SimpleUploadTest: React.FC = () => { Upload File - - + - + - + {!selectedCabinet && ( Select a cabinet first to enable uploads )} - + {selectedCabinet && !isDirectoryPickerSupported() && ( โš ๏ธ Folder uploads may have limited support in this browser @@ -769,11 +769,11 @@ export const CCFilesPanel: React.FC = () => { {artefacts.length > 0 && ( <> - - + { )} - + {/* Directory Upload Dialog */} - !isDirectoryUploading && setShowDirectoryDialog(false)} - maxWidth="md" + !isDirectoryUploading && setShowDirectoryDialog(false)} + maxWidth="md" fullWidth > @@ -804,21 +804,21 @@ export const CCFilesPanel: React.FC = () => { {isDirectoryUploading && } - + {directoryStats && ( {directoryStats.fileCount} files in{' '} - {directoryStats.directoryCount} folders
+ {directoryStats.directoryCount} folders
Total size: {directoryStats.formattedSize}
)} - - { ))}
- + -