diff --git a/cache.go b/cache.go index 374bef8..2402e38 100644 --- a/cache.go +++ b/cache.go @@ -9,17 +9,17 @@ import ( var ( mu sync.RWMutex - CacheUsers = make(map[uint32]*User) - CacheGroups = make(map[uint32]*Group) + CacheUsers = make(map[uuid.UUID]*User) + CacheGroups = make(map[uuid.UUID]*Group) ) -func CacheGetUserById(id uint32) (*User, error) { +func CacheGetUserById(id uuid.UUID) (*User, error) { mu.RLock() defer mu.RUnlock() user, ok := CacheUsers[id] if !ok { - return nil, fmt.Errorf("user %d not found", id) + return nil, fmt.Errorf("user %s not found", id) } return user, nil } @@ -43,7 +43,7 @@ func CacheSaveUser(user *User) { CacheUsers[user.Id] = user } -func CacheDeleteUser(id uint32) { +func CacheDeleteUser(id uuid.UUID) { mu.Lock() defer mu.Unlock() @@ -57,7 +57,7 @@ func CacheSaveGroup(group *Group) { CacheGroups[group.Id] = group } -func CacheDeleteGroup(id uint32) { +func CacheDeleteGroup(id uuid.UUID) { mu.Lock() defer mu.Unlock() delete(CacheGroups, id) @@ -65,7 +65,7 @@ func CacheDeleteGroup(id uint32) { func CacheAddConnection(a, b *User, conn *Connection) { first, second := a, b - if a.Id > b.Id { + if a.Id.String() > b.Id.String() { first, second = b, a } first.Mu.Lock() @@ -78,7 +78,7 @@ func CacheAddConnection(a, b *User, conn *Connection) { func CacheDeleteConnection(a, b *User, id uuid.UUID) { first, second := a, b - if a.Id > b.Id { + if a.Id.String() > b.Id.String() { first, second = b, a } first.Mu.Lock() @@ -96,13 +96,13 @@ func CacheGetConnection(user *User, id uuid.UUID) (*Connection, bool) { return conn, ok } -func CacheGetGroup(id uint32) (*Group, error) { +func CacheGetGroup(id uuid.UUID) (*Group, error) { mu.RLock() defer mu.RUnlock() group, ok := CacheGroups[id] if !ok { - return nil, fmt.Errorf("group %d not found", id) + return nil, fmt.Errorf("group %s not found", id) } return group, nil diff --git a/database.go b/database.go index 1178040..5e5933a 100644 --- a/database.go +++ b/database.go @@ -26,7 +26,7 @@ func DbInit(ctx context.Context) { _, err = dbConn.Exec(ctx, ` CREATE TABLE IF NOT EXISTS users ( - id SERIAL PRIMARY KEY, + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT UNIQUE NOT NULL, pass_hash TEXT NOT NULL, pronouns TEXT DEFAULT NULL, @@ -43,8 +43,8 @@ func DbInit(ctx context.Context) { _, err = dbConn.Exec(ctx, ` CREATE TABLE IF NOT EXISTS user_connections ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - requestor_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, - recipient_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + requestor_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + recipient_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, state SMALLINT NOT NULL DEFAULT 0, created_at TIMESTAMP NOT NULL DEFAULT NOW() ) @@ -56,7 +56,7 @@ func DbInit(ctx context.Context) { _, err = dbConn.Exec(ctx, ` CREATE TABLE IF NOT EXISTS direct_messages ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - sender_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + sender_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, receiver_id UUID NOT NULL REFERENCES user_connections(id) ON DELETE CASCADE, created_at TIMESTAMP NOT NULL DEFAULT NOW(), content TEXT NOT NULL @@ -68,10 +68,10 @@ func DbInit(ctx context.Context) { _, err = dbConn.Exec(ctx, ` CREATE TABLE IF NOT EXISTS chat_groups ( - id SERIAL PRIMARY KEY, + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, - creator_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, - owner_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + creator_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + owner_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, enable_client_colors BOOLEAN NOT NULL DEFAULT true, color_red SMALLINT DEFAULT NULL, color_green SMALLINT DEFAULT NULL, @@ -85,8 +85,8 @@ func DbInit(ctx context.Context) { _, err = dbConn.Exec(ctx, ` CREATE TABLE IF NOT EXISTS chat_group_members ( - group_id INTEGER NOT NULL REFERENCES chat_groups(id) ON DELETE CASCADE, - user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + group_id UUID NOT NULL REFERENCES chat_groups(id) ON DELETE CASCADE, + user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, joined_at TIMESTAMP NOT NULL DEFAULT NOW(), PRIMARY KEY (group_id, user_id) ) @@ -106,7 +106,7 @@ func DbUserSave(ctx context.Context, user *User) error { return err } -func DbUserDelete(ctx context.Context, id uint32) error { +func DbUserDelete(ctx context.Context, id uuid.UUID) error { _, err := dbConn.Exec(ctx, ` DELETE FROM users WHERE id = $1 `, id) @@ -136,9 +136,9 @@ func DbUserGetGroups(ctx context.Context, user *User) error { } defer rows.Close() - user.Groups = make(map[uint32]struct{}) + user.Groups = make(map[uuid.UUID]struct{}) for rows.Next() { - var groupId uint32 + var groupId uuid.UUID if err := rows.Scan(&groupId); err != nil { return err } @@ -312,9 +312,9 @@ func DbGroupGetMembers(ctx context.Context, group *Group) error { } defer rows.Close() - group.Users = make(map[uint32]struct{}) + group.Users = make(map[uuid.UUID]struct{}) for rows.Next() { - var userId uint32 + var userId uuid.UUID if err := rows.Scan(&userId); err != nil { return err } @@ -323,12 +323,12 @@ func DbGroupGetMembers(ctx context.Context, group *Group) error { return rows.Err() } -func DbGroupAddUsers(ctx context.Context, groupId uint32, userIds *[MaxUsersInGroup]uint32) error { +func DbGroupAddUsers(ctx context.Context, groupId uuid.UUID, userIds *[MaxUsersInGroup]uuid.UUID) error { batch := &pgx.Batch{} now := time.Now() var count int for _, uid := range userIds { - if uid == 0 { + if uid == (uuid.UUID{}) { continue } batch.Queue(` @@ -348,11 +348,11 @@ func DbGroupAddUsers(ctx context.Context, groupId uint32, userIds *[MaxUsersInGr return nil } -func DbGroupRemoveUsers(ctx context.Context, groupId uint32, userIds *[MaxUsersInGroup]uint32) (int, error) { +func DbGroupRemoveUsers(ctx context.Context, groupId uuid.UUID, userIds *[MaxUsersInGroup]uuid.UUID) (int, error) { batch := &pgx.Batch{} var count int for _, uid := range userIds { - if uid == 0 { + if uid == (uuid.UUID{}) { continue } batch.Queue(` diff --git a/getter.go b/getter.go index 9de508e..e2f8c46 100644 --- a/getter.go +++ b/getter.go @@ -1,8 +1,12 @@ package main -import "context" +import ( + "context" -func GetUserById(ctx context.Context, userId uint32) (*User, error) { + "github.com/google/uuid" +) + +func GetUserById(ctx context.Context, userId uuid.UUID) (*User, error) { user, err := CacheGetUserById(userId) if err != nil { user = &User{Id: userId} @@ -23,7 +27,7 @@ func GetUserByToken(ctx context.Context, token string) (*User, error) { return GetUserById(ctx, userId) } -func GetGroup(ctx context.Context, groupId uint32) (*Group, error) { +func GetGroup(ctx context.Context, groupId uuid.UUID) (*Group, error) { group, err := CacheGetGroup(groupId) if err != nil { group = &Group{Id: groupId} diff --git a/go-socket b/go-socket index 11959d7..b7dce9d 100755 Binary files a/go-socket and b/go-socket differ diff --git a/httpConnectionAndDm.go b/httpConnectionAndDm.go index a368a96..934893d 100644 --- a/httpConnectionAndDm.go +++ b/httpConnectionAndDm.go @@ -165,7 +165,7 @@ func HttpHandleUserNewConnection(response http.ResponseWriter, request *http.Req http.Error(response, "invalid token", http.StatusUnauthorized) return } - recipientId, err := ConvertStringUint32(request.FormValue("recipient")) + recipientId, err := ConvertStringUuid(request.FormValue("recipient")) if err != nil { http.Error(response, "no such user", http.StatusUnauthorized) return @@ -296,8 +296,7 @@ func HttpHandleUserElevateConnection(response http.ResponseWriter, request *http response.WriteHeader(http.StatusAccepted) - // TODO change to != "" after user id via uuid - if conn.UserWantingToElevate != 0 && conn.UserWantingToElevate != user.Id { + if conn.UserWantingToElevate != (uuid.UUID{}) && conn.UserWantingToElevate != user.Id { switch conn.State { case ConnectionState.Stranger: conn.State = ConnectionState.Friend diff --git a/httpGroup.go b/httpGroup.go index 816263b..7bf6473 100644 --- a/httpGroup.go +++ b/httpGroup.go @@ -11,6 +11,8 @@ import ( "strconv" "strings" "time" + + "github.com/google/uuid" ) func isOwnerOfGroup(user *User, group *Group) bool { @@ -27,7 +29,7 @@ func getIfOwnerUserAndGroup(ctx context.Context, response *http.ResponseWriter, return nil, nil, err } - affectedGroupId, err := ConvertStringUint32(request.FormValue("groupid")) + affectedGroupId, err := ConvertStringUuid(request.FormValue("groupid")) if err != nil { http.Error(*response, "no such group", http.StatusUnauthorized) return nil, nil, err @@ -77,7 +79,7 @@ func HttpHandeGroupCreate(response http.ResponseWriter, request *http.Request) { OwnerId: user.Id, CreatorId: user.Id, Color: color, - Users: map[uint32]struct{}{user.Id: {}}, + Users: map[uuid.UUID]struct{}{user.Id: {}}, } enableUserColors := request.FormValue("enableUserColors") @@ -91,7 +93,7 @@ func HttpHandeGroupCreate(response http.ResponseWriter, request *http.Request) { return } response.WriteHeader(http.StatusCreated) - fmt.Fprintf(response, "%d", group.Id) + fmt.Fprintf(response, "%s", group.Id) } func HttpHandleGroupDelete(response http.ResponseWriter, request *http.Request) { @@ -140,13 +142,13 @@ func HttpHandleGroupAddUsers(response http.ResponseWriter, request *http.Request return } - var ids [MaxUsersInGroup]uint32 + var ids [MaxUsersInGroup]uuid.UUID var idx uint32 = 0 for _, s := range usersStringSlice { if idx >= MaxUsersInGroup { break } - id, err := ConvertStringUint32(strings.TrimSpace(s)) + id, err := ConvertStringUuid(strings.TrimSpace(s)) if err != nil { continue } @@ -192,13 +194,13 @@ func HttpHandleGroupRemoveUser(response http.ResponseWriter, request *http.Reque usersStringSlice := strings.SplitN(usersString, ",", int(MaxUsersInGroup)+1) - var ids [MaxUsersInGroup]uint32 + var ids [MaxUsersInGroup]uuid.UUID var idx uint32 = 0 for _, s := range usersStringSlice { if idx >= MaxUsersInGroup { break } - id, err := ConvertStringUint32(strings.TrimSpace(s)) + id, err := ConvertStringUuid(strings.TrimSpace(s)) if err != nil { continue } @@ -314,7 +316,7 @@ func HttpHandleGroupMessage(response http.ResponseWriter, request *http.Request) return } groupIdStr := request.FormValue("groupid") - groupId, err := ConvertStringUint32(groupIdStr) + groupId, err := ConvertStringUuid(groupIdStr) if err != nil { http.Error(response, "no such group", http.StatusUnauthorized) return @@ -360,13 +362,13 @@ func HttpHandleGroupsGetWithoutMembers(response http.ResponseWriter, request *ht } user.Mu.RLock() - groupIds := make([]uint32, 0, len(user.Groups)) + groupIds := make([]uuid.UUID, 0, len(user.Groups)) for groupId := range user.Groups { groupIds = append(groupIds, groupId) } user.Mu.RUnlock() - groups := make(map[uint32]*Group, len(groupIds)) + groups := make(map[uuid.UUID]*Group, len(groupIds)) for _, groupId := range groupIds { group, err := GetGroup(ctx, groupId) if err != nil { @@ -398,7 +400,7 @@ func HttpHandleGroupMembersGet(response http.ResponseWriter, request *http.Reque } groupStr := request.FormValue("group") - groupId, err := ConvertStringUint32(groupStr) + groupId, err := ConvertStringUuid(groupStr) if err != nil { http.Error(response, "invalid group", http.StatusBadRequest) return diff --git a/structs.go b/structs.go index 78a9993..34bfd9d 100644 --- a/structs.go +++ b/structs.go @@ -18,8 +18,8 @@ type User struct { PasswordHash string CreatedAt time.Time WsConn *websocket.Conn - Id uint32 - Groups map[uint32]struct{} + Id uuid.UUID + Groups map[uuid.UUID]struct{} Connections map[uuid.UUID]*Connection Color [3]uint8 } @@ -30,9 +30,9 @@ type Connection struct { 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 + RequestorId uuid.UUID `json:"requestorId"` + RecipientId uuid.UUID `json:"recipientId"` + UserWantingToElevate uuid.UUID `json:"userWantingToElevate"` // TODO add to database HaveOverflowed bool `json:"-"` State ConnectionState.ConnectionState `json:"state"` } @@ -73,7 +73,7 @@ type Message struct { Id uuid.UUID `json:"id"` Content string `json:"content"` CreatedAt time.Time `json:"createdAt"` - Sender uint32 `json:"sender"` + Sender uuid.UUID `json:"sender"` Receiver uuid.UUID `json:"receiver"` } @@ -83,10 +83,10 @@ type Group struct { CreatedAt time.Time `json:"createdAt"` MessagesBuff [MaxDirectMsgCache]*Message `json:"-"` NextBuffIdx uint32 `json:"-"` - Id uint32 `json:"-"` - CreatorId uint32 `json:"creatorId"` - OwnerId uint32 `json:"ownerId"` - Users map[uint32]struct{} `json:"-"` + Id uuid.UUID `json:"-"` + CreatorId uuid.UUID `json:"creatorId"` + OwnerId uuid.UUID `json:"ownerId"` + Users map[uuid.UUID]struct{} `json:"-"` Color [3]uint8 `json:"color"` EnableUserColors bool `json:"enableUserColors"` HaveMessageBuffOverflowed bool `json:"-"` @@ -94,7 +94,7 @@ type Group struct { type LoginReturn struct { Token string `json:"token"` - UserId uint32 `json:"userId"` + UserId uuid.UUID `json:"userId"` } type WsEventMessage struct { diff --git a/todo.txt b/todo.txt index a806dfe..d956f43 100644 --- a/todo.txt +++ b/todo.txt @@ -1,5 +1,4 @@ group messages history -rewrite to use uuid media support fix color saving to use INT (002255255035) rgba diff --git a/tokens.go b/tokens.go index 7a08042..4fbb255 100644 --- a/tokens.go +++ b/tokens.go @@ -2,26 +2,26 @@ package main import ( "fmt" - "strconv" "time" "github.com/golang-jwt/jwt/v5" + "github.com/google/uuid" ) const tokenSecret = "tmp" // TODO delete in production const tokenExpiration = time.Hour -func TokenCreate(userId uint32) (string, error) { +func TokenCreate(userId uuid.UUID) (string, error) { now := time.Now() signedToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{ - Subject: strconv.FormatUint(uint64(userId), 10), + Subject: userId.String(), IssuedAt: jwt.NewNumericDate(now), ExpiresAt: jwt.NewNumericDate(now.Add(tokenExpiration)), }).SignedString([]byte(tokenSecret)) return signedToken, err } -func TokenValidateGetId(tokenString string) (uint32, error) { +func TokenValidateGetId(tokenString string) (uuid.UUID, error) { token, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) { if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"]) @@ -29,28 +29,28 @@ func TokenValidateGetId(tokenString string) (uint32, error) { return []byte(tokenSecret), nil }) if err != nil { - return 0, err + return uuid.UUID{}, err } claims, ok := token.Claims.(jwt.MapClaims) if !ok || !token.Valid { - return 0, fmt.Errorf("invalid token") + return uuid.UUID{}, fmt.Errorf("invalid token") } exp, ok := claims["exp"].(float64) if !ok || time.Now().Unix() > int64(exp) { - return 0, fmt.Errorf("token expired") + return uuid.UUID{}, fmt.Errorf("token expired") } sub, ok := claims["sub"].(string) if !ok { - return 0, fmt.Errorf("invalid subject claim") + return uuid.UUID{}, fmt.Errorf("invalid subject claim") } - id, err := strconv.ParseUint(sub, 10, 32) + id, err := uuid.Parse(sub) if err != nil { - return 0, fmt.Errorf("invalid subject claim") + return uuid.UUID{}, fmt.Errorf("invalid subject claim") } - return uint32(id), nil + return id, nil }