From c679b18f397b7a0ddb7a112c21c6fbe35de4ece2 Mon Sep 17 00:00:00 2001 From: GitProtogen Date: Mon, 23 Mar 2026 14:24:53 +0100 Subject: [PATCH] ad group logic except somes --- cache.go | 18 ++++++++- convertions.go | 8 ++++ database.go | 46 ++++++++++++++++++++- enums.go | 7 ++++ http.go | 107 ++++++++++++++++++++++++++++++++++++++++++++++++- structs.go | 18 ++++----- 6 files changed, 191 insertions(+), 13 deletions(-) create mode 100644 convertions.go diff --git a/cache.go b/cache.go index 64615ab..067493a 100644 --- a/cache.go +++ b/cache.go @@ -22,11 +22,11 @@ func CacheGetClientById(id uint32) (*Client, error) { return client, nil } -func CacheSetClient(id uint32, client *Client) { +func CacheSetClient(client *Client) { mu.Lock() defer mu.Unlock() - CacheClients[id] = client + CacheClients[client.Id] = client } func CacheDeleteClient(id uint32) { @@ -35,3 +35,17 @@ func CacheDeleteClient(id uint32) { delete(CacheClients, id) } + +func CacheSetGroup() {} + +func CacheGetGroup(id uint32) (*Group, error) { + mu.RLock() + defer mu.RUnlock() + + group, ok := Groups[id] + if !ok { + return nil, fmt.Errorf("group %d not found", id) + } + + return group, nil +} diff --git a/convertions.go b/convertions.go new file mode 100644 index 0000000..d9fc265 --- /dev/null +++ b/convertions.go @@ -0,0 +1,8 @@ +package main + +import "strconv" + +func ConvertStringUint32(s string) (uint32, error) { + v, err := strconv.ParseUint(s, 10, 32) + return uint32(v), err +} diff --git a/database.go b/database.go index 01aa2af..8ee2984 100644 --- a/database.go +++ b/database.go @@ -37,7 +37,7 @@ func DbInit(ctx context.Context) { name VARCHAR(48) NOT NULL, creator_id INTEGER NOT NULL REFERENCES client(id) ON DELETE CASCADE, owner_id INTEGER NOT NULL REFERENCES client(id) ON DELETE CASCADE, - enable_user_colors BOOLEAN NOT NULL DEFAULT true, + enable_client_colors BOOLEAN NOT NULL DEFAULT true, color_red SMALLINT DEFAULT NULL, color_green SMALLINT DEFAULT NULL, color_blue SMALLINT DEFAULT NULL, @@ -77,3 +77,47 @@ func DbSetClientByName(ctx context.Context, client *Client) error { `, client.Name).Scan(&client.Name, &client.PasswordHash, client.Pronouns, client.Color[0], client.Color[1], client.Color[2], client.CreatedAt) return err } + +func DbSetClientById(ctx context.Context, client *Client) error { + err := dbConn.QueryRow(ctx, ` + SELECT name, pass_hash, pronouns, color_red, color_green, color_blue, created_at FROM clients WHERE id = $1 + `, client.Id).Scan(&client.Name, &client.PasswordHash, &client.Pronouns, &client.Color[0], &client.Color[1], &client.Color[2], &client.CreatedAt) + return err +} + +func DbSaveGroupWithoutClients(ctx context.Context, group *Group) error { + err := dbConn.QueryRow(ctx, ` + INSERT INTO chat_groups (name, creator_id, owner_id, enable_client_colors, color_red, color_green, color_blue, created_at) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + RETURNING id + `, group.Name, group.CreatorId, group.OwnerId, group.EnableClientColors, group.Color[0], group.Color[1], group.Color[2], group.CreatedAt). + Scan(&group.Id) + return err +} + +func DbSetGroupById(ctx context.Context, group *Group) error { + err := dbConn.QueryRow(ctx, ` + SELECT name, creator_id, owner_id, enable_client_colors, color_red, color_green, color_blue, created_at FROM chat_groups WHERE id = $1 + `, group.Id).Scan(&group.Name, &group.CreatorId, &group.OwnerId, &group.EnableClientColors, &group.Color[0], &group.Color[1], &group.Color[2], &group.CreatedAt) + if err != nil { + return err + } + + rows, err := dbConn.Query(ctx, ` + SELECT user_id FROM chat_group_members WHERE group_id = $1 + `, group.Id) + if err != nil { + return err + } + defer rows.Close() + + group.Clients = make(map[uint32]struct{}) + for rows.Next() { + var userId uint32 + if err := rows.Scan(&userId); err != nil { + return err + } + group.Clients[userId] = struct{}{} + } + return rows.Err() +} diff --git a/enums.go b/enums.go index aaf8639..ad5d524 100644 --- a/enums.go +++ b/enums.go @@ -6,3 +6,10 @@ const ( BadMessage WsServerResponse = iota InvalidCredentials ) + +var Colors = map[string][3]uint8{ + "red": {255, 0, 0}, + "green": {0, 255, 255}, + "blue": {0, 0, 255}, + "default": {255, 255, 255}, +} diff --git a/http.go b/http.go index 0be6150..3a4ad65 100644 --- a/http.go +++ b/http.go @@ -1,6 +1,7 @@ package main import ( + "encoding/binary" "fmt" "net/http" "strconv" @@ -71,7 +72,7 @@ func HttpHandleNewUser(response http.ResponseWriter, request *http.Request) { err = DbSaveClientWithoutGroups(ctx, newClient) if err != nil { - http.Error(response, "name taken", http.StatusInternalServerError) + http.Error(response, "name taken", http.StatusUnauthorized) return } } @@ -97,6 +98,7 @@ func HttpHandleLogin(response http.ResponseWriter, request *http.Request) { err := DbSetClientByName(ctx, &client) if err != nil { + http.Error(response, "bad login", http.StatusUnauthorized) return } @@ -119,4 +121,107 @@ func HttpHandleGroupCreate(response http.ResponseWriter, request *http.Request) if !isMethodAllowed(&response, request) { return } + + token := request.FormValue("token") + clientId, err := TokenValidateGetId(token) + if err != nil { + http.Error(response, "invalid token", http.StatusUnauthorized) + return + } + + name := request.FormValue("name") + if name == "" { + name = "Best group ever" + } + + colorString := request.FormValue("color") + color, err := parseRgb(colorString) + if err != nil { + var ok bool + color, ok = Colors[colorString] + if !ok { + color = Colors["default"] + } + } + + ctx := request.Context() + + client := Client{Id: clientId} + cacheClient, err := CacheGetClientById(clientId) + if err == nil { + client = *cacheClient + } else { + err = DbSetClientById(ctx, &client) + if err != nil { + http.Error(response, "internal server error", http.StatusInternalServerError) + return + } + } + + group := Group{ + Name: name, + CreatedAt: time.Now(), + OwnerId: clientId, + CreatorId: clientId, + Color: color, + } + + enableClientColors := request.FormValue("enableClientColors") + if enableClientColors == "1" { + group.EnableClientColors = true + } + + err = DbSaveGroupWithoutClients(ctx, &group) + if err != nil { + http.Error(response, "internal server error", http.StatusInternalServerError) + return + } + groupIdBytes := make([]byte, 4) + binary.BigEndian.PutUint32(groupIdBytes, group.Id) + response.Write(groupIdBytes) +} + +func HttpHandleGroupAddClient(response http.ResponseWriter, request *http.Request) { + if !isMethodAllowed(&response, request) { + return + } + + token := request.FormValue("token") + clientId, err := TokenValidateGetId(token) + if err != nil { + http.Error(response, "invalid token", http.StatusUnauthorized) + return + } + + affectedGroupId, err := ConvertStringUint32(request.FormValue("groupid")) + if err != nil { + http.Error(response, "no such group", http.StatusUnauthorized) + return + } + + ctx := request.Context() + + var group Group + groupPtr, err := CacheGetGroup(affectedGroupId) + if err == nil { + group = *groupPtr + } else { + err = DbSetGroupById(ctx, &group) + if err != nil { + http.Error(response, "no such group", http.StatusUnauthorized) + return + } + } + + if group.OwnerId != clientId { + http.Error(response, "no such group", http.StatusUnauthorized) + return + } + + usersToAddString := request.FormValue("users") + usersToAdd := strings.SplitN(usersToAddString, ",", int(MaxGroupsForClient)) + if len(usersToAdd) == 0 { + http.Error(response, "no users to add", http.StatusBadRequest) + } + } diff --git a/structs.go b/structs.go index 18c0d1e..a2d10b1 100644 --- a/structs.go +++ b/structs.go @@ -13,17 +13,17 @@ type Client struct { CreatedAt time.Time WsConn *websocket.Conn Id uint32 - Groups [MaxGroupsForClient]uint32 + Groups map[uint32]struct{} Color [3]uint8 } type Group struct { - Name string - CreatedAt time.Time - Id uint32 - CreatorId uint32 - OwnerId uint32 - Clients [MaxClientsInGroup]uint32 - Color [3]uint8 - EnableUserColors bool + Name string + CreatedAt time.Time + Id uint32 + CreatorId uint32 + OwnerId uint32 + Clients map[uint32]struct{} + Color [3]uint8 + EnableClientColors bool }