Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ PINECONE_INDEX_NAME=codeintel
# Get from: https://app.supabase.com/project/_/settings/api
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=eyJ...
SUPABASE_JWT_SECRET=your-jwt-secret # From Project Settings → API → JWT Secret
# From Project Settings -> API -> JWT Secret
SUPABASE_JWT_SECRET=your-jwt-secret
# From Project Settings -> API -> service_role key
SUPABASE_SERVICE_ROLE_KEY=eyJ...

# Backend API
API_KEY=change-this-secret-key-for-production
Expand All @@ -46,3 +49,7 @@ ENVIRONMENT=development # development, staging, production
# Free tier: 10K requests/month
COHERE_API_KEY=
SEARCH_V2_ENABLED=true

# Voyage AI - code-specific embeddings (Optional - improves code search quality)
# Get from: https://dash.voyageai.com/
VOYAGE_API_KEY=
51 changes: 51 additions & 0 deletions backend/config/startup_checks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
Startup environment validation.
Fails fast with clear error messages if required config is missing.
"""
import os
import sys
from typing import List, Tuple

from services.observability import logger


# (env_var_name, description)
REQUIRED_VARS: List[Tuple[str, str]] = [
("SUPABASE_URL", "Supabase project URL"),
("SUPABASE_ANON_KEY", "Supabase anon/public key"),
("SUPABASE_JWT_SECRET", "Supabase JWT secret for token verification"),
("OPENAI_API_KEY", "OpenAI API key for embeddings"),
("PINECONE_API_KEY", "Pinecone API key for vector storage"),
]

OPTIONAL_VARS: List[Tuple[str, str, str]] = [
("SUPABASE_SERVICE_ROLE_KEY", "Supabase service role key", "Using anon key as fallback"),
("COHERE_API_KEY", "Cohere API key for reranking", "Search reranking disabled"),
("VOYAGE_API_KEY", "Voyage AI key for code embeddings", "Using OpenAI embeddings"),
("SENTRY_DSN", "Sentry DSN for error tracking", "Error tracking disabled"),
("REDIS_HOST", "Redis host for caching", "Using default localhost"),
]


def validate_environment() -> None:
"""Check required env vars exist. Log warnings for optional ones."""
missing: List[str] = []

for var_name, description in REQUIRED_VARS:
value = os.getenv(var_name)
if not value:
missing.append(f" {var_name} -- {description}")

if missing:
msg = "Missing required environment variables:\n" + "\n".join(missing)
msg += "\n\nSee .env.example for configuration reference."
logger.error(msg)
print(f"\n[FATAL] {msg}\n", file=sys.stderr)
sys.exit(1)

# warn about optional vars
for var_name, description, fallback_msg in OPTIONAL_VARS:
if not os.getenv(var_name):
logger.warning(f"{var_name} not set ({description}). {fallback_msg}")

logger.info("Environment validation passed")
3 changes: 2 additions & 1 deletion backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

# Import API config (single source of truth for versioning)
from config.api import API_PREFIX, API_VERSION
from config.startup_checks import validate_environment

# Import routers
from routes.auth import router as auth_router
Expand All @@ -36,7 +37,7 @@
# Lifespan context manager for startup/shutdown
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup
validate_environment()
await load_demo_repos()
yield
# Shutdown (cleanup if needed)
Expand Down
6 changes: 3 additions & 3 deletions backend/services/supabase_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ class SupabaseService:

def __init__(self):
supabase_url = os.getenv("SUPABASE_URL")
# Use service role key to bypass RLS for backend operations
supabase_key = os.getenv("SUPABASE_SERVICE_ROLE_KEY") or os.getenv("SUPABASE_KEY")
# prefer service role key for backend (bypasses RLS), fall back to anon key
supabase_key = os.getenv("SUPABASE_SERVICE_ROLE_KEY") or os.getenv("SUPABASE_ANON_KEY")

if not supabase_url or not supabase_key:
raise ValueError("SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY (or SUPABASE_KEY) must be set")
raise ValueError("SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY (or SUPABASE_ANON_KEY) must be set")

# Create client with options to avoid auth cleanup issues
options = ClientOptions(
Expand Down
10 changes: 7 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,15 @@ services:
- PINECONE_API_KEY=${PINECONE_API_KEY}
- PINECONE_INDEX_NAME=${PINECONE_INDEX_NAME}
- SUPABASE_URL=${SUPABASE_URL}
- SUPABASE_KEY=${SUPABASE_KEY}
- SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}
- SUPABASE_JWT_SECRET=${SUPABASE_JWT_SECRET}
- SUPABASE_SERVICE_ROLE_KEY=${SUPABASE_SERVICE_ROLE_KEY}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- API_KEY=${API_KEY}
- BACKEND_API_URL=http://backend:8000
- DISCORD_FEEDBACK_WEBHOOK=${DISCORD_FEEDBACK_WEBHOOK}
- COHERE_API_KEY=${COHERE_API_KEY}
- VOYAGE_API_KEY=${VOYAGE_API_KEY}
- SENTRY_DSN=${SENTRY_DSN}
- FORWARDED_ALLOW_IPS=*
volumes:
- ./backend/repos:/app/repos
Expand All @@ -62,14 +66,14 @@ services:
args:
- VITE_API_URL=http://localhost:8000
- VITE_SUPABASE_URL=${SUPABASE_URL}
- VITE_SUPABASE_ANON_KEY=${SUPABASE_KEY}
- VITE_SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}
container_name: codeintel-frontend
ports:
- "3000:80"
environment:
- VITE_API_URL=http://localhost:8000
- VITE_SUPABASE_URL=${SUPABASE_URL}
- VITE_SUPABASE_ANON_KEY=${SUPABASE_KEY}
- VITE_SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}
depends_on:
- backend
healthcheck:
Expand Down
Loading