Add change password feature to settings page

- Add PUT /api/me/password endpoint in backend
- Add changePassword API function in frontend
- Add change password form UI in SettingsPanel
- Add i18n translations for change password (zh/en)
- Fix TypeScript type error in i18n context

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Michael Dong
2026-02-05 18:30:57 +08:00
parent 403843acfd
commit 86d3a8c419
5 changed files with 163 additions and 1 deletions

View File

@@ -5,6 +5,7 @@ use sea_orm::{ActiveModelTrait, EntityTrait, Set};
use serde::{Deserialize, Serialize};
use std::io::Write;
use uuid::Uuid;
use bcrypt;
use crate::app_data::AppData;
use crate::entity::user;
@@ -33,6 +34,13 @@ pub struct UpdateSettingsRequest {
pub bark_enabled: Option<bool>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChangePasswordRequest {
pub current_password: String,
pub new_password: String,
}
#[get("")]
async fn get_me(
app_data: web::Data<AppData>,
@@ -208,9 +216,46 @@ async fn upload_avatar(
Ok(HttpResponse::Ok().json(UploadAvatarResponse { avatar_url }))
}
#[put("/password")]
async fn change_password(
app_data: web::Data<AppData>,
auth: AuthUser,
body: web::Json<ChangePasswordRequest>,
) -> Result<impl Responder, ApiError> {
if body.new_password.len() < 6 {
return Err(ApiError::BadRequest("New password must be at least 6 characters".to_string()));
}
let user = user::Entity::find_by_id(auth.user_id)
.one(&app_data.db)
.await?
.ok_or_else(|| ApiError::NotFound("User not found".to_string()))?;
// Verify current password
let valid = bcrypt::verify(&body.current_password, &user.password_hash)
.map_err(|_| ApiError::Internal("Password verification failed".to_string()))?;
if !valid {
return Err(ApiError::BadRequest("Current password is incorrect".to_string()));
}
// Hash new password
let new_password_hash = bcrypt::hash(&body.new_password, 10)
.map_err(|_| ApiError::Internal("Password hashing failed".to_string()))?;
// Update password
let mut active: user::ActiveModel = user.into();
active.password_hash = Set(new_password_hash);
active.updated_at = Set(chrono::Utc::now().fixed_offset());
active.update(&app_data.db).await?;
Ok(HttpResponse::Ok().json(serde_json::json!({"success": true})))
}
pub fn routes() -> Scope {
web::scope("/api/me")
.service(get_me)
.service(update_settings)
.service(upload_avatar)
.service(change_password)
}