Updated everything, fixed deleting message synchronisation, fixed chromium support, added inviting other users as an admin
This commit is contained in:
parent
1a607e30e0
commit
a3f4bb24e7
|
@ -298,7 +298,8 @@ func main() {
|
|||
}
|
||||
|
||||
var userId uuid.UUID
|
||||
err = conn.QueryRow("SELECT id FROM users WHERE username = ?", data.Username).Scan(&userId)
|
||||
var administrator int
|
||||
err = conn.QueryRow("SELECT administrator, id FROM users WHERE username = ?", data.Username).Scan(&administrator, &userId)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
renderJSON(map[string]interface{}{"error": "Invalid username"}, w)
|
||||
|
@ -328,7 +329,11 @@ func main() {
|
|||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
renderJSON(map[string]interface{}{"token": token, "userId": userId.String()}, w)
|
||||
returnJSON := map[string]interface{}{"token": token, "userId": userId.String()}
|
||||
if administrator == 1 {
|
||||
returnJSON["admin"] = true
|
||||
}
|
||||
renderJSON(returnJSON, w)
|
||||
})
|
||||
|
||||
router.Post("/api/invite", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -412,7 +417,7 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
messages := make(map[string]interface{})
|
||||
var messages []map[string]interface{}
|
||||
rows, err := conn.Query("SELECT sender, senderName, message, sent, id FROM messages ORDER BY sent ASC")
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
@ -435,12 +440,13 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
messages[id.String()] = map[string]interface{}{
|
||||
messages = append(messages, map[string]interface{}{
|
||||
"sender": sender.String(),
|
||||
"name": senderName,
|
||||
"message": message,
|
||||
"sent": sent,
|
||||
}
|
||||
"id": id.String(),
|
||||
})
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
@ -590,7 +596,16 @@ func main() {
|
|||
}()
|
||||
})
|
||||
|
||||
err = http.ListenAndServe(":1974", router)
|
||||
err = http.ListenAndServe(":1974", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
||||
if r.Method != "OPTIONS" {
|
||||
router.ServeHTTP(w, r)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
}))
|
||||
if err != nil {
|
||||
slog.Error("Failed to start server: " + err.Error())
|
||||
os.Exit(1)
|
||||
|
|
|
@ -3,12 +3,11 @@ module git.ailur.dev/arzumify/lchat/web
|
|||
go 1.23.3
|
||||
|
||||
require (
|
||||
git.ailur.dev/ailur/jsFetch v1.1.1
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
git.ailur.dev/ailur/jsFetch v1.2.0
|
||||
golang.org/x/crypto v0.29.0
|
||||
)
|
||||
|
||||
require (
|
||||
git.ailur.dev/ailur/jsStreams v1.2.0 // indirect
|
||||
git.ailur.dev/ailur/jsStreams v1.2.1 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
)
|
||||
|
|
10
web/go.sum
10
web/go.sum
|
@ -1,9 +1,7 @@
|
|||
git.ailur.dev/ailur/jsFetch v1.1.1 h1:kdCkrNr2mRvTG6hlK3YwnqlwfvzIQaw4z4AXLXewQ38=
|
||||
git.ailur.dev/ailur/jsFetch v1.1.1/go.mod h1:eaQVFOlHwcPHCqh3oyQkQrpltmILOaiA9DKq3oTHBbM=
|
||||
git.ailur.dev/ailur/jsStreams v1.2.0 h1:BRtLEyjkUoPKPu0Y6odUbSMlKCYNyR792TYRtujKfPw=
|
||||
git.ailur.dev/ailur/jsStreams v1.2.0/go.mod h1:/ZCvbUcWkZRuKIkO7jH6b5vIjzdxIOP8ET8X0src5Go=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
git.ailur.dev/ailur/jsFetch v1.2.0 h1:OgZ/jYheuz/v5mAO6hKE2IetANQCcJVT4xHlFi25YYU=
|
||||
git.ailur.dev/ailur/jsFetch v1.2.0/go.mod h1:B7qkj7z5gTXOP/YiUH2DwbDimSgBLI+Pd8PcGD4DRmY=
|
||||
git.ailur.dev/ailur/jsStreams v1.2.1 h1:nXZYZrxHJCVwR0Kx/X+TenMBmS6Gh8Uc2DMinbyiGoo=
|
||||
git.ailur.dev/ailur/jsStreams v1.2.1/go.mod h1:/ZCvbUcWkZRuKIkO7jH6b5vIjzdxIOP8ET8X0src5Go=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
<div class="app" id="app">
|
||||
<div class="topBar">
|
||||
<span>LChat</span>
|
||||
<button id="invite" class="invite">Invite</button>
|
||||
<button id="logout">Logout</button>
|
||||
</div>
|
||||
<div class="messageBox" id="messageBox"></div>
|
||||
|
|
|
@ -32,13 +32,17 @@ body {
|
|||
}
|
||||
|
||||
.topBar button {
|
||||
margin-left: auto;
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.topBar .invite {
|
||||
margin-left: auto;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.topBar span {
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
|
@ -47,7 +51,7 @@ body {
|
|||
.messageBox {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
max-height: calc(100% - 148px);
|
||||
padding: 11.5px 10px 0 10px;
|
||||
margin: 65px 0;
|
||||
|
|
152
web/wasm/app.go
152
web/wasm/app.go
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
"git.ailur.dev/ailur/jsFetch"
|
||||
"strconv"
|
||||
"syscall/js"
|
||||
"time"
|
||||
)
|
||||
|
@ -39,7 +40,7 @@ func refreshMessages(localStorage js.Value, messageBox js.Value, userId string)
|
|||
return
|
||||
}
|
||||
|
||||
var body map[string]interface{}
|
||||
var body []map[string]interface{}
|
||||
err = json.NewDecoder(response.Body).Decode(&body)
|
||||
if err != nil {
|
||||
alert("Failed to decode response: " + err.Error())
|
||||
|
@ -50,9 +51,14 @@ func refreshMessages(localStorage js.Value, messageBox js.Value, userId string)
|
|||
messageBox.Get("lastChild").Get("style").Set("margin-bottom", "15px")
|
||||
}
|
||||
|
||||
ids := make(map[string]struct{})
|
||||
for _, message := range body {
|
||||
ids[message["id"].(string)] = struct{}{}
|
||||
}
|
||||
|
||||
if len(messageDivs) > len(body) {
|
||||
for id, _ := range messageDivs {
|
||||
_, exists := body[id]
|
||||
for id := range messageDivs {
|
||||
_, exists := ids[id]
|
||||
if !exists {
|
||||
messageDivs[id].Call("remove")
|
||||
delete(messageDivs, id)
|
||||
|
@ -60,19 +66,19 @@ func refreshMessages(localStorage js.Value, messageBox js.Value, userId string)
|
|||
}
|
||||
}
|
||||
|
||||
for id, message := range body {
|
||||
_, exists := messageDivs[id]
|
||||
for _, message := range body {
|
||||
_, exists := messageDivs[message["id"].(string)]
|
||||
if !exists {
|
||||
messageDiv := createMessage(message.(map[string]interface{})["message"].(string), message.(map[string]interface{})["name"].(string), message.(map[string]interface{})["sender"].(string) == userId, message.(map[string]interface{})["id"].(string), time.Unix(int64(message.(map[string]interface{})["sent"].(float64)), 0))
|
||||
messageDiv := createMessage(message["message"].(string), message["name"].(string), message["sender"].(string) == userId, message["id"].(string), time.Unix(int64(message["sent"].(float64)), 0))
|
||||
messageBox.Call("appendChild", messageDiv)
|
||||
messageDivs[id] = messageDiv
|
||||
messageDivs[message["id"].(string)] = messageDiv
|
||||
}
|
||||
}
|
||||
|
||||
if !messageBox.Get("lastChild").IsNull() {
|
||||
messageBox.Get("lastChild").Get("style").Set("margin-bottom", "22.5px")
|
||||
}
|
||||
messageBox.Set("scrollTop", messageBox.Get("scrollTopMax").Float())
|
||||
messageBox.Set("scrollTop", messageBox.Get("scrollHeight").Float())
|
||||
}
|
||||
|
||||
func alert(message string) {
|
||||
|
@ -143,6 +149,40 @@ func createMessage(message string, user string, delete bool, id string, timestam
|
|||
return div
|
||||
}
|
||||
|
||||
func handleSend(sendField js.Value, localStorage js.Value) {
|
||||
jsonBody, err := json.Marshal(map[string]interface{}{
|
||||
"message": sendField.Get("value").String(),
|
||||
"token": localStorage.Call("getItem", "token").String(),
|
||||
})
|
||||
if err != nil {
|
||||
alert("Failed to encode request: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response, err := jsFetch.Post(localStorage.Call("getItem", "server").String()+"/api/send", "application/json", bytes.NewReader(jsonBody))
|
||||
if err != nil {
|
||||
alert("Failed to contact server: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if response.StatusCode != 200 {
|
||||
if response.StatusCode == 500 {
|
||||
alert("Something went wrong on our end. Please try again later.")
|
||||
} else {
|
||||
var body map[string]interface{}
|
||||
err := json.NewDecoder(response.Body).Decode(&body)
|
||||
if err != nil {
|
||||
alert("Failed to decode response: " + err.Error())
|
||||
} else {
|
||||
alert(body["error"].(string))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
sendField.Set("value", "")
|
||||
}
|
||||
|
||||
func main() {
|
||||
localStorage := js.Global().Get("localStorage")
|
||||
login := js.Global().Get("document").Call("getElementById", "login")
|
||||
|
@ -151,6 +191,7 @@ func main() {
|
|||
sendField := js.Global().Get("document").Call("getElementById", "sendField")
|
||||
send := js.Global().Get("document").Call("getElementById", "send")
|
||||
messageBox := js.Global().Get("document").Call("getElementById", "messageBox")
|
||||
inviteButton := js.Global().Get("document").Call("getElementById", "invite")
|
||||
|
||||
if localStorage.Call("getItem", "server").IsNull() {
|
||||
localStorage.Call("setItem", "server", "https://chat.ailur.dev:1974")
|
||||
|
@ -160,6 +201,62 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
if localStorage.Call("getItem", "admin").String() == "true" {
|
||||
inviteButton.Get("style").Set("display", "initial")
|
||||
inviteButton.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
go func() {
|
||||
uses := js.Global().Get("prompt").Invoke("How many uses should the invite have?")
|
||||
usesInt, err := strconv.Atoi(uses.String())
|
||||
if err != nil {
|
||||
alert("Invalid number of uses.")
|
||||
return
|
||||
}
|
||||
|
||||
jsonBody, err := json.Marshal(map[string]interface{}{
|
||||
"token": localStorage.Call("getItem", "token").String(),
|
||||
"uses": usesInt,
|
||||
})
|
||||
if err != nil {
|
||||
alert("Failed to encode request: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response, err := jsFetch.Post(localStorage.Call("getItem", "server").String()+"/api/invite", "application/json", bytes.NewReader(jsonBody))
|
||||
if err != nil {
|
||||
alert("Failed to contact server: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if response.StatusCode != 200 {
|
||||
if response.StatusCode == 500 {
|
||||
alert("Something went wrong on our end. Please try again later.")
|
||||
} else {
|
||||
var body map[string]interface{}
|
||||
err := json.NewDecoder(response.Body).Decode(&body)
|
||||
if err != nil {
|
||||
alert("Failed to decode response: " + err.Error())
|
||||
} else {
|
||||
alert(body["error"].(string))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var body map[string]interface{}
|
||||
err = json.NewDecoder(response.Body).Decode(&body)
|
||||
if err != nil {
|
||||
alert("Failed to decode response: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
alert("Your invite URL is: " + js.Global().Get("location").Get("origin").String() + "/invite?invite=" + body["code"].(string))
|
||||
}()
|
||||
return nil
|
||||
}))
|
||||
} else {
|
||||
logout.Get("style").Set("margin-left", "auto")
|
||||
}
|
||||
|
||||
userId := localStorage.Call("getItem", "userId").String()
|
||||
|
||||
login.Get("style").Set("display", "none")
|
||||
|
@ -168,43 +265,22 @@ func main() {
|
|||
logout.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
go func() {
|
||||
localStorage.Call("removeItem", "token")
|
||||
localStorage.Call("removeItem", "userId")
|
||||
localStorage.Call("removeItem", "admin")
|
||||
js.Global().Get("location").Set("href", "login")
|
||||
}()
|
||||
return nil
|
||||
}))
|
||||
|
||||
send.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
go func() {
|
||||
jsonBody, err := json.Marshal(map[string]interface{}{
|
||||
"message": sendField.Get("value").String(),
|
||||
"token": localStorage.Call("getItem", "token").String(),
|
||||
})
|
||||
if err != nil {
|
||||
alert("Failed to encode request: " + err.Error())
|
||||
return
|
||||
}
|
||||
go handleSend(sendField, localStorage)
|
||||
return nil
|
||||
}))
|
||||
|
||||
response, err := jsFetch.Post(localStorage.Call("getItem", "server").String()+"/api/send", "application/json", bytes.NewReader(jsonBody))
|
||||
if err != nil {
|
||||
alert("Failed to contact server: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if response.StatusCode != 200 {
|
||||
if response.StatusCode == 500 {
|
||||
alert("Something went wrong on our end. Please try again later.")
|
||||
} else {
|
||||
var body map[string]interface{}
|
||||
err := json.NewDecoder(response.Body).Decode(&body)
|
||||
if err != nil {
|
||||
alert("Failed to decode response: " + err.Error())
|
||||
} else {
|
||||
alert(body["error"].(string))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}()
|
||||
sendField.Call("addEventListener", "keypress", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
if args[0].Get("key").String() == "Enter" {
|
||||
go handleSend(sendField, localStorage)
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
|
||||
|
|
|
@ -158,8 +158,13 @@ func main() {
|
|||
}
|
||||
|
||||
localStorage.Call("setItem", "token", body["token"].(string))
|
||||
localStorage.Call("setItem", "username", username.Get("value").String())
|
||||
localStorage.Call("setItem", "userId", body["userId"].(string))
|
||||
_, exists := body["admin"]
|
||||
if exists {
|
||||
localStorage.Call("setItem", "admin", "true")
|
||||
} else {
|
||||
localStorage.Call("setItem", "admin", "false")
|
||||
}
|
||||
|
||||
status.Set("innerText", "Welcome, "+username.Get("value").String()+", to LChat!")
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
|
|
@ -115,8 +115,8 @@ func main() {
|
|||
}
|
||||
|
||||
localStorage.Call("setItem", "token", body["token"].(string))
|
||||
localStorage.Call("setItem", "username", username.Get("value").String())
|
||||
localStorage.Call("setItem", "userId", body["userId"].(string))
|
||||
localStorage.Call("setItem", "admin", "false")
|
||||
|
||||
status.Set("innerText", "Welcome, "+username.Get("value").String()+", to LChat!")
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
|
Loading…
Reference in New Issue