You are reviewing a pull request. Report real problems in the changed code: bugs, security
issues, performance problems, and code that is hard to maintain in a way that will cause bugs.
Don't report style preferences, naming, formatting or missing comments.

The diff shows each file's changes in hunks. Every line has a marker and a line number:
  "+ 12 | ..." is an added line, number 12 in the new version of the file
  "  12 | ..." is an unchanged line, number 12 in the new version
  "-    | ..." is a removed line; it has no new line number, so never point at it

For each problem:
- file_path is the path exactly as it appears after "###"
- line (and end_line, for several lines) are new-file numbers of added or unchanged lines
  shown in the diff. Prefer pointing at added lines
- suggested_code, if you have a concrete fix, replaces lines line..end_line exactly: the
  complete new code for those lines, with no "+"/"-" markers and no line numbers
- confidence is how sure you are the problem is real
- group: when several problems are the same mistake in different places, or fixing one
  problem takes changes in more than one place, report each place separately (with its own
  suggested_code) and give them all the same short group label, like "unclosed-sessions".
  Otherwise null

Some requests start with context: unchanged code from the repository (whole functions, where
the changed code is used, and definitions it uses), numbered like the diff, to help you
understand the change. It isn't part of the change: use it, but only report problems the change
causes, and only point line and end_line at lines of the diff. When such a problem shows up in
the context (a caller the change breaks, a reference it makes wrong), point at the changed lines
that cause it, and set outside_diff to where it shows up. Otherwise outside_diff is null.

Only report problems you are confident about. If the changes look fine, return no suggestions.

===== chunk
### backend/src/main.py (modified)
@@ -1,6 +1,8 @@
    1 | import os
    2 | import json
-     | from fastapi import FastAPI, Request, HTTPException, Header, Depends
+   3 | from dotenv import load_dotenv
+   4 | from fastapi import FastAPI, Request, HTTPException, Header, Depends, Security
+   5 | from fastapi.security import APIKeyHeader
    6 | from fastapi.middleware.cors import CORSMiddleware
    7 | import hmac
    8 | import hashlib
@@ -12,10 +14,38 @@ import httpx
   14 | import asyncio
   15 | from database import get_db, create_pr, update_pr_status, init_db, create_suggestion
   16 | 
+  17 | # Load environment variables from .env file
+  18 | load_dotenv('/home/sgtwickool/repos/codemop/.env')
+  19 | 
   20 | # Configure logging
   21 | logging.basicConfig(level=logging.INFO)
   22 | logger = logging.getLogger(__name__)
   23 | 
+  24 | # API Key Security
+  25 | api_key_header = APIKeyHeader(name="Authorization", auto_error=False)
+  26 | 
+  27 | async def get_api_key(api_key_header: str = Security(api_key_header)):
+  28 |     """Extract and validate API key from Authorization header"""
+  29 |     if not api_key_header:
+  30 |         raise HTTPException(
+  31 |             status_code=401,
+  32 |             detail="Authorization header missing"
+  33 |         )
+  34 |     
+  35 |     # Handle "Bearer " prefix
+  36 |     if api_key_header.startswith("Bearer "):
+  37 |         api_key = api_key_header[7:].strip()
+  38 |     else:
+  39 |         api_key = api_key_header.strip()
+  40 |     
+  41 |     if api_key != API_KEY:
+  42 |         raise HTTPException(
+  43 |             status_code=401,
+  44 |             detail="Invalid API key"
+  45 |         )
+  46 |     
+  47 |     return api_key
+  48 | 
   49 | app = FastAPI()
   50 | 
   51 | # Initialize database on startup
@@ -39,6 +69,10 @@ GITHUB_WEBHOOK_SECRET = os.getenv("GITHUB_WEBHOOK_SECRET")
   69 | AI_API_KEY = os.getenv("AI_API_KEY", "")
   70 | AI_API_URL = os.getenv("AI_API_URL", "https://api.mistral.ai/v1/chat/completions")
   71 | AI_MODEL = os.getenv("AI_MODEL", "codestral-latest")
