rewrite connections, messages system, add dm message history, fix multiple bugs, update machine-client

This commit is contained in:
2026-04-11 13:40:13 +02:00
parent 9804a700ce
commit 47c0bcbb0a
8 changed files with 346 additions and 57 deletions
+44 -3
View File
@@ -3,12 +3,14 @@ package main
import ( import (
"fmt" "fmt"
"sync" "sync"
"github.com/google/uuid"
) )
var ( var (
mu sync.RWMutex mu sync.RWMutex
CacheUsers = make(map[uint32]*User) CacheUsers = make(map[uint32]*User)
Groups = make(map[uint32]*Group) CacheGroups = make(map[uint32]*Group)
) )
func CacheGetUserById(id uint32) (*User, error) { func CacheGetUserById(id uint32) (*User, error) {
@@ -52,14 +54,53 @@ func CacheSaveGroup(group *Group) {
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
Groups[group.Id] = group CacheGroups[group.Id] = group
}
func CacheDeleteGroup(id uint32) {
mu.Lock()
defer mu.Unlock()
delete(CacheGroups, id)
}
func CacheAddConnection(a, b *User, conn *Connection) {
first, second := a, b
if a.Id > b.Id {
first, second = b, a
}
first.Mu.Lock()
second.Mu.Lock()
a.Connections[conn.Id] = conn
b.Connections[conn.Id] = conn
second.Mu.Unlock()
first.Mu.Unlock()
}
func CacheDeleteConnection(a, b *User, id uuid.UUID) {
first, second := a, b
if a.Id > b.Id {
first, second = b, a
}
first.Mu.Lock()
second.Mu.Lock()
delete(a.Connections, id)
delete(b.Connections, id)
second.Mu.Unlock()
first.Mu.Unlock()
}
func CacheGetConnection(user *User, id uuid.UUID) (*Connection, bool) {
user.Mu.RLock()
defer user.Mu.RUnlock()
conn, ok := user.Connections[id]
return conn, ok
} }
func CacheGetGroup(id uint32) (*Group, error) { func CacheGetGroup(id uint32) (*Group, error) {
mu.RLock() mu.RLock()
defer mu.RUnlock() defer mu.RUnlock()
group, ok := Groups[id] group, ok := CacheGroups[id]
if !ok { if !ok {
return nil, fmt.Errorf("group %d not found", id) return nil, fmt.Errorf("group %d not found", id)
} }
+6
View File
@@ -4,6 +4,8 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
"github.com/google/uuid"
) )
func ConvertStringUint32(s string) (uint32, error) { func ConvertStringUint32(s string) (uint32, error) {
@@ -26,3 +28,7 @@ func ConvertStringToRgb(str string) ([3]uint8, error) {
} }
return rgb, nil return rgb, nil
} }
func ConvertStringUuid(str string) (uuid.UUID, error) {
return uuid.Parse(str)
}
+20 -2
View File
@@ -20,6 +20,9 @@ func DbInit(ctx context.Context) {
} }
_, err = dbConn.Exec(ctx, `CREATE EXTENSION IF NOT EXISTS "gen_random_uuid";`) _, err = dbConn.Exec(ctx, `CREATE EXTENSION IF NOT EXISTS "gen_random_uuid";`)
if err != nil {
panic(err)
}
_, err = dbConn.Exec(ctx, ` _, err = dbConn.Exec(ctx, `
CREATE TABLE IF NOT EXISTS users ( CREATE TABLE IF NOT EXISTS users (
@@ -55,6 +58,7 @@ func DbInit(ctx context.Context) {
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
sender_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, sender_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
receiver_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, receiver_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
content TEXT NOT NULL, content TEXT NOT NULL,
is_group_message BOOLEAN DEFAULT FALSE is_group_message BOOLEAN DEFAULT FALSE
) )
@@ -110,7 +114,7 @@ func DbUserDelete(ctx context.Context, id uint32) error {
return err return err
} }
func DbUserGetByName(ctx context.Context, user *User) error { func DbUserGetStandardInfoByName(ctx context.Context, user *User) error {
err := dbConn.QueryRow(ctx, ` err := dbConn.QueryRow(ctx, `
SELECT id, name, pass_hash, pronouns, color_red, color_green, color_blue, created_at FROM users WHERE name = $1 SELECT id, name, pass_hash, pronouns, color_red, color_green, color_blue, created_at FROM users WHERE name = $1
`, user.Name).Scan(&user.Id, &user.Name, &user.PasswordHash, &user.Pronouns, &user.Color[0], &user.Color[1], &user.Color[2], &user.CreatedAt) `, user.Name).Scan(&user.Id, &user.Name, &user.PasswordHash, &user.Pronouns, &user.Color[0], &user.Color[1], &user.Color[2], &user.CreatedAt)
@@ -173,6 +177,13 @@ func DbConnectionSave(ctx context.Context, conn *Connection) error {
`, conn.RequestorId, conn.RecipientId, conn.State, conn.CreatedAt).Scan(&conn.Id) `, conn.RequestorId, conn.RecipientId, conn.State, conn.CreatedAt).Scan(&conn.Id)
} }
func DbConnectionDelete(ctx context.Context, conn *Connection) error {
_, err := dbConn.Exec(ctx, `
DELETE FROM user_connections WHERE id = $1
`, conn.Id)
return err
}
func DbConnectionsGetBelongingToUser(ctx context.Context, user *User) error { func DbConnectionsGetBelongingToUser(ctx context.Context, user *User) error {
rows, err := dbConn.Query(ctx, ` rows, err := dbConn.Query(ctx, `
SELECT id, requestor_id, recipient_id, state, created_at SELECT id, requestor_id, recipient_id, state, created_at
@@ -198,13 +209,20 @@ func DbConnectionsGetBelongingToUser(ctx context.Context, user *User) error {
return rows.Err() return rows.Err()
} }
func DbConnectionSet(ctx context.Context, conn *Connection) error { func DbConnectionUpdateState(ctx context.Context, conn *Connection) error {
_, err := dbConn.Exec(ctx, ` _, err := dbConn.Exec(ctx, `
UPDATE user_connections SET state = $1 WHERE id = $2 UPDATE user_connections SET state = $1 WHERE id = $2
`, conn.State, conn.Id) `, conn.State, conn.Id)
return err return err
} }
func DbMessageSave(ctx context.Context, message *Message) error {
return dbConn.QueryRow(ctx, `
INSERT INTO messages (sender_id, receiver_id, created_at, content, is_group_message) VALUES ($1, $2, $3, $4, $5)
RETURNING id
`, message.Sender, message.Receiver, message.CreatedAt, message.Content, message.IsGroupMessage).Scan(&message.Id)
}
func DbGroupSave(ctx context.Context, group *Group) error { func DbGroupSave(ctx context.Context, group *Group) error {
err := dbConn.QueryRow(ctx, ` err := dbConn.QueryRow(ctx, `
INSERT INTO chat_groups (name, creator_id, owner_id, enable_client_colors, color_red, color_green, color_blue, created_at) INSERT INTO chat_groups (name, creator_id, owner_id, enable_client_colors, color_red, color_green, color_blue, created_at)
+242 -25
View File
@@ -12,6 +12,8 @@ import (
"strings" "strings"
"time" "time"
"go-socket/Enums/ConnectionState"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
@@ -23,20 +25,29 @@ func isMethodAllowed(response *http.ResponseWriter, request *http.Request) bool
return true return true
} }
func getWholeUserFromDb(ctx context.Context, user *User) error {
if err := DbUserGetById(ctx, user); err != nil {
return err
}
if err := DbUserGetGroups(ctx, user); err != nil {
return err
}
if err := DbConnectionsGetBelongingToUser(ctx, user); err != nil {
return err
}
CacheSaveUser(user)
return nil
}
func getUserById(ctx context.Context, userId uint32) (*User, error) { func getUserById(ctx context.Context, userId uint32) (*User, error) {
user, err := CacheGetUserById(userId) user, err := CacheGetUserById(userId)
if err != nil { if err != nil {
user = &User{Id: userId} user = &User{Id: userId}
if err = DbUserGetById(ctx, user); err != nil { err = getWholeUserFromDb(ctx, user)
if err != nil {
return nil, err return nil, err
} }
if err = DbUserGetGroups(ctx, user); err != nil {
return nil, err
}
if err = DbConnectionsGetBelongingToUser(ctx, user); err != nil {
return nil, err
}
CacheSaveUser(user)
} }
return user, nil return user, nil
@@ -98,6 +109,25 @@ func getIfOwnerUserAndGroup(ctx context.Context, response *http.ResponseWriter,
return user, group, nil return user, group, nil
} }
func getUserByTokenGetUserByIdStrHandleHttp(ctx context.Context, response *http.ResponseWriter, token string, userIdStr string) (*User, *User, error) {
user, err := getUserByToken(ctx, token)
if err != nil {
http.Error(*response, "invalid token", http.StatusUnauthorized)
return nil, nil, err
}
affectedUserId, err := ConvertStringUint32(userIdStr)
if err != nil {
http.Error(*response, "no such user", http.StatusUnauthorized)
return nil, nil, err
}
user2, err := getUserById(ctx, affectedUserId)
if err != nil {
http.Error(*response, "no such user", http.StatusUnauthorized)
return nil, nil, err
}
return user, user2, nil
}
func HttpHandleUserNew(response http.ResponseWriter, request *http.Request) { func HttpHandleUserNew(response http.ResponseWriter, request *http.Request) {
if !isMethodAllowed(&response, request) { if !isMethodAllowed(&response, request) {
return return
@@ -234,19 +264,97 @@ func HttpHandleUserMessage(response http.ResponseWriter, request *http.Request)
return return
} }
targetConnection, err := ConvertStringUuid(request.FormValue("connectionid"))
if err != nil {
http.Error(response, "invalid connectionid", http.StatusBadRequest)
return
}
conn, ok := CacheGetConnection(user, targetConnection)
if !ok {
http.Error(response, "invalid connectionid", http.StatusBadRequest)
return
}
msgContent := request.FormValue("msgContent")
if msgContent == "" {
http.Error(response, "empty msgContent", http.StatusBadRequest)
return
}
var target *User
if user.Id == conn.RequestorId {
target, err = getUserById(ctx, conn.RecipientId)
} else if user.Id == conn.RecipientId {
target, err = getUserById(ctx, conn.RequestorId)
} else {
http.Error(response, "invalid connectionid", http.StatusBadRequest)
return
}
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
message := &Message{
Content: msgContent,
CreatedAt: time.Now(),
Sender: user.Id,
Receiver: target.Id,
IsGroupMessage: false,
}
err = DbMessageSave(ctx, message)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
WsSendToUser(target, message)
response.WriteHeader(http.StatusAccepted)
} }
func HttpHandleUserNewConnection(response http.ResponseWriter, request *http.Request) { func HttpHandleUserNewConnection(response http.ResponseWriter, request *http.Request) {
if !isMethodAllowed(&response, request) { if !isMethodAllowed(&response, request) {
return return
} }
ctx := request.Context() ctx := request.Context()
user, err := getUserByToken(ctx, request.FormValue("token")) requestor, recipient, err := getUserByTokenGetUserByIdStrHandleHttp(ctx, &response, request.FormValue("token"), request.FormValue("recipient"))
if err != nil { if err != nil {
http.Error(response, "invalid token", http.StatusUnauthorized)
return return
} }
if requestor.Id == recipient.Id {
http.Error(response, "cannot connect to yourself", http.StatusBadRequest)
return
}
requestor.Mu.RLock()
for _, connection := range requestor.Connections {
if (connection.RequestorId == requestor.Id && connection.RecipientId == recipient.Id) ||
(connection.RecipientId == requestor.Id && connection.RequestorId == recipient.Id) {
requestor.Mu.RUnlock()
http.Error(response, "connection already exists", http.StatusBadRequest)
return
}
}
requestor.Mu.RUnlock()
connection := &Connection{
CreatedAt: time.Now(),
RequestorId: requestor.Id,
RecipientId: recipient.Id,
State: ConnectionState.Stranger,
}
err = DbConnectionSave(ctx, connection)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
CacheAddConnection(requestor, recipient, connection)
response.WriteHeader(http.StatusCreated)
return
} }
func HttpHandleUserDeleteConnection(response http.ResponseWriter, request *http.Request) { func HttpHandleUserDeleteConnection(response http.ResponseWriter, request *http.Request) {
@@ -260,19 +368,94 @@ func HttpHandleUserDeleteConnection(response http.ResponseWriter, request *http.
http.Error(response, "invalid token", http.StatusUnauthorized) http.Error(response, "invalid token", http.StatusUnauthorized)
return return
} }
connectionId, err := ConvertStringUuid(request.FormValue("connectionid"))
if err != nil {
http.Error(response, "invalid connectionid", http.StatusBadRequest)
return
}
conn, ok := CacheGetConnection(user, connectionId)
if !ok {
http.Error(response, "invalid connectionid", http.StatusBadRequest)
return
}
var user2 *User
if conn.RequestorId == user.Id {
recipient, err := getUserById(ctx, conn.RecipientId)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
user2 = recipient
} else if conn.RecipientId == user.Id {
requestor, err := getUserById(ctx, conn.RequestorId)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
user2 = requestor
} else {
http.Error(response, "invalid connectionid", http.StatusBadRequest)
return
}
err = DbConnectionDelete(ctx, conn)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
CacheDeleteConnection(user, user2, connectionId)
response.WriteHeader(http.StatusAccepted)
} }
func HttpHandleUserAcceptConnection(response http.ResponseWriter, request *http.Request) { func HttpHandleUserElevateConnection(response http.ResponseWriter, request *http.Request) {
if !isMethodAllowed(&response, request) { if !isMethodAllowed(&response, request) {
return return
} }
ctx := request.Context() ctx := request.Context()
user, err := getUserByToken(ctx, request.FormValue("token")) user, err := getUserByToken(ctx, request.FormValue("token"))
if err != nil { if err != nil {
http.Error(response, "invalid token", http.StatusUnauthorized) http.Error(response, "invalid token", http.StatusUnauthorized)
return return
} }
connectionId, err := ConvertStringUuid(request.FormValue("connectionid"))
if err != nil {
http.Error(response, "invalid connectionid", http.StatusBadRequest)
return
}
conn, ok := CacheGetConnection(user, connectionId)
if !ok {
http.Error(response, "invalid connectionid", http.StatusBadRequest)
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)
} }
func HttpHandleUserGetConnections(response http.ResponseWriter, request *http.Request) { func HttpHandleUserGetConnections(response http.ResponseWriter, request *http.Request) {
@@ -285,6 +468,18 @@ func HttpHandleUserGetConnections(response http.ResponseWriter, request *http.Re
http.Error(response, "invalid token", http.StatusUnauthorized) http.Error(response, "invalid token", http.StatusUnauthorized)
return return
} }
user.Mu.RLock()
connections := slices.Collect(maps.Values(user.Connections))
user.Mu.RUnlock()
json, err := json2.Marshal(connections)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
response.Write(json)
} }
func HttpHandleTokenNew(response http.ResponseWriter, request *http.Request) { func HttpHandleTokenNew(response http.ResponseWriter, request *http.Request) {
@@ -314,19 +509,14 @@ func HttpHandleTokenNew(response http.ResponseWriter, request *http.Request) {
user, err = CacheGetUserByName(username) user, err = CacheGetUserByName(username)
if err != nil { if err != nil {
user = &User{Name: username} user = &User{Name: username}
if err = DbUserGetByName(ctx, user); err != nil { if err = DbUserGetStandardInfoByName(ctx, user); err != nil {
http.Error(response, "bad login", http.StatusUnauthorized) http.Error(response, "bad login", http.StatusUnauthorized)
return return
} }
if err = DbUserGetGroups(ctx, user); err != nil { if err = getWholeUserFromDb(ctx, user); err != nil {
http.Error(response, "bad login", http.StatusUnauthorized) http.Error(response, "internal server error", http.StatusInternalServerError)
return return
} }
if err = DbUserGetConnections(ctx, user); err != nil {
http.Error(response, "bad login", http.StatusUnauthorized)
return
}
CacheSaveUser(user)
} }
err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password)) err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password))
@@ -342,9 +532,13 @@ func HttpHandleTokenNew(response http.ResponseWriter, request *http.Request) {
} }
json, err := json2.Marshal(LoginReturn{Token: token, UserId: user.Id}) json, err := json2.Marshal(LoginReturn{Token: token, UserId: user.Id})
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusCreated) response.WriteHeader(http.StatusCreated)
response.Write([]byte(json)) response.Write(json)
} }
func HttpHandeGroupCreate(response http.ResponseWriter, request *http.Request) { func HttpHandeGroupCreate(response http.ResponseWriter, request *http.Request) {
@@ -411,6 +605,7 @@ func HttpHandleGroupDelete(response http.ResponseWriter, request *http.Request)
http.Error(response, "internal server error", http.StatusInternalServerError) http.Error(response, "internal server error", http.StatusInternalServerError)
return return
} }
CacheDeleteGroup(group.Id)
response.WriteHeader(http.StatusAccepted) response.WriteHeader(http.StatusAccepted)
} }
@@ -466,6 +661,11 @@ func HttpHandleGroupAddUsers(response http.ResponseWriter, request *http.Request
for i := uint32(0); i < idx; i++ { for i := uint32(0); i < idx; i++ {
group.Users[ids[i]] = struct{}{} group.Users[ids[i]] = struct{}{}
if cachedUser, err := CacheGetUserById(ids[i]); err == nil {
cachedUser.Mu.Lock()
cachedUser.Groups[group.Id] = struct{}{}
cachedUser.Mu.Unlock()
}
} }
response.WriteHeader(http.StatusAccepted) response.WriteHeader(http.StatusAccepted)
@@ -513,6 +713,11 @@ func HttpHandleGroupRemoveUser(response http.ResponseWriter, request *http.Reque
for i := uint32(0); i < idx; i++ { for i := uint32(0); i < idx; i++ {
delete(group.Users, ids[i]) delete(group.Users, ids[i])
if cachedUser, err := CacheGetUserById(ids[i]); err == nil {
cachedUser.Mu.Lock()
delete(cachedUser.Groups, group.Id)
cachedUser.Mu.Unlock()
}
} }
response.WriteHeader(http.StatusAccepted) response.WriteHeader(http.StatusAccepted)
@@ -563,13 +768,16 @@ func HttpHandleGroupChangeOwner(response http.ResponseWriter, request *http.Requ
newOwner, err := CacheGetUserByName(newOwnerName) newOwner, err := CacheGetUserByName(newOwnerName)
if err != nil { if err != nil {
newOwner = &User{Name: newOwnerName} newOwner = &User{Name: newOwnerName}
err = DbUserGetByName(ctx, newOwner) err = DbUserGetStandardInfoByName(ctx, newOwner)
if err != nil { if err != nil {
http.Error(response, "user not in group", http.StatusBadRequest) http.Error(response, "user not in group", http.StatusBadRequest)
return return
} }
CacheSaveUser(newOwner) if err = getWholeUserFromDb(ctx, newOwner); err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
} }
_, ok := group.Users[newOwner.Id] _, ok := group.Users[newOwner.Id]
@@ -646,8 +854,15 @@ func HttpHandleGroupsGetWithoutMembers(response http.ResponseWriter, request *ht
return return
} }
groups := make(map[uint32]*Group, len(user.Groups)) user.Mu.RLock()
groupIds := make([]uint32, 0, len(user.Groups))
for groupId := range user.Groups { for groupId := range user.Groups {
groupIds = append(groupIds, groupId)
}
user.Mu.RUnlock()
groups := make(map[uint32]*Group, len(groupIds))
for _, groupId := range groupIds {
group, err := getGroup(ctx, groupId) group, err := getGroup(ctx, groupId)
if err != nil { if err != nil {
continue continue
@@ -684,7 +899,9 @@ func HttpHandleGroupMembersGet(response http.ResponseWriter, request *http.Reque
return return
} }
user.Mu.RLock()
_, ok := user.Groups[groupId] _, ok := user.Groups[groupId]
user.Mu.RUnlock()
if !ok { if !ok {
http.Error(response, "no such group", http.StatusUnauthorized) http.Error(response, "no such group", http.StatusUnauthorized)
return return
+10 -10
View File
@@ -116,9 +116,9 @@
title: 'POST /new/connection — send connection request', title: 'POST /new/connection — send connection request',
fields: [ fields: [
{ id: 'nconn-token', label: 'token', ph: '' }, { id: 'nconn-token', label: 'token', ph: '' },
{ id: 'nconn-recipientid', label: 'recipientid', ph: 'uint32' }, { id: 'nconn-recipient', label: 'recipient', ph: 'uint32' },
], ],
submit: () => httpPost('/new/connection', { token:'nconn-token', recipientid:'nconn-recipientid' }) submit: () => httpPost('/new/connection', { token:'nconn-token', recipient:'nconn-recipient' })
}, },
'new-token': { 'new-token': {
title: 'POST /new/token — login / get token', title: 'POST /new/token — login / get token',
@@ -158,17 +158,17 @@
title: 'POST /mod/connection/accept — accept connection request', title: 'POST /mod/connection/accept — accept connection request',
fields: [ fields: [
{ id: 'ca-token', label: 'token', ph: '' }, { id: 'ca-token', label: 'token', ph: '' },
{ id: 'ca-connectedid', label: 'connectedid', ph: 'uint32' }, { id: 'ca-connectionid', label: 'connectionid', ph: 'UUID' },
], ],
submit: () => httpPost('/mod/connection/accept', { token:'ca-token', connectedid:'ca-connectedid' }) submit: () => httpPost('/mod/connection/accept', { token:'ca-token', connectionid:'ca-connectionid' })
}, },
'delete-connection': { 'delete-connection': {
title: 'POST /mod/connection/delete — delete connection', title: 'POST /mod/connection/delete — delete connection',
fields: [ fields: [
{ id: 'cd-token', label: 'token', ph: '' }, { id: 'cd-token', label: 'token', ph: '' },
{ id: 'cd-connectedid', label: 'connectedid', ph: 'uint32' }, { id: 'cd-connectionid', label: 'connectionid', ph: 'UUID' },
], ],
submit: () => httpPost('/mod/connection/delete', { token:'cd-token', connectedid:'cd-connectedid' }) submit: () => httpPost('/mod/connection/delete', { token:'cd-token', connectionid:'cd-connectionid' })
}, },
'add-users': { 'add-users': {
title: 'POST /mod/group/addusers — add users (owner only)', title: 'POST /mod/group/addusers — add users (owner only)',
@@ -240,10 +240,10 @@
title: 'POST /msg/user — send direct message to user', title: 'POST /msg/user — send direct message to user',
fields: [ fields: [
{ id: 'mu-token', label: 'token', ph: '' }, { id: 'mu-token', label: 'token', ph: '' },
{ id: 'mu-recipientid', label: 'recipientid', ph: 'uint32' }, { id: 'mu-connectionid', label: 'connectionid', ph: 'UUID' },
{ id: 'mu-message', label: 'message', ph: 'message text' }, { id: 'mu-msgContent', label: 'msgContent', ph: 'message text' },
], ],
submit: () => httpPost('/msg/user', { token:'mu-token', recipientid:'mu-recipientid', message:'mu-message' }) submit: () => httpPost('/msg/user', { token:'mu-token', connectionid:'mu-connectionid', msgContent:'mu-msgContent' })
}, },
'msg-group': { 'msg-group': {
title: 'POST /msg/group — send message to group', title: 'POST /msg/group — send message to group',
@@ -351,7 +351,7 @@
const text = await resp.text(); const text = await resp.text();
log(`HTTP ${resp.status}`, text, resp.ok ? 'log-http' : 'log-err'); log(`HTTP ${resp.status}`, text, resp.ok ? 'log-http' : 'log-err');
if (path === '/new/token' && resp.ok) { if (path === '/new/token' && resp.ok) {
currentToken = text.trim(); try { currentToken = JSON.parse(text).token; } catch(e) {}
autofillTokens(); autofillTokens();
wsConnectAndAuth(); wsConnectAndAuth();
} }
+1 -1
View File
@@ -24,7 +24,7 @@ func main() {
http.HandleFunc("/mod/user/appearence", withCORS(HttpHandleUserModifyAppearance)) http.HandleFunc("/mod/user/appearence", withCORS(HttpHandleUserModifyAppearance))
http.HandleFunc("/mod/user/about", withCORS(HttpHandleUserModifyAbout)) http.HandleFunc("/mod/user/about", withCORS(HttpHandleUserModifyAbout))
http.HandleFunc("/mod/connection/accept", withCORS(HttpHandleUserAcceptConnection)) http.HandleFunc("/mod/connection/accept", withCORS(HttpHandleUserElevateConnection))
http.HandleFunc("/mod/connection/delete", withCORS(HttpHandleUserDeleteConnection)) http.HandleFunc("/mod/connection/delete", withCORS(HttpHandleUserDeleteConnection))
http.HandleFunc("/mod/group/addusers", withCORS(HttpHandleGroupAddUsers)) http.HandleFunc("/mod/group/addusers", withCORS(HttpHandleGroupAddUsers))
http.HandleFunc("/mod/group/removeusers", withCORS(HttpHandleGroupRemoveUser)) http.HandleFunc("/mod/group/removeusers", withCORS(HttpHandleGroupRemoveUser))
+5 -1
View File
@@ -1,14 +1,17 @@
package main package main
import ( import (
"go-socket/Enums/ConnectionState" "sync"
"time" "time"
"go-socket/Enums/ConnectionState"
"github.com/coder/websocket" "github.com/coder/websocket"
"github.com/google/uuid" "github.com/google/uuid"
) )
type User struct { type User struct {
Mu sync.RWMutex
Name string Name string
Pronouns string Pronouns string
PasswordHash string PasswordHash string
@@ -34,6 +37,7 @@ type Message struct {
Content string `json:"content"` Content string `json:"content"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
Sender uint32 `json:"sender"` Sender uint32 `json:"sender"`
Receiver uint32 `json:"receiver"`
IsGroupMessage bool `json:"isGroupMessage"` IsGroupMessage bool `json:"isGroupMessage"`
} }
+7 -4
View File
@@ -3,11 +3,12 @@ package main
import ( import (
"context" "context"
"errors" "errors"
"go-socket/Enums/WsMessageFrom"
"log" "log"
"net/http" "net/http"
"time" "time"
"go-socket/Enums/WsMessageFrom"
"github.com/coder/websocket" "github.com/coder/websocket"
"github.com/coder/websocket/wsjson" "github.com/coder/websocket/wsjson"
) )
@@ -83,11 +84,13 @@ func sendToAllMessageCloseIfTimeout(message *map[string]any) {
} }
} }
func WsSendToUser(from *User, to *User, message string) { func WsSendToUser(to *User, message *Message) {
var msg = map[string]any{ var msg = map[string]any{
"type": WsMessageFrom.DirectMessage, "type": WsMessageFrom.DirectMessage,
"from": from.Id, "id": message.Id,
"content": message, "from": message.Sender,
"created": message.CreatedAt,
"content": message.Content,
} }
sendMessageCloseIfTimeout(to, &msg) sendMessageCloseIfTimeout(to, &msg)
} }