@@ -238,69 +238,23 @@ usersRouter.get(
238238 ( res . locals . correlationId as string | undefined ) ?? getRequestId ( ) ;
239239
240240 try {
241- const userId = req . user ! . id ;
242- const result = await getCurrentUserProfile ( userId ) ;
243-
244- if ( ! result . ok ) {
245- throw result . error ;
246- }
247-
248- const profile = result . value ;
249- logger . info (
250- {
251- correlationId,
252- userId,
253- stellarAddress : profile . stellarAddress ,
254- ...profile . totals ,
255- } ,
256- "user_me_profile_loaded" ,
257- ) ;
258-
259- // Strong ETag on the profile payload; 304 if client already has it.
260- const responsePayload = { data : profile } ;
261- if ( conditionalGet ( responsePayload , req , res ) ) return ;
262- return res . json ( responsePayload ) ;
263- } catch ( e ) {
264- return next ( e ) ;
241+ stellarAddressSchema . parse ( address ) ;
242+ } catch {
243+ return res . status ( 400 ) . json ( { error : { code : "invalid_address" } } ) ;
265244 }
266245 } ,
267246) ;
268247
269- // ---------------------------------------------------------------------------
270- // GET /api/users/:address/predictions
271- // ---------------------------------------------------------------------------
248+ const querySchema = z . object ( {
249+ status : z . enum ( [ "pending" , "confirmed" , "won" , "lost" , "claimed" ] ) . optional ( ) ,
250+ cursor : z
251+ . string ( )
252+ . regex ( / ^ \d { 4 } - \d { 2 } - \d { 2 } T \d { 2 } : \d { 2 } : \d { 2 } \. \d { 3 } Z \| [ 0 - 9 a - f ] { 8 } - [ 0 - 9 a - f ] { 4 } - [ 1 - 5 ] [ 0 - 9 a - f ] { 3 } - [ 8 9 a b ] [ 0 - 9 a - f ] { 3 } - [ 0 - 9 a - f ] { 12 } $ / i)
253+ . optional ( ) ,
254+ limit : z . coerce . number ( ) . int ( ) . min ( 1 ) . max ( 100 ) ,
255+ } ) ;
272256
273- /**
274- * Returns a cursor-paginated list of predictions for the given Stellar address.
275- *
276- * Path parameters:
277- * - :address — a valid 56-char Stellar G-address
278- *
279- * Query parameters:
280- * - status (optional) — filter by prediction status enum
281- * - cursor (optional) — opaque base64url token from the previous page's `nextCursor`
282- * - limit (optional, default 20, max 100) — page size
283- *
284- * Response:
285- * { data: UserPredictionRow[], nextCursor: string | null }
286- *
287- * Caching:
288- * Strong ETag on the page payload; clients may revalidate with If-None-Match
289- * and receive 304 Not Modified when the page is unchanged.
290- *
291- * Errors:
292- * 400 invalid_address — path param is not a valid G… Stellar address
293- * 400 validation_error — query params fail the zod schema
294- * 404 not_found — no user row for that address
295- */
296- usersRouter . get (
297- "/:address/predictions" ,
298- usersRateLimit ,
299- async ( req : Request , res : Response , next : NextFunction ) => {
300- // Prefer the access-log correlation ID; fall back to ALS for non-route callers.
301- const correlationId =
302- ( res . locals . correlationId as string | undefined ) ?? getRequestId ( ) ;
303- const reqId = correlationId ;
257+ const query = querySchema . parse ( { status, cursor, limit } ) ;
304258
305259 try {
306260 // Validate the path parameter :address at the route boundary before touching the DB.
0 commit comments