+  72 | 
+  73 | # API Authentication
+  74 | API_KEY = os.getenv("API_KEY", "")
+  75 | 
   76 | MAX_RETRIES = 3
   77 | RETRY_DELAY = 1.0
   78 | 
@@ -319,6 +353,79 @@ def extract_suggestions_from_text(text: str) -> list:
  353 |     
  354 |     return suggestions
  355 | 
+ 356 | @app.get("/pr/{pr_id}/suggestions")
+ 357 | async def get_suggestions(
+ 358 |     pr_id: int,
+ 359 |     api_key: str = Depends(get_api_key)
+ 360 | ):
+ 361 |     """
+ 362 |     Get all suggestions for a specific PR
+ 363 |     
+ 364 |     Args:
+ 365 |         pr_id: The database ID of the PR
+ 366 |         api_key: Valid API key for authentication
+ 367 |     
+ 368 |     Returns:
+ 369 |         List of suggestions with line numbers, descriptions, and fixes
+ 370 |     """
+ 371 |     db = next(get_db())
+ 372 |     try:
+ 373 |         # Import PR model here to avoid circular imports
+ 374 |         from database import PR, Suggestion
+ 375 |         
+ 376 |         # First check if PR exists
+ 377 |         pr = db.query(PR).filter(PR.id == pr_id).first()
+ 378 |         if not pr:
+ 379 |             raise HTTPException(
+ 380 |                 status_code=404,
+ 381 |                 detail=f"PR with ID {pr_id} not found"
+ 382 |             )
+ 383 |         
+ 384 |         # Get all suggestions for this PR
+ 385 |         suggestions = db.query(Suggestion).filter(Suggestion.pr_id == pr_id).all()
+ 386 |         
+ 387 |         if not suggestions:
+ 388 |             return {
+ 389 |                 "pr_id": pr_id,
+ 390 |                 "github_id": pr.github_id,
+ 391 |                 "repo": pr.repo_full_name,
+ 392 |                 "title": pr.title,
+ 393 |                 "suggestions": [],
+ 394 |                 "message": "No suggestions found for this PR"
+ 395 |             }
+ 396 |         
+ 397 |         # Format suggestions for API response
+ 398 |         formatted_suggestions = []
+ 399 |         for suggestion in suggestions:
+ 400 |             formatted_suggestions.append({
+ 401 |                 "id": suggestion.id,
+ 402 |                 "line_number": suggestion.line_number,
+ 403 |                 "file_path": suggestion.file_path,
+ 404 |                 "description": suggestion.description,
+ 405 |                 "fix": suggestion.fix,
+ 406 |                 "confidence": suggestion.confidence,
+ 407 |                 "created_at": suggestion.created_at.isoformat() if suggestion.created_at else None
+ 408 |             })
+ 409 |         
+ 410 |         return {
+ 411 |             "pr_id": pr_id,
+ 412 |             "github_id": pr.github_id,
+ 413 |             "repo": pr.repo_full_name,
+ 414 |             "title": pr.title,
+ 415 |             "status": pr.status,
+ 416 |             "suggestions_count": len(formatted_suggestions),
+ 417 |             "suggestions": formatted_suggestions
+ 418 |         }
+ 419 |         
+ 420 |     except Exception as e:
+ 421 |         logger.error(f"Error fetching suggestions for PR {pr_id}: {str(e)}")
+ 422 |         raise HTTPException(
+ 423 |             status_code=500,
+ 424 |             detail=f"Error retrieving suggestions: {str(e)}"
+ 425 |         )
+ 426 |     finally:
+ 427 |         db.close()
+ 428 | 
  429 | @app.get("/health")
  430 | async def health_check():
  431 |     return {"status": "healthy"}
