diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index e8ada9e8..b805e271 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -16,8 +16,8 @@ jobs: # These must be configured as secrets in your Gitea repository settings. env: # Public keys needed for the React build process. - REACT_APP_SUPABASE_URL: ${{ secrets.REACT_APP_SUPABASE_URL }} - REACT_APP_SUPABASE_ANON_KEY: ${{ secrets.REACT_APP_SUPABASE_ANON_KEY }} + VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL }} + VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }} # Supabase token for non-interactive CLI authentication. SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} # The project ID for linking the Supabase CLI. @@ -62,28 +62,6 @@ jobs: npm exec -- supabase functions deploy delete-user --project-ref ${{ env.SUPABASE_PROJECT_ID }} npm exec -- supabase functions deploy seed-database --project-ref ${{ env.SUPABASE_PROJECT_ID }} - # Debug step: Verify environment variables are present before build - - name: Debug Environment Variables - # This step now contains comprehensive checks to validate the secret. - env: - # We map the Gitea secret to a temporary env var for shell access. - DEBUG_SECRET: ${{ secrets.VITE_GOOGLE_GENAI_API_KEY }} - run: | - echo "--- Running Comprehensive Secret Debug ---" - echo "Step 1: Check via 'if' condition:" - if [ "${{ secrets.VITE_GOOGLE_GENAI_API_KEY != '' }}" == "true" ]; then - echo " ✅ SUCCESS: The 'if' condition sees the secret is NOT empty." - else - echo " ❌ FAILURE: The 'if' condition sees the secret IS EMPTY." - fi - echo "Step 2: Check via shell variable:" - if [ -n "$DEBUG_SECRET" ]; then - echo " ✅ SUCCESS: The shell variable \$DEBUG_SECRET is NOT empty." - else - echo " ❌ FAILURE: The shell variable \$DEBUG_SECRET is EMPTY." - fi - echo "--- End of Debug ---" - # --- Frontend Deployment --- - name: Build React Application # We set the environment variable directly in the command line for this step. diff --git a/App.tsx b/App.tsx index 77ce41de..90e1d8fa 100644 --- a/App.tsx +++ b/App.tsx @@ -6,14 +6,13 @@ import { PriceChart } from './components/PriceChart'; import { ErrorDisplay } from './components/ErrorDisplay'; import { Header } from './components/Header'; import { isImageAFlyer, extractCoreDataFromImage, extractAddressFromImage, extractLogoFromImage } from './services/geminiService'; -import type { FlyerItem, Flyer, MasterGroceryItem, DealItem, ProcessingStage, StageStatus, Store, Profile, ShoppingList, ShoppingListItem } from './types'; +import type { FlyerItem, Flyer, MasterGroceryItem, DealItem, ProcessingStage, StageStatus, Store, Profile, ShoppingList, ShoppingListItem, Database } from './types'; import { BulkImporter } from './components/BulkImporter'; import { PriceHistoryChart } from './components/PriceHistoryChart'; -import { supabase, uploadFlyerImage, createFlyerRecord, saveFlyerItems, getFlyers, getFlyerItems, initializeSupabase, findFlyerByChecksum, getWatchedItems, addWatchedItem, getAllMasterItems, getFlyerItemsForFlyers, countFlyerItemsForFlyers, getUserProfile, updateUserPreferences, removeWatchedItem, getShoppingLists, createShoppingList, addShoppingListItem, updateShoppingListItem, removeShoppingListItem, deleteShoppingList, uploadLogoAndUpdateStore } from './services/supabaseClient'; +import { supabase, uploadFlyerImage, createFlyerRecord, saveFlyerItems, getFlyers, getFlyerItems, findFlyerByChecksum, getWatchedItems, addWatchedItem, getAllMasterItems, getFlyerItemsForFlyers, countFlyerItemsForFlyers, getUserProfile, updateUserPreferences, removeWatchedItem, getShoppingLists, createShoppingList, addShoppingListItem, updateShoppingListItem, removeShoppingListItem, deleteShoppingList, uploadLogoAndUpdateStore } from './services/supabaseClient'; import { FlyerList } from './components/FlyerList'; import { recordProcessingTime, getAverageProcessingTime } from './utils/processingTimer'; import { ProcessingStatus } from './components/ProcessingStatus'; -import { SupabaseConnector } from './components/SupabaseConnector'; import { generateFileChecksum } from './utils/checksum'; import { convertPdfToImageFiles } from './utils/pdfConverter'; import { BulkImportSummary } from './components/BulkImportSummary'; @@ -52,7 +51,7 @@ function App() { errors: { fileName: string; message: string }[]; } | null>(null); - const [isDbConnected, setIsDbConnected] = useState(!!supabase); + const isDbConnected = !!supabase; const [isReady, setIsReady] = useState(false); const [isDarkMode, setIsDarkMode] = useState(false); const [unitSystem, setUnitSystem] = useState<'metric' | 'imperial'>('imperial'); @@ -180,19 +179,6 @@ function App() { } }, []); - useEffect(() => { - if (!supabase) { - const storedUrl = localStorage.getItem('supabaseUrl'); - const storedKey = localStorage.getItem('supabaseAnonKey'); - if (storedUrl && storedKey) { - initializeSupabase(storedUrl, storedKey); - setIsDbConnected(true); - } - } else { - setIsDbConnected(true); - } - }, []); - // Effect to handle authentication state changes. useEffect(() => { if (!isDbConnected || !supabase) return; @@ -245,7 +231,7 @@ function App() { useEffect(() => { - if (isDbConnected && isReady) { + if (isReady && isDbConnected) { fetchFlyers(); fetchMasterItems(); } @@ -787,20 +773,14 @@ function App() {
- {isDbConnected ? ( - <> - - {isReady && ( - - )} - setIsReady(true)} /> - - ) : ( - setIsDbConnected(true)} /> + + {isReady && isDbConnected && ( + )} + setIsReady(true)} />
diff --git a/services/supabaseClient.ts b/services/supabaseClient.ts index d7e032c6..ebe8c436 100644 --- a/services/supabaseClient.ts +++ b/services/supabaseClient.ts @@ -1,33 +1,21 @@ import { createClient, SupabaseClient } from '@supabase/supabase-js'; -import type { Flyer, FlyerItem, MasterGroceryItem, Profile, ShoppingList, ShoppingListItem } from '../types'; +import type { Flyer, FlyerItem, MasterGroceryItem, Profile, ShoppingList, ShoppingListItem, Store } from '../types'; +import { Database } from '../types/supabase'; -export let supabase: SupabaseClient | null = null; +// In a Vite project, environment variables are exposed on the `import.meta.env` object. +// For security, only variables prefixed with `VITE_` are exposed to the client-side code. +const supabaseUrl = import.meta.env.VITE_SUPABASE_URL; +const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY; -// Attempt to initialize from environment variables -const supabaseUrl = process.env.REACT_APP_SUPABASE_URL; -const supabaseAnonKey = process.env.REACT_APP_SUPABASE_ANON_KEY; - -if (supabaseUrl && supabaseAnonKey) { - try { - supabase = createClient(supabaseUrl, supabaseAnonKey); - } catch (e) { - console.error("Failed to initialize Supabase from env vars:", e); - supabase = null; - } +if (!supabaseUrl || !supabaseAnonKey) { + console.warn("Supabase environment variables not set. Running in no-database mode."); } -/** - * Initializes the Supabase client. Can be called with user-provided credentials. - * @param url - The Supabase project URL. - * @param key - The Supabase anon key. - * @returns The Supabase client instance. - */ -export const initializeSupabase = (url: string, key: string): SupabaseClient => { - if (!supabase) { - supabase = createClient(url, key); - } - return supabase; -}; +// Create and export the Supabase client. +// If the keys are missing, this will be null, and features requiring it will be disabled. +export const supabase = (supabaseUrl && supabaseAnonKey) + ? createClient(supabaseUrl, supabaseAnonKey) + : null; /** * Disconnects the Supabase client by setting the instance to null.