package postgresql import ( "context" "fmt" "strings" "time" "go-socket/packages/convertions" "go-socket/packages/types" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" ) var dbConn *pgxpool.Pool func Init(ctx context.Context) { var err error dbConn, err = pgxpool.New(ctx, "postgres://master:secret@localhost:5432") // TODO change to env in production if err != nil { panic(err) } _, err = dbConn.Exec(ctx, `CREATE EXTENSION IF NOT EXISTS "pgcrypto";`) if err != nil { panic(err) } _, err = dbConn.Exec(ctx, ` CREATE TABLE IF NOT EXISTS users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT UNIQUE NOT NULL, pass_hash TEXT NOT NULL, pronouns TEXT DEFAULT NULL, description TEXT DEFAULT NULL, avatar TEXT DEFAULT NULL, profile_bg TEXT DEFAULT NULL, rgba BIGINT NOT NULL DEFAULT 0 CHECK (rgba BETWEEN 0 AND 4294967295), created_at TIMESTAMP NOT NULL DEFAULT NOW() ) `) if err != nil { panic(err) } _, err = dbConn.Exec(ctx, ` CREATE TABLE IF NOT EXISTS user_connections ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), 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() ) `) if err != nil { panic(err) } _, err = dbConn.Exec(ctx, ` CREATE TABLE IF NOT EXISTS direct_messages ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), 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, attached_file TEXT NOT NULL DEFAULT '' ) `) if err != nil { panic(err) } _, err = dbConn.Exec(ctx, ` CREATE TABLE IF NOT EXISTS hubs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, icon_url TEXT, background_url TEXT, creator UUID NOT NULL REFERENCES users(id), join_role SMALLINT, rgba BIGINT NOT NULL DEFAULT 0 CHECK (rgba BETWEEN 0 AND 4294967295), user_color_allowed BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMP NOT NULL DEFAULT NOW() ) `) if err != nil { panic(err) } _, err = dbConn.Exec(ctx, ` CREATE TABLE IF NOT EXISTS hub_roles ( id SMALLINT NOT NULL, hub_id UUID NOT NULL REFERENCES hubs(id) ON DELETE CASCADE, name TEXT NOT NULL, permissions BIGINT NOT NULL DEFAULT 0, rgba BIGINT NOT NULL DEFAULT 0 CHECK (rgba BETWEEN 0 AND 4294967295), created_at TIMESTAMP NOT NULL DEFAULT NOW(), PRIMARY KEY (id, hub_id) ) `) if err != nil { panic(err) } _, err = dbConn.Exec(ctx, ` CREATE TABLE IF NOT EXISTS hub_channel ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), hub_id UUID NOT NULL REFERENCES hubs(id) ON DELETE CASCADE, name TEXT NOT NULL, description TEXT, icon_url TEXT, position SMALLINT NOT NULL DEFAULT 0, created_at TIMESTAMP NOT NULL DEFAULT NOW(), roles_can_view_0 BIGINT NOT NULL DEFAULT 0, roles_can_view_1 BIGINT NOT NULL DEFAULT 0, roles_can_view_2 BIGINT NOT NULL DEFAULT 0, roles_can_message_0 BIGINT NOT NULL DEFAULT 0, roles_can_message_1 BIGINT NOT NULL DEFAULT 0, roles_can_message_2 BIGINT NOT NULL DEFAULT 0, roles_can_read_history_0 BIGINT NOT NULL DEFAULT 0, roles_can_read_history_1 BIGINT NOT NULL DEFAULT 0, roles_can_read_history_2 BIGINT NOT NULL DEFAULT 0 ) `) if err != nil { panic(err) } _, err = dbConn.Exec(ctx, ` CREATE TABLE IF NOT EXISTS hub_users ( user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, hub_id UUID NOT NULL REFERENCES hubs(id) ON DELETE CASCADE, name TEXT NOT NULL DEFAULT '', roles_0 BIGINT NOT NULL DEFAULT 0, roles_1 BIGINT NOT NULL DEFAULT 0, roles_2 BIGINT NOT NULL DEFAULT 0, is_muted BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMP NOT NULL DEFAULT NOW(), PRIMARY KEY (user_id, hub_id) ) `) if err != nil { panic(err) } _, err = dbConn.Exec(ctx, ` CREATE TABLE IF NOT EXISTS hub_channel_messages ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), sender_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, receiver_id UUID NOT NULL REFERENCES hub_channel(id) ON DELETE CASCADE, created_at TIMESTAMP NOT NULL DEFAULT NOW(), content TEXT NOT NULL, attached_file TEXT NOT NULL DEFAULT '' ) `) if err != nil { panic(err) } } func UserSave(ctx context.Context, user *types.User) error { err := dbConn.QueryRow(ctx, ` INSERT INTO users (name, pass_hash, pronouns, rgba, created_at) VALUES ($1, $2, $3, $4, $5) RETURNING id `, user.Name, user.PasswordHash, user.Pronouns, convertions.RgbaToUint32(user.Color), user.CreatedAt). Scan(&user.Id) return err } func UserDelete(ctx context.Context, id uuid.UUID) error { _, err := dbConn.Exec(ctx, ` DELETE FROM users WHERE id = $1 `, id) return err } 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.AvatarKey, &user.ProfileBgKey) if err == nil { user.Color = convertions.Uint32ToRgba(uint32(rgba)) } return err } 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.AvatarKey, &user.ProfileBgKey) if err == nil { user.Color = convertions.Uint32ToRgba(uint32(rgba)) } return err } func UserUpdateProfile(ctx context.Context, user *types.User, updateList *types.UserProfileUpdate) error { setClauses := make([]string, 0, 3) args := make([]any, 0, 4) argIdx := 1 if updateList.Pronouns { setClauses = append(setClauses, fmt.Sprintf("pronouns = $%d", argIdx)) args = append(args, user.Pronouns) argIdx++ } if updateList.Description { setClauses = append(setClauses, fmt.Sprintf("description = $%d", argIdx)) args = append(args, user.Description) argIdx++ } if updateList.Color { setClauses = append(setClauses, fmt.Sprintf("rgba = $%d", argIdx)) args = append(args, convertions.RgbaToUint32(user.Color)) argIdx++ } if updateList.Avatar { setClauses = append(setClauses, fmt.Sprintf("avatar = $%d", argIdx)) args = append(args, user.AvatarKey) argIdx++ } if updateList.ProfileBg { setClauses = append(setClauses, fmt.Sprintf("profile_bg = $%d", argIdx)) args = append(args, user.ProfileBgKey) argIdx++ } if len(setClauses) == 0 { return nil } query := "UPDATE users SET " + strings.Join(setClauses, ", ") + fmt.Sprintf(" WHERE id = $%d", argIdx) args = append(args, user.Id) _, err := dbConn.Exec(ctx, query, args...) return err } func UserGetBelongingHubs(ctx context.Context, user *types.User) error { rows, err := dbConn.Query(ctx, ` SELECT h.id, h.name, COALESCE(h.icon_url, ''), COALESCE(h.background_url, ''), h.creator, h.join_role, h.rgba, h.user_color_allowed, h.created_at FROM hubs h INNER JOIN hub_users hu ON hu.hub_id = h.id WHERE hu.user_id = $1 `, user.Id) if err != nil { return err } defer rows.Close() if user.Hubs == nil { user.Hubs = make(map[uuid.UUID]*types.Hub) } for rows.Next() { hub := types.NewHub() var joinRoleId *int16 var rgba int64 if err = rows.Scan(&hub.Id, &hub.Name, &hub.IconUrl, &hub.BgUrl, &hub.Creator, &joinRoleId, &rgba, &hub.UserColorAllowed, &hub.CreatedAt); err != nil { return fmt.Errorf("scanning hub row: %w", err) } hub.Color = convertions.Uint32ToRgba(uint32(rgba)) if joinRoleId != nil { hub.JoinRole = &types.HubRole{Id: uint8(*joinRoleId)} } user.Hubs[hub.Id] = hub } return rows.Err() } func UserGetWhole(ctx context.Context, user *types.User) error { if err := UserGetById(ctx, user); err != nil { return err } if err := ConnectionsGetBelongingToUser(ctx, user); err != nil { return err } if err := UserGetBelongingHubs(ctx, user); err != nil { return err } return nil } func ConnectionSave(ctx context.Context, conn *types.Connection) error { return dbConn.QueryRow(ctx, ` INSERT INTO user_connections (requestor_id, recipient_id, state, created_at) VALUES ($1, $2, $3, $4) RETURNING id `, conn.RequestorId, conn.RecipientId, conn.State, conn.CreatedAt).Scan(&conn.Id) } func ConnectionDelete(ctx context.Context, conn *types.Connection) error { _, err := dbConn.Exec(ctx, ` DELETE FROM user_connections WHERE id = $1 `, conn.Id) return err } func ConnectionGetById(ctx context.Context, id uuid.UUID) (*types.Connection, error) { conn := types.NewConn() err := dbConn.QueryRow(ctx, ` SELECT id, requestor_id, recipient_id, state, created_at FROM user_connections WHERE id = $1 `, id).Scan(&conn.Id, &conn.RequestorId, &conn.RecipientId, &conn.State, &conn.CreatedAt) if err != nil { return nil, err } return conn, nil } func ConnectionsGetBelongingToUser(ctx context.Context, user *types.User) error { rows, err := dbConn.Query(ctx, ` SELECT id, requestor_id, recipient_id, state, created_at FROM user_connections WHERE requestor_id = $1 OR recipient_id = $1 `, user.Id) if err != nil { return err } defer rows.Close() if user.Connections == nil { user.Connections = make(map[uuid.UUID]*types.Connection) } for rows.Next() { conn := types.NewConn() if err = rows.Scan(&conn.Id, &conn.RequestorId, &conn.RecipientId, &conn.State, &conn.CreatedAt); err != nil { return fmt.Errorf("scanning connection row: %w", err) } user.Connections[conn.Id] = conn } return rows.Err() } func ConnectionUpdateState(ctx context.Context, conn *types.Connection) error { _, err := dbConn.Exec(ctx, ` UPDATE user_connections SET state = $1 WHERE id = $2 `, conn.State, conn.Id) return err } func ConnectionMessageSave(ctx context.Context, message *types.Message) error { if message.Id != (uuid.UUID{}) { _, err := dbConn.Exec(ctx, ` INSERT INTO direct_messages (id, sender_id, receiver_id, created_at, content, attached_file) VALUES ($1, $2, $3, $4, $5, $6) `, message.Id, message.Sender, message.Receiver, message.CreatedAt, message.Content, message.AttachedFile) return err } return dbConn.QueryRow(ctx, ` INSERT INTO direct_messages (sender_id, receiver_id, created_at, content, attached_file) VALUES ($1, $2, $3, $4, $5) RETURNING id `, message.Sender, message.Receiver, message.CreatedAt, message.Content, message.AttachedFile).Scan(&message.Id) } func ConnectionGetMessagesBefore(ctx context.Context, before time.Time, connection uuid.UUID, cap uint32) ([]*types.Message, error) { rows, err := dbConn.Query(ctx, ` SELECT id, sender_id, receiver_id, created_at, content, attached_file FROM ( SELECT id, sender_id, receiver_id, created_at, content, attached_file FROM direct_messages WHERE receiver_id = $1 AND created_at < $2 ORDER BY created_at DESC LIMIT $3 ) sub ORDER BY created_at ASC `, connection, before, cap) if err != nil { return nil, err } defer rows.Close() messages := make([]*types.Message, 0, cap) for rows.Next() { msg := &types.Message{} if err = rows.Scan(&msg.Id, &msg.Sender, &msg.Receiver, &msg.CreatedAt, &msg.Content, &msg.AttachedFile); err != nil { return nil, err } messages = append(messages, msg) } return messages, rows.Err() } func HubGet(ctx context.Context, hub *types.Hub) error { var joinRoleId *int16 var rgba int64 err := dbConn.QueryRow(ctx, ` SELECT name, COALESCE(icon_url, ''), COALESCE(background_url, ''), creator, join_role, rgba, user_color_allowed, created_at FROM hubs WHERE id = $1 `, hub.Id).Scan(&hub.Name, &hub.IconUrl, &hub.BgUrl, &hub.Creator, &joinRoleId, &rgba, &hub.UserColorAllowed, &hub.CreatedAt) if err != nil { return err } hub.Color = convertions.Uint32ToRgba(uint32(rgba)) if joinRoleId != nil { hub.JoinRole = &types.HubRole{Id: uint8(*joinRoleId)} } return nil } func HubGetWhole(ctx context.Context, hub *types.Hub) error { if err := HubGet(ctx, hub); err != nil { return err } if err := HubRolesGet(ctx, hub); err != nil { return err } if err := HubChannelsGet(ctx, hub); err != nil { return err } if err := HubUsersGet(ctx, hub); err != nil { return err } return nil } func HubRolesGet(ctx context.Context, hub *types.Hub) error { rows, err := dbConn.Query(ctx, ` SELECT id, name, permissions, rgba, created_at FROM hub_roles WHERE hub_id = $1 `, hub.Id) if err != nil { return err } defer rows.Close() for rows.Next() { role := &types.HubRole{} var id int16 var permissions, rgba int64 if err = rows.Scan(&id, &role.Name, &permissions, &rgba, &role.CreatedAt); err != nil { return fmt.Errorf("scanning hub_role row: %w", err) } role.Id = uint8(id) role.Permissions = types.Permissions(uint32(permissions)) role.Color = convertions.Uint32ToRgba(uint32(rgba)) hub.Roles[role.Id] = role } return rows.Err() } func HubUsersGet(ctx context.Context, hub *types.Hub) error { rows, err := dbConn.Query(ctx, ` SELECT user_id, name, roles_0, roles_1, roles_2, is_muted, created_at FROM hub_users WHERE hub_id = $1 `, hub.Id) if err != nil { return err } defer rows.Close() if hub.Users == nil { hub.Users = make(map[uuid.UUID]*types.HubUser) } for rows.Next() { user := types.NewHubUser() var r0, r1, r2 int64 if err = rows.Scan(&user.OriginalId, &user.Name, &r0, &r1, &r2, &user.IsMuted, &user.CreatedAt); err != nil { return fmt.Errorf("scanning hub_user row: %w", err) } user.Roles[0] = uint64(r0) user.Roles[1] = uint64(r1) user.Roles[2] = uint64(r2) hub.Users[user.OriginalId] = user } return rows.Err() } func HubChannelsGet(ctx context.Context, hub *types.Hub) error { rows, err := dbConn.Query(ctx, ` SELECT id, name, COALESCE(description, ''), COALESCE(icon_url, ''), position, created_at, roles_can_view_0, roles_can_view_1, roles_can_view_2, roles_can_message_0, roles_can_message_1, roles_can_message_2, roles_can_read_history_0, roles_can_read_history_1, roles_can_read_history_2 FROM hub_channel WHERE hub_id = $1 `, hub.Id) if err != nil { return err } defer rows.Close() if hub.Channels == nil { hub.Channels = make(map[uuid.UUID]*types.HubChannel) } for rows.Next() { channel := types.NewHubChannel() var pos int16 var v0, v1, v2, m0, m1, m2, rh0, rh1, rh2 int64 if err = rows.Scan( &channel.Id, &channel.Name, &channel.Description, &channel.IconUrl, &pos, &channel.CreatedAt, &v0, &v1, &v2, &m0, &m1, &m2, &rh0, &rh1, &rh2, ); err != nil { return fmt.Errorf("scanning hub_channel row: %w", err) } channel.Position = uint8(pos) channel.RolesCanView = types.HubBoundRoles{uint64(v0), uint64(v1), uint64(v2)} channel.RolesCanMessage = types.HubBoundRoles{uint64(m0), uint64(m1), uint64(m2)} channel.RolesCanReadHistory = types.HubBoundRoles{uint64(rh0), uint64(rh1), uint64(rh2)} hub.Channels[channel.Id] = channel } return rows.Err() } func HubSave(ctx context.Context, hub *types.Hub) error { var joinRoleId *int16 if hub.JoinRole != nil { id := int16(hub.JoinRole.Id) joinRoleId = &id } _, err := dbConn.Exec(ctx, ` INSERT INTO hubs (id, name, icon_url, background_url, creator, join_role, rgba, user_color_allowed, created_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) `, hub.Id, hub.Name, hub.IconUrl, hub.BgUrl, hub.Creator, joinRoleId, convertions.RgbaToUint32(hub.Color), hub.UserColorAllowed, hub.CreatedAt) return err } func HubRoleSave(ctx context.Context, hubId uuid.UUID, role *types.HubRole) error { _, err := dbConn.Exec(ctx, ` INSERT INTO hub_roles (id, hub_id, name, permissions, rgba, created_at) VALUES ($1, $2, $3, $4, $5, $6) `, int16(role.Id), hubId, role.Name, int64(role.Permissions), convertions.RgbaToUint32(role.Color), role.CreatedAt) return err } func HubChannelSave(ctx context.Context, hubId uuid.UUID, channel *types.HubChannel) error { _, err := dbConn.Exec(ctx, ` INSERT INTO hub_channel ( id, hub_id, name, description, icon_url, position, created_at, roles_can_view_0, roles_can_view_1, roles_can_view_2, roles_can_message_0, roles_can_message_1, roles_can_message_2, roles_can_read_history_0, roles_can_read_history_1, roles_can_read_history_2 ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) `, channel.Id, hubId, channel.Name, channel.Description, channel.IconUrl, channel.Position, channel.CreatedAt, int64(channel.RolesCanView[0]), int64(channel.RolesCanView[1]), int64(channel.RolesCanView[2]), int64(channel.RolesCanMessage[0]), int64(channel.RolesCanMessage[1]), int64(channel.RolesCanMessage[2]), int64(channel.RolesCanReadHistory[0]), int64(channel.RolesCanReadHistory[1]), int64(channel.RolesCanReadHistory[2])) return err } func HubUserSave(ctx context.Context, hubId uuid.UUID, hubUser *types.HubUser) error { _, err := dbConn.Exec(ctx, ` INSERT INTO hub_users (user_id, hub_id, name, roles_0, roles_1, roles_2, is_muted, created_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) `, hubUser.OriginalId, hubId, hubUser.Name, int64(hubUser.Roles[0]), int64(hubUser.Roles[1]), int64(hubUser.Roles[2]), hubUser.IsMuted, hubUser.CreatedAt) return err } func HubChannelMessageSave(ctx context.Context, message *types.Message) error { if message.Id != (uuid.UUID{}) { _, err := dbConn.Exec(ctx, ` INSERT INTO hub_channel_messages (id, sender_id, receiver_id, created_at, content, attached_file) VALUES ($1, $2, $3, $4, $5, $6) `, message.Id, message.Sender, message.Receiver, message.CreatedAt, message.Content, message.AttachedFile) return err } return dbConn.QueryRow(ctx, ` INSERT INTO hub_channel_messages (sender_id, receiver_id, created_at, content, attached_file) VALUES ($1, $2, $3, $4, $5) RETURNING id `, message.Sender, message.Receiver, message.CreatedAt, message.Content, message.AttachedFile).Scan(&message.Id) } func HubChannelMessageGet(ctx context.Context, message *types.Message) error { return dbConn.QueryRow(ctx, ` SELECT sender_id, receiver_id, created_at, content, attached_file FROM hub_channel_messages WHERE id = $1 `, message.Id).Scan(&message.Sender, &message.Receiver, &message.CreatedAt, &message.Content, &message.AttachedFile) } func HubChannelGetMessagesBefore(ctx context.Context, before time.Time, channelId uuid.UUID, cap uint32) ([]*types.Message, error) { rows, err := dbConn.Query(ctx, ` SELECT id, sender_id, receiver_id, created_at, content, attached_file FROM ( SELECT id, sender_id, receiver_id, created_at, content, attached_file FROM hub_channel_messages WHERE receiver_id = $1 AND created_at < $2 ORDER BY created_at DESC LIMIT $3 ) sub ORDER BY created_at ASC `, channelId, before, cap) if err != nil { return nil, err } defer rows.Close() messages := make([]*types.Message, 0, cap) for rows.Next() { msg := &types.Message{} if err = rows.Scan(&msg.Id, &msg.Sender, &msg.Receiver, &msg.CreatedAt, &msg.Content, &msg.AttachedFile); err != nil { return nil, err } messages = append(messages, msg) } return messages, rows.Err() } func HubUpdate(ctx context.Context, hub *types.Hub, updateList *types.HubUpdate) error { setClauses := make([]string, 0, 6) args := make([]any, 0, 7) argIdx := 1 if updateList.Name { setClauses = append(setClauses, fmt.Sprintf("name = $%d", argIdx)) args = append(args, hub.Name) argIdx++ } if updateList.IconUrl { setClauses = append(setClauses, fmt.Sprintf("icon_url = $%d", argIdx)) args = append(args, hub.IconUrl) argIdx++ } if updateList.BgUrl { setClauses = append(setClauses, fmt.Sprintf("background_url = $%d", argIdx)) args = append(args, hub.BgUrl) argIdx++ } if updateList.JoinRole { var joinRoleId *int16 if hub.JoinRole != nil { id := int16(hub.JoinRole.Id) joinRoleId = &id } setClauses = append(setClauses, fmt.Sprintf("join_role = $%d", argIdx)) args = append(args, joinRoleId) argIdx++ } if updateList.Color { setClauses = append(setClauses, fmt.Sprintf("rgba = $%d", argIdx)) args = append(args, convertions.RgbaToUint32(hub.Color)) argIdx++ } if updateList.UserColorAllowed { setClauses = append(setClauses, fmt.Sprintf("user_color_allowed = $%d", argIdx)) args = append(args, hub.UserColorAllowed) argIdx++ } if len(setClauses) == 0 { return nil } query := "UPDATE hubs SET " + strings.Join(setClauses, ", ") + fmt.Sprintf(" WHERE id = $%d", argIdx) args = append(args, hub.Id) _, err := dbConn.Exec(ctx, query, args...) return err } func HubRoleUpdate(ctx context.Context, hubId uuid.UUID, role *types.HubRole, updateList *types.HubRoleUpdate) error { setClauses := make([]string, 0, 3) args := make([]any, 0, 5) argIdx := 1 if updateList.Name { setClauses = append(setClauses, fmt.Sprintf("name = $%d", argIdx)) args = append(args, role.Name) argIdx++ } if updateList.Permissions { setClauses = append(setClauses, fmt.Sprintf("permissions = $%d", argIdx)) args = append(args, int64(role.Permissions)) argIdx++ } if updateList.Color { setClauses = append(setClauses, fmt.Sprintf("rgba = $%d", argIdx)) args = append(args, convertions.RgbaToUint32(role.Color)) argIdx++ } if len(setClauses) == 0 { return nil } query := "UPDATE hub_roles SET " + strings.Join(setClauses, ", ") + fmt.Sprintf(" WHERE id = $%d AND hub_id = $%d", argIdx, argIdx+1) args = append(args, int16(role.Id), hubId) _, err := dbConn.Exec(ctx, query, args...) return err } func HubChannelUpdate(ctx context.Context, channel *types.HubChannel, updateList *types.HubChannelUpdate) error { setClauses := make([]string, 0, 13) args := make([]any, 0, 15) argIdx := 1 if updateList.Name { setClauses = append(setClauses, fmt.Sprintf("name = $%d", argIdx)) args = append(args, channel.Name) argIdx++ } if updateList.Description { setClauses = append(setClauses, fmt.Sprintf("description = $%d", argIdx)) args = append(args, channel.Description) argIdx++ } if updateList.IconUrl { setClauses = append(setClauses, fmt.Sprintf("icon_url = $%d", argIdx)) args = append(args, channel.IconUrl) argIdx++ } if updateList.Position { setClauses = append(setClauses, fmt.Sprintf("position = $%d", argIdx)) args = append(args, channel.Position) argIdx++ } if updateList.RolesCanView { setClauses = append(setClauses, fmt.Sprintf("roles_can_view_0 = $%d, roles_can_view_1 = $%d, roles_can_view_2 = $%d", argIdx, argIdx+1, argIdx+2)) args = append(args, int64(channel.RolesCanView[0]), int64(channel.RolesCanView[1]), int64(channel.RolesCanView[2])) argIdx += 3 } if updateList.RolesCanMessage { setClauses = append(setClauses, fmt.Sprintf("roles_can_message_0 = $%d, roles_can_message_1 = $%d, roles_can_message_2 = $%d", argIdx, argIdx+1, argIdx+2)) args = append(args, int64(channel.RolesCanMessage[0]), int64(channel.RolesCanMessage[1]), int64(channel.RolesCanMessage[2])) argIdx += 3 } if updateList.RolesCanReadHistory { setClauses = append(setClauses, fmt.Sprintf("roles_can_read_history_0 = $%d, roles_can_read_history_1 = $%d, roles_can_read_history_2 = $%d", argIdx, argIdx+1, argIdx+2)) args = append(args, int64(channel.RolesCanReadHistory[0]), int64(channel.RolesCanReadHistory[1]), int64(channel.RolesCanReadHistory[2])) argIdx += 3 } if len(setClauses) == 0 { return nil } query := "UPDATE hub_channel SET " + strings.Join(setClauses, ", ") + fmt.Sprintf(" WHERE id = $%d", argIdx) args = append(args, channel.Id) _, err := dbConn.Exec(ctx, query, args...) return err } func HubUserUpdate(ctx context.Context, hubId uuid.UUID, hubUser *types.HubUser, updateList *types.HubUserUpdate) error { setClauses := make([]string, 0, 5) args := make([]any, 0, 7) argIdx := 1 if updateList.Name { setClauses = append(setClauses, fmt.Sprintf("name = $%d", argIdx)) args = append(args, hubUser.Name) argIdx++ } if updateList.Roles { setClauses = append(setClauses, fmt.Sprintf("roles_0 = $%d, roles_1 = $%d, roles_2 = $%d", argIdx, argIdx+1, argIdx+2)) args = append(args, int64(hubUser.Roles[0]), int64(hubUser.Roles[1]), int64(hubUser.Roles[2])) argIdx += 3 } if updateList.IsMuted { setClauses = append(setClauses, fmt.Sprintf("is_muted = $%d", argIdx)) args = append(args, hubUser.IsMuted) argIdx++ } if len(setClauses) == 0 { return nil } query := "UPDATE hub_users SET " + strings.Join(setClauses, ", ") + fmt.Sprintf(" WHERE user_id = $%d AND hub_id = $%d", argIdx, argIdx+1) args = append(args, hubUser.OriginalId, hubId) _, err := dbConn.Exec(ctx, query, args...) return err } func HubDelete(ctx context.Context, hubId uuid.UUID) error { _, err := dbConn.Exec(ctx, `DELETE FROM hubs WHERE id = $1`, hubId) return err } func HubUserDelete(ctx context.Context, hubId uuid.UUID, userId uuid.UUID) error { _, err := dbConn.Exec(ctx, `DELETE FROM hub_users WHERE user_id = $1 AND hub_id = $2`, userId, hubId) return err } func HubRoleDelete(ctx context.Context, hubId uuid.UUID, roleId uint8) error { _, err := dbConn.Exec(ctx, `DELETE FROM hub_roles WHERE id = $1 AND hub_id = $2`, int16(roleId), hubId) return err } func HubChannelDelete(ctx context.Context, channelId uuid.UUID) error { _, err := dbConn.Exec(ctx, `DELETE FROM hub_channel WHERE id = $1`, channelId) return err }