fix getting unread messages, ws endpoint

This commit is contained in:
2026-04-22 14:44:10 +02:00
parent 422c4eb419
commit 5f40a47a02
7 changed files with 21 additions and 11 deletions
Executable
+2
View File
@@ -0,0 +1,2 @@
docker-compose -f docker/minIO/docker-compose.yml up -d
docker-compose -f docker/postgres/docker-compose.yml up -d
BIN
View File
Binary file not shown.
+9
View File
@@ -76,6 +76,7 @@
<button data-form="mod-connection-deelevate" onclick="showForm('mod-connection-deelevate')">POST /connection/deelevate</button> <button data-form="mod-connection-deelevate" onclick="showForm('mod-connection-deelevate')">POST /connection/deelevate</button>
<button data-form="get-user" onclick="showForm('get-user')">GET /user</button> <button data-form="get-user" onclick="showForm('get-user')">GET /user</button>
<button data-form="get-connections" onclick="showForm('get-connections')">GET /connections</button> <button data-form="get-connections" onclick="showForm('get-connections')">GET /connections</button>
<button data-form="get-connections-unread" onclick="showForm('get-connections-unread')">GET /connections/unreadmessages</button>
<button data-form="get-connection-messages" onclick="showForm('get-connection-messages')">GET /connection/messages</button> <button data-form="get-connection-messages" onclick="showForm('get-connection-messages')">GET /connection/messages</button>
<button data-form="file-download" onclick="showForm('file-download')">GET /file</button> <button data-form="file-download" onclick="showForm('file-download')">GET /file</button>
<button data-form="get-user-avatar" onclick="showForm('get-user-avatar')">GET /user/avatar</button> <button data-form="get-user-avatar" onclick="showForm('get-user-avatar')">GET /user/avatar</button>
@@ -168,6 +169,13 @@
<div class="form-actions"><button class="send" onclick="submit('get-connections')">Send</button></div> <div class="form-actions"><button class="send" onclick="submit('get-connections')">Send</button></div>
</div> </div>
<!-- GET /connections/unreadmessages -->
<div class="form-content" id="fc-get-connections-unread">
<div class="field"><label>token</label><input id="gcur-token" placeholder=""></div>
<div class="field"><label>connections</label><input id="gcur-connections" placeholder="UUID,UUID,..."></div>
<div class="form-actions"><button class="send" onclick="submit('get-connections-unread')">Send</button></div>
</div>
<!-- GET /connection/messages --> <!-- GET /connection/messages -->
<div class="form-content" id="fc-get-connection-messages"> <div class="form-content" id="fc-get-connection-messages">
<div class="field"><label>token</label><input id="gcm-token" placeholder=""></div> <div class="field"><label>token</label><input id="gcm-token" placeholder=""></div>
@@ -255,6 +263,7 @@
'mod-connection-deelevate':{ method:'POST', path:'/connection/deelevate', title:'POST /connection/deelevate — de-elevate', fields:[{id:'mcde-token',dest:'header',name:'token'},{id:'mcde-connectionid',dest:'body',name:'connectionid'}] }, 'mod-connection-deelevate':{ method:'POST', path:'/connection/deelevate', title:'POST /connection/deelevate — de-elevate', fields:[{id:'mcde-token',dest:'header',name:'token'},{id:'mcde-connectionid',dest:'body',name:'connectionid'}] },
'get-user': { method:'GET', path:'/user', title:'GET /user — get user info', fields:[{id:'gu-token',dest:'header',name:'token'},{id:'gu-targetid',dest:'query',name:'targetid'}] }, 'get-user': { method:'GET', path:'/user', title:'GET /user — get user info', fields:[{id:'gu-token',dest:'header',name:'token'},{id:'gu-targetid',dest:'query',name:'targetid'}] },
'get-connections': { method:'GET', path:'/connections', title:'GET /connections — get connections', fields:[{id:'gconn-token',dest:'header',name:'token'}] }, 'get-connections': { method:'GET', path:'/connections', title:'GET /connections — get connections', fields:[{id:'gconn-token',dest:'header',name:'token'}] },
'get-connections-unread': { method:'GET', path:'/connections/unreadmessages', title:'GET /connections/unreadmessages — unread counts for given connections (returns []uint32 in same order)', fields:[{id:'gcur-token',dest:'header',name:'token'},{id:'gcur-connections',dest:'query',name:'connections'}] },
'get-connection-messages': { method:'GET', path:'/connection/messages', title:'GET /connection/messages — message history', fields:[{id:'gcm-token',dest:'header',name:'token'},{id:'gcm-connectionid',dest:'query',name:'connectionid'},{id:'gcm-messages',dest:'query',name:'messages'},{id:'gcm-before',dest:'query',name:'before'}] }, 'get-connection-messages': { method:'GET', path:'/connection/messages', title:'GET /connection/messages — message history', fields:[{id:'gcm-token',dest:'header',name:'token'},{id:'gcm-connectionid',dest:'query',name:'connectionid'},{id:'gcm-messages',dest:'query',name:'messages'},{id:'gcm-before',dest:'query',name:'before'}] },
'del-user': { method:'DELETE', path:'/user', title:'DELETE /user — delete own account', fields:[{id:'du-token',dest:'header',name:'token'}] }, 'del-user': { method:'DELETE', path:'/user', title:'DELETE /user — delete own account', fields:[{id:'du-token',dest:'header',name:'token'}] },
'del-connection': { method:'DELETE', path:'/connection', title:'DELETE /connection — delete a connection', fields:[{id:'dc-token',dest:'header',name:'token'},{id:'dc-connectionid',dest:'query',name:'connectionid'}] }, 'del-connection': { method:'DELETE', path:'/connection', title:'DELETE /connection — delete a connection', fields:[{id:'dc-token',dest:'header',name:'token'},{id:'dc-connectionid',dest:'query',name:'connectionid'}] },
+1 -1
View File
@@ -54,7 +54,7 @@ func main() {
http.HandleFunc("GET /file", withCORS(httpRequest.HandleAttachmentFileDownload)) http.HandleFunc("GET /file", withCORS(httpRequest.HandleAttachmentFileDownload))
http.HandleFunc("POST /message", withCORS(httpRequest.HandleDm)) http.HandleFunc("POST /message", withCORS(httpRequest.HandleDm))
http.HandleFunc("/ws", wsServer.ServeWsConnection) http.HandleFunc("GET /ws", wsServer.ServeWsConnection)
log.Println("beep boop; server server started") log.Println("beep boop; server server started")
log.Fatal(http.ListenAndServe(":8080", nil)) log.Fatal(http.ListenAndServe(":8080", nil))
+3 -3
View File
@@ -45,14 +45,14 @@ func StringToUuid(str string) (uuid.UUID, error) {
return uuid.Parse(str) return uuid.Parse(str)
} }
func StringToUuidSlice(uuidStr string) (*[]uuid.UUID, error) { func StringToUuidSlice(uuidStr string) ([]uuid.UUID, error) {
if uuidStr == "" { if uuidStr == "" {
return nil, errors.New("empty string") return nil, errors.New("empty string")
} }
parts := strings.Split(uuidStr, ",") parts := strings.Split(uuidStr, ",")
slice := make([]uuid.UUID, len(parts)) slice := make([]uuid.UUID, 0, len(parts))
for _, part := range parts { for _, part := range parts {
id, err := StringToUuid(part) id, err := StringToUuid(part)
@@ -62,7 +62,7 @@ func StringToUuidSlice(uuidStr string) (*[]uuid.UUID, error) {
slice = append(slice, id) slice = append(slice, id)
} }
return &slice, nil return slice, nil
} }
func StringToTimestamp(str string) (time.Time, error) { func StringToTimestamp(str string) (time.Time, error) {
+5 -6
View File
@@ -74,13 +74,13 @@ func HandleDm(response http.ResponseWriter, request *http.Request) {
Receiver: conn.Id, Receiver: conn.Id,
} }
if user.WsConn != nil { if target.WsConn != nil {
wsServer.WsSendMessageCloseIfTimeout(target, types.WsEventMessage{ wsServer.WsSendMessageCloseIfTimeout(target, types.WsEventMessage{
Type: WsEventType.DirectMessage, Type: WsEventType.DirectMessage,
Event: message, Event: message,
}) })
} else { } else {
cache.IncrementConnectionsUnreadMessages(user.Id, conn.Id) cache.IncrementConnectionsUnreadMessages(target.Id, conn.Id)
} }
err = postgresql.ConnectionMessageSave(ctx, message) err = postgresql.ConnectionMessageSave(ctx, message)
@@ -109,10 +109,9 @@ func HandleUserGetConnectionsUnreadMessages(response http.ResponseWriter, reques
http.Error(response, "invalid uuid format", http.StatusBadRequest) http.Error(response, "invalid uuid format", http.StatusBadRequest)
return return
} }
result := make([]uint32, 0, len(connectionIds))
result := make([]uint32, len(*connectionIds)) for _, connId := range connectionIds {
for _, connId := range *connectionIds {
_, ok := cache.GetConnection(user, connId) _, ok := cache.GetConnection(user, connId)
if !ok { if !ok {
http.Error(response, "no such connection: "+connId.String(), http.StatusUnauthorized) http.Error(response, "no such connection: "+connId.String(), http.StatusUnauthorized)
@@ -120,7 +119,7 @@ func HandleUserGetConnectionsUnreadMessages(response http.ResponseWriter, reques
} }
} }
for _, connId := range *connectionIds { for _, connId := range connectionIds {
count, _ := cache.GetConnectionsUnreadMessages(user.Id, connId) count, _ := cache.GetConnectionsUnreadMessages(user.Id, connId)
cache.DeallocateConnectionsUnreadMessages(user.Id, connId) cache.DeallocateConnectionsUnreadMessages(user.Id, connId)
result = append(result, count) result = append(result, count)
+1 -1
View File
@@ -1,4 +1,4 @@
when user not ws connected collect count of unread messages for each conn when user not ws connected collect count of unread messages for each conn (add db table in future)
add hubs add hubs