Merge backfill into Sync now, replace Full backfill with a destructive Reset all

Sync now now runs Backfill (resumes from the watermark, so a widened history
horizon is picked up automatically) then IncrementalSync then detail-fill, in
one click. "Full backfill" is gone -- in its place, "Reset all" (styled as a
destructive action, gated by a confirm dialog) wipes every synced activity
and its laps/kind assignments and rewinds the backfill watermark, so the next
sync performs a genuinely fresh pull instead of trying to patch up existing
rows with newer schema fields.
This commit is contained in:
2026-07-19 12:41:38 +02:00
parent af41aa0f7f
commit 7f7e4b10f0
10 changed files with 235 additions and 12 deletions

View File

@@ -10,8 +10,17 @@ import (
// internal/sync.Service.FillPendingDetails.
const detailFillBatchSize = 50
// handleSyncRun does a full sync pass: Backfill first (resumes from the
// watermark, so widening the configured history horizon between clicks is
// picked up automatically), then IncrementalSync (catches anything new since
// the latest known activity), then fills in details for whatever's still
// missing them. Activities already fully processed are left untouched --
// see internal/sync.Service.FillPendingDetails.
func (s *Server) handleSyncRun(w http.ResponseWriter, r *http.Request) {
ok := s.backgroundSync(func(ctx context.Context) error {
if err := s.Sync.Backfill(ctx); err != nil {
return err
}
if err := s.Sync.IncrementalSync(ctx); err != nil {
return err
}
@@ -24,12 +33,13 @@ func (s *Server) handleSyncRun(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusAccepted, map[string]string{"status": "started"})
}
func (s *Server) handleSyncBackfill(w http.ResponseWriter, r *http.Request) {
// handleSyncReset wipes every synced activity (and its laps/samples/kind
// assignments) and rewinds the backfill watermark, so the next Sync Now
// performs a genuinely fresh pull from Garmin. Destructive -- the frontend
// gates this behind a confirmation.
func (s *Server) handleSyncReset(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)
return s.Sync.ResetAll(ctx)
})
if !ok {
writeError(w, http.StatusConflict, "a sync is already in progress")