mirror of
https://github.com/gabehf/Koito.git
synced 2026-03-09 07:28:55 -07:00
* timezone overrides and force_tz option * docs for force_tz * add link to time zone names in docs
254 lines
7.6 KiB
Go
254 lines
7.6 KiB
Go
// package handlers implements route handlers
|
|
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
_ "time/tzdata"
|
|
|
|
"github.com/gabehf/koito/internal/cfg"
|
|
"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 {
|
|
|
|
// this map is obviously AI.
|
|
// i manually referenced as many links as I could and couldn't find any
|
|
// incorrect entries here so hopefully it is all correct.
|
|
overrides := map[string]string{
|
|
// --- North America ---
|
|
"America/Indianapolis": "America/Indiana/Indianapolis",
|
|
"America/Knoxville": "America/Indiana/Knoxville",
|
|
"America/Louisville": "America/Kentucky/Louisville",
|
|
"America/Montreal": "America/Toronto",
|
|
"America/Shiprock": "America/Denver",
|
|
"America/Fort_Wayne": "America/Indiana/Indianapolis",
|
|
"America/Virgin": "America/Port_of_Spain",
|
|
"America/Santa_Isabel": "America/Tijuana",
|
|
"America/Ensenada": "America/Tijuana",
|
|
"America/Rosario": "America/Argentina/Cordoba",
|
|
"America/Jujuy": "America/Argentina/Jujuy",
|
|
"America/Mendoza": "America/Argentina/Mendoza",
|
|
"America/Catamarca": "America/Argentina/Catamarca",
|
|
"America/Cordoba": "America/Argentina/Cordoba",
|
|
"America/Buenos_Aires": "America/Argentina/Buenos_Aires",
|
|
"America/Coral_Harbour": "America/Atikokan",
|
|
"America/Atka": "America/Adak",
|
|
"US/Alaska": "America/Anchorage",
|
|
"US/Aleutian": "America/Adak",
|
|
"US/Arizona": "America/Phoenix",
|
|
"US/Central": "America/Chicago",
|
|
"US/Eastern": "America/New_York",
|
|
"US/East-Indiana": "America/Indiana/Indianapolis",
|
|
"US/Hawaii": "Pacific/Honolulu",
|
|
"US/Indiana-Starke": "America/Indiana/Knoxville",
|
|
"US/Michigan": "America/Detroit",
|
|
"US/Mountain": "America/Denver",
|
|
"US/Pacific": "America/Los_Angeles",
|
|
"US/Samoa": "Pacific/Pago_Pago",
|
|
"Canada/Atlantic": "America/Halifax",
|
|
"Canada/Central": "America/Winnipeg",
|
|
"Canada/Eastern": "America/Toronto",
|
|
"Canada/Mountain": "America/Edmonton",
|
|
"Canada/Newfoundland": "America/St_Johns",
|
|
"Canada/Pacific": "America/Vancouver",
|
|
|
|
// --- Asia ---
|
|
"Asia/Calcutta": "Asia/Kolkata",
|
|
"Asia/Saigon": "Asia/Ho_Chi_Minh",
|
|
"Asia/Katmandu": "Asia/Kathmandu",
|
|
"Asia/Rangoon": "Asia/Yangon",
|
|
"Asia/Ulan_Bator": "Asia/Ulaanbaatar",
|
|
"Asia/Macao": "Asia/Macau",
|
|
"Asia/Tel_Aviv": "Asia/Jerusalem",
|
|
"Asia/Ashkhabad": "Asia/Ashgabat",
|
|
"Asia/Chungking": "Asia/Chongqing",
|
|
"Asia/Dacca": "Asia/Dhaka",
|
|
"Asia/Istanbul": "Europe/Istanbul",
|
|
"Asia/Kashgar": "Asia/Urumqi",
|
|
"Asia/Thimbu": "Asia/Thimphu",
|
|
"Asia/Ujung_Pandang": "Asia/Makassar",
|
|
"ROC": "Asia/Taipei",
|
|
"Iran": "Asia/Tehran",
|
|
"Israel": "Asia/Jerusalem",
|
|
"Japan": "Asia/Tokyo",
|
|
"Singapore": "Asia/Singapore",
|
|
"Hongkong": "Asia/Hong_Kong",
|
|
|
|
// --- Europe ---
|
|
"Europe/Kiev": "Europe/Kyiv",
|
|
"Europe/Belfast": "Europe/London",
|
|
"Europe/Tiraspol": "Europe/Chisinau",
|
|
"Europe/Nicosia": "Asia/Nicosia",
|
|
"Europe/Moscow": "Europe/Moscow",
|
|
"W-SU": "Europe/Moscow",
|
|
"GB": "Europe/London",
|
|
"GB-Eire": "Europe/London",
|
|
"Eire": "Europe/Dublin",
|
|
"Poland": "Europe/Warsaw",
|
|
"Portugal": "Europe/Lisbon",
|
|
"Turkey": "Europe/Istanbul",
|
|
|
|
// --- Australia / Pacific ---
|
|
"Australia/ACT": "Australia/Sydney",
|
|
"Australia/Canberra": "Australia/Sydney",
|
|
"Australia/LHI": "Australia/Lord_Howe",
|
|
"Australia/North": "Australia/Darwin",
|
|
"Australia/NSW": "Australia/Sydney",
|
|
"Australia/Queensland": "Australia/Brisbane",
|
|
"Australia/South": "Australia/Adelaide",
|
|
"Australia/Tasmania": "Australia/Hobart",
|
|
"Australia/Victoria": "Australia/Melbourne",
|
|
"Australia/West": "Australia/Perth",
|
|
"Australia/Yancowinna": "Australia/Broken_Hill",
|
|
"Pacific/Samoa": "Pacific/Pago_Pago",
|
|
"Pacific/Yap": "Pacific/Chuuk",
|
|
"Pacific/Truk": "Pacific/Chuuk",
|
|
"Pacific/Ponape": "Pacific/Pohnpei",
|
|
"NZ": "Pacific/Auckland",
|
|
"NZ-CHAT": "Pacific/Chatham",
|
|
|
|
// --- Africa ---
|
|
"Africa/Asmera": "Africa/Asmara",
|
|
"Africa/Timbuktu": "Africa/Bamako",
|
|
"Egypt": "Africa/Cairo",
|
|
"Libya": "Africa/Tripoli",
|
|
|
|
// --- Atlantic ---
|
|
"Atlantic/Faeroe": "Atlantic/Faroe",
|
|
"Atlantic/Jan_Mayen": "Europe/Oslo",
|
|
"Iceland": "Atlantic/Reykjavik",
|
|
|
|
// --- Etc / Misc ---
|
|
"UTC": "UTC",
|
|
"Etc/UTC": "UTC",
|
|
"Etc/GMT": "UTC",
|
|
"GMT": "UTC",
|
|
"Zulu": "UTC",
|
|
"Universal": "UTC",
|
|
}
|
|
|
|
if cfg.ForceTZ() != nil {
|
|
return cfg.ForceTZ()
|
|
}
|
|
|
|
if tz := r.URL.Query().Get("tz"); tz != "" {
|
|
if fixedTz, exists := overrides[tz]; exists {
|
|
tz = fixedTz
|
|
}
|
|
if loc, err := time.LoadLocation(tz); err == nil {
|
|
return loc
|
|
}
|
|
}
|
|
|
|
if c, err := r.Cookie("tz"); err == nil {
|
|
var tz string
|
|
if fixedTz, exists := overrides[c.Value]; exists {
|
|
tz = fixedTz
|
|
} else {
|
|
tz = c.Value
|
|
}
|
|
if loc, err := time.LoadLocation(tz); err == nil {
|
|
return loc
|
|
}
|
|
}
|
|
|
|
return time.Now().Location()
|
|
}
|