Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ebe06e4
[bugfix]: add missing closing div tag
smokeyScraper Jun 15, 2025
3f103ab
[bugfix]: temporary workaround for auth fix
smokeyScraper Jun 15, 2025
2ef6f40
[refactor]: add supabase credentials to config
smokeyScraper Jun 17, 2025
13c459a
[feat]: make sync supabase functions to async
smokeyScraper Jun 17, 2025
7236056
[feature]: implement github authentication using supabase
smokeyScraper Jun 17, 2025
4884600
Merge pull request #79 from smokeyScraper/frontend-fix
chandansgowda Jun 18, 2025
8d877d2
Merge pull request #80 from smokeyScraper/auth_discord
chandansgowda Jun 18, 2025
3c46867
[feat]: update supabase models and SQL scripts
smokeyScraper Jun 21, 2025
6f69639
[refactor]: migrate supabase client interactions to async
smokeyScraper Jun 21, 2025
6eb3f6e
[feature]: implement secure token based verification with expiry
smokeyScraper Jun 21, 2025
f110530
[feat]: add callback endpoint and post-login checks
smokeyScraper Jun 21, 2025
8317030
[feat]: align discord bot to support token based verification
smokeyScraper Jun 21, 2025
b4d2c43
[chore]: add backend url
smokeyScraper Jun 21, 2025
7222885
[feat]: align whole system in a FastAPI server
smokeyScraper Jun 21, 2025
09d7027
[chore]: add BACKEND_URL to .env.example
smokeyScraper Jun 21, 2025
4a05d78
[fixes]: coderabbit suggested fixes
smokeyScraper Jun 22, 2025
e1111ef
Merge pull request #81 from smokeyScraper/supabase_config
chandansgowda Jun 22, 2025
60262c6
[refactor]: remove vectorizer dependency
smokeyScraper Jun 23, 2025
f10c790
[feat]: weaviate db models alignment to support user indexing and search
smokeyScraper Jun 23, 2025
6070611
[chore]: add weaviate db instance creation and population scripts
smokeyScraper Jun 23, 2025
c2aded9
Merge pull request #83 from smokeyScraper/weaviate_config
chandansgowda Jun 23, 2025
5cacdbd
[feat]: update weaviate interactions to async
smokeyScraper Jun 26, 2025
0f61011
[feat]: Enhance user profiling with pull request data
smokeyScraper Jun 26, 2025
448cd6d
[chore]: update weaviate schema creationg and testing population scripts
smokeyScraper Jun 26, 2025
0dab6c1
[feat]: add health check endpoint and update weaviate interactions to…
smokeyScraper Jun 26, 2025
5341aae
[feat]: add functions to interact with weaviate DB
smokeyScraper Jun 26, 2025
455d2b9
[feat]: add user_profiling logic using GitHub REST API
smokeyScraper Jun 26, 2025
13993f5
[chore]: dependency updates
smokeyScraper Jun 26, 2025
2616103
Merge pull request #84 from smokeyScraper/user_profiling
chandansgowda Jun 27, 2025
03a95e3
Merge branch 'feature/rabbitmq-integration' into main
ShivamMenda Jun 28, 2025
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
8 changes: 6 additions & 2 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@
# PORT=8000
# CORS_ORIGINS=http://localhost:3000

# SUPABASE_URL=
# SUPABASE_SERVICE_ROLE_KEY=
SUPABASE_URL=
SUPABASE_SERVICE_ROLE_KEY=

DISCORD_BOT_TOKEN=
# ENABLE_DISCORD_BOT=true

GITHUB_TOKEN=

# EMBEDDING_MODEL=BAAI/bge-small-en-v1.5
# EMBEDDING_MAX_BATCH_SIZE=32
# EMBEDDING_DEVICE=cpu

BACKEND_URL=

GEMINI_API_KEY=
TAVILY_API_KEY=

Expand Down
284 changes: 284 additions & 0 deletions backend/app/api/v1/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
from fastapi import APIRouter, Request, HTTPException, Query
from fastapi.responses import HTMLResponse
from app.db.supabase.supabase_client import get_supabase_client
from app.db.supabase.users_service import find_user_by_session_and_verify, get_verification_session_info
from app.db.weaviate.user_profiling import profile_user_from_github
from typing import Optional
import logging
import asyncio

logger = logging.getLogger(__name__)
router = APIRouter()

@router.get("/callback", response_class=HTMLResponse)
async def auth_callback(request: Request, code: Optional[str] = Query(None), session: Optional[str] = Query(None)):
"""
Handles the OAuth callback from Supabase after a user authorizes on GitHub.
"""
logger.info(
f"OAuth callback received with code: {'[PRESENT]' if code else '[MISSING]'}, session: {'[PRESENT]' if session else '[MISSING]'}")

if not code:
logger.error("Missing authorization code in callback")
return _error_response("Missing authorization code. Please try the verification process again.")

if not session:
logger.error("Missing session ID in callback")
return _error_response("Missing session ID. Please try the !verify_github command again.")

# Check if session is valid and not expired
session_info = await get_verification_session_info(session)
if not session_info:
logger.error(f"Invalid or expired session ID: {session}")
return _error_response("Your verification session has expired. Please run the !verify_github command again.")

supabase = get_supabase_client()
try:
# Exchange code for session
logger.info("Exchanging authorization code for session")
session_response = await supabase.auth.exchange_code_for_session({
"auth_code": code,
})

if not session_response or not session_response.user:
logger.error("Failed to exchange code for session")
return _error_response("Authentication failed. Could not retrieve user session.")

user = session_response.user
logger.info(f"Successfully got user session for user: {user.id}")

# Extract GitHub info from user metadata
github_id = user.user_metadata.get("provider_id")
github_username = user.user_metadata.get("user_name")
email = user.email

