diff --git a/go-socket b/go-socket index c1ed7a7..84a1554 100755 Binary files a/go-socket and b/go-socket differ diff --git a/main.go b/main.go index 4054f15..774f7b2 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ func withCORS(h http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Headers", "token, Content-Type") + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS") h(w, r) } } @@ -30,7 +31,7 @@ func main() { http.HandleFunc("OPTIONS /", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Headers", "token, Content-Type") - w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PATCH, DELETE, OPTIONS") + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS") w.WriteHeader(http.StatusNoContent) }) @@ -61,6 +62,7 @@ func main() { http.HandleFunc("POST /connection/message", withCORS(httpRequest.HandleDm)) http.HandleFunc("GET /ws", wsServer.ServeWsConnection) + http.Handle("GET /client/", http.StripPrefix("/client/", http.FileServer(http.Dir("machine-client")))) log.Println("beep boop; server server started") log.Fatal(http.ListenAndServe(":"+strconv.Itoa(int(config.Port)), nil)) diff --git a/packages/Enums/WsEventType/WsMessageFrom.go b/packages/Enums/WsEventType/WsMessageFrom.go index 2a94746..8dff508 100644 --- a/packages/Enums/WsEventType/WsMessageFrom.go +++ b/packages/Enums/WsEventType/WsMessageFrom.go @@ -9,5 +9,8 @@ const ( ConnectionDeleted ConnectionElevated ConnectionDeElevated + UserProfileChange + UserAvatarChange + UserProfileBgChange HubMessage ) diff --git a/packages/httpRequest/connectionsAndDms.go b/packages/httpRequest/connectionsAndDms.go index 97f2b4c..e9b1342 100644 --- a/packages/httpRequest/connectionsAndDms.go +++ b/packages/httpRequest/connectionsAndDms.go @@ -52,14 +52,7 @@ func HandleDm(response http.ResponseWriter, request *http.Request) { var target *types.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 - } + target, err = getUserById(ctx, conn.GetSecondUser(user.Id)) if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) @@ -279,23 +272,9 @@ func HandleUserDeleteConnection(response http.ResponseWriter, request *http.Requ return } - var user2 *types.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) + user2, err := getUserById(ctx, conn.GetSecondUser(user.Id)) + if err != nil { + http.Error(response, "internal server error", http.StatusInternalServerError) return } @@ -349,12 +328,7 @@ func HandleUserElevateConnection(response http.ResponseWriter, request *http.Req } response.Write([]byte("elevated")) - var user2 *types.User - if conn.RequestorId == user.Id { - user2, err = getUserById(ctx, conn.RecipientId) - } else { - user2, err = getUserById(ctx, conn.RequestorId) - } + user2, err := getUserById(ctx, conn.GetSecondUser(user.Id)) if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return @@ -400,11 +374,10 @@ func HandleUserDeElevateConnection(response http.ResponseWriter, request *http.R return } - var user2 *types.User - if conn.RequestorId == user.Id { - user2, err = getUserById(ctx, conn.RecipientId) - } else { - user2, err = getUserById(ctx, conn.RequestorId) + user2, err := getUserById(ctx, conn.GetSecondUser(user.Id)) + if err != nil { + http.Error(response, "internal server error", http.StatusInternalServerError) + return } wsServer.WsSendMessageCloseIfTimeout(user2, types.WsEventMessage{ diff --git a/packages/httpRequest/files.go b/packages/httpRequest/files.go index 94e0027..cbf0880 100644 --- a/packages/httpRequest/files.go +++ b/packages/httpRequest/files.go @@ -2,10 +2,15 @@ package httpRequest import ( json2 "encoding/json" + "maps" + "net/http" + "slices" + "strings" + + "go-socket/packages/Enums/WsEventType" "go-socket/packages/postgresql" "go-socket/packages/types" - "net/http" - "strings" + "go-socket/packages/wsServer" "go-socket/packages/config" "go-socket/packages/convertions" @@ -100,21 +105,39 @@ func HandleSetUserAvatar(response http.ResponseWriter, request *http.Request) { return } - if user.AvatarUrl != "" { - if err = minio.Delete(ctx, user.AvatarUrl); err != nil { + if user.AvatarType != "" { + if err = minio.Delete(ctx, user.AvatarType); err != nil { minio.Delete(ctx, key) http.Error(response, "internal server error", http.StatusInternalServerError) return } } - user.AvatarUrl = key + user.AvatarType = key err = postgresql.UserUpdateProfile(ctx, user, &types.UserProfileUpdateList{Avatar: true}) if err != nil { http.Error(response, "failed to update user avatar", http.StatusInternalServerError) - minio.Delete(ctx, user.AvatarUrl) + minio.Delete(ctx, user.AvatarType) return } + user.Mu.RLock() + connections := slices.Collect(maps.Values(user.Connections)) + user.Mu.RUnlock() + + for _, conn := range connections { + targetId := conn.GetSecondUser(user.Id) + target, err := getUserById(ctx, targetId) + if err != nil { + continue + } + wsServer.WsSendMessageCloseIfTimeout(target, types.WsEventMessage{ + Type: WsEventType.UserAvatarChange, + Event: &map[string]any{ + "userId": user.Id, + }, + }) + } + response.WriteHeader(http.StatusCreated) } @@ -142,12 +165,12 @@ func HandleGetUserAvatar(response http.ResponseWriter, request *http.Request) { return } - if target.AvatarUrl == "" { + if target.AvatarType == "" { http.Error(response, "user have no avatar", http.StatusNoContent) return } - url, meta, err := minio.GetDownloadUrlAndMetadata(ctx, target.AvatarUrl) + url, meta, err := minio.GetDownloadUrlAndMetadata(ctx, target.AvatarType) if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return @@ -203,21 +226,38 @@ func HandleSetUserProfileBg(response http.ResponseWriter, request *http.Request) return } - if user.ProfileBgUrl != "" { - if err = minio.Delete(ctx, user.ProfileBgUrl); err != nil { + if user.ProfileBgType != "" { + if err = minio.Delete(ctx, user.ProfileBgType); err != nil { minio.Delete(ctx, key) http.Error(response, "internal server error", http.StatusInternalServerError) return } } - user.ProfileBgUrl = key + user.ProfileBgType = key err = postgresql.UserUpdateProfile(ctx, user, &types.UserProfileUpdateList{ProfileBg: true}) if err != nil { http.Error(response, "failed to update user profile background", http.StatusInternalServerError) - minio.Delete(ctx, user.ProfileBgUrl) + minio.Delete(ctx, user.ProfileBgType) return } + user.Mu.RLock() + connections := slices.Collect(maps.Values(user.Connections)) + user.Mu.RUnlock() + + for _, conn := range connections { + target, err := getUserById(ctx, conn.GetSecondUser(user.Id)) + if err != nil { + continue + } + wsServer.WsSendMessageCloseIfTimeout(target, types.WsEventMessage{ + Type: WsEventType.UserProfileBgChange, + Event: &map[string]any{ + "userId": user.Id, + }, + }) + } + response.WriteHeader(http.StatusCreated) } @@ -245,12 +285,12 @@ func HandleGetUserProfileBg(response http.ResponseWriter, request *http.Request) return } - if target.ProfileBgUrl == "" { + if target.ProfileBgType == "" { http.Error(response, "user have no profile background", http.StatusNoContent) return } - url, meta, err := minio.GetDownloadUrlAndMetadata(ctx, target.ProfileBgUrl) + url, meta, err := minio.GetDownloadUrlAndMetadata(ctx, target.ProfileBgType) if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return diff --git a/packages/httpRequest/helper.go b/packages/httpRequest/helper.go index 8cf209b..415bed6 100644 --- a/packages/httpRequest/helper.go +++ b/packages/httpRequest/helper.go @@ -31,6 +31,7 @@ func validCheckWithResponseOnFail(response *http.ResponseWriter, request *http.R } if request.ContentLength > maxSize { + io.Copy(io.Discard, request.Body) http.Error(*response, "Request too large", http.StatusRequestEntityTooLarge) return false } diff --git a/packages/httpRequest/hubs.go b/packages/httpRequest/hubs.go index cbb227d..fe5d03b 100644 --- a/packages/httpRequest/hubs.go +++ b/packages/httpRequest/hubs.go @@ -155,3 +155,16 @@ func HandleHubJoin(response http.ResponseWriter, request *http.Request) { hub.Users[hubUser.OriginalId] = hubUser addHubUserToPermissionCache(hub, hubUser) } + +// func HandleHubMessage(response http.ResponseWriter, request *http.Request) { +// if !validCheckWithResponseOnFail(&response, request, normal) { +// return +// } +// ctx := request.Context() +// user, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request.Header.Get("token"), request.FormValue("hubid")) +// if err != nil { +// return +// } +// +// +// } diff --git a/packages/httpRequest/user.go b/packages/httpRequest/user.go index aef2487..b784d82 100644 --- a/packages/httpRequest/user.go +++ b/packages/httpRequest/user.go @@ -2,15 +2,19 @@ package httpRequest import ( json2 "encoding/json" + "maps" "net/http" + "slices" "time" + "go-socket/packages/Enums/WsEventType" "go-socket/packages/cache" "go-socket/packages/convertions" "go-socket/packages/passwords" "go-socket/packages/postgresql" "go-socket/packages/tokens" "go-socket/packages/types" + "go-socket/packages/wsServer" "golang.org/x/crypto/bcrypt" ) @@ -150,6 +154,7 @@ func HandleUserModProfile(response http.ResponseWriter, request *http.Request) { } updateList := &types.UserProfileUpdateList{} + updatedValues := map[string]any{} if pronouns := request.FormValue("pronouns"); pronouns != "" { if len(pronouns) > 32 { @@ -158,6 +163,7 @@ func HandleUserModProfile(response http.ResponseWriter, request *http.Request) { } user.Pronouns = pronouns updateList.Pronouns = true + updatedValues["pronouns"] = pronouns } if description := request.FormValue("description"); description != "" { @@ -167,6 +173,7 @@ func HandleUserModProfile(response http.ResponseWriter, request *http.Request) { } user.Description = description updateList.Description = true + updatedValues["description"] = description } if colorStr := request.FormValue("color"); colorStr != "" { @@ -177,6 +184,12 @@ func HandleUserModProfile(response http.ResponseWriter, request *http.Request) { } user.Color = color updateList.Color = true + updatedValues["color"] = color + } + + if len(updatedValues) == 0 { + http.Error(response, "no values", http.StatusBadRequest) + return } err = postgresql.UserUpdateProfile(ctx, user, updateList) @@ -184,6 +197,24 @@ func HandleUserModProfile(response http.ResponseWriter, request *http.Request) { http.Error(response, "internal server error", http.StatusInternalServerError) return } + user.Mu.RLock() + connections := slices.Collect(maps.Values(user.Connections)) + user.Mu.RUnlock() + + for _, conn := range connections { + target, err := getUserById(ctx, conn.GetSecondUser(user.Id)) + if err != nil { + continue + } + wsServer.WsSendMessageCloseIfTimeout(target, types.WsEventMessage{ + Type: WsEventType.UserProfileChange, + Event: &map[string]any{ + "userId": user.Id, + "profileChangeList": updatedValues, + }, + }) + } + response.WriteHeader(http.StatusAccepted) } diff --git a/packages/postgresql/postgresql.go b/packages/postgresql/postgresql.go index 4220870..7620345 100644 --- a/packages/postgresql/postgresql.go +++ b/packages/postgresql/postgresql.go @@ -95,7 +95,7 @@ func UserGetStandardInfoByName(ctx context.Context, user *types.User) error { var rgba int64 err := dbConn.QueryRow(ctx, ` SELECT id, name, pass_hash, COALESCE(pronouns, ''), rgba, created_at, COALESCE(avatar, ''), COALESCE(profile_bg, '') FROM users WHERE name = $1 - `, user.Name).Scan(&user.Id, &user.Name, &user.PasswordHash, &user.Pronouns, &rgba, &user.CreatedAt, &user.AvatarUrl, &user.ProfileBgUrl) + `, user.Name).Scan(&user.Id, &user.Name, &user.PasswordHash, &user.Pronouns, &rgba, &user.CreatedAt, &user.AvatarType, &user.ProfileBgType) if err == nil { user.Color = convertions.Uint32ToRgba(uint32(rgba)) } @@ -106,7 +106,7 @@ func UserGetById(ctx context.Context, user *types.User) error { var rgba int64 err := dbConn.QueryRow(ctx, ` SELECT name, pass_hash, COALESCE(pronouns, ''), rgba, created_at, COALESCE(avatar, ''), COALESCE(profile_bg, '') FROM users WHERE id = $1 - `, user.Id).Scan(&user.Name, &user.PasswordHash, &user.Pronouns, &rgba, &user.CreatedAt, &user.AvatarUrl, &user.ProfileBgUrl) + `, user.Id).Scan(&user.Name, &user.PasswordHash, &user.Pronouns, &rgba, &user.CreatedAt, &user.AvatarType, &user.ProfileBgType) if err == nil { user.Color = convertions.Uint32ToRgba(uint32(rgba)) } @@ -135,12 +135,12 @@ func UserUpdateProfile(ctx context.Context, user *types.User, updateList *types. } if updateList.Avatar { setClauses = append(setClauses, fmt.Sprintf("avatar = $%d", argIdx)) - args = append(args, user.AvatarUrl) + args = append(args, user.AvatarType) argIdx++ } if updateList.ProfileBg { setClauses = append(setClauses, fmt.Sprintf("profile_bg = $%d", argIdx)) - args = append(args, user.ProfileBgUrl) + args = append(args, user.ProfileBgType) argIdx++ } diff --git a/packages/types/types.go b/packages/types/types.go index f182630..2703269 100644 --- a/packages/types/types.go +++ b/packages/types/types.go @@ -26,18 +26,18 @@ func (r Rgba) GetRandom() Rgba { } type User struct { - Mu sync.RWMutex `json:"-"` - Name string `json:"name"` - Pronouns string `json:"pronouns"` - Description string `json:"description"` - AvatarUrl string `json:"avatarUrl"` - ProfileBgUrl string `json:"profileBackgroundUrl"` - PasswordHash string `json:"-"` - CreatedAt time.Time `json:"createdAt"` - WsConn *websocket.Conn `json:"-"` - Id uuid.UUID `json:"-"` - Connections map[uuid.UUID]*Connection `json:"-"` - Color Rgba `json:"color"` + Mu sync.RWMutex `json:"-"` + Name string `json:"name"` + Pronouns string `json:"pronouns"` + Description string `json:"description"` + AvatarType string `json:"avatarType"` + ProfileBgType string `json:"profileBackgroundType"` + PasswordHash string `json:"-"` + CreatedAt time.Time `json:"createdAt"` + WsConn *websocket.Conn `json:"-"` + Id uuid.UUID `json:"-"` + Connections map[uuid.UUID]*Connection `json:"-"` + Color Rgba `json:"color"` } type UserProfileUpdateList struct { @@ -95,6 +95,13 @@ func (conn *Connection) GetSortedMessagesBuff() ([]*Message, uint32) { return sorted, size } +func (conn *Connection) GetSecondUser(id uuid.UUID) uuid.UUID { + if id == conn.RequestorId { + return conn.RecipientId + } + return conn.RequestorId +} + type ConnectionStatusSetData struct { Id uuid.UUID `json:"id"` NewState ConnectionState.ConnectionState `json:"newState"`