package main import ( "bytes" "net/url" "strings" "time" "crypto/ed25519" "encoding/base64" "encoding/json" "syscall/js" "git.ailur.dev/ailur/jsFetch" "golang.org/x/crypto/argon2" ) func main() { localStorage := js.Global().Get("localStorage") status := js.Global().Get("document").Call("getElementById", "status") signupButton := js.Global().Get("document").Call("getElementById", "signup") username := js.Global().Get("document").Call("getElementById", "username") password := js.Global().Get("document").Call("getElementById", "password") inviteCheck := js.Global().Get("document").Call("getElementById", "inviteCheck") container := js.Global().Get("document").Call("getElementById", "container") if localStorage.Call("getItem", "server").IsNull() { localStorage.Call("setItem", "server", "https://chat.ailur.dev:1974") } if !localStorage.Call("getItem", "token").IsNull() { js.Global().Get("location").Set("href", "../") return } query, err := url.ParseQuery(strings.TrimPrefix(js.Global().Get("location").Get("search").String(), "?")) if err != nil { inviteCheck.Set("innerText", "Failed to parse query: "+err.Error()) return } if !query.Has("invite") || len(query.Get("invite")) != 16 { inviteCheck.Set("innerText", "Sorry, your invite link is invalid.") return } else { inviteCheck.Get("style").Set("display", "none") container.Get("style").Set("display", "initial") } signupButton.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} { go func() { username.Set("disabled", true) password.Set("disabled", true) signupButton.Set("disabled", true) status.Set("innerText", "Generating encryption keys...") hash := argon2.IDKey( []byte(password.Get("value").String()), []byte(username.Get("value").String()), 32, 19264, 1, 32, ) status.Set("innerText", "Contacting server...") jsonBody, err := json.Marshal(map[string]interface{}{ "username": username.Get("value").String(), "publicKey": base64.StdEncoding.EncodeToString(ed25519.NewKeyFromSeed(hash).Public().(ed25519.PublicKey)), "invite": query.Get("invite"), }) if err != nil { status.Set("innerText", "Failed to encode request: "+err.Error()) username.Set("disabled", false) password.Set("disabled", false) signupButton.Set("disabled", false) return } response, err := jsFetch.Post(localStorage.Call("getItem", "server").String()+"/api/signup", "application/json", bytes.NewReader(jsonBody)) if err != nil { status.Set("innerText", "Failed to contact server: "+err.Error()) username.Set("disabled", false) password.Set("disabled", false) signupButton.Set("disabled", false) return } if response.StatusCode != 200 { if response.StatusCode == 500 { status.Set("innerText", "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 { status.Set("innerText", "Failed to decode response: "+err.Error()) } else { status.Set("innerText", body["error"].(string)) } } username.Set("disabled", false) password.Set("disabled", false) signupButton.Set("disabled", false) return } var body map[string]interface{} err = json.NewDecoder(response.Body).Decode(&body) if err != nil { status.Set("innerText", "Failed to decode response: "+err.Error()) username.Set("disabled", false) password.Set("disabled", false) signupButton.Set("disabled", false) return } localStorage.Call("setItem", "token", body["token"].(string)) localStorage.Call("setItem", "username", username.Get("value").String()) localStorage.Call("setItem", "userId", body["userId"].(string)) status.Set("innerText", "Welcome, "+username.Get("value").String()+", to LChat!") time.Sleep(300 * time.Millisecond) js.Global().Get("location").Set("href", "../") }() return nil })) select {} }