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/convertions" "go-socket/packages/globals" "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 !postValidCheckWithResponseOnFail(&response, request, false) { return } ctx := request.Context() user, err := getUserByToken(ctx, request.FormValue("token")) if err != nil { http.Error(response, "invalid token", http.StatusUnauthorized) return } targetConnection, err := convertions.ConvertStringUuid(request.FormValue("connectionid")) if err != nil { http.Error(response, "invalid connectionid", http.StatusBadRequest) return } conn, ok := cache.CacheGetConnection(user, targetConnection) if !ok { http.Error(response, "invalid connectionid", http.StatusBadRequest) 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, targetConnection.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, } wsServer.WsSendMessageCloseIfTimeout(target, types.WsEventMessage{ Type: WsEventType.DirectMessage, Event: message, }) err = postgresql.ConnectionMessageSave(ctx, message) if err != nil { http.Error(response, "internal server error", http.StatusInternalServerError) return } response.WriteHeader(http.StatusAccepted) } func HandleUserGetConnectionMessages(response http.ResponseWriter, request *http.Request) { if !postValidCheckWithResponseOnFail(&response, request, false) { return } ctx := request.Context() user, err := getUserByToken(ctx, request.FormValue("token")) if err != nil { http.Error(response, "invalid token", http.StatusUnauthorized) return } connectionId, err := convertions.ConvertStringUuid(request.FormValue("connectionid")) if err != nil { http.Error(response, "invalid connectionid", http.StatusBadRequest) return } before, err := convertions.ConvertStringTimestamp(request.FormValue("before")) if err != nil { before = time.Now() } messagesCap, err := convertions.StringToUint32(request.FormValue("messages")) if err != nil { messagesCap = globals.MaxDirectMsgCache } conn, ok := cache.CacheGetConnection(user, connectionId) if !ok { http.Error(response, "invalid connectionid", http.StatusBadRequest) return } 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, connectionId, 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 !postValidCheckWithResponseOnFail(&response, request, false) { return } ctx := request.Context() requestor, err := getUserByToken(ctx, request.FormValue("token")) if err != nil { http.Error(response, "invalid token", http.StatusUnauthorized) return } recipientId, err := convertions.ConvertStringUuid(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.CacheAddConnection(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 !postValidCheckWithResponseOnFail(&response, request, false) { return } ctx := request.Context() user, err := getUserByToken(ctx, request.FormValue("token")) if err != nil { http.Error(response, "invalid token", http.StatusUnauthorized) return } connectionId, err := convertions.ConvertStringUuid(request.FormValue("connectionid")) if err != nil { http.Error(response, "invalid connectionid", http.StatusBadRequest) return } conn, ok := cache.CacheGetConnection(user, connectionId) if !ok { http.Error(response, "invalid connectionid", http.StatusBadRequest) 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.CacheDeleteConnection(user, user2, connectionId) wsServer.WsSendMessageCloseIfTimeout(user2, types.WsEventMessage{ Type: WsEventType.ConnectionDeleted, Event: connectionId, }) response.WriteHeader(http.StatusAccepted) } func HandleUserElevateConnection(response http.ResponseWriter, request *http.Request) { if !postValidCheckWithResponseOnFail(&response, request, false) { return } ctx := request.Context() user, err := getUserByToken(ctx, request.FormValue("token")) if err != nil { http.Error(response, "invalid token", http.StatusUnauthorized) return } connectionId, err := convertions.ConvertStringUuid(request.FormValue("connectionid")) if err != nil { http.Error(response, "invalid connectionid", http.StatusBadRequest) return } conn, ok := cache.CacheGetConnection(user, connectionId) if !ok { http.Error(response, "invalid connectionid", http.StatusBadRequest) 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 case ConnectionState.GroupFellow: conn.State = ConnectionState.Stranger 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.ConnectionElevationData{ Id: connectionId, NewState: conn.State, }, }) return } conn.UserWantingToElevate = user.Id response.Write([]byte("waiting for second user to elevate")) } func HandleUserGetConnections(response http.ResponseWriter, request *http.Request) { if !postValidCheckWithResponseOnFail(&response, request, false) { return } ctx := request.Context() user, err := getUserByToken(ctx, request.FormValue("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) }