Sync bug fixes across the board!
All checks were successful
Ascently - Sync Deploy / build-and-push (push) Successful in 2m15s
All checks were successful
Ascently - Sync Deploy / build-and-push (push) Successful in 2m15s
This commit is contained in:
124
sync/main.go
124
sync/main.go
@@ -11,16 +11,11 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
const VERSION = "2.4.0"
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
const VERSION = "2.5.0"
|
||||
|
||||
type ClimbDataBackup struct {
|
||||
ExportedAt string `json:"exportedAt"`
|
||||
@@ -41,11 +36,12 @@ type DeltaSyncRequest struct {
|
||||
}
|
||||
|
||||
type DeltaSyncResponse struct {
|
||||
ServerTime string `json:"serverTime"`
|
||||
Gyms []BackupGym `json:"gyms"`
|
||||
Problems []BackupProblem `json:"problems"`
|
||||
Sessions []BackupClimbSession `json:"sessions"`
|
||||
Attempts []BackupAttempt `json:"attempts"`
|
||||
ServerTime string `json:"serverTime"`
|
||||
RequestFullSync bool `json:"requestFullSync,omitempty"`
|
||||
Gyms []BackupGym `json:"gyms"`
|
||||
Problems []BackupProblem `json:"problems"`
|
||||
Sessions []BackupClimbSession `json:"sessions"`
|
||||
Attempts []BackupAttempt `json:"attempts"`
|
||||
}
|
||||
|
||||
type BackupGym struct {
|
||||
@@ -282,6 +278,81 @@ func (s *SyncServer) mergeAttempts(existing []BackupAttempt, updates []BackupAtt
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *SyncServer) cleanupTombstones(backup *ClimbDataBackup) {
|
||||
cutoffTime := time.Now().UTC().Add(-90 * 24 * time.Hour)
|
||||
log.Printf("Cleaning up tombstones older than %s", cutoffTime.Format(time.RFC3339))
|
||||
|
||||
// Gyms
|
||||
activeGyms := make([]BackupGym, 0, len(backup.Gyms))
|
||||
for _, item := range backup.Gyms {
|
||||
if !item.IsDeleted {
|
||||
activeGyms = append(activeGyms, item)
|
||||
continue
|
||||
}
|
||||
updatedAt, err := time.Parse(time.RFC3339, item.UpdatedAt)
|
||||
if err == nil && updatedAt.After(cutoffTime) {
|
||||
activeGyms = append(activeGyms, item)
|
||||
} else {
|
||||
log.Printf("Pruning deleted gym: %s", item.ID)
|
||||
}
|
||||
}
|
||||
backup.Gyms = activeGyms
|
||||
|
||||
// Problems
|
||||
activeProblems := make([]BackupProblem, 0, len(backup.Problems))
|
||||
for _, item := range backup.Problems {
|
||||
if !item.IsDeleted {
|
||||
activeProblems = append(activeProblems, item)
|
||||
continue
|
||||
}
|
||||
updatedAt, err := time.Parse(time.RFC3339, item.UpdatedAt)
|
||||
if err == nil && updatedAt.After(cutoffTime) {
|
||||
activeProblems = append(activeProblems, item)
|
||||
} else {
|
||||
log.Printf("Pruning deleted problem: %s", item.ID)
|
||||
}
|
||||
}
|
||||
backup.Problems = activeProblems
|
||||
|
||||
// Sessions
|
||||
activeSessions := make([]BackupClimbSession, 0, len(backup.Sessions))
|
||||
for _, item := range backup.Sessions {
|
||||
if !item.IsDeleted {
|
||||
activeSessions = append(activeSessions, item)
|
||||
continue
|
||||
}
|
||||
updatedAt, err := time.Parse(time.RFC3339, item.UpdatedAt)
|
||||
if err == nil && updatedAt.After(cutoffTime) {
|
||||
activeSessions = append(activeSessions, item)
|
||||
} else {
|
||||
log.Printf("Pruning deleted session: %s", item.ID)
|
||||
}
|
||||
}
|
||||
backup.Sessions = activeSessions
|
||||
|
||||
// Attempts
|
||||
activeAttempts := make([]BackupAttempt, 0, len(backup.Attempts))
|
||||
for _, item := range backup.Attempts {
|
||||
if !item.IsDeleted {
|
||||
activeAttempts = append(activeAttempts, item)
|
||||
continue
|
||||
}
|
||||
|
||||
timeStr := item.CreatedAt
|
||||
if item.UpdatedAt != nil {
|
||||
timeStr = *item.UpdatedAt
|
||||
}
|
||||
|
||||
updatedAt, err := time.Parse(time.RFC3339, timeStr)
|
||||
if err == nil && updatedAt.After(cutoffTime) {
|
||||
activeAttempts = append(activeAttempts, item)
|
||||
} else {
|
||||
log.Printf("Pruning deleted attempt: %s", item.ID)
|
||||
}
|
||||
}
|
||||
backup.Attempts = activeAttempts
|
||||
}
|
||||
|
||||
func (s *SyncServer) saveData(backup *ClimbDataBackup) error {
|
||||
backup.ExportedAt = time.Now().UTC().Format(time.RFC3339)
|
||||
|
||||
@@ -339,6 +410,8 @@ func (s *SyncServer) handlePut(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
s.cleanupTombstones(&backup)
|
||||
|
||||
if err := s.saveData(&backup); err != nil {
|
||||
log.Printf("Failed to save data: %v", err)
|
||||
http.Error(w, "Failed to save data", http.StatusInternalServerError)
|
||||
@@ -476,14 +549,33 @@ func (s *SyncServer) handleDeltaSync(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
clientLastSyncCheck, err := time.Parse(time.RFC3339, deltaRequest.LastSyncTime)
|
||||
isServerEmpty := len(serverBackup.Gyms) == 0 && len(serverBackup.Problems) == 0 &&
|
||||
len(serverBackup.Sessions) == 0 && len(serverBackup.Attempts) == 0
|
||||
|
||||
if err == nil && !clientLastSyncCheck.IsZero() && isServerEmpty {
|
||||
log.Printf("Server is empty but client has sync history. Requesting full sync.")
|
||||
response := DeltaSyncResponse{
|
||||
ServerTime: time.Now().UTC().Format(time.RFC3339),
|
||||
RequestFullSync: true,
|
||||
Gyms: []BackupGym{},
|
||||
Problems: []BackupProblem{},
|
||||
Sessions: []BackupClimbSession{},
|
||||
Attempts: []BackupAttempt{},
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(response)
|
||||
return
|
||||
}
|
||||
|
||||
// Merge client changes into server data
|
||||
// Note: We no longer need separate deletion handling as IsDeleted is part of the struct
|
||||
// and handled by standard merge logic (latest timestamp wins)
|
||||
serverBackup.Gyms = s.mergeGyms(serverBackup.Gyms, deltaRequest.Gyms)
|
||||
serverBackup.Problems = s.mergeProblems(serverBackup.Problems, deltaRequest.Problems)
|
||||
serverBackup.Sessions = s.mergeSessions(serverBackup.Sessions, deltaRequest.Sessions)
|
||||
serverBackup.Attempts = s.mergeAttempts(serverBackup.Attempts, deltaRequest.Attempts)
|
||||
|
||||
s.cleanupTombstones(serverBackup)
|
||||
|
||||
// Save merged data
|
||||
if err := s.saveData(serverBackup); err != nil {
|
||||
log.Printf("Failed to save data: %v", err)
|
||||
@@ -565,7 +657,9 @@ func (s *SyncServer) handleSync(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
godotenv.Load()
|
||||
authToken := os.Getenv("AUTH_TOKEN")
|
||||
print(authToken)
|
||||
if authToken == "" {
|
||||
log.Fatal("AUTH_TOKEN environment variable is required")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user