diff --git a/backend/internal/api/auth.go b/backend/internal/api/auth.go index a6de82c..295e2ff 100644 --- a/backend/internal/api/auth.go +++ b/backend/internal/api/auth.go @@ -25,24 +25,31 @@ func authStatusString(s garmin.AuthStatus) string { } } -func (s *Server) recordAuthResult(res garmin.AuthResult) { +func (s *Server) recordAuthResult(userID int64, res garmin.AuthResult) { s.mu.Lock() - s.authStatus = res.Status - s.authMessage = res.Message + s.userAuthStatus[userID] = res.Status + s.userAuthMessage[userID] = res.Message s.mu.Unlock() } func (s *Server) handleAuthLogin(w http.ResponseWriter, r *http.Request) { - res, err := s.Garmin.Authenticate(r.Context()) + userID := userIDFromContext(r.Context()) + client, err := s.garminFor(r.Context(), userID) + if err != nil { + writeError(w, http.StatusInternalServerError, err.Error()) + return + } + res, err := client.Authenticate(r.Context()) if err != nil { writeError(w, http.StatusBadGateway, err.Error()) return } - s.recordAuthResult(res) + s.recordAuthResult(userID, res) writeJSON(w, http.StatusOK, authResponse{Status: authStatusString(res.Status), Message: res.Message}) } func (s *Server) handleAuthMFA(w http.ResponseWriter, r *http.Request) { + userID := userIDFromContext(r.Context()) var body struct { Code string `json:"code"` } @@ -55,18 +62,24 @@ func (s *Server) handleAuthMFA(w http.ResponseWriter, r *http.Request) { return } - res, err := s.Garmin.CompleteMFA(r.Context(), body.Code) + client, err := s.garminFor(r.Context(), userID) + if err != nil { + writeError(w, http.StatusInternalServerError, err.Error()) + return + } + res, err := client.CompleteMFA(r.Context(), body.Code) if err != nil { writeError(w, http.StatusBadGateway, err.Error()) return } - s.recordAuthResult(res) + s.recordAuthResult(userID, res) writeJSON(w, http.StatusOK, authResponse{Status: authStatusString(res.Status), Message: res.Message}) } func (s *Server) handleAuthStatus(w http.ResponseWriter, r *http.Request) { + userID := userIDFromContext(r.Context()) s.mu.Lock() - status, msg := s.authStatus, s.authMessage + status, msg := s.userAuthStatus[userID], s.userAuthMessage[userID] s.mu.Unlock() writeJSON(w, http.StatusOK, authResponse{Status: authStatusString(status), Message: msg}) } diff --git a/backend/internal/api/kinds.go b/backend/internal/api/kinds.go index 11f86ab..ad2e91e 100644 --- a/backend/internal/api/kinds.go +++ b/backend/internal/api/kinds.go @@ -24,7 +24,8 @@ type workoutKindResponse struct { } func (s *Server) toWorkoutKindResponse(r *http.Request, k store.WorkoutKind) (workoutKindResponse, error) { - pace, err := s.DB.GetWorkoutTypePace(r.Context(), k.ID) + userID := userIDFromContext(r.Context()) + pace, err := s.DB.GetWorkoutTypePace(r.Context(), userID, k.ID) if err != nil { return workoutKindResponse{}, err } @@ -71,8 +72,9 @@ func (req workoutKindRequest) validate() (classify.Node, error) { } func (s *Server) handleListWorkoutKinds(w http.ResponseWriter, r *http.Request) { + userID := userIDFromContext(r.Context()) activeOnly := r.URL.Query().Get("include_inactive") != "true" - kinds, err := s.DB.ListWorkoutKinds(r.Context(), activeOnly) + kinds, err := s.DB.ListWorkoutKinds(r.Context(), userID, activeOnly) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return @@ -90,12 +92,13 @@ func (s *Server) handleListWorkoutKinds(w http.ResponseWriter, r *http.Request) } func (s *Server) handleGetWorkoutKind(w http.ResponseWriter, r *http.Request) { + userID := userIDFromContext(r.Context()) id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) if err != nil { writeError(w, http.StatusBadRequest, "invalid workout kind id") return } - kind, ok, err := s.DB.GetWorkoutKind(r.Context(), id) + kind, ok, err := s.DB.GetWorkoutKind(r.Context(), userID, id) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return @@ -113,12 +116,13 @@ func (s *Server) handleGetWorkoutKind(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleUpdateWorkoutKind(w http.ResponseWriter, r *http.Request) { + userID := userIDFromContext(r.Context()) id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) if err != nil { writeError(w, http.StatusBadRequest, "invalid workout kind id") return } - existing, ok, err := s.DB.GetWorkoutKind(r.Context(), id) + existing, ok, err := s.DB.GetWorkoutKind(r.Context(), userID, id) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return @@ -143,14 +147,14 @@ func (s *Server) handleUpdateWorkoutKind(w http.ResponseWriter, r *http.Request) isActive = *req.IsActive } - if err := s.DB.UpdateWorkoutKind(r.Context(), store.WorkoutKind{ + if err := s.DB.UpdateWorkoutKind(r.Context(), userID, store.WorkoutKind{ ID: id, Name: req.Name, Description: req.Description, Color: req.Color, RuleJSON: string(req.Rule), Priority: req.Priority, IsActive: isActive, }); err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } - if err := s.DB.UpdateWorkoutTypePace(r.Context(), store.WorkoutTypePace{ + if err := s.DB.UpdateWorkoutTypePace(r.Context(), userID, store.WorkoutTypePace{ WorkoutKindID: id, PaceMinSecPerKm: req.PaceMinSecPerKm, PaceMaxSecPerKm: req.PaceMaxSecPerKm, @@ -161,7 +165,7 @@ func (s *Server) handleUpdateWorkoutKind(w http.ResponseWriter, r *http.Request) return } - kind, _, _ := s.DB.GetWorkoutKind(r.Context(), id) + kind, _, _ := s.DB.GetWorkoutKind(r.Context(), userID, id) resp, err := s.toWorkoutKindResponse(r, kind) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) diff --git a/backend/internal/api/profile.go b/backend/internal/api/profile.go index 63df4dc..6b477ba 100644 --- a/backend/internal/api/profile.go +++ b/backend/internal/api/profile.go @@ -36,7 +36,8 @@ func validateProfile(p store.Profile) error { } func (s *Server) handleGetProfile(w http.ResponseWriter, r *http.Request) { - p, err := s.DB.GetProfile(r.Context()) + userID := userIDFromContext(r.Context()) + p, err := s.DB.GetProfile(r.Context(), userID) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return @@ -45,6 +46,7 @@ func (s *Server) handleGetProfile(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleUpdateProfile(w http.ResponseWriter, r *http.Request) { + userID := userIDFromContext(r.Context()) var p store.Profile if err := json.NewDecoder(r.Body).Decode(&p); err != nil { writeError(w, http.StatusBadRequest, "invalid request body") @@ -55,13 +57,18 @@ func (s *Server) handleUpdateProfile(w http.ResponseWriter, r *http.Request) { return } - if err := s.DB.UpdateProfile(r.Context(), p); err != nil { + if err := s.DB.UpdateProfile(r.Context(), userID, p); err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } - s.Garmin.UpdateCredentials(p.GarminEmail, p.GarminPassword) + client, err := s.garminFor(r.Context(), userID) + if err != nil { + writeError(w, http.StatusInternalServerError, err.Error()) + return + } + client.UpdateCredentials(p.GarminEmail, p.GarminPassword) - updated, err := s.DB.GetProfile(r.Context()) + updated, err := s.DB.GetProfile(r.Context(), userID) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return diff --git a/backend/internal/api/progression.go b/backend/internal/api/progression.go index 6ca681d..fe3dbae 100644 --- a/backend/internal/api/progression.go +++ b/backend/internal/api/progression.go @@ -61,6 +61,7 @@ func metricValue(metric string, a store.Activity) (float64, bool) { // handleProgression returns a time series of the requested metric for every // activity currently assigned to a workout kind, for progression charts. func (s *Server) handleProgression(w http.ResponseWriter, r *http.Request) { + userID := userIDFromContext(r.Context()) kindID, err := strconv.ParseInt(chi.URLParam(r, "kindID"), 10, 64) if err != nil { writeError(w, http.StatusBadRequest, "invalid workout kind id") @@ -72,7 +73,7 @@ func (s *Server) handleProgression(w http.ResponseWriter, r *http.Request) { } from, to := r.URL.Query().Get("from"), r.URL.Query().Get("to") - assignments, err := s.DB.AssignmentsForKind(r.Context(), kindID) + assignments, err := s.DB.AssignmentsForKind(r.Context(), userID, kindID) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return @@ -80,7 +81,7 @@ func (s *Server) handleProgression(w http.ResponseWriter, r *http.Request) { points := []progressionPoint{} for _, a := range assignments { - activity, ok, err := s.DB.GetActivity(r.Context(), a.ActivityID) + activity, ok, err := s.DB.GetActivity(r.Context(), userID, a.ActivityID) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return