rework ws responses to user, send events to user via ws

This commit is contained in:
2026-04-12 13:18:54 +02:00
parent 9076a5c5c8
commit 63b585313a
9 changed files with 149 additions and 103 deletions
+11
View File
@@ -0,0 +1,11 @@
package WsEventType
type WsEventType uint8
const (
Authentication WsEventType = iota
DirectMessage
ConnectionCreated
ConnectionDeleted
ConnectionElevated
)
-9
View File
@@ -1,9 +0,0 @@
package WsMessageFrom
type WsMessageToUserFrom uint8
const (
Server WsMessageToUserFrom = iota
DirectMessage
Group
)
BIN
View File
Binary file not shown.
+60 -23
View File
@@ -8,6 +8,7 @@ import (
"time"
"go-socket/Enums/ConnectionState"
"go-socket/Enums/WsEventType"
"github.com/google/uuid"
)
@@ -64,7 +65,10 @@ func HttpHandleDm(response http.ResponseWriter, request *http.Request) {
Receiver: conn.Id,
}
WsMessageSendToUser(target, message)
WsSendMessageCloseIfTimeout(target, WsEventMessage{
Type: WsEventType.DirectMessage,
Event: message,
})
err = DbMessageSave(ctx, message)
if err != nil {
@@ -201,6 +205,11 @@ func HttpHandleUserNewConnection(response http.ResponseWriter, request *http.Req
}
CacheAddConnection(requestor, recipient, connection)
WsSendMessageCloseIfTimeout(recipient, WsEventMessage{
Type: WsEventType.ConnectionCreated,
Event: connection,
})
response.WriteHeader(http.StatusCreated)
return
}
@@ -256,6 +265,10 @@ func HttpHandleUserDeleteConnection(response http.ResponseWriter, request *http.
}
CacheDeleteConnection(user, user2, connectionId)
WsSendMessageCloseIfTimeout(user2, WsEventMessage{
Type: WsEventType.ConnectionDeleted,
Event: connectionId,
})
response.WriteHeader(http.StatusAccepted)
}
@@ -281,29 +294,53 @@ func HttpHandleUserElevateConnection(response http.ResponseWriter, request *http
return
}
if conn.RecipientId != user.Id {
http.Error(response, "invalid connectionid", http.StatusBadRequest)
return
}
switch conn.State {
case ConnectionState.Stranger:
conn.State = ConnectionState.Friend
break
case ConnectionState.GroupFellow:
conn.State = ConnectionState.Stranger
break
default:
http.Error(response, "cannot elevate further", http.StatusBadRequest)
return
}
err = DbConnectionUpdateState(ctx, conn)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusAccepted)
// TODO change to != "" after user id via uuid
if conn.UserWantingToElevate != 0 && conn.UserWantingToElevate != user.Id {
switch conn.State {
case ConnectionState.Stranger:
conn.State = ConnectionState.Friend
break
case ConnectionState.GroupFellow:
conn.State = ConnectionState.Stranger
break
default:
http.Error(response, "cannot elevate further", http.StatusBadRequest)
return
}
err = DbConnectionUpdateState(ctx, conn)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
response.Write([]byte("elevated"))
var user2 *User
if conn.RequestorId == user.Id {
user2, err = GetUserById(ctx, conn.RecipientId)
} else {
user2, err = GetUserById(ctx, conn.RequestorId)
}
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
WsSendMessageCloseIfTimeout(user2, WsEventMessage{
Type: WsEventType.ConnectionElevated,
Event: ConnectionElevationData{
Id: connectionId,
NewState: conn.State,
},
})
return
}
conn.UserWantingToElevate = user.Id
response.Write([]byte("waiting for second user to elevate"))
}
func HttpHandleUserGetConnections(response http.ResponseWriter, request *http.Request) {
+1 -1
View File
@@ -338,7 +338,7 @@ func HttpHandleGroupMessage(response http.ResponseWriter, request *http.Request)
return
}
err = WsSendToGroup(group, user, content)
err = WsSendToGroupAsUser(group, user, content)
if err != nil {
http.Error(response, err.Error(), http.StatusBadRequest)
return
+2 -2
View File
@@ -53,13 +53,13 @@ func HttpHandleUserNewToken(response http.ResponseWriter, request *http.Request)
token, err := TokenCreate(user.Id)
if err != nil {
http.Error(response, "internal server error2", http.StatusInternalServerError)
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
json, err := json2.Marshal(LoginReturn{Token: token, UserId: user.Id})
if err != nil {
http.Error(response, "internal server error3", http.StatusInternalServerError)
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
+8 -9
View File
@@ -73,7 +73,6 @@
<button onclick="showForm('mod-user-appearance')">POST /mod/user/appearence</button>
<button onclick="showForm('mod-user-about')">POST /mod/user/about</button>
<button onclick="showForm('accept-connection')">POST /mod/connection/accept</button>
<button onclick="showForm('delete-connection')" class="warn">POST /mod/connection/delete</button>
<button onclick="showForm('add-users')">POST /mod/group/addusers</button>
<button onclick="showForm('remove-users')">POST /mod/group/removeusers</button>
<button onclick="showForm('group-color')">POST /mod/group/color</button>
@@ -81,6 +80,7 @@
<button onclick="showForm('get-groups')">POST /get/groups</button>
<button onclick="showForm('get-connections')">POST /get/connections</button>
<button onclick="showForm('get-members')">POST /get/group/members</button>
<button onclick="showForm('del-user')" class="warn">POST /del/user</button>
<button onclick="showForm('del-group')" class="warn">POST /del/group</button>
<button onclick="showForm('del-connection')" class="warn">POST /del/connection</button>
<button onclick="showForm('get-connection-messages')">POST /get/connection/messages</button>
@@ -164,14 +164,6 @@
],
submit: () => httpPost('/mod/connection/accept', { token:'ca-token', connectionid:'ca-connectionid' })
},
'delete-connection': {
title: 'POST /mod/connection/delete — delete connection',
fields: [
{ id: 'cd-token', label: 'token', ph: '' },
{ id: 'cd-connectionid', label: 'connectionid', ph: 'UUID' },
],
submit: () => httpPost('/mod/connection/delete', { token:'cd-token', connectionid:'cd-connectionid' })
},
'add-users': {
title: 'POST /mod/group/addusers — add users (owner only)',
fields: [
@@ -230,6 +222,13 @@
],
submit: () => httpPost('/get/group/members', { token:'gm-token', group:'gm-group' })
},
'del-user': {
title: 'POST /del/user — delete own account',
fields: [
{ id: 'du-token', label: 'token', ph: '' },
],
submit: () => httpPost('/del/user', { token:'du-token' })
},
'del-group': {
title: 'POST /del/group — delete group (owner only)',
fields: [
+31 -14
View File
@@ -5,6 +5,7 @@ import (
"time"
"go-socket/Enums/ConnectionState"
"go-socket/Enums/WsEventType"
"github.com/coder/websocket"
"github.com/google/uuid"
@@ -24,15 +25,16 @@ type User struct {
}
type Connection struct {
Mu sync.RWMutex `json:"-"`
Id uuid.UUID `json:"id"`
CreatedAt time.Time `json:"createdAt"`
MessagesBuff [MaxDirectMsgCache]*Message `json:"-"`
NextBuffIdx uint32 `json:"-"`
HaveOverflowed bool `json:"-"`
RequestorId uint32 `json:"requestorId"`
RecipientId uint32 `json:"recipientId"`
State ConnectionState.ConnectionState `json:"state"`
Mu sync.RWMutex `json:"-"`
Id uuid.UUID `json:"id"`
CreatedAt time.Time `json:"createdAt"`
MessagesBuff [MaxDirectMsgCache]*Message `json:"-"`
NextBuffIdx uint32 `json:"-"`
RequestorId uint32 `json:"requestorId"`
RecipientId uint32 `json:"recipientId"`
UserWantingToElevate uint32 `json:"userWantingToElevate"` // TODO add to database
HaveOverflowed bool `json:"-"`
State ConnectionState.ConnectionState `json:"state"`
}
func (conn *Connection) AddMessageToBuff(message *Message) {
@@ -62,12 +64,17 @@ func (conn *Connection) GetSortedMessagesBuff() (*[MaxDirectMsgCache]*Message, u
return sorted, MaxDirectMsgCache
}
type ConnectionElevationData struct {
Id uuid.UUID `json:"id"`
NewState ConnectionState.ConnectionState `json:"newState"`
}
type Message struct {
Id uuid.UUID `json:"id"`
Content string `json:"content"`
CreatedAt time.Time `json:"createdAt"`
Sender uint32 `json:"sender"`
Receiver uuid.UUID `json:"receiver"`
Id uuid.UUID `json:"id"`
Content string `json:"content"`
CreatedAt time.Time `json:"createdAt"`
Sender uint32 `json:"sender"`
Receiver uuid.UUID `json:"receiver"`
}
type Group struct {
@@ -85,3 +92,13 @@ type LoginReturn struct {
Token string `json:"token"`
UserId uint32 `json:"userId"`
}
type WsEventMessage struct {
Type WsEventType.WsEventType `json:"type"`
Event any `json:"event"`
}
type WsAuthMessage struct {
Success bool `json:"success"`
Error string `json:"error"`
}
+36 -45
View File
@@ -7,7 +7,7 @@ import (
"net/http"
"time"
"go-socket/Enums/WsMessageFrom"
"go-socket/Enums/WsEventType"
"github.com/coder/websocket"
"github.com/coder/websocket/wsjson"
@@ -54,21 +54,7 @@ func ServeWsConnection(responseWriter http.ResponseWriter, request *http.Request
}
}
func sendMessageStructCloseIfTimeout(user *User, message *Message) {
if user.WsConn == nil {
return
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err := wsjson.Write(ctx, user.WsConn, message)
if err != nil {
log.Printf("json write error: %v", err)
}
}
func sendMessageCloseIfTimeout(user *User, message *map[string]any) {
func WsSendMessageCloseIfTimeout(user *User, message any) {
if user.WsConn == nil {
return
}
@@ -94,65 +80,64 @@ func sendToAllMessageCloseIfTimeout(message *map[string]any) {
mu.RUnlock()
for _, user := range users {
sendMessageCloseIfTimeout(user, message)
WsSendMessageCloseIfTimeout(user, message)
}
}
func WsMessageSendToUser(to *User, message *Message) {
sendMessageStructCloseIfTimeout(to, message)
}
func WsSendToGroup(group *Group, sender *User, message string) error {
func WsSendToGroupAsUser(group *Group, sender *User, message string) error {
for groupUserId := range group.Users {
groupUser, err := CacheGetUserById(groupUserId)
if err != nil || groupUser.Id == sender.Id {
continue
}
// TODO update on groups rework
var msg = map[string]any{
"type": WsMessageFrom.Group,
// "type": WsEventType.Group,
"from": group.Id,
"sender": sender.Id,
"content": message,
}
sendMessageCloseIfTimeout(groupUser, &msg)
WsSendMessageCloseIfTimeout(groupUser, &msg)
}
return nil
}
func handleAuthenticatedMessage(user *User, userMessage *map[string]any) bool {
sendMessageCloseIfTimeout(user, userMessage)
WsSendMessageCloseIfTimeout(user, userMessage)
return true
}
func handleUnauthenticatedMessage(ctx context.Context, user *User, userMessage *map[string]any) bool {
token, ok := (*userMessage)["token"].(string)
response := WsEventMessage{Type: WsEventType.Authentication}
if !ok {
var msg = map[string]any{
"type": WsMessageFrom.Server,
"error": "no token in message",
response.Event = WsAuthMessage{
Success: false,
Error: "no token in message",
}
sendMessageCloseIfTimeout(user, &msg)
WsSendMessageCloseIfTimeout(user, response)
return false
}
userId, err := TokenValidateGetId(token)
if err != nil {
var msg = map[string]any{
"type": WsMessageFrom.Server,
"error": "invalid token",
response.Event = WsAuthMessage{
Success: false,
Error: "invalid token",
}
sendMessageCloseIfTimeout(user, &msg)
WsSendMessageCloseIfTimeout(user, response)
return false
}
userFromCache, err := CacheGetUserById(userId)
if err != nil {
var msg = map[string]any{
"type": WsMessageFrom.Server,
"error": "user not found",
response.Event = WsAuthMessage{
Success: false,
Error: "user not found",
}
sendMessageCloseIfTimeout(user, &msg)
WsSendMessageCloseIfTimeout(user, response)
return false
}
@@ -166,25 +151,31 @@ func handleUnauthenticatedMessage(ctx context.Context, user *User, userMessage *
err = DbGroupGetById(ctx, dbGroup)
if err != nil {
var msg = map[string]any{
"type": "server",
"error": "invalid user data",
response.Event = WsAuthMessage{
Success: false,
Error: "invalid user data",
}
sendMessageCloseIfTimeout(user, &msg)
WsSendMessageCloseIfTimeout(user, response)
return false
}
err = DbGroupGetMembers(ctx, dbGroup)
if err != nil {
var msg = map[string]any{
"type": "server",
"error": "invalid user data",
response.Event = WsAuthMessage{
Success: false,
Error: "invalid user data",
}
sendMessageCloseIfTimeout(user, &msg)
WsSendMessageCloseIfTimeout(user, response)
return false
}
CacheSaveGroup(dbGroup)
}
}
response.Event = WsAuthMessage{
Success: true,
Error: "",
}
WsSendMessageCloseIfTimeout(user, response)
return true
}