Supabase initialization and connection

This commit is contained in:
Mike Kell 2025-07-22 18:56:40 -04:00
parent 74be830758
commit add3904a73
8 changed files with 131 additions and 24 deletions

0
backend/auth/__init__.py Normal file
View File

40
backend/auth/jwt.py Normal file
View File

@ -0,0 +1,40 @@
# backend/auth/jwt.py
import requests
from jose import jwt
from jose.exceptions import JWTError
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from typing import Dict
SUPABASE_PROJECT_ID = "lcoretjgpauozmuoedus" # <-- Replace with your project ref
SUPABASE_JWKS_URL = f"https://{SUPABASE_PROJECT_ID}.supabase.co/auth/v1/keys"
# Fetch JWKs from Supabase
jwks = requests.get(SUPABASE_JWKS_URL).json()
# Define FastAPI's bearer auth scheme
auth_scheme = HTTPBearer()
# Decode + verify JWT token
def verify_jwt_token(token: str) -> Dict:
try:
header = jwt.get_unverified_header(token)
kid = header["kid"]
key = next((k for k in jwks["keys"] if k["kid"] == kid), None)
if key is None:
raise HTTPException(status_code=403, detail="Invalid Supabase JWT: No matching key")
payload = jwt.decode(token, key, algorithms=["RS256"], options={"verify_aud": False})
return payload
except JWTError as e:
raise HTTPException(status_code=403, detail=f"Invalid Supabase JWT: {str(e)}")
# Dependency for protected endpoints
def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(auth_scheme)
) -> Dict:
token = credentials.credentials
return verify_jwt_token(token)

0
backend/db/__init__.py Normal file
View File

30
backend/db/session.py Normal file
View File

@ -0,0 +1,30 @@
# backend/db/session.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import NullPool
from dotenv import load_dotenv
import os
# Load environment variables
load_dotenv()
# Read .env settings
USER = os.getenv("user")
PASSWORD = os.getenv("password")
HOST = os.getenv("host")
PORT = os.getenv("port")
DBNAME = os.getenv("dbname")
# Supabase Transaction Pooler (IPv4-safe) URI
DATABASE_URL = f"postgresql+psycopg2://{USER}:{PASSWORD}@{HOST}:{PORT}/{DBNAME}?sslmode=require"
# SQLAlchemy engine with NullPool for Supabase
engine = create_engine(DATABASE_URL, poolclass=NullPool)
# Session factory (used for queries)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# Base class for ORM models
Base = declarative_base()

View File

@ -1,28 +1,33 @@
from sqlalchemy import create_engine
from dotenv import load_dotenv
import os
# backend/main.py
# Load .env vars
load_dotenv()
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from sqlalchemy import text
from db.session import engine
from fastapi import Depends
from auth.jwt import get_current_user
# Get env vars
USER = os.getenv("user")
PASSWORD = os.getenv("password")
HOST = os.getenv("host")
PORT = os.getenv("port")
DBNAME = os.getenv("dbname")
app = FastAPI()
# Full SQLAlchemy URI for Session Pooler
DATABASE_URL = (
f"postgresql+psycopg2://{USER}:{PASSWORD}@{HOST}:{PORT}/{DBNAME}?sslmode=require"
)
@app.get("/healthz")
def health_check():
return {"status": "ok"}
# Use NullPool to defer to Supabase's pooler
from sqlalchemy.pool import NullPool
engine = create_engine(DATABASE_URL, poolclass=NullPool)
try:
with engine.connect() as conn:
print("✅ Supabase Session Pooler connection successful.")
except Exception as e:
print(f"❌ Connection failed: {e}")
@app.get("/supabase-check")
def supabase_check():
try:
with engine.connect() as conn:
result = conn.execute(text("SELECT current_database(), current_user;"))
db, user = result.fetchone()
return {"status": "connected", "db": db, "user": user}
except Exception as e:
return JSONResponse(status_code=500, content={"status": "error", "error": str(e)})
@app.get("/me")
def me(user: dict = Depends(get_current_user)):
return {
"id": user.get("sub"),
"email": user.get("email"),
"role": user.get("role"),
"tenant_id": user.get("tenant_id", "unknown")
}

View File

@ -3,3 +3,5 @@ uvicorn[standard]==0.27.1
sqlalchemy==2.0.30
psycopg2-binary==2.9.9
python-dotenv==1.0.1
python-jose
requests

28
backend/test-db.py Normal file
View File

@ -0,0 +1,28 @@
from sqlalchemy import create_engine
from dotenv import load_dotenv
import os
# Load .env vars
load_dotenv()
# Get env vars
USER = os.getenv("user")
PASSWORD = os.getenv("password")
HOST = os.getenv("host")
PORT = os.getenv("port")
DBNAME = os.getenv("dbname")
# Full SQLAlchemy URI for Session Pooler
DATABASE_URL = (
f"postgresql+psycopg2://{USER}:{PASSWORD}@{HOST}:{PORT}/{DBNAME}?sslmode=require"
)
# Use NullPool to defer to Supabase's pooler
from sqlalchemy.pool import NullPool
engine = create_engine(DATABASE_URL, poolclass=NullPool)
try:
with engine.connect() as conn:
print("✅ Supabase Session Pooler connection successful.")
except Exception as e:
print(f"❌ Connection failed: {e}")

View File

@ -11,6 +11,8 @@ services:
- internal_only
expose:
- "8000"
ports:
- "8000:8000"
networks: