Files
minstrel/internal/api/me_timezone.go
T
bvandeusenandClaude Opus 5.5 08a2c06b85 docs: api comments say what the endpoints do, not how they came to be (#6277)
Rewrites the internal/api comment blocks to rule 32: task, milestone and
rule ids gone, the stories behind them restated as the constraint they
left, stale references corrected (the stream secret is wired from config,
the timezone handler already refreshes the scheduler, the Android app is
the native client, the design docs cited are not in the repo). Also
rewraps comment lines over 80 columns left by the earlier batches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 12:23:58 -04:00

68 lines
2.1 KiB
Go

// PUT /api/me/timezone — accept the client's IANA timezone and store
// it on the user row. The system-playlist scheduler reads this column
// to fire each user's daily 03:00 build in their local time. The
// handler calls Scheduler.Refresh so a change takes effect at once rather
// than at the hourly reconciliation pass.
package api
import (
"encoding/json"
"net/http"
"time"
"git.fabledsword.com/bvandeusen/minstrel/internal/apierror"
"git.fabledsword.com/bvandeusen/minstrel/internal/auth"
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
)
// putTimezoneBody is the JSON body for PUT /api/me/timezone.
type putTimezoneBody struct {
Timezone string `json:"timezone"`
}
func (h *handlers) handlePutTimezone(w http.ResponseWriter, r *http.Request) {
user, ok := auth.UserFromContext(r.Context())
if !ok {
writeErr(w, apierror.Unauthorized("unauthorized", "authentication required"))
return
}
var body putTimezoneBody
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
writeErr(w, apierror.BadRequest("bad_body", "invalid JSON body"))
return
}
if body.Timezone == "" {
writeErr(w, apierror.BadRequest("bad_body", "timezone is required"))
return
}
if _, err := time.LoadLocation(body.Timezone); err != nil {
writeErr(w, apierror.BadRequest("invalid_timezone",
"timezone must be a valid IANA name (e.g. America/New_York)"))
return
}
q := dbq.New(h.pool)
if err := q.UpdateUserTimezone(r.Context(), dbq.UpdateUserTimezoneParams{
ID: user.ID,
Timezone: body.Timezone,
}); err != nil {
h.logger.Error("api: update timezone", "err", err)
writeErr(w, apierror.InternalMsg("update failed", err))
return
}
// Reschedule the user's daily build at their new timezone. Failure
// here doesn't undo the DB write — the hourly reconciliation pass
// would pick up the new tz within an hour at worst.
if h.playlistScheduler != nil {
if err := h.playlistScheduler.Refresh(r.Context(), user.ID); err != nil {
h.logger.Warn("api: scheduler refresh after timezone update",
"user_id", uuidToString(user.ID), "err", err)
}
}
w.WriteHeader(http.StatusNoContent)
}