Koito/engine/handlers/handlers.go
Gabe Farrell f48dd6c039
fix: respect client timezone for requests (#119)
* maybe fixed for total listen activity

* maybe actually fixed now

* fix unset location panics
2026-01-10 01:45:31 -05:00

123 lines
2.8 KiB
Go

// package handlers implements route handlers
package handlers
import (
"net/http"
"strconv"
"strings"
"time"
"github.com/gabehf/koito/internal/db"
"github.com/gabehf/koito/internal/logger"
)
const defaultLimitSize = 100
const maximumLimit = 500
func OptsFromRequest(r *http.Request) db.GetItemsOpts {
l := logger.FromContext(r.Context())
l.Debug().Msg("OptsFromRequest: Parsing query parameters")
limitStr := r.URL.Query().Get("limit")
limit, err := strconv.Atoi(limitStr)
if err != nil {
l.Debug().Msgf("OptsFromRequest: Query parameter 'limit' not specified, using default %d", defaultLimitSize)
limit = defaultLimitSize
}
if limit > maximumLimit {
l.Debug().Msgf("OptsFromRequest: Limit exceeds maximum %d, using default %d", maximumLimit, defaultLimitSize)
limit = defaultLimitSize
}
pageStr := r.URL.Query().Get("page")
page, _ := strconv.Atoi(pageStr)
if page < 1 {
l.Debug().Msg("OptsFromRequest: Page parameter is less than 1, defaulting to 1")
page = 1
}
artistIdStr := r.URL.Query().Get("artist_id")
artistId, _ := strconv.Atoi(artistIdStr)
albumIdStr := r.URL.Query().Get("album_id")
albumId, _ := strconv.Atoi(albumIdStr)
trackIdStr := r.URL.Query().Get("track_id")
trackId, _ := strconv.Atoi(trackIdStr)
tf := TimeframeFromRequest(r)
var period db.Period
switch strings.ToLower(r.URL.Query().Get("period")) {
case "day":
period = db.PeriodDay
case "week":
period = db.PeriodWeek
case "month":
period = db.PeriodMonth
case "year":
period = db.PeriodYear
case "all_time":
period = db.PeriodAllTime
}
l.Debug().Msgf("OptsFromRequest: Parsed options: limit=%d, page=%d, week=%d, month=%d, year=%d, from=%d, to=%d, artist_id=%d, album_id=%d, track_id=%d, period=%s",
limit, page, tf.Week, tf.Month, tf.Year, tf.FromUnix, tf.ToUnix, artistId, albumId, trackId, period)
return db.GetItemsOpts{
Limit: limit,
Page: page,
Timeframe: tf,
ArtistID: artistId,
AlbumID: albumId,
TrackID: trackId,
}
}
func TimeframeFromRequest(r *http.Request) db.Timeframe {
q := r.URL.Query()
parseInt := func(key string) int {
v := q.Get(key)
if v == "" {
return 0
}
i, _ := strconv.Atoi(v)
return i
}
parseInt64 := func(key string) int64 {
v := q.Get(key)
if v == "" {
return 0
}
i, _ := strconv.ParseInt(v, 10, 64)
return i
}
return db.Timeframe{
Period: db.Period(q.Get("period")),
Year: parseInt("year"),
Month: parseInt("month"),
Week: parseInt("week"),
FromUnix: parseInt64("from"),
ToUnix: parseInt64("to"),
Timezone: parseTZ(r),
}
}
func parseTZ(r *http.Request) *time.Location {
if tz := r.URL.Query().Get("tz"); tz != "" {
if loc, err := time.LoadLocation(tz); err == nil {
return loc
}
}
if c, err := r.Cookie("tz"); err == nil {
if loc, err := time.LoadLocation(c.Value); err == nil {
return loc
}
}
return time.Now().Location()
}