diff --git a/README.md b/README.md index 07d283d..a22858c 100644 --- a/README.md +++ b/README.md @@ -91,19 +91,18 @@ cd inpact #### 2. Frontend Setup 1. Navigate to the frontend directory: + ```sh cd frontend ``` 2. Install dependencies: + ```sh npm install ``` - -3. Create a `.env` file using `.env-example` file: - - +3. Create a `.env` file using `.env.example` at the frontend root: 4. Get your Supabase credentials: - Go to [Supabase](https://supabase.com/) @@ -115,25 +114,26 @@ npm install #### 3. Backend Setup 1. Navigate to the backend directory: + ```sh cd ../backend ``` 2. Install dependencies: + ```sh pip install -r requirements.txt ``` - 3. Navigate to the app directory: + ```sh cd app ``` -4. Create a `.env` file using `.env-example` as a reference. +4. Create a `.env` file using `.env.example` as a reference. 5. Obtain Supabase credentials: - - Go to [Supabase](https://supabase.com/) - Log in and create a new project - Click on the project and remember the project password @@ -160,20 +160,20 @@ cd app dbname=postgres ``` - 6. Get the Groq API key: - Visit [Groq Console](https://console.groq.com/) - Create an API key and paste it into the `.env` file #### 4. Start Development Servers - 1. Start the frontend server (from the frontend directory): + ```sh npm run dev ``` 2. Start the backend server (from the backend/app directory): + ```sh uvicorn main:app --reload ``` @@ -183,12 +183,10 @@ uvicorn main:app --reload To populate the database with initial data, follow these steps: 1. **Open Supabase Dashboard** - - Go to [Supabase](https://supabase.com/) and log in. - Select your created project. 2. **Access the SQL Editor** - - In the left sidebar, click on **SQL Editor**. 3. **Run the SQL Script** @@ -266,6 +264,4 @@ graph TD; For queries, issues, or feature requests, please raise an issue or reach out on our Discord server. - Happy Coding! - diff --git a/SUPABASE_SETUP.md b/SUPABASE_SETUP.md new file mode 100644 index 0000000..24eb65f --- /dev/null +++ b/SUPABASE_SETUP.md @@ -0,0 +1,164 @@ +# Supabase Integration Setup + +This project uses Supabase for authentication, database, and backend services. + +## 🚀 Quick Start + +### Frontend Setup (Next.js) + +1. **Install dependencies** (already done): + + ```bash + cd frontend + npm install + ``` + +2. **Configure environment variables**: + - Copy `.env.example` to `.env.local`: + ```bash + cp .env.example .env.local + ``` + - Update with your Supabase credentials: + ```env + NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co + NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key-here + ``` + +3. **Use the Supabase client**: + + ```typescript + import { supabase } from "@/lib/supabaseClient"; + + // Example: Fetch data + const { data, error } = await supabase.from("your_table").select("*"); + ``` + +### Backend Setup (FastAPI) + +1. **Install dependencies**: + + ```bash + cd backend + pip install -r requirements.txt + ``` + +2. **Configure environment variables**: + - Copy `env_example` to `.env`: + ```bash + cp env_example .env + ``` + - Update with your Supabase credentials: + ```env + SUPABASE_URL=https://your-project.supabase.co + SUPABASE_KEY=your-anon-key-here + ``` + > **Note:** Use the public Supabase Anon Key (`SUPABASE_KEY`) for backend configuration. Do **not** use or store the Service Role key in your backend `.env` file. + +3. **Use the Supabase client**: + + ```python + from app.services.supabase_client import supabase + + # Example: Fetch data + response = supabase.table('your_table').select('*').execute() + ``` + +## 🔍 Health Check + +Test your Supabase connection: + +```bash +# Start the backend server +cd backend +uvicorn app.main:app --reload + +# Check Supabase connection +curl http://localhost:8000/health/supabase +``` + +Expected response: + +```json +{ + "connected": true, + "message": "Supabase client initialized", + "status": "ready" +} +``` + +## 📁 Project Structure + +### Frontend + +``` +frontend/ +├── lib/ +│ └── supabaseClient.ts # Supabase client initialization +├── types/ +│ └── supabase.ts # TypeScript types for database +└── .env.example # Environment variables template +``` + +### Backend + +``` +backend/ +├── app/ +│ ├── core/ +│ │ └── config.py # Configuration settings +│ ├── services/ +│ │ └── supabase_client.py # Supabase client initialization +│ └── api/ +│ └── routes/ +│ └── health.py # Health check endpoints +└── env_example # Environment variables template +``` + +## 🔑 Environment Variables + +### Frontend (Next.js) + +- `NEXT_PUBLIC_SUPABASE_URL`: Your Supabase project URL +- `NEXT_PUBLIC_SUPABASE_ANON_KEY`: Supabase anon/public key (safe for client-side) + +### Backend (FastAPI) + +- `SUPABASE_URL`: Your Supabase project URL +- `SUPABASE_KEY`: Supabase anon/public key (safe for backend, matches config.py) +- `DATABASE_URL`: (Optional) Direct PostgreSQL connection string +- `ALLOWED_ORIGINS`: CORS allowed origins (comma-separated) + +## 🔒 Security Notes + +1. **Never commit `.env` files** - they are in `.gitignore` +2. **Frontend uses anon key** - safe for browser, limited permissions +3. **Backend uses anon key** - safe for backend, never expose service role key or store it in backend environment files +4. **Rotate keys if exposed** - generate new keys in Supabase dashboard +5. **Use environment-specific keys** - different keys for dev/staging/prod + +## 📚 Next Steps + +1. Create your database tables in Supabase Dashboard +2. Update `frontend/types/supabase.ts` with your table types +3. Implement authentication flows +4. Add Row Level Security (RLS) policies in Supabase + +## 🛠️ Useful Commands + +```bash +# Frontend development +cd frontend && npm run dev + +# Backend development +cd backend && uvicorn app.main:app --reload + +# Install new dependencies +cd frontend && npm install +cd backend && pip install && pip freeze > requirements.txt +``` + +## 📖 Documentation + +- [Supabase Documentation](https://supabase.com/docs) +- [Next.js + Supabase Guide](https://supabase.com/docs/guides/getting-started/quickstarts/nextjs) +- [Python + Supabase Guide](https://supabase.com/docs/reference/python/introduction) diff --git a/backend/app/api/routes/health.py b/backend/app/api/routes/health.py new file mode 100644 index 0000000..6f42dbf --- /dev/null +++ b/backend/app/api/routes/health.py @@ -0,0 +1,51 @@ +""" +Health check routes for monitoring service status +""" +from fastapi import APIRouter, HTTPException + +router = APIRouter(prefix="/health", tags=["health"]) + +@router.get("/") +def health_check(): + """Basic health check endpoint""" + return {"status": "healthy", "message": "Backend is running"} + +@router.get("/supabase") +def check_supabase(): + """ + Check Supabase connection status. + This endpoint attempts to query Supabase to verify the connection. + """ + try: + from app.services.supabase_client import supabase + + # Attempt a simple query to verify connection + response = supabase.table("_supabase_test").select("*").limit(1).execute() + + return { + "connected": True, + "message": "Supabase connection is working!", + "status": "healthy" + } + except Exception as e: + error_msg = str(e) + # Detect table-not-found error (Supabase/PostgREST or DB error) + if ( + "does not exist" in error_msg or + "relation" in error_msg and "does not exist" in error_msg or + "Could not find the table" in error_msg or + "PGRST205" in error_msg + ): + return { + "connected": True, + "message": "Supabase client initialized (no tables queried yet)", + "status": "ready", + "note": error_msg + } + # For any other error, treat as unhealthy + return { + "connected": False, + "message": "Supabase connection failed", + "status": "unhealthy", + "note": error_msg + } diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 64ba7f2..ea4d812 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -1,9 +1,28 @@ # Configuration settings for FastAPI app from pydantic_settings import BaseSettings +from typing import Optional class Settings(BaseSettings): - database_url: str - ai_api_key: str + # Supabase Configuration + supabase_url: str + supabase_key: str # Use the public Anon Key instead of the Service Key + + # Database Configuration + database_url: Optional[str] = None + + # AI Configuration + ai_api_key: Optional[str] = None + groq_api_key: Optional[str] = None + + # CORS Configuration + allowed_origins: str = "http://localhost:3000" + + # Server Configuration + host: str = "0.0.0.0" + port: int = 8000 + + # Application Settings + app_name: Optional[str] = None model_config = { "env_file": ".env" diff --git a/backend/app/main.py b/backend/app/main.py index 1b0dad4..347eb09 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,10 +1,24 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware import os +from app.api.routes import health +from app.services.supabase_client import supabase app = FastAPI(title="Inpact Backend", version="0.1.0") -# --- CORS Setup (so frontend can talk to backend) --- +# Verify Supabase client initialization on startup +try: + # Try a lightweight query + response = supabase.table("_supabase_test").select("*").limit(1).execute() + print("✅ Supabase client initialized successfully.") +except Exception as e: + error_msg = str(e) + if "Could not find the table" in error_msg: + print("⚠️ Supabase client connected, but test table does not exist. Connection is working.") + else: + print(f"❌ Failed to verify Supabase connection: {e}") + +# --- CORS Setup --- app.add_middleware( CORSMiddleware, allow_origins=os.getenv("ALLOWED_ORIGINS", "http://localhost:3000").split(","), @@ -13,6 +27,8 @@ allow_headers=["*"], ) +app.include_router(health.router) + @app.get("/") def root(): return {"message": "Welcome to Inpact Backend 🚀"} diff --git a/backend/app/services/__init__.py b/backend/app/services/__init__.py new file mode 100644 index 0000000..824534c --- /dev/null +++ b/backend/app/services/__init__.py @@ -0,0 +1,3 @@ +""" +Services module for backend application +""" diff --git a/backend/app/services/supabase_client.py b/backend/app/services/supabase_client.py new file mode 100644 index 0000000..d1078b2 --- /dev/null +++ b/backend/app/services/supabase_client.py @@ -0,0 +1,5 @@ +from supabase import create_client, Client +from app.core.config import settings + +# Initialize Supabase client with anon key (public) +supabase: Client = create_client(settings.supabase_url, settings.supabase_key) diff --git a/backend/env_example b/backend/env_example index a56d896..47cae52 100644 --- a/backend/env_example +++ b/backend/env_example @@ -1,6 +1,23 @@ # Example environment file for backend -DATABASE_URL=postgresql://user:password@localhost:5432/inpactdb -AI_API_KEY=your-ai-api-key-here +# Supabase Configuration (Required) -ALLOWED_ORIGINS=http://localhost:3000 +# Get these from: https://app.supabase.com/project/_/settings/api + +SUPABASE_URL=https://your-project.supabase.co +SUPABASE_KEY=your-supabase-anon-key-here + +# Database Configuration (Optional - Supabase PostgreSQL direct connection) + +# Get this from: Settings → Database → Connection string → URI + +DATABASE_URL=postgresql://postgres.your-project-ref:[YOUR-PASSWORD]@aws-0-region.pooler.supabase.com:6543/postgres + +# AI Configuration (Optional) + +GROQ_API_KEY=your-groq-api-key +AI_API_KEY=your-openai-api-key-optional + +# CORS Origins (comma-separated) + +ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3001 diff --git a/backend/requirements.txt b/backend/requirements.txt index da081ce..6b66b15 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -18,6 +18,7 @@ pytokens==0.2.0 ruff==0.14.3 sniffio==1.3.1 starlette==0.49.1 +supabase>=2.0.0 typing-inspection==0.4.2 typing_extensions==4.15.0 uvicorn==0.38.0 diff --git a/frontend/.env.example b/frontend/.env.example index e419c8d..35d1b06 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -2,4 +2,5 @@ NEXT_PUBLIC_API_URL=http://localhost:8000 NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co NEXT_PUBLIC_SUPABASE_ANON_KEY=your-key-here NEXT_PUBLIC_APP_NAME=InPact AI -NEXT_PUBLIC_APP_URL=http://localhost:3000 \ No newline at end of file +NEXT_PUBLIC_APP_URL=http://localhost:3000 + diff --git a/frontend/lib/supabaseClient.ts b/frontend/lib/supabaseClient.ts new file mode 100644 index 0000000..5951780 --- /dev/null +++ b/frontend/lib/supabaseClient.ts @@ -0,0 +1,12 @@ +import { createClient } from "@supabase/supabase-js"; + +const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; +const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; + +if (!supabaseUrl || !supabaseAnonKey) { + throw new Error( + "Missing Supabase environment variables. Please check your .env file." + ); +} + +export const supabase = createClient(supabaseUrl, supabaseAnonKey); diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 0352f50..e83516c 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -11,7 +15,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", + "jsx": "react-jsx", "incremental": true, "plugins": [ { @@ -19,7 +23,9 @@ } ], "paths": { - "@/*": ["./*"] + "@/*": [ + "./*" + ] } }, "include": [ @@ -30,5 +36,7 @@ ".next/dev/types/**/*.ts", "**/*.mts" ], - "exclude": ["node_modules"] + "exclude": [ + "node_modules" + ] } diff --git a/frontend/types/supabase.ts b/frontend/types/supabase.ts new file mode 100644 index 0000000..d08b75d --- /dev/null +++ b/frontend/types/supabase.ts @@ -0,0 +1,42 @@ +export type Json = + | string + | number + | boolean + | null + | { [key: string]: Json | undefined } + | Json[]; + +export interface Database { + public: { + Tables: { + // Add your table types here as you create them + // Example: + // users: { + // Row: { + // id: string + // email: string + // created_at: string + // } + // Insert: { + // id?: string + // email: string + // created_at?: string + // } + // Update: { + // id?: string + // email?: string + // created_at?: string + // } + // } + }; + Views: { + [_ in never]: never; + }; + Functions: { + [_ in never]: never; + }; + Enums: { + [_ in never]: never; + }; + }; +}