449 lines
12 KiB
Go
449 lines
12 KiB
Go
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.NewConn()
|
|
connection.CreatedAt = time.Now()
|
|
connection.RequestorId = requestor.Id
|
|
connection.RecipientId = recipient.Id
|
|
connection.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)
|
|
}
|