From: PhiTux Date: Tue, 9 Sep 2025 17:07:45 +0000 (+0200) Subject: user can change his username X-Git-Url: http://git.99rst.org/?a=commitdiff_plain;h=37de3036fefc59e768c7bc2572f07d3606227bef;p=DailyTxT.git user can change his username --- diff --git a/backend/handlers/users.go b/backend/handlers/users.go index 47b85bb..b1d3bed 100644 --- a/backend/handlers/users.go +++ b/backend/handlers/users.go @@ -1029,3 +1029,112 @@ func CreateBackupCodes(w http.ResponseWriter, r *http.Request) { "available_backup_codes": available_backup_codes, }) } + +// ChangeUsername handles changing a user's username +func ChangeUsername(w http.ResponseWriter, r *http.Request) { + // Get user info from context + userID, ok := r.Context().Value(utils.UserIDKey).(int) + if !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + // Parse request + var req struct { + NewUsername string `json:"new_username"` + Password string `json:"password"` + } + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, "Invalid request body", http.StatusBadRequest) + return + } + + // Validate input + req.NewUsername = strings.TrimSpace(req.NewUsername) + if req.NewUsername == "" { + utils.JSONResponse(w, http.StatusBadRequest, map[string]any{ + "success": false, + "message": "Username cannot be empty", + }) + return + } + + // Get users + users, err := utils.GetUsers() + if err != nil { + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + usersList, ok := users["users"].([]any) + if !ok { + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + // Check if new username is already taken (case-insensitive) + for _, u := range usersList { + user, ok := u.(map[string]any) + if !ok { + continue + } + + existingUsername, ok := user["username"].(string) + if !ok { + continue + } + + // Skip current user + if int(user["user_id"].(float64)) == userID { + continue + } + + // Case-insensitive comparison + if strings.EqualFold(existingUsername, req.NewUsername) { + utils.JSONResponse(w, http.StatusOK, map[string]any{ + "success": false, + "username_taken": true, + }) + return + } + } + + // check password + derivedKey, availableBackupCodes, err := utils.CheckPasswordForUser(userID, req.Password) + if err != nil || len(derivedKey) == 0 { + utils.JSONResponse(w, http.StatusOK, map[string]any{ + "success": false, + "password_incorrect": true, + }) + return + } + + // Update username + for _, u := range usersList { + user, ok := u.(map[string]any) + if !ok { + continue + } + + if int(user["user_id"].(float64)) == userID { + user["username"] = req.NewUsername + //usersList[currentUserIndex] = user + users["users"] = usersList + break + } + } + + // Save users file + if err := utils.WriteUsers(users); err != nil { + utils.Logger.Printf("Error saving users after username change: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + utils.Logger.Printf("Username changed for user ID %d to '%s'", userID, req.NewUsername) + + utils.JSONResponse(w, http.StatusOK, map[string]any{ + "success": true, + "available_backup_codes": availableBackupCodes, + }) +} diff --git a/backend/main.go b/backend/main.go index ed9bbc6..46ba822 100644 --- a/backend/main.go +++ b/backend/main.go @@ -63,6 +63,7 @@ func main() { mux.HandleFunc("GET /users/getUserSettings", middleware.RequireAuth(handlers.GetUserSettings)) mux.HandleFunc("POST /users/saveUserSettings", middleware.RequireAuth(handlers.SaveUserSettings)) mux.HandleFunc("POST /users/changePassword", middleware.RequireAuth(handlers.ChangePassword)) + mux.HandleFunc("POST /users/changeUsername", middleware.RequireAuth(handlers.ChangeUsername)) mux.HandleFunc("POST /users/deleteAccount", middleware.RequireAuth(handlers.DeleteAccount)) mux.HandleFunc("POST /users/createBackupCodes", middleware.RequireAuth(handlers.CreateBackupCodes)) mux.HandleFunc("GET /users/statistics", middleware.RequireAuth(handlers.GetStatistics)) diff --git a/frontend/src/routes/(authed)/+layout.svelte b/frontend/src/routes/(authed)/+layout.svelte index dc0d1c5..f873263 100644 --- a/frontend/src/routes/(authed)/+layout.svelte +++ b/frontend/src/routes/(authed)/+layout.svelte @@ -685,6 +685,13 @@ let deleteAccountPasswordIncorrect = $state(false); let showDeleteAccountSuccess = $state(false); + let newUsername = $state(''); + let changeUsernamePassword = $state(''); + let isChangingUsername = $state(false); + let changeUsernameSuccess = $state(false); + let changeUsernameError = $state(''); + let changeUsernamePasswordIncorrect = $state(false); + function deleteAccount() { if (isDeletingAccount) return; isDeletingAccount = true; @@ -719,10 +726,56 @@ }); } + let currentUser = $state(localStorage.getItem('user')); + + function changeUsername() { + changeUsernameSuccess = false; + changeUsernameError = ''; + changeUsernamePasswordIncorrect = false; + + if (!newUsername.trim()) { + changeUsernameError = $t('settings.change_username.empty_username'); + return; + } + + if (isChangingUsername) return; + isChangingUsername = true; + + axios + .post(API_URL + '/users/changeUsername', { + new_username: newUsername.trim(), + password: changeUsernamePassword + }) + .then((response) => { + if (response.data.success) { + changeUsernameSuccess = true; + // Update localStorage with new username + localStorage.setItem('user', newUsername.trim()); + // Clear form + newUsername = ''; + changeUsernamePassword = ''; + } else { + if (response.data.password_incorrect) { + changeUsernamePasswordIncorrect = true; + } else if (response.data.username_taken) { + changeUsernameError = $t('settings.change_username.username_taken'); + } else { + changeUsernameError = $t('settings.change_username.error'); + } + } + }) + .catch((error) => { + console.error(error); + changeUsernameError = $t('settings.change_username.error'); + }) + .finally(() => { + isChangingUsername = false; + }); + } + let backupCodesPassword = $state(''); let isGeneratingBackupCodes = $state(false); let backupCodes = $state([]); - let showBackupCodesPasswordIncorrect = $state(false); let codesCopiedSuccess = $state(false); let showBackupCodesError = $state(false); @@ -730,7 +783,6 @@ if (isGeneratingBackupCodes) return; isGeneratingBackupCodes = true; - showBackupCodesPasswordIncorrect = false; showBackupCodesError = false; backupCodes = []; @@ -1781,6 +1833,7 @@ class="btn btn-primary" onclick={createBackupCodes} data-sveltekit-noscroll + disabled={isGeneratingBackupCodes || !backupCodesPassword.trim()} > {$t('settings.backup_codes.generate_button')} {#if isGeneratingBackupCodes} @@ -1813,7 +1866,71 @@ {/if} -
Username ändern
+
+
{$t('settings.change_username')}
+
+ {@html $t('settings.change_username.description')} +
+
+ {$t('settings.change_username.current_username')}: {currentUser} +
+ +
+
+ + +
+
+ + +
+ +
+ + {#if changeUsernameSuccess} + + {/if} + {#if changeUsernamePasswordIncorrect} + + {:else if changeUsernameError} + + {/if} +
{$t('settings.delete_account')}