Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
927d1a2
chore: add Clerk authentication configuration to environment files an…
SumitPatel-HQ Mar 30, 2026
ee5e550
chore: routes management
SumitPatel-HQ Mar 30, 2026
6c09a22
feat(backend): add Clerk JWT auth middleware with optional/required d…
SumitPatel-HQ Mar 30, 2026
928431a
feat(backend): integrate optional auth into all database routes
SumitPatel-HQ Mar 30, 2026
3724737
feat(backend): integrate optional auth into query routes
SumitPatel-HQ Mar 30, 2026
a320c60
feat(frontend): create TypeScript API type definitions
SumitPatel-HQ Mar 30, 2026
850bc16
feat(frontend): create server-side API client
SumitPatel-HQ Mar 30, 2026
12f2562
feat(frontend): create client-side API client
SumitPatel-HQ Mar 30, 2026
1418fa1
feat(frontend): create useApi React hook
SumitPatel-HQ Mar 30, 2026
1ce013b
feat(frontend): configure Next.js API proxy
SumitPatel-HQ Mar 30, 2026
4b3383a
feat(frontend): rename proxy to middleware and exclude API routes
SumitPatel-HQ Mar 30, 2026
e678104
feat(frontend): add database upload modal component
SumitPatel-HQ Mar 30, 2026
9406d04
refactor(frontend): convert databases page to Server Component
SumitPatel-HQ Mar 30, 2026
6d8a72f
feat(frontend): add database card component with view/delete actions
SumitPatel-HQ Mar 30, 2026
16abf28
feat(frontend): integrate databases view with upload and delete
SumitPatel-HQ Mar 30, 2026
eaca5a6
refactor(frontend): convert dashboard home to Server Component
SumitPatel-HQ Mar 30, 2026
28160aa
feat(frontend): add working quick actions to dashboard home
SumitPatel-HQ Mar 30, 2026
3ba8e41
feat(frontend): add query interface and results components
SumitPatel-HQ Mar 30, 2026
8d2c184
feat(frontend): integrate query interface into chat page
SumitPatel-HQ Mar 30, 2026
dddff9f
feat(frontend): implement database overview page with real data
SumitPatel-HQ Mar 30, 2026
7cd5af2
feat(frontend): implement database-specific chat page
SumitPatel-HQ Mar 30, 2026
c9599b8
feat(frontend): implement schema viewer page with column details
SumitPatel-HQ Mar 30, 2026
0cc3c4d
fix(frontend): resolve integration type issues and schema rendering
SumitPatel-HQ Mar 30, 2026
c330466
fix(frontend): polish query send controls and resolve chat-input lint…
SumitPatel-HQ Mar 30, 2026
0ba22ff
feat(frontend): complete remaining database detail routes
SumitPatel-HQ Mar 30, 2026
e3ddb82
chore(frontend): switch Clerk entrypoint back to proxy.ts
SumitPatel-HQ Mar 30, 2026
86387c5
feat(frontend): polish database detail nav and align tables API type
SumitPatel-HQ Mar 30, 2026
8cd80f7
feat: implement AI chat input component with supporting UI primitives…
SumitPatel-HQ Mar 30, 2026
35d6b48
chore: Lint errors & build error Fixes
SumitPatel-HQ Mar 30, 2026
f86b8e9
Fix workflow indentation and use Gemini model
SumitPatel-HQ Mar 30, 2026
5a79a8f
Merge branch 'main' into it1
SumitPatel-HQ Mar 30, 2026
ced28d6
Merge branch 'main' into it1
SumitPatel-HQ Mar 30, 2026
4cbb27c
Fix model name quoting in workflow
SumitPatel-HQ Mar 30, 2026
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 .github/workflows/opencode.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ jobs:
env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
with:
model: opencode/MiniMax M2.5 Free
model: "opencode/MiniMax M2.5 Free"
4 changes: 4 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ LLM_MAX_RETRIES=3

# gRPC Configuration
GRPC_VERBOSITY=ERROR

# Clerk authentication
CLERK_JWKS_URL=https://quiet-dassie-42.clerk.accounts.dev/.well-known/jwks.json
CLERK_ISSUER=https://quiet-dassie-42.clerk.accounts.dev
4 changes: 4 additions & 0 deletions backend/api/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ class Settings:

# CORS Configuration
CORS_ORIGINS: list = ["http://localhost:3000"]

# Clerk authentication
CLERK_JWKS_URL: str = os.getenv("CLERK_JWKS_URL", "")
CLERK_ISSUER: str = os.getenv("CLERK_ISSUER", "")

# Paths
BASE_DIR: str = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
Expand Down
5 changes: 5 additions & 0 deletions backend/api/middleware/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""Authentication and authorization middleware"""

from .auth import get_current_user, get_optional_user

__all__ = ["get_current_user", "get_optional_user"]
128 changes: 128 additions & 0 deletions backend/api/middleware/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"""Clerk JWT authentication middleware"""

import logging
from typing import Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt
from jwt import PyJWKClient

from api.config.settings import settings

logger = logging.getLogger(__name__)

# HTTP Bearer token scheme (optional)
bearer_scheme = HTTPBearer(auto_error=False)


def verify_clerk_token(token: str) -> dict:
"""
Verify Clerk JWT token using JWKS endpoint.

Args:
token: JWT token string

Returns:
Decoded token payload containing user_id (sub), email, etc.

Raises:
HTTPException: If token is invalid or expired
"""
try:
# Initialize JWKS client with Clerk's public keys
jwks_client = PyJWKClient(settings.CLERK_JWKS_URL)

# Get the signing key from the token header
signing_key = jwks_client.get_signing_key_from_jwt(token)

# Decode and verify the token
payload = jwt.decode(
token,
signing_key.key,
algorithms=["RS256"],
issuer=settings.CLERK_ISSUER,
options={
"verify_signature": True,
"verify_exp": True,
"verify_iat": True,
"verify_iss": True,
},
)

return payload

except jwt.ExpiredSignatureError:
logger.warning("Token expired")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Token has expired"
)
except jwt.InvalidTokenError as e:
logger.warning(f"Invalid token: {str(e)}")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication token",
)
except Exception as e:
logger.error(f"Token verification failed: {str(e)}")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication failed"
)


async def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme),
) -> dict:
"""
FastAPI dependency for REQUIRED authentication.
Raises 401 if no valid token is provided.

Usage:
@router.get("/protected")
async def protected_route(user: dict = Depends(get_current_user)):
user_id = user["sub"]
...

Returns:
Decoded JWT payload with user information
"""
if not credentials:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Missing authentication token",
)

payload = verify_clerk_token(credentials.credentials)
logger.info(f"Authenticated user: {payload.get('sub')}")
return payload


async def get_optional_user(
credentials: Optional[HTTPAuthorizationCredentials] = Depends(bearer_scheme),
) -> Optional[dict]:
"""
FastAPI dependency for OPTIONAL authentication.
Returns None if no token provided or token is invalid.

Usage:
@router.get("/public-or-private")
async def flexible_route(user: Optional[dict] = Depends(get_optional_user)):
if user:
user_id = user["sub"]
# Return personalized data
else:
# Return public data

Returns:
Decoded JWT payload if valid token provided, None otherwise
"""
if not credentials:
return None

try:
payload = verify_clerk_token(credentials.credentials)
logger.info(f"Optional auth - authenticated user: {payload.get('sub')}")
return payload
except HTTPException:
# Token invalid - return None instead of raising error
logger.info("Optional auth - invalid/expired token, returning None")
return None
Loading
Loading