Files
rideaware-api/internal/integration/intervals_handler.go
2026-05-17 20:39:47 -05:00

168 lines
5.4 KiB
Go

package integration
import (
"encoding/json"
"log"
"net/http"
"strconv"
"rideaware/internal/config"
"rideaware/internal/middleware"
)
type IntervalsHandler struct {
client *IntervalsClient
oauthService *OAuthService
}
func NewIntervalsHandler() *IntervalsHandler {
return &IntervalsHandler{
client: NewIntervalsClient(),
oauthService: NewOAuthService(),
}
}
// SaveApiKey POST /api/protected/intervals/connect
func (h *IntervalsHandler) SaveApiKey(w http.ResponseWriter, r *http.Request) {
claims := r.Context().Value(middleware.UserContextKey).(*config.CustomClaims)
if claims == nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(map[string]string{"error": "unauthorized"})
return
}
var req struct {
APIKey string `json:"api_key"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.APIKey == "" {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{"error": "api_key is required"})
return
}
if err := h.client.SaveApiKey(claims.UserID, req.APIKey); err != nil {
log.Printf("Intervals.icu save key error: %v", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{"error": "failed to save API key"})
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"message": "Intervals.icu connected"})
}
// ConnectionStatus GET /api/protected/intervals/status
func (h *IntervalsHandler) ConnectionStatus(w http.ResponseWriter, r *http.Request) {
claims := r.Context().Value(middleware.UserContextKey).(*config.CustomClaims)
if claims == nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(map[string]string{"error": "unauthorized"})
return
}
status, err := h.oauthService.GetConnectionStatus(claims.UserID, intervalsProvider)
if err != nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(status)
}
// Disconnect DELETE /api/protected/intervals/disconnect
func (h *IntervalsHandler) Disconnect(w http.ResponseWriter, r *http.Request) {
claims := r.Context().Value(middleware.UserContextKey).(*config.CustomClaims)
if claims == nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(map[string]string{"error": "unauthorized"})
return
}
if err := h.oauthService.Disconnect(claims.UserID, intervalsProvider); err != nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"message": "Intervals.icu disconnected"})
}
// PushWorkout POST /api/protected/workouts/push/intervals?id=X
func (h *IntervalsHandler) PushWorkout(w http.ResponseWriter, r *http.Request) {
claims := r.Context().Value(middleware.UserContextKey).(*config.CustomClaims)
if claims == nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(map[string]string{"error": "unauthorized"})
return
}
idStr := r.URL.Query().Get("id")
if idStr == "" {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{"error": "workout id is required"})
return
}
id, err := strconv.ParseUint(idStr, 10, 64)
if err != nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{"error": "invalid workout id"})
return
}
if err := h.client.PushWorkout(uint(id), claims.UserID); err != nil {
log.Printf("Intervals.icu push error: %v", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"message": "workout pushed to Intervals.icu"})
}
// SyncActivities POST /api/protected/intervals/sync?days=30
func (h *IntervalsHandler) SyncActivities(w http.ResponseWriter, r *http.Request) {
claims := r.Context().Value(middleware.UserContextKey).(*config.CustomClaims)
if claims == nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(map[string]string{"error": "unauthorized"})
return
}
days := 30
if dStr := r.URL.Query().Get("days"); dStr != "" {
if d, err := strconv.Atoi(dStr); err == nil && d > 0 {
days = d
}
}
imported, err := h.client.SyncActivities(claims.UserID, days)
if err != nil {
log.Printf("Intervals.icu sync error: %v", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]int{"imported": imported})
}