feat(auth): 添加 Refresh Token 双 Token 机制

- 添加 /api/refresh-token 接口支持 Token 续期
- 登录接口返回 access_token 和 refresh_token
- 新增 refresh_tokens 表存储 refresh_token
- 部署脚本添加数据库备份和迁移功能
- deploy.sh 添加 4 项 API 测试
- 更新 AGENTS.md 文档
This commit is contained in:
2026-04-19 16:01:41 +08:00
parent 52991dcfd5
commit e14c85436b
11 changed files with 872 additions and 98 deletions

View File

@@ -28,14 +28,28 @@ pub struct TempTokenClaims {
pub resource_id: i32, // 允许访问的资源ID如天气数据id
}
// 登录成功后的令牌响应结构体
/// 仅包含操作状态和令牌信息
// 登录成功后的令牌响应结构体(双 token
#[derive(Debug, Serialize, Clone)]
pub struct LoginResponse {
pub success: bool,
pub token: String,
pub refresh_token: String,
}
impl LoginResponse {
pub fn new(token: String, refresh_token: String) -> Self {
Self {
success: true,
token,
refresh_token,
}
}
}
// 兼容旧的 TokenResponse
#[derive(Debug, Serialize, Clone)]
pub struct TokenResponse {
/// 操作状态true表示登录成功false表示失败
pub success: bool,
/// 登录成功后生成的JWT令牌客户端后续请求需携带此令牌
pub token: String,
}
@@ -48,6 +62,20 @@ impl TokenResponse {
}
}
// Refresh Token 请求
#[derive(Debug, Deserialize)]
pub struct RefreshTokenRequest {
pub refresh_token: String,
}
// Token 刷新响应
#[derive(Debug, Serialize)]
pub struct TokenRefreshResponse {
pub success: bool,
pub token: String,
pub refresh_token: String,
}
#[derive(Debug, Deserialize)]
pub struct WeChatLoginRequest {
pub code: String,
@@ -60,13 +88,6 @@ pub struct WeChatApiResponse {
pub errmsg: Option<String>,
}
#[derive(Debug, Serialize)]
pub struct ErrorResponse {
pub error: String,
pub errcode: Option<i32>,
pub errmsg: Option<String>,
}
// 定义新类型,包装 Vec<f64>(当前 crate 内的类型)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FloatVec(pub Vec<f64>);
@@ -316,6 +337,7 @@ pub struct AppState {
pub wechat_appid: String,
pub wechat_secret: String,
pub free_user_data_limit: i32,
pub app_version: String,
}
impl AppState {
@@ -330,6 +352,8 @@ impl AppState {
free_user_data_limit: std::env::var("FREE_USER_DATA_LIMIT")
.map(|v| v.parse().unwrap_or(20))
.unwrap_or(20),
app_version: std::env::var("APP_VERSION")
.unwrap_or_else(|_| "unknown".to_string()),
})
}
}