From 9d279c96804b46a359fc4adbf2c0b5ac9734b2ae Mon Sep 17 00:00:00 2001 From: Sisi Date: Sun, 5 Apr 2026 12:55:36 +0200 Subject: [PATCH] fix changes from debug, edit groups return, add new connections func --- buglist.md | 35 ----------------------------------- http.go | 35 +++++++++++++++++++++-------------- main.go | 4 ++++ structs.go | 34 ++++++++++++---------------------- wsServer.go | 15 +++++++++++---- 5 files changed, 48 insertions(+), 75 deletions(-) delete mode 100644 buglist.md diff --git a/buglist.md b/buglist.md deleted file mode 100644 index 3555029..0000000 --- a/buglist.md +++ /dev/null @@ -1,35 +0,0 @@ -# Bug List - -## Open - -### Bug 1 — `WsSendToUser`: sends message to sender, not recipient -**File:** `wsServer.go:86` -`to *User` parameter is never used — message is sent back to `from` instead. -```go -sendMessageCloseIfTimeout(from, &msg) // should be `to` -``` - -### Bug 2 — `ServeWsConnection`: `ignoreCache` captured by value in defer -**File:** `wsServer.go:30` -`ignoreCache` is `false` at defer registration; setting it to `true` later has no effect. -The user is always evicted from cache on disconnect. -```go -defer closeConnection(&user, ignoreCache) // wrong -defer func() { closeConnection(&user, ignoreCache) }() // correct -``` - -### Bug 3 — Deadlock in `sendToAllMessageCloseIfTimeout` -**File:** `wsServer.go:72`, `cache.go:52` -`sendToAllMessageCloseIfTimeout` holds `mu.RLock()`, calls `sendMessageCloseIfTimeout`, -which on timeout calls `closeConnection` → `CacheDeleteUser` → `mu.Lock()`. -Upgrading RLock to write Lock deadlocks. - -### Bug 4 — `closeConnection`: nil `WsConn` not guarded -**File:** `wsServer.go:179` -`user.WsConn.CloseNow()` panics if `WsConn` is nil (e.g. unauthenticated user). -`sendMessageCloseIfTimeout` guards against this but `closeConnection` does not. - -### Bug 5 — `HttpHandleUserAcceptConnection`: potential nil dereference -**File:** `http.go:410` -`target.Connections[user.Id].IsAccepted = true` panics if the entry is missing -(DB inconsistency). The sender side is guarded but the recipient side is not. diff --git a/http.go b/http.go index 2a60684..965e8fa 100644 --- a/http.go +++ b/http.go @@ -381,7 +381,7 @@ func HttpHandleUserAcceptConnection(response http.ResponseWriter, request *http. } targetId, err := ConvertStringUint32(request.FormValue("connectedid")) - if err != nil { + if err != nil || user.Connections[targetId] == nil { http.Error(response, "invalid recipient id", http.StatusBadRequest) return } @@ -400,7 +400,7 @@ func HttpHandleUserAcceptConnection(response http.ResponseWriter, request *http. return } } - if user.Connections[targetId] == nil { + if target.Connections[user.Id] == nil { http.Error(response, "invalid recipient id", http.StatusBadRequest) return } @@ -420,6 +420,23 @@ func HttpHandleUserAcceptConnection(response http.ResponseWriter, request *http. response.WriteHeader(http.StatusAccepted) } +func HttpHandleUserGetConnections(response http.ResponseWriter, request *http.Request) { + ctx := request.Context() + user, err := getUser(ctx, request.FormValue("token")) + if err != nil { + http.Error(response, "invalid token", http.StatusUnauthorized) + return + } + + json, err := json2.Marshal(user.Connections) + if err != nil { + http.Error(response, "internal server error", http.StatusInternalServerError) + return + } + response.WriteHeader(http.StatusAccepted) + response.Write(json) +} + func HttpHandleTokenNew(response http.ResponseWriter, request *http.Request) { if !isMethodAllowed(&response, request) { return @@ -777,23 +794,13 @@ func HttpHandleGroupsGetWithoutMembers(response http.ResponseWriter, request *ht return } - groups := make([]GroupNoMembers, 0, len(user.Groups)) - + groups := make(map[uint32]*Group, len(user.Groups)) for groupId := range user.Groups { group, err := getGroup(ctx, groupId) if err != nil { continue } - - groups = append(groups, GroupNoMembers{ - Id: groupId, - Name: group.Name, - CreatedAt: group.CreatedAt, - CreatorId: group.CreatorId, - OwnerId: group.OwnerId, - Color: group.Color, - EnableUsersColors: group.EnableUserColors, - }) + groups[groupId] = group } json, err := json2.Marshal(groups) diff --git a/main.go b/main.go index 486ed83..a7c46e0 100644 --- a/main.go +++ b/main.go @@ -18,17 +18,21 @@ func main() { DbInit(ctx) http.HandleFunc("/new/user", withCORS(HttpHandleUserNew)) + http.HandleFunc("/new/connection", withCORS(HttpHandleUserNewConnection)) http.HandleFunc("/new/token", withCORS(HttpHandleTokenNew)) http.HandleFunc("/new/group", withCORS(HttpHandeGroupCreate)) http.HandleFunc("/mod/user/appearence", withCORS(HttpHandleUserModifyAppearance)) http.HandleFunc("/mod/user/about", withCORS(HttpHandleUserModifyAbout)) + http.HandleFunc("/mod/connection/accept", withCORS(HttpHandleUserAcceptConnection)) + http.HandleFunc("/mod/connection/delete", withCORS(HttpHandleUserDeleteConnection)) http.HandleFunc("/mod/group/addusers", withCORS(HttpHandleGroupAddUser)) http.HandleFunc("/mod/group/removeusers", withCORS(HttpHandleGroupRemoveUser)) http.HandleFunc("/mod/group/color", withCORS(HttpHandleGroupChangeColor)) http.HandleFunc("/mod/group/owner", withCORS(HttpHandleGroupChangeOwner)) http.HandleFunc("/get/groups", withCORS(HttpHandleGroupsGetWithoutMembers)) + http.HandleFunc("/get/connections", withCORS(HttpHandleUserGetConnections)) http.HandleFunc("/get/group/members", withCORS(HttpHandleGroupMembersGet)) http.HandleFunc("/del/group", withCORS(HttpHandleGroupDelete)) diff --git a/structs.go b/structs.go index b68b887..1ebea9c 100644 --- a/structs.go +++ b/structs.go @@ -19,29 +19,19 @@ type User struct { } type Connection struct { - CreatedAt time.Time - With uint32 - IsFromUser bool - IsAccepted bool + CreatedAt time.Time `json:"createdAt"` + With uint32 `json:"-"` + IsFromUser bool `json:"isFromUser"` + IsAccepted bool `json:"isAccepted"` } type Group struct { - Name string - CreatedAt time.Time - Id uint32 - CreatorId uint32 - OwnerId uint32 - Users map[uint32]struct{} - Color [3]uint8 - EnableUserColors bool -} - -type GroupNoMembers struct { - Name string `json:"name"` - CreatedAt time.Time `json:"createdAt"` - Id uint32 `json:"id"` - CreatorId uint32 `json:"creatorId"` - OwnerId uint32 `json:"ownerId"` - Color [3]uint8 `json:"color"` - EnableUsersColors bool `json:"enableUsersColors"` + Name string `json:"name"` + CreatedAt time.Time `json:"createdAt"` + Id uint32 `json:"-"` + CreatorId uint32 `json:"creatorId"` + OwnerId uint32 `json:"ownerId"` + Users map[uint32]struct{} `json:"-"` + Color [3]uint8 `json:"color"` + EnableUserColors bool `json:"enableUserColors"` } diff --git a/wsServer.go b/wsServer.go index fe03862..7b734b2 100644 --- a/wsServer.go +++ b/wsServer.go @@ -27,7 +27,7 @@ func ServeWsConnection(responseWriter http.ResponseWriter, request *http.Request var isAuthenticated bool var ignoreCache bool - defer closeConnection(&user, ignoreCache) + defer func() { closeConnection(&user, ignoreCache) }() for { var userMessage map[string]any err = wsjson.Read(ctx, connection, &userMessage) @@ -71,8 +71,13 @@ func sendMessageCloseIfTimeout(user *User, message *map[string]any) { func sendToAllMessageCloseIfTimeout(message *map[string]any) { mu.RLock() - defer mu.RUnlock() + users := make([]*User, 0, len(CacheUsers)) for _, user := range CacheUsers { + users = append(users, user) + } + mu.RUnlock() + + for _, user := range users { sendMessageCloseIfTimeout(user, message) } } @@ -83,7 +88,7 @@ func WsSendToUser(from *User, to *User, message string) { "from": from.Id, "content": message, } - sendMessageCloseIfTimeout(from, &msg) + sendMessageCloseIfTimeout(to, &msg) } func WsSendToGroup(group *Group, sender *User, message string) error { @@ -176,5 +181,7 @@ func closeConnection(user *User, ignoreCache bool) { if !ignoreCache { CacheDeleteUser(user.Id) } - user.WsConn.CloseNow() + if user.WsConn != nil { + user.WsConn.CloseNow() + } }