Files
core/internal/api/timeline.go

64 lines
1.5 KiB
Go

package api
import (
"net/http"
"strconv"
"strings"
"reanimator/internal/repository/timeline"
)
func writeTimelineResponse(w http.ResponseWriter, r *http.Request, repo *timeline.EventRepository, subjectType string, subjectID string) {
limit := 50
if raw := r.URL.Query().Get("limit"); raw != "" {
if parsed, err := strconv.Atoi(raw); err == nil {
limit = parsed
} else {
writeError(w, http.StatusBadRequest, "invalid limit")
return
}
}
var cursor *timeline.Cursor
if raw := r.URL.Query().Get("cursor"); raw != "" {
decoded, err := timeline.DecodeCursor(raw)
if err != nil {
writeError(w, http.StatusBadRequest, "invalid cursor")
return
}
cursor = &decoded
}
items, next, err := repo.List(r.Context(), subjectType, subjectID, limit, cursor)
if err != nil {
writeError(w, http.StatusInternalServerError, "list timeline failed")
return
}
var nextCursor *string
if next != nil {
value := timeline.EncodeCursor(*next)
nextCursor = &value
}
writeJSON(w, http.StatusOK, map[string]any{
"items": items,
"next_cursor": nextCursor,
})
}
func parseTimelineID(path, prefix string) string {
if !strings.HasPrefix(path, prefix) || !strings.HasSuffix(path, "/timeline") {
return ""
}
trimmed := strings.TrimPrefix(path, prefix)
trimmed = strings.TrimSuffix(trimmed, "/timeline")
if trimmed == "" || strings.Contains(trimmed, "/") {
return ""
}
if !entityIDPattern.MatchString(trimmed) {
return ""
}
return trimmed
}