Initial commit: smartrun MVP
Garmin run classification and progression tracker. Go backend (MCP client to mcp-garmin, SQLite store, deterministic rule engine, REST API) and React/TS frontend (Dashboard, Review Queue, Workout Kinds). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
77
backend/internal/api/sync.go
Normal file
77
backend/internal/api/sync.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// detailFillBatchSize bounds how many activities' details/splits are fetched
|
||||
// per sync trigger, matching the sequential rate-limited fetch in
|
||||
// internal/sync.Service.FillPendingDetails.
|
||||
const detailFillBatchSize = 50
|
||||
|
||||
func (s *Server) handleSyncRun(w http.ResponseWriter, r *http.Request) {
|
||||
ok := s.backgroundSync(func(ctx context.Context) error {
|
||||
if err := s.Sync.IncrementalSync(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.Sync.FillPendingDetails(ctx, detailFillBatchSize)
|
||||
})
|
||||
if !ok {
|
||||
writeError(w, http.StatusConflict, "a sync is already in progress")
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusAccepted, map[string]string{"status": "started"})
|
||||
}
|
||||
|
||||
func (s *Server) handleSyncBackfill(w http.ResponseWriter, r *http.Request) {
|
||||
ok := s.backgroundSync(func(ctx context.Context) error {
|
||||
if err := s.Sync.Backfill(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.Sync.FillPendingDetails(ctx, detailFillBatchSize)
|
||||
})
|
||||
if !ok {
|
||||
writeError(w, http.StatusConflict, "a sync is already in progress")
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusAccepted, map[string]string{"status": "started"})
|
||||
}
|
||||
|
||||
func (s *Server) handleSyncRuns(w http.ResponseWriter, r *http.Request) {
|
||||
runs, err := s.DB.ListSyncRuns(r.Context(), 20)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, runs)
|
||||
}
|
||||
|
||||
func (s *Server) handleSyncStatus(w http.ResponseWriter, r *http.Request) {
|
||||
run, ok, err := s.DB.LatestSyncRun(r.Context())
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
remaining, err := s.DB.CountActivitiesMissingDetails(r.Context())
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
inProgress := s.syncRunning
|
||||
s.mu.Unlock()
|
||||
|
||||
progress := s.Sync.Progress()
|
||||
|
||||
resp := map[string]any{
|
||||
"in_progress": inProgress,
|
||||
"detail_fill_progress": progress,
|
||||
"activities_pending_details": remaining,
|
||||
}
|
||||
if ok {
|
||||
resp["last_run"] = run
|
||||
}
|
||||
writeJSON(w, http.StatusOK, resp)
|
||||
}
|
||||
Reference in New Issue
Block a user