Fix: Garmin Connect authentication and profile fetching (profile URL and GarminTime parsing)

This commit is contained in:
2025-09-18 13:31:49 -07:00
parent 2fdfbea34e
commit 48bf00150b
3 changed files with 42 additions and 4 deletions

0
GEMINI.md Normal file
View File

View File

@@ -83,7 +83,7 @@ func (c *Client) Login(email, password string) error {
}
}
ssoClient := sso.NewClient(host)
ssoClient := sso.NewClient(c.Domain)
oauth2Token, mfaContext, err := ssoClient.Login(email, password)
if err != nil {
return &errors.AuthenticationError{
@@ -145,7 +145,7 @@ func (c *Client) GetUserProfile() (*types.UserProfile, error) {
if strings.HasPrefix(c.Domain, "127.0.0.1") {
scheme = "http"
}
profileURL := fmt.Sprintf("%s://%s/userprofile-service/socialProfile", scheme, c.Domain)
profileURL := fmt.Sprintf("%s://connectapi.%s/userprofile-service/socialProfile", scheme, c.Domain)
req, err := http.NewRequest("GET", profileURL, nil)
if err != nil {
@@ -358,7 +358,12 @@ func (c *Client) GetActivities(limit int) ([]types.Activity, error) {
limit = 10
}
activitiesURL := fmt.Sprintf("https://connectapi.%s/activitylist-service/activities/search/activities?limit=%d&start=0", c.Domain, limit)
scheme := "https"
if strings.HasPrefix(c.Domain, "127.0.0.1") {
scheme = "http"
}
activitiesURL := fmt.Sprintf("%s://connectapi.%s/activitylist-service/activities/search/activities?limit=%d&start=0", scheme, c.Domain, limit)
req, err := http.NewRequest("GET", activitiesURL, nil)
if err != nil {

View File

@@ -1,12 +1,45 @@
package types
import "time"
import (
"fmt"
"strings"
"time"
)
// GarminTime represents Garmin's timestamp format with custom JSON parsing
type GarminTime struct {
time.Time
}
// UnmarshalJSON implements the json.Unmarshaler interface.
// It parses Garmin's specific timestamp format.
func (gt *GarminTime) UnmarshalJSON(b []byte) (err error) {
s := strings.Trim(string(b), `"`)
if s == "null" {
return nil
}
// Try parsing with milliseconds (e.g., "2018-09-01T00:13:25.000")
// Garmin sometimes returns .0 for milliseconds, which Go's time.Parse handles as .000
// The 'Z' in the layout indicates a UTC time without a specific offset, which is often how these are interpreted.
// If the input string does not contain 'Z', it will be parsed as local time.
// For consistency, we'll assume UTC if no timezone is specified.
layouts := []string{
"2006-01-02T15:04:05.0", // Example: 2018-09-01T00:13:25.0
"2006-01-02T15:04:05", // Example: 2018-09-01T00:13:25
"2006-01-02", // Example: 2018-09-01
}
for _, layout := range layouts {
if t, err := time.Parse(layout, s); err == nil {
gt.Time = t
return nil
}
}
return fmt.Errorf("cannot parse %q into a GarminTime", s)
}
// SessionData represents saved session information
type SessionData struct {
Domain string `json:"domain"`