346 lines
7.9 KiB
Go
346 lines
7.9 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
func isMethodAllowed(response *http.ResponseWriter, request *http.Request) bool {
|
|
if request.Method != http.MethodPost {
|
|
http.Error(*response, "POST only", http.StatusMethodNotAllowed)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func parseRgb(str string) ([3]uint8, error) {
|
|
parts := strings.SplitN(str, ",", 4)
|
|
if len(parts) != 3 {
|
|
return [3]uint8{}, fmt.Errorf("invalid rgb")
|
|
}
|
|
var rgb [3]uint8
|
|
for i, p := range parts {
|
|
n, err := strconv.ParseUint(strings.TrimSpace(p), 10, 8)
|
|
if err != nil {
|
|
return [3]uint8{}, fmt.Errorf("invalid component %d: %w", i, err)
|
|
}
|
|
rgb[i] = uint8(n)
|
|
}
|
|
return rgb, nil
|
|
}
|
|
|
|
func HttpHandleNewUser(response http.ResponseWriter, request *http.Request) {
|
|
if !isMethodAllowed(&response, request) {
|
|
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 password", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
color, err := parseRgb(request.FormValue("color"))
|
|
if err != nil {
|
|
http.Error(response, "bad color", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
hashedPassword, err := PasswordHash(password)
|
|
if err != nil {
|
|
http.Error(response, "internal server error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
newClient := &Client{
|
|
Name: username,
|
|
PasswordHash: hashedPassword,
|
|
Color: color,
|
|
CreatedAt: time.Now(),
|
|
}
|
|
|
|
ctx := request.Context()
|
|
|
|
err = DbSaveClientWithoutGroups(ctx, newClient)
|
|
if err != nil {
|
|
http.Error(response, "name taken", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
response.WriteHeader(http.StatusAccepted)
|
|
response.Write([]byte("created"))
|
|
}
|
|
|
|
func HttpHandleNewToken(response http.ResponseWriter, request *http.Request) {
|
|
if !isMethodAllowed(&response, request) {
|
|
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 password", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
var (
|
|
client *Client
|
|
err error
|
|
ctx = request.Context()
|
|
)
|
|
|
|
client, err = CacheGetClientByName(username)
|
|
if err != nil {
|
|
client = &Client{Name: username}
|
|
|
|
err := DbSetClientByName(ctx, client)
|
|
if err != nil {
|
|
http.Error(response, "bad login1", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
CacheSaveClient(client)
|
|
}
|
|
|
|
err = bcrypt.CompareHashAndPassword([]byte(client.PasswordHash), []byte(password))
|
|
if err != nil {
|
|
http.Error(response, "bad login2", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
token, err := TokenCreate(client.Id)
|
|
if err != nil {
|
|
http.Error(response, "internal server error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
response.WriteHeader(http.StatusAccepted)
|
|
response.Write([]byte(token))
|
|
}
|
|
|
|
func HttpHandeNewGroup(response http.ResponseWriter, request *http.Request) {
|
|
if !isMethodAllowed(&response, request) {
|
|
return
|
|
}
|
|
|
|
token := request.FormValue("token")
|
|
clientId, err := TokenValidateGetId(token)
|
|
if err != nil {
|
|
http.Error(response, "invalid token", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
name := request.FormValue("name")
|
|
if name == "" {
|
|
name = "Best group ever"
|
|
}
|
|
|
|
colorString := request.FormValue("color")
|
|
color, err := parseRgb(colorString)
|
|
if err != nil {
|
|
var ok bool
|
|
color, ok = Colors[colorString]
|
|
if !ok {
|
|
color = Colors["default"]
|
|
}
|
|
}
|
|
|
|
ctx := request.Context()
|
|
|
|
client := Client{Id: clientId}
|
|
cacheClient, err := CacheGetClientById(clientId)
|
|
if err == nil {
|
|
client = *cacheClient
|
|
} else {
|
|
err = DbSetClientByIdWithoutGroups(ctx, &client)
|
|
if err != nil {
|
|
http.Error(response, "internal server error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
|
|
group := Group{
|
|
Name: name,
|
|
CreatedAt: time.Now(),
|
|
OwnerId: clientId,
|
|
CreatorId: clientId,
|
|
Color: color,
|
|
Clients: map[uint32]struct{}{clientId: {}},
|
|
}
|
|
|
|
enableClientColors := request.FormValue("enableClientColors")
|
|
if enableClientColors == "1" {
|
|
group.EnableClientColors = true
|
|
}
|
|
|
|
err = DbSaveGroupWithoutClients(ctx, &group)
|
|
if err != nil {
|
|
http.Error(response, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
groupIdBytes := make([]byte, 4)
|
|
binary.BigEndian.PutUint32(groupIdBytes, group.Id)
|
|
|
|
response.WriteHeader(http.StatusCreated)
|
|
response.Write(groupIdBytes)
|
|
}
|
|
|
|
func HttpHandleGroupAddClient(response http.ResponseWriter, request *http.Request) {
|
|
if !isMethodAllowed(&response, request) {
|
|
return
|
|
}
|
|
|
|
token := request.FormValue("token")
|
|
clientId, err := TokenValidateGetId(token)
|
|
if err != nil {
|
|
http.Error(response, "invalid token", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
affectedGroupId, err := ConvertStringUint32(request.FormValue("groupid"))
|
|
if err != nil {
|
|
http.Error(response, "no such group", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
ctx := request.Context()
|
|
|
|
var group Group
|
|
group.Id = affectedGroupId
|
|
groupPtr, err := CacheGetGroup(affectedGroupId)
|
|
if err == nil {
|
|
group = *groupPtr
|
|
} else {
|
|
err = DbSetGroupByIdWithoutClients(ctx, &group)
|
|
if err != nil {
|
|
http.Error(response, "no such group", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
}
|
|
|
|
if group.OwnerId != clientId {
|
|
http.Error(response, "no such group", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
usersToAddString := request.FormValue("users")
|
|
var remainingUsersCount = int(MaxClientsInGroup) - len(group.Clients)
|
|
if remainingUsersCount < 1 {
|
|
http.Error(response, "max users", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
userIdStrings := strings.SplitN(usersToAddString, ",", remainingUsersCount+1)
|
|
if len(userIdStrings) == 0 {
|
|
http.Error(response, "no users to add", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
clientIds := make([]uint32, 0, len(userIdStrings))
|
|
for _, s := range userIdStrings {
|
|
id, err := ConvertStringUint32(strings.TrimSpace(s))
|
|
if err != nil {
|
|
continue
|
|
}
|
|
clientIds = append(clientIds, id)
|
|
}
|
|
if len(clientIds) == 0 {
|
|
http.Error(response, "no valid users", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
err = DbAddClientsToGroup(ctx, group.Id, clientIds)
|
|
if err != nil {
|
|
http.Error(response, "internal server error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
response.WriteHeader(http.StatusAccepted)
|
|
_, err = response.Write([]byte("ok"))
|
|
if err != nil {
|
|
http.Error(response, "internal server error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
|
|
func HttpHandleNewMessage(response http.ResponseWriter, request *http.Request) {
|
|
if !isMethodAllowed(&response, request) {
|
|
return
|
|
}
|
|
|
|
token := request.FormValue("token")
|
|
if token == "" {
|
|
http.Error(response, "invalid token", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
targetStr := request.FormValue("subject")
|
|
if targetStr == "" {
|
|
http.Error(response, "invalid subject", http.StatusBadRequest)
|
|
return
|
|
}
|
|
targetId, err := ConvertStringUint32(targetStr)
|
|
if err != nil {
|
|
http.Error(response, "invalid subject", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
content := request.FormValue("content")
|
|
if content == "" {
|
|
http.Error(response, "invalid content", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
clientId, err := TokenValidateGetId(token)
|
|
if err != nil {
|
|
http.Error(response, "invalid token", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
ctx := request.Context()
|
|
err = WsSendToGroup(ctx, targetId, clientId, content)
|
|
if err != nil {
|
|
http.Error(response, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
response.WriteHeader(http.StatusAccepted)
|
|
response.Write([]byte("sent"))
|
|
}
|
|
|
|
func HttpHandleGroupsGeWithoutMembers(response http.ResponseWriter, request *http.Request) {
|
|
if !isMethodAllowed(&response, request) {
|
|
return
|
|
}
|
|
|
|
token := request.FormValue("token")
|
|
if token == "" {
|
|
http.Error(response, "invalid token", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
clientId, err := TokenValidateGetId(token)
|
|
if err != nil {
|
|
http.Error(response, "invalid token", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
ctx := request.Context()
|
|
|
|
}
|