Updated everything, fixed deleting message synchronisation, fixed chromium support, added inviting other users as an admin

This commit is contained in:
Tracker-Friendly 2024-11-13 17:59:01 +00:00
parent 1a607e30e0
commit a3f4bb24e7
8 changed files with 155 additions and 57 deletions

View File

@ -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)

View File

@ -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
)

View File

@ -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=

View File

@ -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>

View File

@ -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;

View File

@ -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,38 +149,7 @@ func createMessage(message string, user string, delete bool, id string, timestam
return div
}
func main() {
localStorage := js.Global().Get("localStorage")
login := js.Global().Get("document").Call("getElementById", "login")
app := js.Global().Get("document").Call("getElementById", "app")
logout := js.Global().Get("document").Call("getElementById", "logout")
sendField := js.Global().Get("document").Call("getElementById", "sendField")
send := js.Global().Get("document").Call("getElementById", "send")
messageBox := js.Global().Get("document").Call("getElementById", "messageBox")
if localStorage.Call("getItem", "server").IsNull() {
localStorage.Call("setItem", "server", "https://chat.ailur.dev:1974")
}
if localStorage.Call("getItem", "token").IsNull() {
return
}
userId := localStorage.Call("getItem", "userId").String()
login.Get("style").Set("display", "none")
app.Get("style").Set("display", "initial")
logout.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
go func() {
localStorage.Call("removeItem", "token")
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() {
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(),
@ -204,9 +179,110 @@ func main() {
}
return
}
sendField.Set("value", "")
}
func main() {
localStorage := js.Global().Get("localStorage")
login := js.Global().Get("document").Call("getElementById", "login")
app := js.Global().Get("document").Call("getElementById", "app")
logout := js.Global().Get("document").Call("getElementById", "logout")
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")
}
if localStorage.Call("getItem", "token").IsNull() {
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")
app.Get("style").Set("display", "initial")
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 handleSend(sendField, localStorage)
return nil
}))
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
}))
go refreshMessages(localStorage, messageBox, userId)

View File

@ -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)

View File

@ -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)