+36
internal/api/handlers.go
+36
internal/api/handlers.go
···
164
164
resp.json(stats)
165
165
}
166
166
167
+
// handleGetRandomEndpoint returns a random endpoint of specified type
168
+
func (s *Server) handleGetRandomEndpoint(w http.ResponseWriter, r *http.Request) {
169
+
resp := newResponse(w)
170
+
171
+
// Get required type parameter
172
+
endpointType := r.URL.Query().Get("type")
173
+
if endpointType == "" {
174
+
resp.error("type parameter is required", http.StatusBadRequest)
175
+
return
176
+
}
177
+
178
+
// Get optional status parameter
179
+
status := r.URL.Query().Get("status")
180
+
181
+
filter := &storage.EndpointFilter{
182
+
Type: endpointType,
183
+
Status: status,
184
+
Random: true,
185
+
Limit: 1,
186
+
Offset: 0,
187
+
}
188
+
189
+
endpoints, err := s.db.GetEndpoints(r.Context(), filter)
190
+
if err != nil {
191
+
resp.error(err.Error(), http.StatusInternalServerError)
192
+
return
193
+
}
194
+
195
+
if len(endpoints) == 0 {
196
+
resp.error("no endpoints found matching criteria", http.StatusNotFound)
197
+
return
198
+
}
199
+
200
+
resp.json(formatEndpointResponse(endpoints[0]))
201
+
}
202
+
167
203
// ===== PDS HANDLERS =====
168
204
169
205
func (s *Server) handleGetPDSList(w http.ResponseWriter, r *http.Request) {
+1
internal/api/server.go
+1
internal/api/server.go
···
61
61
// Generic endpoints (keep as-is)
62
62
api.HandleFunc("/endpoints", s.handleGetEndpoints).Methods("GET")
63
63
api.HandleFunc("/endpoints/stats", s.handleGetEndpointStats).Methods("GET")
64
+
api.HandleFunc("/endpoints/random", s.handleGetRandomEndpoint).Methods("GET")
64
65
65
66
//PDS-specific endpoints (virtual, created via JOINs)
66
67
api.HandleFunc("/pds", s.handleGetPDSList).Methods("GET")
+14
-2
internal/storage/postgres.go
+14
-2
internal/storage/postgres.go
···
359
359
}
360
360
}
361
361
362
-
// NEW: Order by server_did and discovered_at to get primary endpoints
363
-
query += " ORDER BY COALESCE(server_did, id::text), discovered_at ASC"
362
+
// NEW: Choose ordering strategy
363
+
if filter != nil && filter.Random {
364
+
// For random selection, we need to wrap in a subquery
365
+
query = fmt.Sprintf(`
366
+
WITH filtered_endpoints AS (
367
+
%s
368
+
)
369
+
SELECT * FROM filtered_endpoints
370
+
ORDER BY RANDOM()
371
+
`, query)
372
+
} else {
373
+
// Original ordering for non-random queries
374
+
query += " ORDER BY COALESCE(server_did, id::text), discovered_at ASC"
375
+
}
364
376
365
377
if filter != nil && filter.Limit > 0 {
366
378
query += fmt.Sprintf(" LIMIT $%d OFFSET $%d", argIdx, argIdx+1)
+3
-2
internal/storage/types.go
+3
-2
internal/storage/types.go
···
79
79
Type string // "pds", "labeler", etc.
80
80
Status string
81
81
MinUserCount int64
82
-
OnlyStale bool // NEW: Only return endpoints that need re-checking
83
-
RecheckInterval time.Duration // NEW: How long before an endpoint is considered stale
82
+
OnlyStale bool
83
+
RecheckInterval time.Duration
84
+
Random bool // NEW: Return results in random order
84
85
Limit int
85
86
Offset int
86
87
}