if not github_id or not github_username:
logger.error(f"Missing GitHub details - ID: {github_id}, Username: {github_username}")
return _error_response("Could not retrieve GitHub details from user session.")

# Verify user using session ID
logger.info(f"Verifying user with session ID: {session}")
verified_user = await find_user_by_session_and_verify(
session_id=session,
github_id=str(github_id),
github_username=github_username,
email=email
)

if not verified_user:
logger.error("User verification failed - no pending verification found")
return _error_response("No pending verification found or verification has expired. Please try the !verify_github command again.")

logger.info(f"Successfully verified user: {verified_user.id}!")

logger.info(f"Indexing user: {verified_user.id} into Weaviate...")
try:
asyncio.create_task(profile_user_from_github(str(verified_user.id), github_username))
logger.info(f"User profiling started in background for: {verified_user.id}")
except Exception as e:
logger.error(f"Error starting user profiling: {verified_user.id}: {str(e)}")

return _success_response(github_username)

except Exception as e:
logger.error(f"Unexpected error in OAuth callback: {str(e)}", exc_info=True)

# Handle specific error cases
if "already linked" in str(e):
return _error_response(f"Error: {str(e)}")

return _error_response("An unexpected error occurred during verification. Please try again.")

@router.get("/session/{session_id}")
async def get_session_status(session_id: str):
"""Get the status of a verification session"""
session_info = await get_verification_session_info(session_id)
if not session_info:
raise HTTPException(status_code=404, detail="Session not found or expired")

return {
"valid": True,
"discord_id": session_info["discord_id"],
"expiry_time": session_info["expiry_time"],
"time_remaining": session_info["time_remaining"]
}

def _success_response(github_username: str) -> str:
"""Generate success HTML response"""
return f"""
<!DOCTYPE html>
<html>
<head>
<title>Verification Successful!</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
margin: 0;
padding: 20px;
box-sizing: border-box;
}}
.container {{
text-align: center;
padding: 40px;
background: white;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
max-width: 500px;
width: 100%;
}}
h1 {{
color: #28a745;
margin-bottom: 20px;
font-size: 2rem;
}}
.github-info {{
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
border-left: 4px solid #28a745;
}}
code {{
background: #e9ecef;
padding: 4px 8px;
border-radius: 4px;
font-family: 'Monaco', 'Consolas', monospace;
color: #495057;
font-weight: bold;
}}
.close-btn {{
margin-top: 20px;
padding: 12px 24px;
background: #007bff;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
}}
.close-btn:hover {{
background: #0056b3;
}}
.success-icon {{
font-size: 4rem;
margin-bottom: 20px;
}}
.auto-close {{
margin-top: 15px;
color: #6c757d;
font-size: 0.9rem;
}}
</style>
</head>
<body>
<div class="container">
<div class="success-icon">βœ…</div>
<h1>Verification Successful!</h1>
<div class="github-info">
<p><strong>Your Discord account has been successfully linked!</strong></p>
<p>GitHub User: <code>{github_username}</code></p>
</div>
<p>You can now access all features that require GitHub authentication.</p>
<button class="close-btn" onclick="window.close()">Close Window</button>
<p class="auto-close">This window will close automatically in 5 seconds.</p>
</div>
<script>
// Auto-close after 5 seconds
setTimeout(() => {{
window.close();
}}, 5000);
</script>
</body>
</html>
"""

def _error_response(error_message: str) -> str:
"""Generate error HTML response"""
return f"""
<!DOCTYPE html>
<html>
<head>
<title>Verification Failed</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
margin: 0;
padding: 20px;
box-sizing: border-box;
}}
.container {{
text-align: center;
padding: 40px;
background: white;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
max-width: 500px;
width: 100%;
}}
h1 {{
color: #dc3545;
margin-bottom: 20px;
font-size: 2rem;
}}
.error-message {{
background: #f8d7da;
color: #721c24;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
border-left: 4px solid #dc3545;
}}
.close-btn {{
margin-top: 20px;
padding: 12px 24px;
background: #dc3545;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
}}
.close-btn:hover {{
background: #c82333;
}}
.error-icon {{
font-size: 4rem;
margin-bottom: 20px;
}}
.help-text {{
color: #6c757d;
font-size: 0.9rem;
margin-top: 15px;
}}
</style>
</head>
<body>
<div class="container">
<div class="error-icon">❌</div>
<h1>Verification Failed</h1>
<div class="error-message">
<p>{error_message}</p>
</div>
<p>Please return to Discord and try the <code>!verify_github</code> command again.</p>
<button class="close-btn" onclick="window.close()">Close Window</button>
<p class="help-text">If you continue to experience issues, please contact support.</p>
</div>
</body>
</html>
"""
18 changes: 14 additions & 4 deletions backend/app/core/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from pydantic_settings import BaseSettings
from dotenv import load_dotenv
from pydantic import field_validator

load_dotenv()

Expand All @@ -14,10 +15,9 @@ class Settings(BaseSettings):
github_token: str = ""
discord_bot_token: str = ""

# TODO: Add DB configuration
# Database
# Supabase
# Weaviate
# DB configuration
supabase_url: str
supabase_key: str

# LangSmith Tracing
langsmith_tracing: bool = False
Expand All @@ -32,6 +32,16 @@ class Settings(BaseSettings):
agent_timeout: int = 30
max_retries: int = 3

# Backend URL
backend_url: str = ""

@field_validator("supabase_url", "supabase_key", mode="before")
@classmethod
def _not_empty(cls, v, field):
if not v:
raise ValueError(f"{field.name} must be set")
return v

class Config:
env_file = ".env"
extra = "ignore" # to prevent errors from extra env variables
Expand Down
Loading