Spaces:
Running
Running
Dashm commited on
Commit ·
435cf23
1
Parent(s): 0c0cc35
Fix: read env vars at runtime not import time for HF Space secrets
Browse files- backend/config.py +12 -7
- backend/database.py +6 -3
- backend/routes/auth.py +4 -3
- backend/routes/products.py +2 -2
backend/config.py
CHANGED
|
@@ -8,13 +8,18 @@ from dotenv import load_dotenv
|
|
| 8 |
|
| 9 |
load_dotenv(override=False) # Don't override existing env vars (e.g. HF Space secrets)
|
| 10 |
|
| 11 |
-
# Supabase
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
# JWT Auth
|
| 20 |
JWT_SECRET = os.getenv("JWT_SECRET", "change-this-in-production")
|
|
|
|
| 8 |
|
| 9 |
load_dotenv(override=False) # Don't override existing env vars (e.g. HF Space secrets)
|
| 10 |
|
| 11 |
+
# Supabase — read lazily at runtime so HF Space secrets are available
|
| 12 |
+
def get_supabase_url(): return os.environ.get("SUPABASE_URL", "")
|
| 13 |
+
def get_supabase_key(): return os.environ.get("SUPABASE_KEY", "")
|
| 14 |
+
def get_supabase_service_key(): return os.environ.get("SUPABASE_SERVICE_KEY", "")
|
| 15 |
+
def get_database_url(): return os.environ.get("DATABASE_URL", "")
|
| 16 |
+
def get_jwt_secret(): return os.environ.get("JWT_SECRET", "change-this-in-production")
|
| 17 |
+
|
| 18 |
+
# Keep these as constants too for backwards compatibility
|
| 19 |
+
SUPABASE_URL = os.environ.get("SUPABASE_URL", "")
|
| 20 |
+
SUPABASE_KEY = os.environ.get("SUPABASE_KEY", "")
|
| 21 |
+
SUPABASE_SERVICE_KEY = os.environ.get("SUPABASE_SERVICE_KEY", "")
|
| 22 |
+
DATABASE_URL = os.environ.get("DATABASE_URL", "")
|
| 23 |
|
| 24 |
# JWT Auth
|
| 25 |
JWT_SECRET = os.getenv("JWT_SECRET", "change-this-in-production")
|
backend/database.py
CHANGED
|
@@ -4,11 +4,11 @@ Uses psycopg (v3) for direct PostgreSQL/pgvector queries (search),
|
|
| 4 |
and supabase-py for standard CRUD operations.
|
| 5 |
"""
|
| 6 |
|
|
|
|
| 7 |
import psycopg
|
| 8 |
from psycopg.rows import dict_row
|
| 9 |
import numpy as np
|
| 10 |
from supabase import create_client, Client
|
| 11 |
-
from config import SUPABASE_URL, SUPABASE_KEY, DATABASE_URL
|
| 12 |
|
| 13 |
# --- Supabase Client (for CRUD operations) ---
|
| 14 |
|
|
@@ -19,7 +19,10 @@ def get_supabase() -> Client:
|
|
| 19 |
"""Get or create the Supabase client for standard CRUD operations."""
|
| 20 |
global _supabase_client
|
| 21 |
if _supabase_client is None:
|
| 22 |
-
_supabase_client = create_client(
|
|
|
|
|
|
|
|
|
|
| 23 |
return _supabase_client
|
| 24 |
|
| 25 |
|
|
@@ -27,7 +30,7 @@ def get_supabase() -> Client:
|
|
| 27 |
|
| 28 |
def get_db_connection():
|
| 29 |
"""Create a new psycopg3 connection for pgvector queries."""
|
| 30 |
-
conn = psycopg.connect(DATABASE_URL, row_factory=dict_row)
|
| 31 |
return conn
|
| 32 |
|
| 33 |
|
|
|
|
| 4 |
and supabase-py for standard CRUD operations.
|
| 5 |
"""
|
| 6 |
|
| 7 |
+
import os
|
| 8 |
import psycopg
|
| 9 |
from psycopg.rows import dict_row
|
| 10 |
import numpy as np
|
| 11 |
from supabase import create_client, Client
|
|
|
|
| 12 |
|
| 13 |
# --- Supabase Client (for CRUD operations) ---
|
| 14 |
|
|
|
|
| 19 |
"""Get or create the Supabase client for standard CRUD operations."""
|
| 20 |
global _supabase_client
|
| 21 |
if _supabase_client is None:
|
| 22 |
+
_supabase_client = create_client(
|
| 23 |
+
os.environ["SUPABASE_URL"],
|
| 24 |
+
os.environ["SUPABASE_KEY"],
|
| 25 |
+
)
|
| 26 |
return _supabase_client
|
| 27 |
|
| 28 |
|
|
|
|
| 30 |
|
| 31 |
def get_db_connection():
|
| 32 |
"""Create a new psycopg3 connection for pgvector queries."""
|
| 33 |
+
conn = psycopg.connect(os.environ["DATABASE_URL"], row_factory=dict_row)
|
| 34 |
return conn
|
| 35 |
|
| 36 |
|
backend/routes/auth.py
CHANGED
|
@@ -10,8 +10,9 @@ from pydantic import BaseModel
|
|
| 10 |
from datetime import datetime, timedelta, timezone
|
| 11 |
import bcrypt
|
| 12 |
import jwt
|
|
|
|
| 13 |
from database import get_supabase
|
| 14 |
-
from config import
|
| 15 |
|
| 16 |
router = APIRouter(prefix="/auth", tags=["Authentication"])
|
| 17 |
security = HTTPBearer()
|
|
@@ -58,13 +59,13 @@ def create_token(user_id: str, email: str, role: str = "buyer") -> str:
|
|
| 58 |
"exp": datetime.now(timezone.utc) + timedelta(hours=JWT_EXPIRATION_HOURS),
|
| 59 |
"iat": datetime.now(timezone.utc),
|
| 60 |
}
|
| 61 |
-
return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)
|
| 62 |
|
| 63 |
|
| 64 |
def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)) -> dict:
|
| 65 |
"""Dependency that verifies JWT token and returns the payload."""
|
| 66 |
try:
|
| 67 |
-
payload = jwt.decode(credentials.credentials, JWT_SECRET, algorithms=[JWT_ALGORITHM])
|
| 68 |
return payload
|
| 69 |
except jwt.ExpiredSignatureError:
|
| 70 |
raise HTTPException(status_code=401, detail="Token expired")
|
|
|
|
| 10 |
from datetime import datetime, timedelta, timezone
|
| 11 |
import bcrypt
|
| 12 |
import jwt
|
| 13 |
+
import os
|
| 14 |
from database import get_supabase
|
| 15 |
+
from config import JWT_ALGORITHM, JWT_EXPIRATION_HOURS
|
| 16 |
|
| 17 |
router = APIRouter(prefix="/auth", tags=["Authentication"])
|
| 18 |
security = HTTPBearer()
|
|
|
|
| 59 |
"exp": datetime.now(timezone.utc) + timedelta(hours=JWT_EXPIRATION_HOURS),
|
| 60 |
"iat": datetime.now(timezone.utc),
|
| 61 |
}
|
| 62 |
+
return jwt.encode(payload, os.environ.get("JWT_SECRET", "change-this-in-production"), algorithm=JWT_ALGORITHM)
|
| 63 |
|
| 64 |
|
| 65 |
def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)) -> dict:
|
| 66 |
"""Dependency that verifies JWT token and returns the payload."""
|
| 67 |
try:
|
| 68 |
+
payload = jwt.decode(credentials.credentials, os.environ.get("JWT_SECRET", "change-this-in-production"), algorithms=[JWT_ALGORITHM])
|
| 69 |
return payload
|
| 70 |
except jwt.ExpiredSignatureError:
|
| 71 |
raise HTTPException(status_code=401, detail="Token expired")
|
backend/routes/products.py
CHANGED
|
@@ -9,10 +9,10 @@ from typing import Optional
|
|
| 9 |
import uuid
|
| 10 |
import base64
|
| 11 |
|
|
|
|
| 12 |
from database import get_supabase, store_product_embedding
|
| 13 |
from models.bert_service import bert_service
|
| 14 |
from routes.auth import get_current_user
|
| 15 |
-
from config import SUPABASE_URL
|
| 16 |
|
| 17 |
router = APIRouter(prefix="/products", tags=["Products"])
|
| 18 |
|
|
@@ -106,7 +106,7 @@ async def upload_image(
|
|
| 106 |
raise HTTPException(status_code=500, detail=f"Failed to upload image: {str(e)}")
|
| 107 |
|
| 108 |
# Get public URL
|
| 109 |
-
public_url = f"{SUPABASE_URL}/storage/v1/object/public/product-images/{filename}"
|
| 110 |
|
| 111 |
return {"url": public_url, "filename": filename}
|
| 112 |
|
|
|
|
| 9 |
import uuid
|
| 10 |
import base64
|
| 11 |
|
| 12 |
+
import os
|
| 13 |
from database import get_supabase, store_product_embedding
|
| 14 |
from models.bert_service import bert_service
|
| 15 |
from routes.auth import get_current_user
|
|
|
|
| 16 |
|
| 17 |
router = APIRouter(prefix="/products", tags=["Products"])
|
| 18 |
|
|
|
|
| 106 |
raise HTTPException(status_code=500, detail=f"Failed to upload image: {str(e)}")
|
| 107 |
|
| 108 |
# Get public URL
|
| 109 |
+
public_url = f"{os.environ.get('SUPABASE_URL', '')}/storage/v1/object/public/product-images/{filename}"
|
| 110 |
|
| 111 |
return {"url": public_url, "filename": filename}
|
| 112 |
|