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
|
||||
}
|
||||
|
||||
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;
|
||||
`
|
||||
|
||||
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
44
main.go
@@ -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"))
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
146
socket/socket.go
146
socket/socket.go
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user