"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,
+ })
+}
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;
});
}
+ 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);
if (isGeneratingBackupCodes) return;
isGeneratingBackupCodes = true;
- showBackupCodesPasswordIncorrect = false;
showBackupCodesError = false;
backupCodes = [];
class="btn btn-primary"
onclick={createBackupCodes}
data-sveltekit-noscroll
+ disabled={isGeneratingBackupCodes || !backupCodesPassword.trim()}
>
{$t('settings.backup_codes.generate_button')}
{#if isGeneratingBackupCodes}
</div>
{/if}
</div>
- <div><h5>Username ändern</h5></div>
+ <div>
+ <h5>{$t('settings.change_username')}</h5>
+ <div class="form-text">
+ {@html $t('settings.change_username.description')}
+ </div>
+ <div class="mb-3">
+ {$t('settings.change_username.current_username')}: {currentUser}
+ </div>
+
+ <form onsubmit={changeUsername}>
+ <div class="form-floating mb-3">
+ <input
+ type="text"
+ class="form-control"
+ id="newUsername"
+ placeholder={$t('settings.change_username.new_username')}
+ bind:value={newUsername}
+ disabled={isChangingUsername}
+ />
+ <label for="newUsername"
+ >{$t('settings.change_username.new_username')}</label
+ >
+ </div>
+ <div class="form-floating mb-3">
+ <input
+ type="password"
+ class="form-control"
+ id="changeUsernamePassword"
+ placeholder={$t('settings.password.current_password')}
+ bind:value={changeUsernamePassword}
+ disabled={isChangingUsername}
+ />
+ <label for="changeUsernamePassword"
+ >{$t('settings.password.current_password')}</label
+ >
+ </div>
+ <button
+ class="btn btn-primary"
+ onclick={changeUsername}
+ disabled={isChangingUsername ||
+ !newUsername.trim() ||
+ !changeUsernamePassword.trim()}
+ >
+ {#if isChangingUsername}
+ <span class="spinner-border spinner-border-sm me-2"></span>
+ {/if}
+ {$t('settings.change_username.button')}
+ </button>
+ </form>
+
+ {#if changeUsernameSuccess}
+ <div class="alert alert-success mt-2" role="alert" transition:slide>
+ {$t('settings.change_username.success')}
+ </div>
+ {/if}
+ {#if changeUsernamePasswordIncorrect}
+ <div class="alert alert-danger mt-2" role="alert" transition:slide>
+ {$t('settings.password.current_password_incorrect')}
+ </div>
+ {:else if changeUsernameError}
+ <div class="alert alert-danger mt-2" role="alert" transition:slide>
+ {changeUsernameError}
+ </div>
+ {/if}
+ </div>
<div>
<h5>{$t('settings.delete_account')}</h5>
<p>