finished rewriting socket but it isn't working 😊
This commit is contained in:
@@ -347,3 +347,17 @@ func ContactSuggestion(db *sql.DB, contactUsername string) ([]string, error) {
|
|||||||
}
|
}
|
||||||
return suggestions, nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -170,6 +170,11 @@ func RemoveUserFromGroup(db *sql.DB, userID uuid.UUID, groupID uuid.UUID) (strin
|
|||||||
WHERE conversation_id = $1 AND user_id = $2;
|
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)
|
isOwner, err := IsGroupOwner(db, userID, groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "Failed to remove user from group", fmt.Errorf("failed to check if user is group owner: %w", err)
|
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 {
|
if rowsAffected == 0 {
|
||||||
return "User is not a member of the group", nil
|
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
|
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
|
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
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
46
main.go
46
main.go
@@ -1,11 +1,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gofiber/contrib/websocket"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"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"
|
"log"
|
||||||
"relay-server/database"
|
"relay-server/database"
|
||||||
"relay-server/router"
|
"relay-server/router"
|
||||||
|
"relay-server/socket"
|
||||||
"relay-server/utils"
|
"relay-server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,14 +17,14 @@ func main() {
|
|||||||
ErrorHandler: utils.ErrorHandler,
|
ErrorHandler: utils.ErrorHandler,
|
||||||
})
|
})
|
||||||
|
|
||||||
app.Use(func(c *fiber.Ctx) error {
|
server := socketio.NewServer(nil)
|
||||||
if websocket.IsWebSocketUpgrade(c) {
|
|
||||||
c.Locals("allowed", true)
|
|
||||||
return c.Next()
|
|
||||||
}
|
|
||||||
return fiber.ErrUpgradeRequired
|
|
||||||
})
|
|
||||||
|
|
||||||
|
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()
|
db, err := database.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to initialize database")
|
log.Fatal("Failed to initialize database")
|
||||||
@@ -34,6 +37,29 @@ func main() {
|
|||||||
log.Println("Database connection closed")
|
log.Println("Database connection closed")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
router.SetupRoutes(app)
|
err = socket.InitializeSocket(server)
|
||||||
app.Listen(":3000")
|
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)
|
||||||
|
|
||||||
|
log.Fatal(app.Listen(":3000"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package model
|
|||||||
import (
|
import (
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserClaims struct {
|
type UserClaims struct {
|
||||||
@@ -16,11 +15,11 @@ type Contact struct {
|
|||||||
ConversationID uuid.UUID `json:"conversation_id"`
|
ConversationID uuid.UUID `json:"conversation_id"`
|
||||||
UserID uuid.UUID `json:"user_id"`
|
UserID uuid.UUID `json:"user_id"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
LastActive *time.Time `json:"last_active"`
|
LastActive *string `json:"last_active"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
LastReadMessageID *int `json:"last_read_message_id"`
|
LastReadMessageID *int `json:"last_read_message_id"`
|
||||||
LastMessageID *int `json:"last_message_id"`
|
LastMessageID *int `json:"last_message_id"`
|
||||||
LastMessage *time.Time `json:"last_message"`
|
LastMessage *string `json:"last_message"`
|
||||||
LastMessageTime *string `json:"last_message_time"`
|
LastMessageTime *string `json:"last_message_time"`
|
||||||
LastMessageSender *string `json:"last_message_sender"`
|
LastMessageSender *string `json:"last_message_sender"`
|
||||||
}
|
}
|
||||||
@@ -32,7 +31,7 @@ type ContactSuggestion struct {
|
|||||||
type Message struct {
|
type Message struct {
|
||||||
ID int `json:"message_id"`
|
ID int `json:"message_id"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
SentAt time.Time `json:"sent_at"`
|
SentAt string `json:"sent_at"`
|
||||||
Sender string `json:"sender"`
|
Sender string `json:"sender"`
|
||||||
SenderID uuid.UUID `json:"sender_id"`
|
SenderID uuid.UUID `json:"sender_id"`
|
||||||
AttachmentUrl *string `json:"attachment_url"`
|
AttachmentUrl *string `json:"attachment_url"`
|
||||||
|
|||||||
@@ -39,6 +39,6 @@ func SetupRoutes(app *fiber.App) {
|
|||||||
groups.Get("/getMembers/:groupID", handlers.GetMembers)
|
groups.Get("/getMembers/:groupID", handlers.GetMembers)
|
||||||
|
|
||||||
// Socket group
|
// Socket group
|
||||||
socket := chat.Group("/ws", middleware.Protected(), logger.New())
|
//socket := chat.Group("/ws", middleware.Protected(), logger.New())
|
||||||
socket.Get("/:id")
|
//socket.Get("/:id")
|
||||||
}
|
}
|
||||||
|
|||||||
144
socket/socket.go
144
socket/socket.go
@@ -10,6 +10,11 @@ import (
|
|||||||
socketio "github.com/googollee/go-socket.io"
|
socketio "github.com/googollee/go-socket.io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type JoinRoomResponse struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
// Message represents the chat message structure
|
// Message represents the chat message structure
|
||||||
type Message struct {
|
type Message struct {
|
||||||
Content string `json:"message"`
|
Content string `json:"message"`
|
||||||
@@ -43,11 +48,10 @@ type SocketResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// InitializeSocket sets up and configures the Socket.IO server
|
// InitializeSocket sets up and configures the Socket.IO server
|
||||||
func InitializeSocket() (*socketio.Server, error) {
|
func InitializeSocket(server *socketio.Server) error {
|
||||||
server := socketio.NewServer(nil)
|
|
||||||
|
|
||||||
// Middleware for authentication
|
// Middleware for authentication
|
||||||
server.OnConnect("/", func(s socketio.Conn) error {
|
server.OnConnect("/", func(s socketio.Conn) error {
|
||||||
|
s.SetContext("")
|
||||||
token := s.RemoteHeader().Get("Authorization")
|
token := s.RemoteHeader().Get("Authorization")
|
||||||
if token == "" {
|
if token == "" {
|
||||||
log.Println("(socket) Not logged in")
|
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)
|
log.Printf("(socket) Failed to get user conversations: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Println("CONVERSATIONS", conversations)
|
||||||
// Join all conversations
|
// Join all conversations
|
||||||
for _, conv := range conversations {
|
for _, conv := range conversations {
|
||||||
s.Join(conv)
|
s.Join(conv)
|
||||||
}
|
}
|
||||||
s.Join(userID) // Join user's personal room
|
s.Join(userID)
|
||||||
log.Printf("User: %s joined to: %v\n", username, conversations)
|
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"}
|
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 {
|
if err != nil {
|
||||||
return SocketResponse{Status: "error", Message: err.Error()}
|
return SocketResponse{Status: "error", Message: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove user from room
|
// Remove user from room
|
||||||
sockets := server.Sockets(data.GroupID)
|
server.ForEach("/", data.GroupID.String(), func(c socketio.Conn) {
|
||||||
for _, socket := range sockets {
|
// Get the context and check user ID
|
||||||
if socket.Context().(map[string]interface{})["user_id"] == data.UserID {
|
if ctxData, ok := c.Context().(map[string]interface{}); ok {
|
||||||
socket.Leave(data.GroupID)
|
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("", groupIDstr, "left group", data)
|
||||||
server.BroadcastToRoom("", data.UserID, "left group", data)
|
server.BroadcastToRoom("", userIDstr, "left group", data)
|
||||||
|
|
||||||
return SocketResponse{Status: "ok", Message: "Successfully removed user from group"}
|
return SocketResponse{Status: "ok", Message: "Successfully removed user from group"}
|
||||||
})
|
})
|
||||||
@@ -200,59 +213,118 @@ func InitializeSocket() (*socketio.Server, error) {
|
|||||||
// Handle administrator operations
|
// Handle administrator operations
|
||||||
server.OnEvent("/", "added administrator", func(s socketio.Conn, data GroupUserData) SocketResponse {
|
server.OnEvent("/", "added administrator", func(s socketio.Conn, data GroupUserData) SocketResponse {
|
||||||
ctx := s.Context().(map[string]interface{})
|
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"}
|
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"}
|
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 {
|
if err != nil || !isAdmin {
|
||||||
return SocketResponse{Status: "error", Message: "You are not an administrator"}
|
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 {
|
if err != nil {
|
||||||
return SocketResponse{Status: "error", Message: err.Error()}
|
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"}
|
return SocketResponse{Status: "ok", Message: "Successfully added administrator"}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Handle message read status
|
// 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{})
|
ctx := s.Context().(map[string]interface{})
|
||||||
userID := ctx["user_id"].(string)
|
userIDstr := ctx["user_id"].(string)
|
||||||
|
|
||||||
if data.ConversationID == "" || data.MessageID == "" {
|
userID, err := uuid.Parse(userIDstr)
|
||||||
return
|
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 {
|
if err != nil {
|
||||||
log.Printf("Failed to update message read status: %v\n", err)
|
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
|
// Handle disconnection
|
||||||
server.OnDisconnect("/", func(s socketio.Conn, reason string) {
|
server.OnDisconnect("/", func(s socketio.Conn, reason string) {
|
||||||
log.Printf("(socket) %s disconnected due to: %s\n", s.ID(), reason)
|
log.Printf("(socket) %s disconnected due to: %s\n", s.ID(), reason)
|
||||||
})
|
})
|
||||||
|
return nil
|
||||||
return server, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following functions would need to be implemented according to your specific needs:
|
|
||||||
// - verifyJwtToken
|
|
||||||
// - isValidUsername
|
|
||||||
// - getConversationsForUser
|
|
||||||
// - insertMessage
|
|
||||||
// - deleteMessage
|
|
||||||
// - removeUserFromGroupById
|
|
||||||
// - isAdmin
|
|
||||||
// - addAdministrator
|
|
||||||
// - updateContactStatus
|
|
||||||
|
|||||||
Reference in New Issue
Block a user