Files
pedestrian-simulator/server/user.go
Steven Polley 72b94597ca
All checks were successful
pedestrian-simulator / build (push) Successful in 1m17s
user profiles
2026-01-13 13:10:55 -07:00

106 lines
3.0 KiB
Go

package main
import (
"database/sql"
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
type User struct {
FitbitUserID string `json:"fitbit_user_id"`
DisplayName string `json:"display_name"`
AvatarURL string `json:"avatar_url"`
CreatedAt time.Time `json:"created_at"`
}
// GetUser retrieves a user by Fitbit user ID from the database
func GetUser(fitbitUserID string) (*User, error) {
var user User
err := db.QueryRow("SELECT fitbit_user_id, display_name, avatar_url, created_at FROM users WHERE fitbit_user_id = ?", fitbitUserID).
Scan(&user.FitbitUserID, &user.DisplayName, &user.AvatarURL, &user.CreatedAt)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, err
}
return &user, nil
}
// GetPublicUser retrieves public user info by Fitbit user ID
func GetPublicUser(fitbitUserID string) (*User, error) {
var user User
err := db.QueryRow("SELECT fitbit_user_id, display_name, avatar_url, created_at FROM users WHERE fitbit_user_id = ?", fitbitUserID).
Scan(&user.FitbitUserID, &user.DisplayName, &user.AvatarURL, &user.CreatedAt)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, err
}
// Ensure we don't return sensitive info if struct expands later, though current struct is safe for public
return &user, nil
}
// CreateOrUpdateUser adds or updates a user in the database
func CreateOrUpdateUser(fitbitUserID, displayName, avatarURL string) (*User, error) {
_, err := db.Exec(`
INSERT INTO users (fitbit_user_id, display_name, avatar_url, created_at)
VALUES (?, ?, ?, NOW())
ON DUPLICATE KEY UPDATE display_name = ?, avatar_url = ?
`, fitbitUserID, displayName, avatarURL, displayName, avatarURL)
if err != nil {
return nil, err
}
// Create user directory for KML files
userDir := fmt.Sprintf("data/users/%s/kml", fitbitUserID)
if err := os.MkdirAll(userDir, 0755); err != nil {
return nil, err
}
return GetUser(fitbitUserID)
}
// FetchFitbitUserProfile fetches the user's profile from Fitbit API
func FetchFitbitUserProfile(accessToken string) (userID, displayName, avatarURL string, err error) {
apiURL := "https://api.fitbit.com/1/user/-/profile.json"
req, _ := http.NewRequest("GET", apiURL, nil)
req.Header.Set("Authorization", "Bearer "+accessToken)
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return "", "", "", err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return "", "", "", fmt.Errorf("fitbit profile api error: %s", resp.Status)
}
var result struct {
User struct {
EncodedID string `json:"encodedId"`
DisplayName string `json:"displayName"`
Avatar string `json:"avatar"`
Avatar150 string `json:"avatar150"`
} `json:"user"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return "", "", "", err
}
avatar := result.User.Avatar150
if avatar == "" {
avatar = result.User.Avatar
}
return result.User.EncodedID, result.User.DisplayName, avatar, nil
}