feat: 全面重构前端UI及完善后端功能
前端重构: - 重构Layout为左侧导航+顶栏的现代管理后台布局 - 重构设备管理页面(Devices.vue):左侧分组面板+右侧设备列表 - 重构设备详情(DeviceDetail.vue):集成硬件资产/软件资产/变更记录标签页 - 移除独立资产管理页面,功能合并至设备详情 - 重构Dashboard/登录/设置/告警/水印/上网管控等页面样式 - 新增全局CSS变量和统一样式系统 - 添加分组管理UI:新建/重命名/删除分组,移动设备到分组 后端完善: - 新增分组CRUD API(groups.rs):创建/重命名/删除分组,设备分组移动 - 客户端硬件采集:完善GPU/主板/序列号/磁盘信息采集(Windows PowerShell) - 客户端软件采集:通过Windows注册表读取已安装软件列表 - 新增SoftwareAssetReport消息类型(0x09)及处理链路 - 数据库新增upsert_software方法处理软件资产存储 - 服务端推送软件资产配置给新注册设备 - 修复密码修改功能,添加旧密码验证
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use axum::{extract::State, Json, http::StatusCode, extract::Request, middleware::Next, response::Response};
|
||||
use axum::{extract::State, Json, http::StatusCode, extract::Request, middleware::Next, response::Response, Extension};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use jsonwebtoken::{encode, decode, Header, EncodingKey, DecodingKey, Validation};
|
||||
use std::sync::Arc;
|
||||
@@ -45,6 +45,12 @@ pub struct RefreshRequest {
|
||||
pub refresh_token: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ChangePasswordRequest {
|
||||
pub old_password: String,
|
||||
pub new_password: String,
|
||||
}
|
||||
|
||||
/// In-memory rate limiter for login attempts
|
||||
#[derive(Clone, Default)]
|
||||
pub struct LoginRateLimiter {
|
||||
@@ -293,3 +299,46 @@ pub async fn require_admin(
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub async fn change_password(
|
||||
State(state): State<AppState>,
|
||||
claims: axum::Extension<Claims>,
|
||||
Json(req): Json<ChangePasswordRequest>,
|
||||
) -> Result<(StatusCode, Json<ApiResponse<()>>), StatusCode> {
|
||||
if req.new_password.len() < 6 {
|
||||
return Ok((StatusCode::BAD_REQUEST, Json(ApiResponse::error("新密码至少6位"))));
|
||||
}
|
||||
|
||||
// Verify old password
|
||||
let hash: String = sqlx::query_scalar::<_, String>(
|
||||
"SELECT password FROM users WHERE id = ?"
|
||||
)
|
||||
.bind(claims.sub)
|
||||
.fetch_one(&state.db)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
if !bcrypt::verify(&req.old_password, &hash).unwrap_or(false) {
|
||||
return Ok((StatusCode::BAD_REQUEST, Json(ApiResponse::error("当前密码错误"))));
|
||||
}
|
||||
|
||||
// Update password
|
||||
let new_hash = bcrypt::hash(&req.new_password, 12).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
sqlx::query("UPDATE users SET password = ? WHERE id = ?")
|
||||
.bind(&new_hash)
|
||||
.bind(claims.sub)
|
||||
.execute(&state.db)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
// Audit log
|
||||
let _ = sqlx::query(
|
||||
"INSERT INTO admin_audit_log (user_id, action, detail) VALUES (?, 'change_password', ?)"
|
||||
)
|
||||
.bind(claims.sub)
|
||||
.bind(format!("User {} changed password", claims.username))
|
||||
.execute(&state.db)
|
||||
.await;
|
||||
|
||||
Ok((StatusCode::OK, Json(ApiResponse::ok(()))))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user