Skip to content
Closed
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ node_modules
.pnp.js
myenv/
venv/

*.md

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop ignoring all Markdown files

Adding *.md at the repository root causes every new Markdown document (design docs, runbooks, ADRs, etc.) to be silently ignored by Git, which makes documentation changes easy to lose and hard to review. This is a broad ignore rule unrelated to UI cleanup and is likely to create maintainability issues for future contributions.

Useful? React with 👍 / 👎.


# testing
coverage
Expand Down
68 changes: 6 additions & 62 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
Protly Backend — FastAPI server for ESMFold protein structure prediction.

Endpoints:
GET /api/health → health check
POST /api/predict → accepts { sequence: str }, returns PDB + pLDDT data
GET /api/health → health check
POST /api/predict → accepts { sequence: str }, returns PDB + pLDDT data
GET /api/uniprot/search → proxy UniProt search
GET /api/uniprot/entry/{id} → fetch full UniProt entry
POST /api/analyze → Biopython lab-readiness metrics
"""

import re
Expand All @@ -22,7 +25,6 @@
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
from jose import jwt, JWTError
from dotenv import load_dotenv

load_dotenv()
Expand All @@ -46,15 +48,6 @@
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)

# ---------------------------------------------------------------------------
# Supabase Auth Config
# ---------------------------------------------------------------------------
SUPABASE_URL = os.getenv("SUPABASE_URL", "")
SUPABASE_JWT_SECRET = os.getenv("SUPABASE_JWT_SECRET", "")
SUPABASE_ANON_KEY = os.getenv("SUPABASE_ANON_KEY", "")

if not SUPABASE_JWT_SECRET:
logger.warning("SUPABASE_JWT_SECRET is not set — JWT auth verification will be skipped!")

# ---------------------------------------------------------------------------
# CORS — allow the Vite dev server and common local origins
Expand All @@ -68,7 +61,7 @@
allow_origins=ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["GET", "POST", "OPTIONS"],
allow_headers=["Authorization", "Content-Type", "X-Requested-With"],
allow_headers=["Content-Type", "X-Requested-With"],
)


Expand All @@ -88,55 +81,6 @@ async def add_security_headers(request: Request, call_next):
return response


# ---------------------------------------------------------------------------
# JWT Authentication middleware
# ---------------------------------------------------------------------------
# Endpoints that do NOT require authentication
PUBLIC_PATHS = {"/api/health", "/docs", "/openapi.json", "/redoc"}


@app.middleware("http")
async def authenticate_requests(request: Request, call_next):
"""Validate Supabase JWT on protected endpoints."""
path = request.url.path

# Skip auth for public endpoints and CORS preflight
if path in PUBLIC_PATHS or request.method == "OPTIONS":
return await call_next(request)

# If no JWT secret configured, skip validation (dev mode)
if not SUPABASE_JWT_SECRET:
return await call_next(request)

# Extract Bearer token
auth_header = request.headers.get("Authorization", "")
if not auth_header.startswith("Bearer "):
return JSONResponse(
status_code=401,
content={"detail": "Missing or invalid Authorization header. Please sign in."},
)

token = auth_header.split(" ", 1)[1]

try:
payload = jwt.decode(
token,
SUPABASE_JWT_SECRET,
algorithms=["HS256"],
audience="authenticated",
)
# Attach user info to request state for downstream handlers
request.state.user_id = payload.get("sub")
request.state.user_email = payload.get("email", "")
except JWTError as exc:
logger.warning("JWT verification failed: %s", exc)
return JSONResponse(
status_code=401,
content={"detail": "Invalid or expired token. Please sign in again."},
)

return await call_next(request)


# ---------------------------------------------------------------------------
# Request logging middleware
Expand Down
28 changes: 9 additions & 19 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading