Files

372 lines
9.3 KiB
Go

package httpRequest
import (
"encoding/json"
"fmt"
"maps"
"net/http"
"slices"
"time"
"go-socket/packages/Enums/WsEventType"
"go-socket/packages/cache"
"go-socket/packages/convertions"
"go-socket/packages/passwords"
"go-socket/packages/postgresql"
"go-socket/packages/tokens"
"go-socket/packages/types"
"go-socket/packages/wsServer"
"golang.org/x/crypto/bcrypt"
"github.com/google/uuid"
)
func HandleUserNewToken(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
username := request.FormValue("username")
if len(username) < 4 {
http.Error(response, "no or short username", http.StatusBadRequest)
return
}
password := request.FormValue("password")
if len(password) < 8 {
http.Error(response, "no or short passwords", http.StatusBadRequest)
return
}
var (
user *types.User
err error
ctx = request.Context()
)
user, err = cache.GetUserByName(username)
if err != nil {
user = &types.User{Name: username, Hubs: make(map[uuid.UUID]*types.Hub)}
if err = postgresql.UserGetStandardInfoByName(ctx, user); err != nil {
http.Error(response, "bad login", http.StatusUnauthorized)
return
}
if err = postgresql.UserGetWhole(ctx, user); err != nil {
http.Error(response, err.Error(), http.StatusInternalServerError)
return
}
}
err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password))
if err != nil {
http.Error(response, "bad login", http.StatusUnauthorized)
return
}
user.ChannelUnreadMessage = make(map[uuid.UUID]uint8)
cache.SaveUser(user)
token, err := tokens.TokenCreate(user.Id)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
data, err := json.Marshal(types.LoginResponse{Token: token, UserId: user.Id})
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusCreated)
response.Write(data)
}
func HandleUserNew(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
username := request.FormValue("username")
if len(username) < 4 || len(username) > 30 {
http.Error(response, "no or short/too long username", http.StatusBadRequest)
return
}
password := request.FormValue("password")
if len(password) < 8 {
http.Error(response, "no or short passwords", http.StatusBadRequest)
return
}
hashedPassword, err := passwords.PasswordHash(password)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
newUser := &types.User{
Name: username,
PasswordHash: hashedPassword,
Color: types.RandomRgba(),
CreatedAt: time.Now(),
}
ctx := request.Context()
err = postgresql.UserSave(ctx, newUser)
if err != nil {
http.Error(response, "name taken", http.StatusConflict)
return
}
response.WriteHeader(http.StatusCreated)
}
func HandleUserDelete(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
ctx := request.Context()
userId, err := tokens.TokenValidateGetId(request.Header.Get("token"))
if err != nil {
http.Error(response, "invalid token", http.StatusUnauthorized)
return
}
err = postgresql.UserDelete(ctx, userId)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
cache.DeleteUser(userId)
response.WriteHeader(http.StatusAccepted)
}
func HandleUserModProfile(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
}
updateList := &types.UserProfileUpdate{}
updatedValues := map[string]any{}
if pronouns := request.FormValue("pronouns"); pronouns != "" {
if len(pronouns) > 32 {
http.Error(response, "pronouns too long", http.StatusBadRequest)
return
}
user.Pronouns = pronouns
updateList.Pronouns = true
updatedValues["pronouns"] = pronouns
}
if description := request.FormValue("description"); description != "" {
if len(description) > 256 {
http.Error(response, "description too long", http.StatusBadRequest)
return
}
user.Description = description
updateList.Description = true
updatedValues["description"] = description
}
if colorStr := request.FormValue("color"); colorStr != "" {
color, err := convertions.StringToRgba(colorStr)
if err != nil {
http.Error(response, "invalid color", http.StatusBadRequest)
return
}
user.Color = color
updateList.Color = true
updatedValues["color"] = color
}
if len(updatedValues) == 0 {
http.Error(response, "no values", http.StatusBadRequest)
return
}
err = postgresql.UserUpdateProfile(ctx, user, updateList)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
user.Mu.RLock()
connections := slices.Collect(maps.Values(user.Connections))
user.Mu.RUnlock()
for _, conn := range connections {
target, err := getUserById(ctx, conn.GetSecondUser(user.Id))
if err != nil {
continue
}
wsServer.WsSendMessageCloseIfTimeout(target, &types.WsEventMessage{
Type: WsEventType.UserProfileChange,
Event: &map[string]any{
"userId": user.Id,
"profileChangeList": updatedValues,
},
})
}
user.Mu.RLock()
hubs := slices.Collect(maps.Values(user.Hubs))
user.Mu.RUnlock()
for _, hub := range hubs {
hub.Mu.RLock()
hubUsers := slices.Collect(maps.Values(hub.Users))
hub.Mu.RUnlock()
for _, hubUser := range hubUsers {
if hubUser.OriginalId == user.Id {
continue
}
target, err := getUserById(ctx, hubUser.OriginalId)
if err != nil {
continue
}
wsServer.WsSendMessageCloseIfTimeout(target, &types.WsHubSpecificHubEventMessage{
Type: WsEventType.UserProfileChange,
HubId: hub.Id,
Event: &map[string]any{
"userId": user.Id,
"profileChangeList": updatedValues,
},
})
}
}
response.WriteHeader(http.StatusAccepted)
}
func HandleUserGetUser(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
ctx := request.Context()
_, err := getUserByToken(ctx, request.Header.Get("token"))
if err != nil {
http.Error(response, "invalid token", http.StatusUnauthorized)
return
}
targetId, err := convertions.StringToUuid(request.URL.Query().Get("target_id"))
if err != nil {
http.Error(response, "invalid target_id", http.StatusBadRequest)
return
}
target, err := getUserById(ctx, targetId)
if err != nil {
http.Error(response, "user not found", http.StatusNotFound)
return
}
userData, err := json.Marshal(target)
if err != nil {
http.Error(response, "json parse error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusAccepted)
response.Write(userData)
}
func HandleUserGetUsers(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
ctx := request.Context()
_, err := getUserByToken(ctx, request.Header.Get("token"))
if err != nil {
http.Error(response, "invalid token", http.StatusUnauthorized)
return
}
targetIds, err := convertions.StringToUuids(request.URL.Query().Get("target_ids"))
if err != nil {
http.Error(response, "invalid targetids", http.StatusBadRequest)
return
}
users, err := cache.GetUsersByIds(targetIds)
if err != nil {
http.Error(response, err.Error(), http.StatusBadRequest)
return
}
userData, err := json.Marshal(users)
if err != nil {
http.Error(response, "json parse error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusAccepted)
response.Write(userData)
}
func HandleGetHubs(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.StatusBadRequest)
return
}
user.Mu.RLock()
hubs := slices.Collect(maps.Keys(user.Hubs))
user.Mu.RUnlock()
if len(hubs) == 0 {
response.WriteHeader(http.StatusNoContent)
response.Write([]byte("no hubs found"))
return
}
converted, err := json.Marshal(hubs)
if err != nil {
http.Error(response, "json error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
response.Write(converted)
}
func HandleGetChannelUnreadMessagesCount(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
}
targetId, err := convertions.StringToUuid(request.URL.Query().Get("target_id"))
if err != nil {
http.Error(response, "invalid targetid", http.StatusBadRequest)
return
}
response.WriteHeader(http.StatusOK)
user.Mu.Lock()
response.Write([]byte(fmt.Sprintf("%d", user.ChannelUnreadMessage[targetId])))
user.ChannelUnreadMessage[targetId] = 0
user.Mu.Unlock()
}