finished rewriting socket but it isn't working 😊

This commit is contained in:
slawk0
2025-02-11 23:03:31 +01:00
parent 248966d63f
commit e6817cb4c6
6 changed files with 214 additions and 61 deletions

View File

@@ -347,3 +347,17 @@ func ContactSuggestion(db *sql.DB, contactUsername string) ([]string, error) {
}
return suggestions, nil
}
func UpdateContactStatus(db *sql.DB, userID uuid.UUID, conversationID uuid.UUID, lastReadMessageID int) error {
query := `
UPDATE Memberships
SET last_read_message_id = $1
WHERE user_id = $2
AND conversation_id = $3;
`
_, err := db.Exec(query, lastReadMessageID, userID, conversationID)
if err != nil {
return fmt.Errorf("failed to update contact status: %w", err)
}
return nil
}

View File

@@ -170,6 +170,11 @@ func RemoveUserFromGroup(db *sql.DB, userID uuid.UUID, groupID uuid.UUID) (strin
WHERE conversation_id = $1 AND user_id = $2;
`
removeAdministratorPermissionsQuery := `
DELETE FROM GroupAdmins
WHERE conversation_id = $1 AND user_id = $2;
`
isOwner, err := IsGroupOwner(db, userID, groupID)
if err != nil {
return "Failed to remove user from group", fmt.Errorf("failed to check if user is group owner: %w", err)
@@ -188,6 +193,12 @@ func RemoveUserFromGroup(db *sql.DB, userID uuid.UUID, groupID uuid.UUID) (strin
if rowsAffected == 0 {
return "User is not a member of the group", nil
}
// If user is an admin, remove admin permissions
_, err = db.Exec(removeAdministratorPermissionsQuery, groupID, userID)
if err != nil {
return "Failed to remove user from group", fmt.Errorf("failed to remove admin permissions: %w", err)
}
return "Successfully removed user from group", nil
}
@@ -207,3 +218,34 @@ func IsGroupOwner(db *sql.DB, userID uuid.UUID, groupID uuid.UUID) (bool, error)
}
return isOwner, nil
}
func AddAdministrator(db *sql.DB, userID uuid.UUID, groupID uuid.UUID, grantedBy uuid.UUID) (string, error) {
query := `
INSERT INTO GroupAdmins (conversation_id, user_id, granted_by, is_owner)
VALUES ($1, $2, $3, false)
RETURNING granted_at;
`
isOwner, err := IsGroupOwner(db, grantedBy, groupID)
if err != nil {
return "Failed to add administrator", err
}
if !isOwner {
return "You are not the group owner", nil
}
isMember, err := IsMember(db, userID, groupID)
if err != nil {
return "Failed to add administrator", err
}
if !isMember {
return "User is not a member of the group", nil
}
var grantedAt string
err = db.QueryRow(query, groupID, userID, grantedBy).Scan(&grantedAt)
if err != nil {
return "Failed to add administrator", fmt.Errorf("failed to add administrator: %w", err)
}
return "Successfully added administrator", nil
}

44
main.go
View File

@@ -1,11 +1,14 @@
package main
import (
"github.com/gofiber/contrib/websocket"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/adaptor"
"github.com/gofiber/fiber/v2/middleware/cors"
socketio "github.com/googollee/go-socket.io"
"log"
"relay-server/database"
"relay-server/router"
"relay-server/socket"
"relay-server/utils"
)
@@ -14,14 +17,14 @@ func main() {
ErrorHandler: utils.ErrorHandler,
})
app.Use(func(c *fiber.Ctx) error {
if websocket.IsWebSocketUpgrade(c) {
c.Locals("allowed", true)
return c.Next()
}
return fiber.ErrUpgradeRequired
})
server := socketio.NewServer(nil)
app.Use(cors.New(cors.Config{
AllowOrigins: "http://localhost:5173",
AllowHeaders: "Origin, Content-Type, Accept, Authorization",
AllowMethods: "GET,POST,PUT,DELETE",
//AllowCredentials: true,
}))
db, err := database.Init()
if err != nil {
log.Fatal("Failed to initialize database")
@@ -34,6 +37,29 @@ func main() {
log.Println("Database connection closed")
}()
err = socket.InitializeSocket(server)
if err != nil {
log.Println(err)
}
go func() {
err := server.Serve()
if err != nil {
log.Println(err)
}
}()
defer func(server *socketio.Server) {
err := server.Close()
if err != nil {
log.Println("disconnected from server", err)
}
}(server)
app.Use("/socket.io/*", adaptor.HTTPHandler(server))
app.Get("/socket.io/", adaptor.HTTPHandler(server))
// Setup routes
router.SetupRoutes(app)
app.Listen(":3000")
log.Fatal(app.Listen(":3000"))
}

View File

@@ -3,7 +3,6 @@ package model
import (
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"time"
)
type UserClaims struct {
@@ -12,17 +11,17 @@ type UserClaims struct {
jwt.RegisteredClaims
}
type Contact struct {
ID int `json:"contact_id"`
ConversationID uuid.UUID `json:"conversation_id"`
UserID uuid.UUID `json:"user_id"`
Username string `json:"username"`
LastActive *time.Time `json:"last_active"`
Type string `json:"type"`
LastReadMessageID *int `json:"last_read_message_id"`
LastMessageID *int `json:"last_message_id"`
LastMessage *time.Time `json:"last_message"`
LastMessageTime *string `json:"last_message_time"`
LastMessageSender *string `json:"last_message_sender"`
ID int `json:"contact_id"`
ConversationID uuid.UUID `json:"conversation_id"`
UserID uuid.UUID `json:"user_id"`
Username string `json:"username"`
LastActive *string `json:"last_active"`
Type string `json:"type"`
LastReadMessageID *int `json:"last_read_message_id"`
LastMessageID *int `json:"last_message_id"`
LastMessage *string `json:"last_message"`
LastMessageTime *string `json:"last_message_time"`
LastMessageSender *string `json:"last_message_sender"`
}
type ContactSuggestion struct {
@@ -32,7 +31,7 @@ type ContactSuggestion struct {
type Message struct {
ID int `json:"message_id"`
Message string `json:"message"`
SentAt time.Time `json:"sent_at"`
SentAt string `json:"sent_at"`
Sender string `json:"sender"`
SenderID uuid.UUID `json:"sender_id"`
AttachmentUrl *string `json:"attachment_url"`

View File

@@ -39,6 +39,6 @@ func SetupRoutes(app *fiber.App) {
groups.Get("/getMembers/:groupID", handlers.GetMembers)
// Socket group
socket := chat.Group("/ws", middleware.Protected(), logger.New())
socket.Get("/:id")
//socket := chat.Group("/ws", middleware.Protected(), logger.New())
//socket.Get("/:id")
}

View File

@@ -10,6 +10,11 @@ import (
socketio "github.com/googollee/go-socket.io"
)
type JoinRoomResponse struct {
Status string `json:"status"`
Message string `json:"message"`
}
// Message represents the chat message structure
type Message struct {
Content string `json:"message"`
@@ -43,11 +48,10 @@ type SocketResponse struct {
}
// InitializeSocket sets up and configures the Socket.IO server
func InitializeSocket() (*socketio.Server, error) {
server := socketio.NewServer(nil)
func InitializeSocket(server *socketio.Server) error {
// Middleware for authentication
server.OnConnect("/", func(s socketio.Conn) error {
s.SetContext("")
token := s.RemoteHeader().Get("Authorization")
if token == "" {
log.Println("(socket) Not logged in")
@@ -90,12 +94,12 @@ func InitializeSocket() (*socketio.Server, error) {
log.Printf("(socket) Failed to get user conversations: %v\n", err)
return
}
log.Println("CONVERSATIONS", conversations)
// Join all conversations
for _, conv := range conversations {
s.Join(conv)
}
s.Join(userID) // Join user's personal room
s.Join(userID)
log.Printf("User: %s joined to: %v\n", username, conversations)
})
@@ -178,21 +182,30 @@ func InitializeSocket() (*socketio.Server, error) {
return SocketResponse{Status: "error", Message: "No user id provided"}
}
err := removeUserFromGroupById(data.GroupID, data.UserID)
msg, err := database.RemoveUserFromGroup(database.DB, data.GroupID, data.UserID)
if msg != "" {
return SocketResponse{Status: "error", Message: msg}
}
if err != nil {
return SocketResponse{Status: "error", Message: err.Error()}
}
// Remove user from room
sockets := server.Sockets(data.GroupID)
for _, socket := range sockets {
if socket.Context().(map[string]interface{})["user_id"] == data.UserID {
socket.Leave(data.GroupID)
server.ForEach("/", data.GroupID.String(), func(c socketio.Conn) {
// Get the context and check user ID
if ctxData, ok := c.Context().(map[string]interface{}); ok {
if userIDFromCtx, ok := ctxData["user_id"].(string); ok {
if userIDFromCtx == data.UserID.String() {
c.Leave(data.GroupID.String())
}
}
}
}
})
groupIDstr := data.GroupID.String()
userIDstr := data.UserID.String()
server.BroadcastToRoom("", data.GroupID, "left group", data)
server.BroadcastToRoom("", data.UserID, "left group", data)
server.BroadcastToRoom("", groupIDstr, "left group", data)
server.BroadcastToRoom("", userIDstr, "left group", data)
return SocketResponse{Status: "ok", Message: "Successfully removed user from group"}
})
@@ -200,59 +213,118 @@ func InitializeSocket() (*socketio.Server, error) {
// Handle administrator operations
server.OnEvent("/", "added administrator", func(s socketio.Conn, data GroupUserData) SocketResponse {
ctx := s.Context().(map[string]interface{})
userID := ctx["user_id"].(string)
userIDstr := ctx["user_id"].(string)
if data.GroupID == "" {
userID, err := uuid.Parse(userIDstr)
if err != nil {
return SocketResponse{Status: "error", Message: "Invalid user id"}
}
if data.GroupID == uuid.Nil {
return SocketResponse{Status: "error", Message: "No conversation id provided"}
}
if data.UserID == "" {
if data.UserID == uuid.Nil {
return SocketResponse{Status: "error", Message: "No user id provided"}
}
isAdmin, err := isAdmin(userID, data.GroupID)
isAdmin, err := database.IsAdmin(database.DB, data.UserID, data.GroupID)
if err != nil || !isAdmin {
return SocketResponse{Status: "error", Message: "You are not an administrator"}
}
err = addAdministrator(data.GroupID, data.UserID, userID)
msg, err := database.AddAdministrator(database.DB, data.GroupID, data.UserID, userID)
if err != nil {
return SocketResponse{Status: "error", Message: err.Error()}
}
if msg != "" {
return SocketResponse{Status: "error", Message: msg}
}
server.BroadcastToRoom("", data.GroupID, "added administrator", data)
groupIDstr := data.GroupID.String()
server.BroadcastToRoom("", groupIDstr, "added administrator", data)
return SocketResponse{Status: "ok", Message: "Successfully added administrator"}
})
// Handle message read status
server.OnEvent("/", "message read", func(s socketio.Conn, data MessageReadData) {
server.OnEvent("/", "message read", func(s socketio.Conn, data MessageReadData) SocketResponse {
ctx := s.Context().(map[string]interface{})
userID := ctx["user_id"].(string)
userIDstr := ctx["user_id"].(string)
if data.ConversationID == "" || data.MessageID == "" {
return
userID, err := uuid.Parse(userIDstr)
if err != nil {
return SocketResponse{Status: "error", Message: "Invalid user id"}
}
err := updateContactStatus(userID, data.ConversationID, data.MessageID)
if data.ConversationID == uuid.Nil || data.MessageID == 0 {
return SocketResponse{Status: "error", Message: "Invalid conversation or message id"}
}
err = database.UpdateContactStatus(database.DB, userID, data.ConversationID, data.MessageID)
if err != nil {
log.Printf("Failed to update message read status: %v\n", err)
}
return SocketResponse{Status: "ok", Message: "Successfully updated message read status"}
})
server.OnEvent("/", "join room", func(s socketio.Conn, conversationIDstr string, callback func(JoinRoomResponse)) {
// Get user data from context
ctx := s.Context().(map[string]interface{})
userIDstr := ctx["user_id"].(string)
username := ctx["username"].(string)
log.Printf("JOIN ROOM SDJKLSDFJKLSDFJKLSDFJKLSDFSDFJKLSLDFJKJLSDFKSDFJKLSDFJKLSDFJKLSDFJLKSFDLJKSDFJKLSDFJLKSDFJLKSDFJLKSDFJLSDFKSDFJKLSDFJKLJKL")
userID, err := uuid.Parse(userIDstr)
if err != nil {
callback(JoinRoomResponse{
Status: "error",
Message: "Invalid user id",
})
return
}
conversationID, err := uuid.Parse(conversationIDstr)
if err != nil {
callback(JoinRoomResponse{
Status: "error",
Message: "Invalid conversation id",
})
return
}
// Check if user is member of the conversation
isMember, err := database.IsMember(database.DB, userID, conversationID)
if err != nil {
callback(JoinRoomResponse{
Status: "error",
Message: "Failed to check conversation membership",
})
return
}
if isMember {
log.Printf("Join room for: %s, room: %s", username, conversationID)
s.Join(conversationIDstr)
callback(JoinRoomResponse{
Status: "ok",
Message: "Successfully joined to conversation",
})
} else {
callback(JoinRoomResponse{
Status: "error",
Message: "You are not member of this group",
})
}
})
server.OnEvent("/", "*", func(s socketio.Conn, event string, data interface{}) {
log.Printf("Received event: %s, data: %v\n", event, data)
})
server.OnError("/", func(s socketio.Conn, e error) {
log.Printf("Error: %v\n", e)
})
// Handle disconnection
server.OnDisconnect("/", func(s socketio.Conn, reason string) {
log.Printf("(socket) %s disconnected due to: %s\n", s.ID(), reason)
})
return server, nil
return nil
}
// The following functions would need to be implemented according to your specific needs:
// - verifyJwtToken
// - isValidUsername
// - getConversationsForUser
// - insertMessage
// - deleteMessage
// - removeUserFromGroupById
// - isAdmin
// - addAdministrator
// - updateContactStatus