package httpRequest import ( json2 "encoding/json" "maps" "net/http" "slices" "strings" "time" "go-socket/packages/Enums/ConnectionState" "go-socket/packages/Enums/WsEventType" "go-socket/packages/cache" "go-socket/packages/config" "go-socket/packages/convertions" "go-socket/packages/postgresql" "go-socket/packages/types" "go-socket/packages/wsServer" "github.com/google/uuid" ) func HandleDm(response http.ResponseWriter, request *http.Request) { if !validCheckWithResponseOnFail(&response, request, normal) { return } ctx := request.Context() user, err := getUserByToken(ctx, request.Header.Get("token")) if err != nil { http.Error(response, "invalid token", http.StatusUnauthorized) return } conn, ok := getConnectionWithResponseOnFail(&response, request, user) if !ok { return } msgContent := request.FormValue("msgContent") attachedFile := request.FormValue("attachedFile") if msgContent == "" && attachedFile == "" { http.Error(response, "empty msgContent", http.StatusBadRequest) return } if attachedFile != "" && !strings.HasPrefix(attachedFile, conn.Id.String()+"/") { http.Error(response, "invalid attachedFile", http.StatusBadRequest) return } var target *types.User if user.Id == conn.RequestorId { target, err = getUserById(ctx, conn.RecipientId) } else if user.Id == conn.RecipientId { target, err = getUserById(ctx, conn.RequestorId) } else { http.Error(response, "invalid connectionid", http.StatusBadRequest) return } if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return } message := &types.Message{ Id: uuid.New(), Content: msgContent, AttachedFile: attachedFile, CreatedAt: time.Now(), Sender: user.Id, Receiver: conn.Id, } if target.WsConn != nil { wsServer.WsSendMessageCloseIfTimeout(target, types.WsEventMessage{ Type: WsEventType.DirectMessage, Event: message, }) } else { cache.IncrementConnectionsUnreadMessages(target.Id, conn.Id) } err = postgresql.ConnectionMessageSave(ctx, message) if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return } response.WriteHeader(http.StatusAccepted) } func HandleUserGetConnectionsUnreadMessages(response http.ResponseWriter, request *http.Request) { if !validCheckWithResponseOnFail(&response, request, normal) { return } ctx := request.Context() user, err := getUserByToken(ctx, request.Header.Get("token")) if err != nil { http.Error(response, "invalid token", http.StatusUnauthorized) return } connectionIds, err := convertions.StringToUuidSlice(request.URL.Query().Get("connections")) if err != nil { http.Error(response, "invalid uuid format", http.StatusBadRequest) return } result := make([]uint32, 0, len(connectionIds)) for _, connId := range connectionIds { _, ok := cache.GetConnection(user, connId) if !ok { http.Error(response, "no such connection: "+connId.String(), http.StatusUnauthorized) return } } for _, connId := range connectionIds { count, _ := cache.GetConnectionsUnreadMessages(user.Id, connId) cache.DeallocateConnectionsUnreadMessages(user.Id, connId) result = append(result, count) } json, err := json2.Marshal(result) if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return } response.WriteHeader(http.StatusAccepted) response.Write(json) } func HandleUserGetConnectionMessages(response http.ResponseWriter, request *http.Request) { if !validCheckWithResponseOnFail(&response, request, normal) { return } ctx := request.Context() user, err := getUserByToken(ctx, request.Header.Get("token")) if err != nil { http.Error(response, "invalid token", http.StatusUnauthorized) return } conn, ok := getConnectionWithResponseOnFail(&response, request, user) if !ok { return } before, err := convertions.StringToTimestamp(request.URL.Query().Get("before")) if err != nil { before = time.Now() } messagesCap, err := convertions.StringToUint32(request.URL.Query().Get("messages")) if err != nil { messagesCap = config.MaxDirectMsgCache } buffer, bufferSize := conn.GetSortedMessagesBuff() var validBufCount uint32 for validBufCount < bufferSize && buffer[validBufCount].CreatedAt.Before(before) { validBufCount++ } var messages []*types.Message if validBufCount >= messagesCap { start := validBufCount - messagesCap messages = make([]*types.Message, messagesCap) for i := uint32(0); i < messagesCap; i++ { messages[i] = buffer[start+i] } } else { remaining := messagesCap - validBufCount cutoff := before if validBufCount > 0 { cutoff = buffer[0].CreatedAt } dbMessages, err := postgresql.ConnectionGetMessagesBefore(ctx, cutoff, conn.Id, remaining) if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return } messages = make([]*types.Message, 0, uint32(len(dbMessages))+validBufCount) messages = append(messages, dbMessages...) for i := uint32(0); i < validBufCount; i++ { messages = append(messages, buffer[i]) } } json, err := json2.Marshal(messages) if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return } response.WriteHeader(http.StatusOK) response.Write(json) } func HandleUserNewConnection(response http.ResponseWriter, request *http.Request) { if !validCheckWithResponseOnFail(&response, request, normal) { return } ctx := request.Context() requestor, err := getUserByToken(ctx, request.Header.Get("token")) if err != nil { http.Error(response, "invalid token", http.StatusUnauthorized) return } recipientId, err := convertions.StringToUuid(request.FormValue("recipient")) if err != nil { http.Error(response, "no such user", http.StatusUnauthorized) return } recipient, err := getUserById(ctx, recipientId) if err != nil { http.Error(response, "no such user", http.StatusUnauthorized) return } if requestor.Id == recipient.Id { http.Error(response, "cannot connect to yourself", http.StatusBadRequest) return } requestor.Mu.RLock() for _, connection := range requestor.Connections { if (connection.RequestorId == requestor.Id && connection.RecipientId == recipient.Id) || (connection.RecipientId == requestor.Id && connection.RequestorId == recipient.Id) { requestor.Mu.RUnlock() http.Error(response, "connection already exists", http.StatusBadRequest) return } } requestor.Mu.RUnlock() connection := &types.Connection{ CreatedAt: time.Now(), RequestorId: requestor.Id, RecipientId: recipient.Id, State: ConnectionState.Stranger, } err = postgresql.ConnectionSave(ctx, connection) if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return } cache.AddConnection(requestor, recipient, connection) wsServer.WsSendMessageCloseIfTimeout(recipient, types.WsEventMessage{ Type: WsEventType.ConnectionCreated, Event: connection, }) response.WriteHeader(http.StatusCreated) return } func HandleUserDeleteConnection(response http.ResponseWriter, request *http.Request) { if !validCheckWithResponseOnFail(&response, request, normal) { return } ctx := request.Context() user, err := getUserByToken(ctx, request.Header.Get("token")) if err != nil { http.Error(response, "invalid token", http.StatusUnauthorized) return } conn, ok := getConnectionWithResponseOnFail(&response, request, user) if !ok { return } var user2 *types.User if conn.RequestorId == user.Id { recipient, err := getUserById(ctx, conn.RecipientId) if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return } user2 = recipient } else if conn.RecipientId == user.Id { requestor, err := getUserById(ctx, conn.RequestorId) if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return } user2 = requestor } else { http.Error(response, "invalid connectionid", http.StatusBadRequest) return } err = postgresql.ConnectionDelete(ctx, conn) if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return } cache.DeleteConnection(user, user2, conn.Id) wsServer.WsSendMessageCloseIfTimeout(user2, types.WsEventMessage{ Type: WsEventType.ConnectionDeleted, Event: conn.Id, }) response.WriteHeader(http.StatusAccepted) response.Write(conn.Id[:]) } func HandleUserElevateConnection(response http.ResponseWriter, request *http.Request) { if !validCheckWithResponseOnFail(&response, request, normal) { return } ctx := request.Context() user, err := getUserByToken(ctx, request.Header.Get("token")) if err != nil { http.Error(response, "invalid token", http.StatusUnauthorized) return } conn, ok := getConnectionWithResponseOnFail(&response, request, user) if !ok { return } response.WriteHeader(http.StatusAccepted) if conn.UserWantingToElevate != (uuid.UUID{}) && conn.UserWantingToElevate != user.Id { switch conn.State { case ConnectionState.Stranger: conn.State = ConnectionState.Friend break default: http.Error(response, "cannot elevate further", http.StatusBadRequest) return } err = postgresql.ConnectionUpdateState(ctx, conn) if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return } response.Write([]byte("elevated")) var user2 *types.User if conn.RequestorId == user.Id { user2, err = getUserById(ctx, conn.RecipientId) } else { user2, err = getUserById(ctx, conn.RequestorId) } if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return } wsServer.WsSendMessageCloseIfTimeout(user2, types.WsEventMessage{ Type: WsEventType.ConnectionElevated, Event: types.ConnectionStatusSetData{ Id: conn.Id, NewState: conn.State, }, }) return } conn.UserWantingToElevate = user.Id response.Write([]byte("waiting for second user to elevate")) } func HandleUserDeElevateConnection(response http.ResponseWriter, request *http.Request) { if !validCheckWithResponseOnFail(&response, request, normal) { return } ctx := request.Context() user, err := getUserByToken(ctx, request.Header.Get("token")) if err != nil { http.Error(response, "invalid token", http.StatusUnauthorized) return } conn, ok := getConnectionWithResponseOnFail(&response, request, user) if !ok { return } if conn.State != ConnectionState.Stranger { conn.State = ConnectionState.Stranger err = postgresql.ConnectionUpdateState(ctx, conn) if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return } var user2 *types.User if conn.RequestorId == user.Id { user2, err = getUserById(ctx, conn.RecipientId) } else { user2, err = getUserById(ctx, conn.RequestorId) } wsServer.WsSendMessageCloseIfTimeout(user2, types.WsEventMessage{ Type: WsEventType.ConnectionDeElevated, Event: types.ConnectionStatusSetData{ Id: conn.Id, NewState: conn.State, }, }) response.Write([]byte("de elevated")) return } response.WriteHeader(http.StatusConflict) response.Write([]byte("already lowest possible")) } func HandleUserGetConnections(response http.ResponseWriter, request *http.Request) { if !validCheckWithResponseOnFail(&response, request, normal) { return } ctx := request.Context() user, err := getUserByToken(ctx, request.Header.Get("token")) if err != nil { http.Error(response, "invalid token", http.StatusUnauthorized) return } user.Mu.RLock() connections := slices.Collect(maps.Values(user.Connections)) user.Mu.RUnlock() json, err := json2.Marshal(connections) if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return } response.WriteHeader(http.StatusOK) response.Write(json) }