add join role endpoint and persistency for hubs

This commit is contained in:
2026-05-06 19:08:18 +02:00
parent 03c13a6e8a
commit f68a249268
12 changed files with 991 additions and 206 deletions
+464 -13
View File
@@ -6,7 +6,6 @@ import (
"strings"
"time"
"go-socket/packages/cache"
"go-socket/packages/convertions"
"go-socket/packages/types"
@@ -79,10 +78,10 @@ func Init(ctx context.Context) {
icon_url TEXT,
background_url TEXT,
creator UUID NOT NULL REFERENCES users(id),
join_role, // TODO set role uuid
join_role SMALLINT,
rgba BIGINT NOT NULL DEFAULT 0 CHECK (rgba BETWEEN 0 AND 4294967295),
user_color_allowed BOOLEAN DEAFAULT FALSE,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
user_color_allowed BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
)
`)
if err != nil {
@@ -91,12 +90,13 @@ func Init(ctx context.Context) {
_, err = dbConn.Exec(ctx, `
CREATE TABLE IF NOT EXISTS hub_roles (
id TINYINT PRIMARY KEY,
parent_id UUID NOT NULL REFERACES hubs(id) ON DELETE CASCADE
id SMALLINT NOT NULL,
hub_id UUID NOT NULL REFERENCES hubs(id) ON DELETE CASCADE,
name TEXT NOT NULL,
permissions INT NOT NULL DEFAULT 0,
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 {
@@ -105,11 +105,53 @@ func Init(ctx context.Context) {
_, err = dbConn.Exec(ctx, `
CREATE TABLE IF NOT EXISTS hub_channel (
id TINYINT PRIMARY KEY,
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
hub_id UUID NOT NULL REFERENCES hubs(id) ON DELETE CASCADE,
name TEXT NOT NULL,
permissions INT NOT NULL DEFAULT 0,
rgba BIGINT NOT NULL DEFAULT 0 CHECK (rgba BETWEEN 0 AND 4294967295),
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 {
@@ -156,7 +198,7 @@ func UserGetById(ctx context.Context, user *types.User) error {
return err
}
func UserUpdateProfile(ctx context.Context, user *types.User, updateList *types.UserProfileUpdateList) error {
func UserUpdateProfile(ctx context.Context, user *types.User, updateList *types.UserProfileUpdate) error {
setClauses := make([]string, 0, 3)
args := make([]any, 0, 4)
argIdx := 1
@@ -198,15 +240,49 @@ func UserUpdateProfile(ctx context.Context, user *types.User, updateList *types.
return err
}
func GetWholeUser(ctx context.Context, user *types.User) error {
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
}
cache.SaveUser(user)
return nil
}
@@ -310,3 +386,378 @@ func ConnectionGetMessagesBefore(ctx context.Context, before time.Time, connecti
}
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 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
}