
- Set up project structure with FastAPI and SQLite - Implement user authentication with JWT - Create database models for users, events, bets, and transactions - Add API endpoints for user management - Add API endpoints for events and betting functionality - Add wallet management for deposits and withdrawals - Configure Alembic for database migrations - Add linting with Ruff - Add documentation in README
137 lines
4.2 KiB
Python
137 lines
4.2 KiB
Python
from typing import Any, Optional
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app import crud, models, schemas
|
|
from app.api import deps
|
|
from app.models.bet import BetStatus
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/", response_model=list[schemas.Bet])
|
|
def read_bets(
|
|
db: Session = Depends(deps.get_db),
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
status: Optional[BetStatus] = None,
|
|
current_user: models.User = Depends(deps.get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Retrieve bets for current user.
|
|
"""
|
|
bets = crud.get_user_bets(
|
|
db, user_id=current_user.id, skip=skip, limit=limit, status=status,
|
|
)
|
|
return bets
|
|
|
|
|
|
@router.post("/", response_model=schemas.Bet)
|
|
def create_bet(
|
|
*,
|
|
db: Session = Depends(deps.get_db),
|
|
bet_in: schemas.BetCreate,
|
|
current_user: models.User = Depends(deps.get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Create a new bet.
|
|
"""
|
|
# Check if user has enough balance
|
|
if current_user.balance < bet_in.amount:
|
|
raise HTTPException(
|
|
status_code=400, detail="Insufficient balance for this bet",
|
|
)
|
|
|
|
# Check if amount is positive
|
|
if bet_in.amount <= 0:
|
|
raise HTTPException(
|
|
status_code=400, detail="Bet amount must be greater than 0",
|
|
)
|
|
|
|
# Check if outcome exists and is active
|
|
outcome = crud.get_outcome(db, outcome_id=bet_in.outcome_id)
|
|
if not outcome:
|
|
raise HTTPException(status_code=404, detail="Outcome not found")
|
|
|
|
if not outcome.is_active:
|
|
raise HTTPException(status_code=400, detail="This outcome is not available for betting")
|
|
|
|
# Check if market is active
|
|
if not outcome.market.is_active:
|
|
raise HTTPException(status_code=400, detail="This market is not available for betting")
|
|
|
|
# Check if event is upcoming or live
|
|
event = outcome.market.event
|
|
if event.status not in [models.event.EventStatus.UPCOMING, models.event.EventStatus.LIVE]:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"Cannot place bets on events with status {event.status}",
|
|
)
|
|
|
|
bet = crud.create_bet(db, bet_in=bet_in, user_id=current_user.id)
|
|
if not bet:
|
|
raise HTTPException(status_code=400, detail="Could not create bet")
|
|
|
|
return bet
|
|
|
|
|
|
@router.get("/{bet_id}", response_model=schemas.Bet)
|
|
def read_bet(
|
|
*,
|
|
db: Session = Depends(deps.get_db),
|
|
bet_id: int,
|
|
current_user: models.User = Depends(deps.get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Get specific bet by ID.
|
|
"""
|
|
bet = crud.get_bet(db, bet_id=bet_id)
|
|
if not bet:
|
|
raise HTTPException(status_code=404, detail="Bet not found")
|
|
|
|
# Only allow user to see their own bets (or admin)
|
|
if bet.user_id != current_user.id and not current_user.is_admin:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
return bet
|
|
|
|
|
|
@router.post("/{bet_id}/cancel", response_model=schemas.Bet)
|
|
def cancel_bet(
|
|
*,
|
|
db: Session = Depends(deps.get_db),
|
|
bet_id: int,
|
|
current_user: models.User = Depends(deps.get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Cancel a pending bet.
|
|
"""
|
|
bet = crud.get_bet(db, bet_id=bet_id)
|
|
if not bet:
|
|
raise HTTPException(status_code=404, detail="Bet not found")
|
|
|
|
# Only allow user to cancel their own bets (or admin)
|
|
if bet.user_id != current_user.id and not current_user.is_admin:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
# Only pending bets can be cancelled
|
|
if bet.status != BetStatus.PENDING:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"Cannot cancel a bet with status {bet.status}",
|
|
)
|
|
|
|
# Get the outcome and check if event has started
|
|
outcome = crud.get_outcome(db, outcome_id=bet.outcome_id)
|
|
event = outcome.market.event
|
|
|
|
# Only allow cancellation if event hasn't started yet
|
|
if event.status != models.event.EventStatus.UPCOMING:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="Cannot cancel bets on events that have already started",
|
|
)
|
|
|
|
bet = crud.update_bet_status(db, bet_id=bet_id, status=BetStatus.CANCELLED)
|
|
return bet |