created dummy fastapi

This commit is contained in:
Bruce Lin 2025-07-12 17:53:01 -04:00
parent 6fc7704f6a
commit 5d258bf67f
7 changed files with 378 additions and 0 deletions

33
api/web/Dockerfile Normal file
View File

@ -0,0 +1,33 @@
# Use Python 3.12 slim image
FROM python:3.12-slim
# Set working directory
WORKDIR /app
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
&& rm -rf /var/lib/apt/lists/*
# Copy requirements first for better caching
COPY requirements.txt .
# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Create non-root user
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
USER appuser
# Expose port
EXPOSE 8000
# Run the application
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

71
api/web/app.py Normal file
View File

@ -0,0 +1,71 @@
from datetime import timedelta
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from fastapi.middleware.cors import CORSMiddleware
from users import Token, User, fake_users_db
from auth_utils import (
authenticate_user,
create_access_token,
get_current_active_user,
ACCESS_TOKEN_EXPIRE_MINUTES
)
app = FastAPI(title="Trading Agents API", version="1.0.0")
# Configure CORS
origins = [
"http://localhost",
"http://localhost:3000",
"http://localhost:8000",
"http://localhost:8080",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.post("/token", response_model=Token)
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
"""Login endpoint to get access token."""
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
@app.get("/users/me", response_model=User)
async def read_users_me(current_user: User = Depends(get_current_active_user)):
"""Get current user information."""
return current_user
@app.get("/protected")
async def protected_route(current_user: User = Depends(get_current_active_user)):
"""Example protected route."""
return {"message": f"Hello {current_user.username}, this is a protected route!"}
@app.get("/")
async def root():
"""Root endpoint."""
return {"message": "Trading Agents API with OAuth2"}
@app.get("/health")
async def health_check():
"""Health check endpoint."""
return {"status": "healthy"}

87
api/web/auth_utils.py Normal file
View File

@ -0,0 +1,87 @@
from datetime import datetime, timedelta
from typing import Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from passlib.context import CryptContext
from users import UserInDB, TokenData, fake_users_db
# Configuration
SECRET_KEY = "your-secret-key-here-change-in-production"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
# Password hashing
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# OAuth2 scheme
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""Verify a plain password against its hash."""
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str) -> str:
"""Hash a password."""
return pwd_context.hash(password)
def get_user(db: dict, username: str) -> Optional[UserInDB]:
"""Get a user from the database."""
if username in db:
user_dict = db[username]
return UserInDB(**user_dict)
return None
def authenticate_user(fake_db: dict, username: str, password: str) -> Optional[UserInDB]:
"""Authenticate a user."""
user = get_user(fake_db, username)
if not user:
return None
if not verify_password(password, user.hashed_password):
return None
return user
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
"""Create a JWT access token."""
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
async def get_current_user(token: str = Depends(oauth2_scheme)) -> UserInDB:
"""Get the current authenticated user from JWT token."""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except JWTError:
raise credentials_exception
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
return user
async def get_current_active_user(current_user: UserInDB = Depends(get_current_user)) -> UserInDB:
"""Get the current active user."""
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user

5
api/web/requirements.txt Normal file
View File

@ -0,0 +1,5 @@
fastapi==0.104.1
uvicorn[standard]==0.24.0
python-jose[cryptography]==3.3.0
passlib[bcrypt]==1.7.4
python-multipart==0.0.6

52
api/web/start_webserver.sh Executable file
View File

@ -0,0 +1,52 @@
#!/bin/bash
# Script to build and run the FastAPI web server using Docker
# Set variables
IMAGE_NAME="tradingagents-api-web"
CONTAINER_NAME="tradingagents-api-web-container"
PORT=8000
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
echo -e "${YELLOW}Building Docker image...${NC}"
docker build -t ${IMAGE_NAME} .
if [ $? -eq 0 ]; then
echo -e "${GREEN}Docker image built successfully!${NC}"
else
echo -e "${RED}Failed to build Docker image${NC}"
exit 1
fi
# Stop and remove existing container if it exists
echo -e "${YELLOW}Checking for existing container...${NC}"
if [ "$(docker ps -aq -f name=${CONTAINER_NAME})" ]; then
echo -e "${YELLOW}Stopping and removing existing container...${NC}"
docker stop ${CONTAINER_NAME}
docker rm ${CONTAINER_NAME}
fi
# Run the container
echo -e "${YELLOW}Starting container...${NC}"
docker run -d \
--name ${CONTAINER_NAME} \
-p ${PORT}:8000 \
-e SECRET_KEY="${SECRET_KEY:-your-secret-key-here-change-in-production}" \
${IMAGE_NAME}
if [ $? -eq 0 ]; then
echo -e "${GREEN}Container started successfully!${NC}"
echo -e "${GREEN}API is available at: http://localhost:${PORT}${NC}"
echo -e "${GREEN}API documentation: http://localhost:${PORT}/docs${NC}"
echo ""
echo -e "${YELLOW}To view logs:${NC} docker logs -f ${CONTAINER_NAME}"
echo -e "${YELLOW}To stop:${NC} docker stop ${CONTAINER_NAME}"
else
echo -e "${RED}Failed to start container${NC}"
exit 1
fi

96
api/web/test_api.sh Executable file
View File

@ -0,0 +1,96 @@
#!/bin/bash
# Test commands for the Trading Agents API
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
API_URL="http://localhost:8000"
echo -e "${BLUE}=== Trading Agents API Test Commands ===${NC}\n"
# 1. Health check
echo -e "${YELLOW}1. Health Check:${NC}"
echo "curl -X GET ${API_URL}/health"
echo -e "${GREEN}Expected: {\"status\":\"healthy\"}${NC}\n"
# 2. Root endpoint
echo -e "${YELLOW}2. Root Endpoint:${NC}"
echo "curl -X GET ${API_URL}/"
echo -e "${GREEN}Expected: {\"message\":\"Trading Agents API with OAuth2\"}${NC}\n"
# 3. Get token (login)
echo -e "${YELLOW}3. Login (Get Access Token):${NC}"
echo "curl -X POST ${API_URL}/token \\"
echo " -H \"Content-Type: application/x-www-form-urlencoded\" \\"
echo " -d \"username=testuser&password=secret\""
echo -e "${GREEN}Expected: {\"access_token\":\"<token>\",\"token_type\":\"bearer\"}${NC}\n"
# 4. Access protected endpoint without token (should fail)
echo -e "${YELLOW}4. Access Protected Route (No Token - Should Fail):${NC}"
echo "curl -X GET ${API_URL}/protected"
echo -e "${RED}Expected: 401 Unauthorized${NC}\n"
# 5. Access protected endpoint with token
echo -e "${YELLOW}5. Access Protected Route (With Token):${NC}"
echo "# First get the token:"
echo "TOKEN=\$(curl -s -X POST ${API_URL}/token \\"
echo " -H \"Content-Type: application/x-www-form-urlencoded\" \\"
echo " -d \"username=testuser&password=secret\" | jq -r '.access_token')"
echo ""
echo "# Then use it:"
echo "curl -X GET ${API_URL}/protected \\"
echo " -H \"Authorization: Bearer \$TOKEN\""
echo -e "${GREEN}Expected: {\"message\":\"Hello testuser, this is a protected route!\"}${NC}\n"
# 6. Get current user info
echo -e "${YELLOW}6. Get Current User Info:${NC}"
echo "curl -X GET ${API_URL}/users/me \\"
echo " -H \"Authorization: Bearer \$TOKEN\""
echo -e "${GREEN}Expected: User information JSON${NC}\n"
# 7. View API documentation
echo -e "${YELLOW}7. API Documentation:${NC}"
echo "Open in browser: ${API_URL}/docs"
echo "or"
echo "curl -X GET ${API_URL}/openapi.json | jq ."
echo ""
# One-liner test script
echo -e "${BLUE}=== Quick Test Script ===${NC}"
echo -e "${YELLOW}Run this to test all endpoints:${NC}\n"
cat << 'EOF'
#!/bin/bash
API_URL="http://localhost:8000"
# Test health
echo "Testing health endpoint..."
curl -s ${API_URL}/health | jq .
# Get token
echo -e "\nGetting access token..."
TOKEN=$(curl -s -X POST ${API_URL}/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=testuser&password=secret" | jq -r '.access_token')
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
echo "Failed to get token!"
exit 1
fi
echo "Token obtained successfully!"
# Test protected endpoint
echo -e "\nTesting protected endpoint..."
curl -s -X GET ${API_URL}/protected \
-H "Authorization: Bearer $TOKEN" | jq .
# Get user info
echo -e "\nGetting user info..."
curl -s -X GET ${API_URL}/users/me \
-H "Authorization: Bearer $TOKEN" | jq .
EOF

34
api/web/users.py Normal file
View File

@ -0,0 +1,34 @@
from typing import Optional
from pydantic import BaseModel
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: Optional[str] = None
class User(BaseModel):
username: str
email: Optional[str] = None
full_name: Optional[str] = None
disabled: Optional[bool] = None
class UserInDB(User):
hashed_password: str
# Fake database for demonstration
fake_users_db = {
"testuser": {
"username": "testuser",
"full_name": "Test User",
"email": "test@example.com",
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW", # secret
"disabled": False,
}
}