Ability to change username
This commit is contained in:
parent
ef8abec7bf
commit
fedfbed4eb
|
@ -18,6 +18,7 @@ import (
|
|||
"code.nonshy.com/nonshy/website/pkg/session"
|
||||
"code.nonshy.com/nonshy/website/pkg/templates"
|
||||
"code.nonshy.com/nonshy/website/pkg/utility"
|
||||
"code.nonshy.com/nonshy/website/pkg/worker"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
|
@ -50,6 +51,10 @@ func Settings() http.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
// Is the user currently in the chat room? Gate username changes when so.
|
||||
var isOnChat = worker.GetChatStatistics().IsOnline(user.Username)
|
||||
vars["OnChat"] = isOnChat
|
||||
|
||||
// URL hashtag to redirect to
|
||||
var hashtag string
|
||||
|
||||
|
@ -295,6 +300,7 @@ func Settings() http.HandlerFunc {
|
|||
var (
|
||||
oldPassword = r.PostFormValue("old_password")
|
||||
changeEmail = strings.TrimSpace(strings.ToLower(r.PostFormValue("change_email")))
|
||||
changeUsername = strings.TrimSpace(strings.ToLower(r.PostFormValue("change_username")))
|
||||
password1 = strings.TrimSpace(r.PostFormValue("new_password"))
|
||||
password2 = strings.TrimSpace(r.PostFormValue("new_password2"))
|
||||
)
|
||||
|
@ -302,23 +308,69 @@ func Settings() http.HandlerFunc {
|
|||
// Their old password is needed to make any changes to their account.
|
||||
if err := user.CheckPassword(oldPassword); err != nil {
|
||||
session.FlashError(w, r, "Could not make changes to your account settings as the 'current password' you entered was incorrect.")
|
||||
templates.Redirect(w, r.URL.Path)
|
||||
templates.Redirect(w, r.URL.Path+hashtag)
|
||||
return
|
||||
}
|
||||
|
||||
// Changing their username?
|
||||
if changeUsername != user.Username {
|
||||
// Not if they are in the chat room!
|
||||
if isOnChat {
|
||||
session.FlashError(w, r, "Your username could not be changed right now because you are logged into the chat room. Please exit the chat room, wait a minute, and try your request again.")
|
||||
templates.Redirect(w, r.URL.Path+hashtag)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the new name is OK.
|
||||
if err := models.IsValidUsername(changeUsername); err != nil {
|
||||
session.FlashError(w, r, "Could not change your username: %s", err.Error())
|
||||
templates.Redirect(w, r.URL.Path+hashtag)
|
||||
return
|
||||
}
|
||||
|
||||
// Set their name.
|
||||
origUsername := user.Username
|
||||
user.Username = changeUsername
|
||||
if err := user.Save(); err != nil {
|
||||
session.FlashError(w, r, "Error saving your new username: %s", err)
|
||||
} else {
|
||||
session.Flash(w, r, "Your username has been updated to: %s", user.Username)
|
||||
|
||||
// Notify the admin about this to keep tabs if someone is acting strangely
|
||||
// with too-frequent username changes.
|
||||
fb := &models.Feedback{
|
||||
Intent: "report",
|
||||
Subject: "Change of username",
|
||||
UserID: user.ID,
|
||||
TableName: "users",
|
||||
TableID: user.ID,
|
||||
Message: fmt.Sprintf(
|
||||
"A user has modified their username on their profile page!\n\n"+
|
||||
"* Original: %s\n* Updated: %s",
|
||||
origUsername, changeUsername,
|
||||
),
|
||||
}
|
||||
|
||||
// Save the feedback.
|
||||
if err := models.CreateFeedback(fb); err != nil {
|
||||
log.Error("Couldn't save feedback from user updating their DOB: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Changing their email?
|
||||
if changeEmail != user.Email {
|
||||
// Validate the email.
|
||||
if _, err := nm.ParseAddress(changeEmail); err != nil {
|
||||
session.FlashError(w, r, "The email address you entered is not valid: %s", err)
|
||||
templates.Redirect(w, r.URL.Path)
|
||||
templates.Redirect(w, r.URL.Path+hashtag)
|
||||
return
|
||||
}
|
||||
|
||||
// Email must not already exist.
|
||||
if _, err := models.FindUser(changeEmail); err == nil {
|
||||
session.FlashError(w, r, "That email address is already in use.")
|
||||
templates.Redirect(w, r.URL.Path)
|
||||
templates.Redirect(w, r.URL.Path+hashtag)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -330,7 +382,7 @@ func Settings() http.HandlerFunc {
|
|||
}
|
||||
if err := redis.Set(fmt.Sprintf(config.ChangeEmailRedisKey, token.Token), token, config.SignupTokenExpires); err != nil {
|
||||
session.FlashError(w, r, "Failed to create change email token: %s", err)
|
||||
templates.Redirect(w, r.URL.Path)
|
||||
templates.Redirect(w, r.URL.Path+hashtag)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,9 @@ func Signup() http.HandlerFunc {
|
|||
password = strings.TrimSpace(r.PostFormValue("password"))
|
||||
password2 = strings.TrimSpace(r.PostFormValue("password2"))
|
||||
dob = r.PostFormValue("dob")
|
||||
|
||||
// Validation errors but still show the form again.
|
||||
hasError bool
|
||||
)
|
||||
|
||||
// Don't let them sneakily change their verified email address on us.
|
||||
|
@ -95,21 +98,12 @@ func Signup() http.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
// Reserved username check.
|
||||
for _, cmp := range config.ReservedUsernames {
|
||||
if username == cmp {
|
||||
session.FlashError(w, r, "That username is reserved, please choose a different username.")
|
||||
templates.Redirect(w, r.URL.Path+"?token="+tokenStr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Cache username in case of passwd validation errors.
|
||||
vars["Email"] = email
|
||||
vars["Username"] = username
|
||||
|
||||
// Is the app not configured to send email?
|
||||
if !config.Current.Mail.Enabled {
|
||||
if !config.Current.Mail.Enabled && !config.SkipEmailVerification {
|
||||
session.FlashError(w, r, "This app is not configured to send email so you can not sign up at this time. "+
|
||||
"Please contact the website administrator about this issue!")
|
||||
templates.Redirect(w, r.URL.Path)
|
||||
|
@ -209,7 +203,6 @@ func Signup() http.HandlerFunc {
|
|||
}
|
||||
|
||||
// Full sign-up step (w/ email verification token), validate more things.
|
||||
var hasError bool
|
||||
if len(password) < 3 {
|
||||
session.FlashError(w, r, "Please enter a password longer than 3 characters.")
|
||||
hasError = true
|
||||
|
@ -218,8 +211,9 @@ func Signup() http.HandlerFunc {
|
|||
hasError = true
|
||||
}
|
||||
|
||||
if !config.UsernameRegexp.MatchString(username) {
|
||||
session.FlashError(w, r, "Your username must consist of only numbers, letters, - . and be 3-32 characters.")
|
||||
// Validate the username is OK: well formatted, not reserved, not existing.
|
||||
if err := models.IsValidUsername(username); err != nil {
|
||||
session.FlashError(w, r, err.Error())
|
||||
hasError = true
|
||||
}
|
||||
|
||||
|
|
|
@ -153,6 +153,10 @@ func Landing() http.HandlerFunc {
|
|||
log.Error("SendBlocklist: %s", err)
|
||||
}
|
||||
|
||||
// Mark them as online immediately: so e.g. on the Change Username screen we leave no window
|
||||
// of time where they can exist in chat but change their name on the site.
|
||||
worker.GetChatStatistics().SetOnlineNow(currentUser.Username)
|
||||
|
||||
// Redirect them to the chat room.
|
||||
templates.Redirect(w, strings.TrimSuffix(chatURL, "/")+"/?jwt="+ss)
|
||||
return
|
||||
|
|
|
@ -186,6 +186,28 @@ func FindUser(username string) (*User, error) {
|
|||
return u, result.Error
|
||||
}
|
||||
|
||||
// IsValidUsername checks if a username is available and not reserved.
|
||||
func IsValidUsername(username string) error {
|
||||
// Check the formatting of the name.
|
||||
if !config.UsernameRegexp.MatchString(username) {
|
||||
return errors.New("Your username must consist of only numbers, letters, - . and be 3-32 characters.")
|
||||
}
|
||||
|
||||
// Reserved username check.
|
||||
for _, cmp := range config.ReservedUsernames {
|
||||
if username == cmp {
|
||||
return errors.New("That username is reserved, please choose a different username.")
|
||||
}
|
||||
}
|
||||
|
||||
// Does the username already exist?
|
||||
if _, err := FindUser(username); err == nil {
|
||||
return errors.New("That username already exists. Please try a different username.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsShyFrom tells whether the user is shy from the perspective of the other user.
|
||||
//
|
||||
// That is, depending on our profile visibility and friendship status.
|
||||
|
|
|
@ -2,7 +2,7 @@ package worker
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -22,16 +22,10 @@ type ChatStatistics struct {
|
|||
}
|
||||
|
||||
// GetChatStatistics returns the latest (cached) chat statistics.
|
||||
func GetChatStatistics() ChatStatistics {
|
||||
func GetChatStatistics() *ChatStatistics {
|
||||
chatStatisticsMu.RLock()
|
||||
defer chatStatisticsMu.RUnlock()
|
||||
|
||||
if cachedChatStatistics != nil {
|
||||
return *cachedChatStatistics
|
||||
}
|
||||
return ChatStatistics{
|
||||
Usernames: []string{},
|
||||
}
|
||||
return cachedChatStatistics
|
||||
}
|
||||
|
||||
// SetChatStatistics updates the cached chat statistics, holding a write lock briefly.
|
||||
|
@ -42,7 +36,7 @@ func SetChatStatistics(stats *ChatStatistics) {
|
|||
}
|
||||
|
||||
// IsOnline returns whether the username is currently logged-in to chat.
|
||||
func (cs ChatStatistics) IsOnline(username string) bool {
|
||||
func (cs *ChatStatistics) IsOnline(username string) bool {
|
||||
for _, user := range cs.Usernames {
|
||||
if user == username {
|
||||
return true
|
||||
|
@ -51,10 +45,20 @@ func (cs ChatStatistics) IsOnline(username string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// SetOnlineNow patches the current ChatStatistics to mark a user as online immediately, e.g.
|
||||
// because the main site has just sent them to the chat with a JWT token.
|
||||
func (cs *ChatStatistics) SetOnlineNow(username string) {
|
||||
if !cs.IsOnline(username) {
|
||||
chatStatisticsMu.Lock()
|
||||
defer chatStatisticsMu.Unlock()
|
||||
cs.Usernames = append(cs.Usernames, username)
|
||||
}
|
||||
}
|
||||
|
||||
type UserOnChatMap map[string]bool
|
||||
|
||||
// MapUsersOnline returns a hashmap of usernames to online status.
|
||||
func (cs ChatStatistics) MapUsersOnline(usernames []string) UserOnChatMap {
|
||||
func (cs *ChatStatistics) MapUsersOnline(usernames []string) UserOnChatMap {
|
||||
var result = UserOnChatMap{}
|
||||
for _, user := range cs.Usernames {
|
||||
result[user] = true
|
||||
|
@ -68,7 +72,7 @@ func (m UserOnChatMap) Get(username string) bool {
|
|||
}
|
||||
|
||||
var (
|
||||
cachedChatStatistics *ChatStatistics
|
||||
cachedChatStatistics = &ChatStatistics{}
|
||||
chatStatisticsMu sync.RWMutex
|
||||
)
|
||||
|
||||
|
@ -117,7 +121,7 @@ func DoCheckBareRTC() {
|
|||
|
||||
if res.StatusCode == http.StatusOK {
|
||||
var cs ChatStatistics
|
||||
body, _ := ioutil.ReadAll(res.Body)
|
||||
body, _ := io.ReadAll(res.Body)
|
||||
res.Body.Close()
|
||||
if err = json.Unmarshal(body, &cs); err != nil {
|
||||
log.Error("WatchBareRTC: json decode error: %s", err)
|
||||
|
|
|
@ -1120,7 +1120,7 @@
|
|||
<header class="card-header has-background-link">
|
||||
<p class="card-header-title has-text-light">
|
||||
<i class="fa fa-lock pr-2"></i>
|
||||
Update E-mail or Password
|
||||
Update E-mail, Username or Password
|
||||
</p>
|
||||
</header>
|
||||
|
||||
|
@ -1129,6 +1129,13 @@
|
|||
<input type="hidden" name="intent" value="settings">
|
||||
{{InputCSRF}}
|
||||
|
||||
<div class="block">
|
||||
You may use the fields below to change the e-mail address you log in with,
|
||||
change your username on the site, or set a new password. You will need to
|
||||
confirm your current password first before making these changes to your
|
||||
account.
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="old_password">
|
||||
Current Password
|
||||
|
@ -1152,6 +1159,28 @@
|
|||
value="{{.CurrentUser.Email}}">
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="change_username">Change Username</label>
|
||||
<input type="text" class="input"
|
||||
id="change_username"
|
||||
name="change_username"
|
||||
placeholder="{{.CurrentUser.Username}}"
|
||||
value="{{.CurrentUser.Username}}"
|
||||
{{if .OnChat}}readonly{{end}}>
|
||||
<p class="help">
|
||||
{{if .OnChat}}
|
||||
<span class="has-text-danger">
|
||||
<i class="fa fa-exclamation-triangle mr-1"></i>
|
||||
You are currently logged into the chat room, so your username may not be
|
||||
updated at this time. To change your username, please log out of the chat
|
||||
room and wait a minute before reloading this page.
|
||||
</span>
|
||||
{{else}}
|
||||
Usernames are 3 to 32 characters a-z 0-9 . -
|
||||
{{end}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Change Password</label>
|
||||
<input type="password" class="input mb-2"
|
||||
|
|
Loading…
Reference in New Issue
Block a user