rewrite connections, messages system, add dm message history, fix multiple bugs, update machine-client
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
|
|||||||
@@ -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
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user