@@ -109,6 +109,17 @@ def raise_rate_limited(detail: str, info: dict) -> None:
109109STARTING_BALANCE = 1000.0 # New agent starting balance
110110
111111
112+ def _validate_uuid (value : str , param_name : str = "id" ) -> None :
113+ """Validate that a string is a valid UUID. Raises 400 if not."""
114+ try :
115+ uuid .UUID (value )
116+ except (ValueError , AttributeError ):
117+ raise HTTPException (
118+ status_code = 400 ,
119+ detail = f"Invalid { param_name } : '{ value } ' is not a valid UUID" ,
120+ )
121+
122+
112123def generate_verification_code () -> str :
113124 """Generate a cryptographically secure verification code like 'crab-reef-A1B2C3D4'.
114125
@@ -1766,6 +1777,7 @@ async def list_markets(status: Optional[str] = None):
17661777@app .get ("/markets/{market_id}" , response_model = MarketDetail )
17671778async def get_market (market_id : str ):
17681779 """Get market details including current probability."""
1780+ _validate_uuid (market_id , "market_id" )
17691781 market = db .get_market (market_id )
17701782 if not market :
17711783 raise HTTPException (status_code = 404 , detail = "Market not found" )
@@ -1897,6 +1909,7 @@ async def create_market(req: MarketCreate, user: dict = Depends(require_auth)):
18971909@app .post ("/markets/{market_id}/resolve" , response_model = MarketDetail )
18981910async def resolve_market (market_id : str , req : MarketResolve , user : dict = Depends (require_auth )):
18991911 """Resolve a market. Only creator can resolve."""
1912+ _validate_uuid (market_id , "market_id" )
19001913 market = db .get_market (market_id )
19011914 if not market :
19021915 raise HTTPException (status_code = 404 , detail = "Market not found" )
@@ -1957,6 +1970,7 @@ async def place_bet(market_id: str, req: BetRequest, response: Response, user: d
19571970 - `X-RateLimit-Reset`: Unix timestamp when window resets
19581971 - `Retry-After` (on 429 only): Seconds to wait
19591972 """
1973+ _validate_uuid (market_id , "market_id" )
19601974 # Require twitter verification before trading
19611975 if user .get ("status" ) != "claimed" :
19621976 raise HTTPException (
@@ -2085,6 +2099,7 @@ async def place_bet_plural_alias(market_id: str, req: BetRequest, user: dict = D
20852099@app .post ("/markets/{market_id}/sell" , response_model = SellResponse )
20862100async def sell_shares (market_id : str , req : SellRequest , user : dict = Depends (require_auth )):
20872101 """Sell shares back to the market."""
2102+ _validate_uuid (market_id , "market_id" )
20882103 # Require twitter verification before trading
20892104 if user .get ("status" ) != "claimed" :
20902105 raise HTTPException (
@@ -2172,6 +2187,7 @@ async def sell_shares(market_id: str, req: SellRequest, user: dict = Depends(req
21722187@app .get ("/markets/{market_id}/positions" , response_model = MarketPositions )
21732188async def get_positions (market_id : str ):
21742189 """Get all positions for a market."""
2190+ _validate_uuid (market_id , "market_id" )
21752191 market = db .get_market (market_id )
21762192 if not market :
21772193 raise HTTPException (status_code = 404 , detail = "Market not found" )
@@ -2200,6 +2216,7 @@ async def get_positions(market_id: str):
22002216@app .get ("/markets/{market_id}/history" , response_model = MarketHistory )
22012217async def get_market_history (market_id : str ):
22022218 """Get probability history for charts."""
2219+ _validate_uuid (market_id , "market_id" )
22032220 market = db .get_market (market_id )
22042221 if not market :
22052222 raise HTTPException (status_code = 404 , detail = "Market not found" )
@@ -2235,6 +2252,7 @@ async def get_market_history(market_id: str):
22352252@app .get ("/markets/{market_id}/bets" , response_model = List [BetHistoryItem ])
22362253async def get_market_bets (market_id : str ):
22372254 """Get all bets for a market."""
2255+ _validate_uuid (market_id , "market_id" )
22382256 market = db .get_market (market_id )
22392257 if not market :
22402258 raise HTTPException (status_code = 404 , detail = "Market not found" )
@@ -2269,6 +2287,7 @@ async def get_market_bets(market_id: str):
22692287@app .get ("/markets/{market_id}/comments" , response_model = MarketComments )
22702288async def get_comments (market_id : str ):
22712289 """Get all comments for a market."""
2290+ _validate_uuid (market_id , "market_id" )
22722291 market = db .get_market (market_id )
22732292 if not market :
22742293 raise HTTPException (status_code = 404 , detail = "Market not found" )
@@ -2311,6 +2330,7 @@ async def get_comments(market_id: str):
23112330@app .post ("/markets/{market_id}/comments" , response_model = Comment )
23122331async def create_comment (market_id : str , req : CommentCreate , user : dict = Depends (require_auth )):
23132332 """Create a comment on a market."""
2333+ _validate_uuid (market_id , "market_id" )
23142334 market = db .get_market (market_id )
23152335 if not market :
23162336 raise HTTPException (status_code = 404 , detail = "Market not found" )
@@ -2354,6 +2374,7 @@ async def request_resolution(market_id: str, user: dict = Depends(require_auth))
23542374 Only the market creator can request resolution.
23552375 The committee will research and vote, with majority (5+) deciding the outcome.
23562376 """
2377+ _validate_uuid (market_id , "market_id" )
23572378 market = db .get_market (market_id )
23582379 if not market :
23592380 raise HTTPException (status_code = 404 , detail = "Market not found" )
@@ -2435,6 +2456,7 @@ async def request_resolution(market_id: str, user: dict = Depends(require_auth))
24352456@app .get ("/markets/{market_id}/resolution-votes" , response_model = ResolutionResult )
24362457async def get_resolution_votes (market_id : str ):
24372458 """Get the resolution committee votes for a market."""
2459+ _validate_uuid (market_id , "market_id" )
24382460 market = db .get_market (market_id )
24392461 if not market :
24402462 raise HTTPException (status_code = 404 , detail = "Market not found" )
@@ -2603,6 +2625,7 @@ async def get_my_bets(
26032625@app .get ("/users/{user_id}" , response_model = UserProfile )
26042626async def get_user (user_id : str ):
26052627 """Get public user profile."""
2628+ _validate_uuid (user_id , "user_id" )
26062629 user = db .get_user (user_id )
26072630 if not user :
26082631 raise HTTPException (status_code = 404 , detail = "User not found" )
@@ -2876,6 +2899,7 @@ async def get_claim_info(user_id: str):
28762899
28772900 Returns the verification code and instructions for claiming.
28782901 """
2902+ _validate_uuid (user_id , "user_id" )
28792903 user = db .get_user (user_id )
28802904 if not user :
28812905 raise HTTPException (status_code = 404 , detail = "Agent not found" )
@@ -3160,10 +3184,34 @@ async def get_chat_messages(limit: int = 50, since: Optional[str] = None, channe
31603184
31613185@app .get ("/health" )
31623186async def health ():
3187+ from fastapi .responses import JSONResponse
3188+
3189+ # Verify database is reachable
3190+ db_status = "ok"
3191+ try :
3192+ conn = db ._get_conn ()
3193+ try :
3194+ with conn .cursor () as cur :
3195+ cur .execute ("SELECT 1" )
3196+ finally :
3197+ db ._put_conn (conn )
3198+ except Exception :
3199+ db_status = "unreachable"
3200+
3201+ if db_status != "ok" :
3202+ return JSONResponse (
3203+ status_code = 503 ,
3204+ content = {
3205+ "status" : "degraded" ,
3206+ "db" : db_status ,
3207+ },
3208+ )
3209+
31633210 market_count = len (db .list_markets ())
31643211 user_count = len (db .users )
31653212 return {
31663213 "status" : "ok" ,
3214+ "db" : "ok" ,
31673215 "markets" : market_count ,
31683216 "users" : user_count ,
31693217 "currency" : {
0 commit comments