Signup page: check for username uniqueness

face-detect
Noah Petherbridge 2023-08-12 22:37:09 -07:00
parent be0d401de8
commit 4d1a057a73
3 changed files with 130 additions and 0 deletions

View File

@ -0,0 +1,57 @@
package api
import (
"fmt"
"net/http"
"strings"
"code.nonshy.com/nonshy/website/pkg/models"
)
// UsernameCheck API.
func UsernameCheck() http.HandlerFunc {
// Request JSON schema.
type Request struct {
Username string `json:"username"`
}
// Response JSON schema.
type Response struct {
OK bool `json:"OK"`
Error string `json:"error,omitempty"`
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
SendJSON(w, http.StatusNotAcceptable, Response{
Error: "POST method only",
})
return
}
// Parse request payload.
var req Request
if err := ParseJSON(r, &req); err != nil {
SendJSON(w, http.StatusBadRequest, Response{
Error: fmt.Sprintf("Error with request payload: %s", err),
})
return
}
// Username to test.
var username = strings.TrimSpace(strings.ToLower(req.Username))
// Does it exist?
if _, err := models.FindUser(username); err == nil {
SendJSON(w, http.StatusOK, Response{
Error: "That username is already taken, please try another one.",
})
return
}
// Send success response.
SendJSON(w, http.StatusOK, Response{
OK: true,
})
})
}

View File

@ -93,6 +93,7 @@ func New() http.Handler {
// JSON API endpoints.
mux.HandleFunc("/v1/version", api.Version())
mux.HandleFunc("/v1/users/me", api.LoginOK())
mux.HandleFunc("/v1/users/check-username", api.UsernameCheck())
mux.Handle("/v1/likes", middleware.LoginRequired(api.Likes()))
mux.Handle("/v1/notifications/read", middleware.LoginRequired(api.ReadNotification()))
mux.Handle("/v1/notifications/delete", middleware.LoginRequired(api.ClearNotification()))

View File

@ -108,6 +108,20 @@
value="{{.Username}}"
required>
<small class="has-text-grey">Usernames are 3 to 32 characters a-z 0-9 . -</small>
<!-- Username checking -->
<div class="notification is-info is-light py-2 px-4 mt-1"
id="username-checking" style="display: none">
<i class="fa fa-spinner fa-spin mr-1"></i> Checking username...
</div>
<div class="notification is-success is-light py-2 px-4 mt-1"
id="username-ok" style="display: none">
<i class="fa fa-check mr-1"></i> Looks good!
</div>
<div class="notification is-danger is-light py-2 px-4 mt-1"
id="username-error" style="display: none">
<i class="fa fa-xmark mr-1"></i> That username is already taken!
</div>
</div>
<div class="field">
<label class="label" for="password">Enter a passphrase:</label>
@ -153,3 +167,61 @@
</div>
</div>
{{end}}
{{define "scripts"}}
<script>
window.addEventListener("DOMContentLoaded", (event) => {
// Set up username checking script.
const $username = document.querySelector("#username"),
$unCheck = document.querySelector("#username-checking"),
$unOK = document.querySelector("#username-ok"),
$unError = document.querySelector("#username-error");
let onChange = (e) => {
$unCheck.style.display = "block";
$unOK.style.display = "none";
$unError.style.display = "none";
if ($username.value.length < 3) {
$unCheck.style.display = "none";
return;
};
return fetch("/v1/users/check-username", {
method: "POST",
mode: "same-origin",
cache: "no-cache",
credentials: "same-origin",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
"username": $username.value,
}),
})
.then((response) => response.json())
.then((data) => {
if (data.StatusCode !== 200) {
window.alert(data.data.error);
return;
}
let result = data.data;
if (result.OK) {
$unOK.style.display = "block";
$unError.style.display = "none";
} else {
$unOK.style.display = "none";
$unError.style.display = "block";
}
}).catch(resp => {
window.alert(resp);
}).finally(() => {
$unCheck.style.display = "none";
})
};
$username.addEventListener("change", onChange);
$username.addEventListener("blur", onChange);
});
</script>
{{end}}