A community based topic aggregation platform built on atproto
1package discover
2
3import (
4 "Coves/internal/api/handlers/common"
5 "Coves/internal/core/discover"
6 "Coves/internal/core/posts"
7 "Coves/internal/core/votes"
8 "encoding/json"
9 "log"
10 "net/http"
11 "strconv"
12)
13
14// GetDiscoverHandler handles discover feed retrieval
15type GetDiscoverHandler struct {
16 service discover.Service
17 voteService votes.Service
18}
19
20// NewGetDiscoverHandler creates a new discover handler
21func NewGetDiscoverHandler(service discover.Service, voteService votes.Service) *GetDiscoverHandler {
22 return &GetDiscoverHandler{
23 service: service,
24 voteService: voteService,
25 }
26}
27
28// HandleGetDiscover retrieves posts from all communities (public feed)
29// GET /xrpc/social.coves.feed.getDiscover?sort=hot&limit=15&cursor=...
30// Public endpoint with optional auth - if authenticated, includes viewer vote state
31func (h *GetDiscoverHandler) HandleGetDiscover(w http.ResponseWriter, r *http.Request) {
32 if r.Method != http.MethodGet {
33 http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
34 return
35 }
36
37 // Parse query parameters
38 req := h.parseRequest(r)
39
40 // Get discover feed
41 response, err := h.service.GetDiscover(r.Context(), req)
42 if err != nil {
43 handleServiceError(w, err)
44 return
45 }
46
47 // Populate viewer vote state if authenticated
48 common.PopulateViewerVoteState(r.Context(), r, h.voteService, response.Feed)
49
50 // Transform blob refs to URLs for all posts
51 for _, feedPost := range response.Feed {
52 if feedPost.Post != nil {
53 posts.TransformBlobRefsToURLs(feedPost.Post)
54 }
55 }
56
57 // Return feed
58 w.Header().Set("Content-Type", "application/json")
59 w.WriteHeader(http.StatusOK)
60 if err := json.NewEncoder(w).Encode(response); err != nil {
61 log.Printf("ERROR: Failed to encode discover response: %v", err)
62 }
63}
64
65// parseRequest parses query parameters into GetDiscoverRequest
66func (h *GetDiscoverHandler) parseRequest(r *http.Request) discover.GetDiscoverRequest {
67 req := discover.GetDiscoverRequest{}
68
69 // Optional: sort (default: hot)
70 req.Sort = r.URL.Query().Get("sort")
71 if req.Sort == "" {
72 req.Sort = "hot"
73 }
74
75 // Optional: timeframe (default: day for top sort)
76 req.Timeframe = r.URL.Query().Get("timeframe")
77 if req.Timeframe == "" && req.Sort == "top" {
78 req.Timeframe = "day"
79 }
80
81 // Optional: limit (default: 15, max: 50)
82 req.Limit = 15
83 if limitStr := r.URL.Query().Get("limit"); limitStr != "" {
84 if limit, err := strconv.Atoi(limitStr); err == nil {
85 req.Limit = limit
86 }
87 }
88
89 // Optional: cursor
90 if cursor := r.URL.Query().Get("cursor"); cursor != "" {
91 req.Cursor = &cursor
92 }
93
94 return req
95}