Add change password (Preferences > Change Password)
This commit is contained in:
@@ -164,6 +164,16 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<h3>Change Password</h3>
|
||||
<div class="panel-section">
|
||||
<form @submit.prevent="changePassword" class="admin-form">
|
||||
<input v-model="pwCurrent" type="password" placeholder="Current password" required />
|
||||
<input v-model="pwNew" type="password" placeholder="New password" required />
|
||||
<button type="submit">Change</button>
|
||||
</form>
|
||||
<p v-if="pwMsg" class="admin-msg">{{ pwMsg }}</p>
|
||||
</div>
|
||||
|
||||
<h3>Two-Factor Authentication</h3>
|
||||
<div class="panel-section" v-if="!totpEnabled">
|
||||
<button @click="setupTOTP" class="action-btn">Enable 2FA</button>
|
||||
@@ -293,6 +303,11 @@ const totpUri = ref('')
|
||||
const totpSecret = ref('')
|
||||
const totpCode = ref('')
|
||||
|
||||
// Password change
|
||||
const pwCurrent = ref('')
|
||||
const pwNew = ref('')
|
||||
const pwMsg = ref('')
|
||||
|
||||
// Git remotes
|
||||
const gitRemotes = ref([])
|
||||
const newRemote = ref({ name: '', url: '' })
|
||||
@@ -527,6 +542,17 @@ function applyThemeFromPrefs() {
|
||||
savePrefs()
|
||||
}
|
||||
|
||||
async function changePassword() {
|
||||
try {
|
||||
await api('/api/auth/change-password', { current_password: pwCurrent.value, new_password: pwNew.value })
|
||||
pwMsg.value = 'Password changed successfully'
|
||||
pwCurrent.value = ''
|
||||
pwNew.value = ''
|
||||
} catch (e) {
|
||||
pwMsg.value = 'Failed — check current password'
|
||||
}
|
||||
}
|
||||
|
||||
// ─── TOTP ────────────────────────────────────────────────────────────────────
|
||||
|
||||
async function setupTOTP() {
|
||||
|
||||
@@ -92,6 +92,34 @@ func (s *Server) handleLogout(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, 200, map[string]string{"status": "ok"})
|
||||
}
|
||||
|
||||
func (s *Server) handleChangePassword(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
CurrentPassword string `json:"current_password"`
|
||||
NewPassword string `json:"new_password"`
|
||||
}
|
||||
if err := decodeBody(r, &req); err != nil || req.CurrentPassword == "" || req.NewPassword == "" {
|
||||
writeJSON(w, 400, map[string]string{"error": "current_password and new_password required"})
|
||||
return
|
||||
}
|
||||
|
||||
userID := getUserID(r)
|
||||
var hash string
|
||||
s.db.QueryRow("SELECT password_hash FROM users WHERE id = ?", userID).Scan(&hash)
|
||||
if !auth.CheckPassword(hash, req.CurrentPassword) {
|
||||
writeJSON(w, 401, map[string]string{"error": "current password is incorrect"})
|
||||
return
|
||||
}
|
||||
|
||||
newHash, err := auth.HashPassword(req.NewPassword)
|
||||
if err != nil {
|
||||
writeJSON(w, 500, map[string]string{"error": "failed to hash password"})
|
||||
return
|
||||
}
|
||||
|
||||
s.db.Exec("UPDATE users SET password_hash = ?, updated_at = datetime('now') WHERE id = ?", newHash, userID)
|
||||
writeJSON(w, 200, map[string]string{"status": "password changed"})
|
||||
}
|
||||
|
||||
// ─── Users ───────────────────────────────────────────────────────────────────
|
||||
|
||||
func (s *Server) handleCreateUser(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -27,6 +27,7 @@ func NewRouter(db *sql.DB, dataDir, secret string) http.Handler {
|
||||
// Users (admin)
|
||||
mux.HandleFunc("POST /api/users/create", s.requireAdmin(s.handleCreateUser))
|
||||
mux.HandleFunc("POST /api/users/list", s.requireAdmin(s.handleListUsers))
|
||||
mux.HandleFunc("POST /api/auth/change-password", s.requireAuth(s.handleChangePassword))
|
||||
|
||||
// Files
|
||||
mux.HandleFunc("POST /api/files/list", s.requireAuth(s.handleListFiles))
|
||||
|
||||
Reference in New Issue
Block a user