package api import ( "net/http" "atmoquest/internal/badge" ) // badgeResponse is the JSON shape for a badge. type badgeResponse struct { Name string `json:"name"` EarnedAt string `json:"earnedAt"` } // ListBadges returns all badges earned by the authenticated user. // GET /api/v1/badges func (h *Handlers) ListBadges(w http.ResponseWriter, r *http.Request) { did, ok := h.requireAuth(w, r) if !ok { return } rows, err := h.DB.QueryContext(r.Context(), ` SELECT d.name, e.earned_at FROM earned_badges e JOIN badge_definitions d ON d.id = e.badge_id WHERE e.did = ? ORDER BY e.earned_at DESC `, did.String()) if err != nil { writeJSON(w, http.StatusOK, []badgeResponse{}) return } defer rows.Close() resp := make([]badgeResponse, 0) for rows.Next() { var b badgeResponse if rows.Scan(&b.Name, &b.EarnedAt) == nil { resp = append(resp, b) } } writeJSON(w, http.StatusOK, resp) } // RecentBadges returns badges earned in the last 60 seconds. // GET /api/v1/badges/recent func (h *Handlers) RecentBadges(w http.ResponseWriter, r *http.Request) { did, ok := h.requireAuth(w, r) if !ok { return } rows, err := h.DB.QueryContext(r.Context(), ` SELECT d.name FROM earned_badges e JOIN badge_definitions d ON d.id = e.badge_id WHERE e.did = ? AND e.earned_at >= datetime('now', '-60 seconds') ORDER BY e.earned_at DESC `, did.String()) if err != nil { writeJSON(w, http.StatusOK, map[string]any{"badges": []string{}}) return } defer rows.Close() var names []string for rows.Next() { var name string if rows.Scan(&name) == nil { names = append(names, name) } } if names == nil { names = []string{} } w.Header().Set("Cache-Control", "no-cache") writeJSON(w, http.StatusOK, map[string]any{"badges": names}) } // Ensure badge package is reachable (used in future device/push handlers). var _ = badge.Design{}