package handler import ( "encoding/json" "fmt" "net/http" "time" "gitea.kleinsense.nl/DariusKlein/kleinTodo/common" "gitea.kleinsense.nl/DariusKlein/kleinTodo/common/jwt" ) func SyncHandler(w http.ResponseWriter, r *http.Request) { fmt.Println("SyncHandler: starting request processing...") // 1. Setup: Auth, Decode, Database Connection user, err := jwt.GetVerifiedUser(r) if handleError(w, http.StatusUnauthorized, err) { return } fmt.Printf("SyncHandler: authenticated user %s\n", user) var todoList common.TodoList if handleError(w, http.StatusBadRequest, json.NewDecoder(r.Body).Decode(&todoList)) { return } fmt.Printf("SyncHandler: received %d todos from client\n", len(todoList.Todos)) store, err := common.GetServerDataStore() if handleError(w, http.StatusInternalServerError, err) { return } fmt.Println("SyncHandler: connected to datastore") // 2. Load Server State serverTodos := store.GetTodoMap(user) fmt.Printf("SyncHandler: loaded %d server todos\n", len(serverTodos)) response := common.SyncResponse{ SyncedTodos: []common.Todo{}, MisMatchingTodos: []common.MisMatchingTodo{}, } fmt.Println("SyncHandler: processing client todos...") // 3. Process Client Updates for _, clientTodo := range todoList.Todos { // Default to "Now" if clientTodo.LastModified.IsZero() { clientTodo.LastModified = time.Now().UTC() } serverTodo, exists := serverTodos[clientTodo.Id] // New Item (Does not exist on server) if !exists { if clientTodo.Deleted { continue // Ignore deleted items that don't exist } if handleError(w, http.StatusInternalServerError, (&clientTodo).Store(store, user)) { return } // Add to map so it gets returned in the "Synced" list serverTodos[clientTodo.Id] = clientTodo continue } // Item is deleted (Client wants to delete) if clientTodo.Deleted { // Only delete if client is newer than server if clientTodo.LastModified.After(serverTodo.LastModified) { if handleError(w, http.StatusInternalServerError, store.RemoveValueFromBucket(user, clientTodo.Id)) { return } // Remove from map so it is NOT returned in the "Synced" list delete(serverTodos, clientTodo.Id) } continue } // Item is updated (Item exists and is not deleted) --- if serverTodo.IsEqual(clientTodo) { continue } isMergeable := serverTodo.IsEqualIgnoringStatus(clientTodo) && clientTodo.LastModified.After(serverTodo.LastModified) if isMergeable { if handleError(w, http.StatusInternalServerError, (&clientTodo).Store(store, user)) { return } serverTodos[clientTodo.Id] = clientTodo } else { response.MisMatchingTodos = append(response.MisMatchingTodos, common.MisMatchingTodo{ ServerTodo: serverTodo, LocalTodo: clientTodo, }) delete(serverTodos, clientTodo.Id) } } fmt.Println("SyncHandler: building final response...") // Build Final Response for _, todo := range serverTodos { response.SyncedTodos = append(response.SyncedTodos, todo) } fmt.Printf("SyncHandler: returning %d synced todos and %d mismatches\n", len(response.SyncedTodos), len(response.MisMatchingTodos)) w.Header().Set("Content-Type", "application/json") handleError(w, http.StatusInternalServerError, json.NewEncoder(w).Encode(response)) }