Fixed sync logic + code cleanup
All checks were successful
build and deploy kleinTodo / build (push) Successful in 17s

This commit is contained in:
Darius klein 2026-01-18 14:27:16 +01:00
parent deb43b98aa
commit 58bde59342
4 changed files with 49 additions and 28 deletions

View File

@ -80,6 +80,10 @@ func syncAction(context context.Context, c *cli.Command) error {
response.SyncedTodos = append(response.SyncedTodos, todo.ServerTodo) response.SyncedTodos = append(response.SyncedTodos, todo.ServerTodo)
} else { } else {
response.SyncedTodos = append(response.SyncedTodos, todo.LocalTodo) response.SyncedTodos = append(response.SyncedTodos, todo.LocalTodo)
if common.AskUserBool("Do you wish to push this to the server?") {
println("TODO not yet implemented")
// TODO push to server
}
} }
} }
} }

View File

@ -108,7 +108,8 @@ func (todo Todo) PrintIndexed(index int) {
} }
func (t Todo) IsEqual(other Todo) bool { func (t Todo) IsEqual(other Todo) bool {
return t.Name == other.Name && return t.Id == other.Id &&
t.Name == other.Name &&
t.Description == other.Description && t.Description == other.Description &&
t.Status == other.Status && t.Status == other.Status &&
t.Owner == other.Owner && t.Owner == other.Owner &&
@ -116,7 +117,8 @@ func (t Todo) IsEqual(other Todo) bool {
} }
func (t Todo) IsEqualIgnoringStatus(other Todo) bool { func (t Todo) IsEqualIgnoringStatus(other Todo) bool {
return t.Name == other.Name && return t.Id == other.Id &&
t.Name == other.Name &&
t.Description == other.Description && t.Description == other.Description &&
t.Owner == other.Owner && t.Owner == other.Owner &&
t.Deleted == other.Deleted t.Deleted == other.Deleted

View File

@ -21,6 +21,7 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
if handleError(w, http.StatusInternalServerError, err) { if handleError(w, http.StatusInternalServerError, err) {
return return
} }
// Check password
password := db.GetFromBucketByKey(common.UserBucket, user.Username) password := db.GetFromBucketByKey(common.UserBucket, user.Username)
if user.ComparePasswords(password) { if user.ComparePasswords(password) {
w.Header().Set(common.AuthHeader, jwt.CreateUserJWT(user.Username)) w.Header().Set(common.AuthHeader, jwt.CreateUserJWT(user.Username))

View File

@ -10,14 +10,14 @@ import (
) )
func SyncHandler(w http.ResponseWriter, r *http.Request) { func SyncHandler(w http.ResponseWriter, r *http.Request) {
// 1. Setup: Auth, Decode, Database Connection
user, err := jwt.GetVerifiedUser(r) user, err := jwt.GetVerifiedUser(r)
if handleError(w, http.StatusUnauthorized, err) { if handleError(w, http.StatusUnauthorized, err) {
return return
} }
var todoList common.TodoList var todoList common.TodoList
err = json.NewDecoder(r.Body).Decode(&todoList) if handleError(w, http.StatusBadRequest, json.NewDecoder(r.Body).Decode(&todoList)) {
if handleError(w, http.StatusBadRequest, err) {
return return
} }
@ -26,62 +26,76 @@ func SyncHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
// 2. Load Server State
serverTodos := store.GetTodoMap(user) serverTodos := store.GetTodoMap(user)
response := common.SyncResponse{
var response = common.SyncResponse{
SyncedTodos: []common.Todo{}, SyncedTodos: []common.Todo{},
MisMatchingTodos: []common.MisMatchingTodo{}, MisMatchingTodos: []common.MisMatchingTodo{},
} }
// 3. Process Client Updates
for _, clientTodo := range todoList.Todos { for _, clientTodo := range todoList.Todos {
// Default to "Now"
if clientTodo.LastModified.IsZero() { if clientTodo.LastModified.IsZero() {
clientTodo.LastModified = time.Now().UTC() clientTodo.LastModified = time.Now().UTC()
} }
serverTodo, exists := serverTodos[clientTodo.Id] serverTodo, exists := serverTodos[clientTodo.Id]
// New Item (Does not exist on server)
if !exists { if !exists {
if clientTodo.Deleted { if clientTodo.Deleted {
continue continue // Ignore deleted items that don't exist
} }
err = clientTodo.Store(store, user)
if handleError(w, http.StatusInternalServerError, err) { if handleError(w, http.StatusInternalServerError, clientTodo.Store(store, user)) {
return return
} }
// Add to map so it gets returned in the "Synced" list
serverTodos[clientTodo.Id] = clientTodo serverTodos[clientTodo.Id] = clientTodo
continue continue
} }
// Item is deleted (Client wants to delete)
if clientTodo.Deleted { if clientTodo.Deleted {
// Only delete if client is newer than server
if clientTodo.LastModified.After(serverTodo.LastModified) { if clientTodo.LastModified.After(serverTodo.LastModified) {
err = store.RemoveValueFromBucket(user, clientTodo.Id) if handleError(w, http.StatusInternalServerError, store.RemoveValueFromBucket(user, clientTodo.Id)) {
if handleError(w, http.StatusInternalServerError, err) {
return return
} }
// Remove from map so it is NOT returned in the "Synced" list
delete(serverTodos, clientTodo.Id) delete(serverTodos, clientTodo.Id)
} }
} else if !serverTodo.IsEqual(clientTodo) { continue
if serverTodo.IsEqualIgnoringStatus(clientTodo) && clientTodo.LastModified.After(serverTodo.LastModified) { }
err = clientTodo.Store(store, user)
if handleError(w, http.StatusInternalServerError, err) { // Item is updated (Item exists and is not deleted) ---
return if serverTodo.IsEqual(clientTodo) {
} continue
serverTodos[clientTodo.Id] = clientTodo }
} else {
response.MisMatchingTodos = append(response.MisMatchingTodos, common.MisMatchingTodo{ isMergeable := serverTodo.IsEqualIgnoringStatus(clientTodo) && clientTodo.LastModified.After(serverTodo.LastModified)
ServerTodo: serverTodo,
LocalTodo: clientTodo, if isMergeable {
}) if handleError(w, http.StatusInternalServerError, clientTodo.Store(store, user)) {
delete(serverTodos, clientTodo.Id) return
} }
serverTodos[clientTodo.Id] = clientTodo
} else {
response.MisMatchingTodos = append(response.MisMatchingTodos, common.MisMatchingTodo{
ServerTodo: serverTodo,
LocalTodo: clientTodo,
})
delete(serverTodos, clientTodo.Id)
} }
} }
// Build Final Response
for _, todo := range serverTodos { for _, todo := range serverTodos {
response.SyncedTodos = append(response.SyncedTodos, todo) response.SyncedTodos = append(response.SyncedTodos, todo)
} }
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(response) handleError(w, http.StatusInternalServerError, json.NewEncoder(w).Encode(response))
if handleError(w, http.StatusInternalServerError, err) {
return
}
} }