feat(auth): 添加沙箱测试用 mock_login 接口 (GET /api/mock-login)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
use actix_web::{web, HttpResponse, Responder, post};
|
||||
use actix_web::{web, HttpResponse, Responder, post, get};
|
||||
use chrono::{Utc, Duration};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use reqwest::Client;
|
||||
use sqlx::postgres::PgPool;
|
||||
use tracing::{debug, error, info, warn};
|
||||
@@ -190,3 +191,129 @@ pub async fn refresh_token(
|
||||
};
|
||||
HttpResponse::Ok().json(response)
|
||||
}
|
||||
|
||||
/// 模拟登录请求(仅供测试/沙箱环境使用)
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct MockLoginRequest {
|
||||
/// 指定用户 ID(如不指定则自动创建测试用户)
|
||||
pub user_id: Option<i32>,
|
||||
}
|
||||
|
||||
/// 模拟登录响应
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct MockLoginResponse {
|
||||
pub success: bool,
|
||||
pub token: String,
|
||||
pub refresh_token: String,
|
||||
pub user_id: i32,
|
||||
}
|
||||
|
||||
/// GET /api/mock-login
|
||||
/// 沙箱测试用:无需微信 code,直接返回 JWT
|
||||
/// 通过环境变量 MOCK_LOGIN_ENABLED 控制是否启用
|
||||
#[get("/api/mock-login")]
|
||||
pub async fn mock_login(
|
||||
pool: web::Data<PgPool>,
|
||||
query: web::Query<MockLoginRequest>,
|
||||
app_state: web::Data<AppState>,
|
||||
) -> impl Responder {
|
||||
// 检查是否启用
|
||||
if std::env::var("MOCK_LOGIN_ENABLED").ok() != Some("true".to_string()) {
|
||||
return HttpResponse::NotFound().json(ErrorResponse::<()>::error("模拟登录未启用"));
|
||||
}
|
||||
|
||||
let user_id = match query.user_id {
|
||||
Some(id) => {
|
||||
// 验证用户存在
|
||||
match sqlx::query_as::<_, (i32,)>(("SELECT id FROM users WHERE id = $1"))
|
||||
.bind(id)
|
||||
.fetch_optional(pool.get_ref())
|
||||
.await
|
||||
{
|
||||
Ok(Some((uid,))) => uid,
|
||||
Ok(None) => {
|
||||
return HttpResponse::BadRequest()
|
||||
.json(ErrorResponse::<()>::error("用户不存在"));
|
||||
}
|
||||
Err(e) => {
|
||||
error!("查询用户失败: {}", e);
|
||||
return HttpResponse::InternalServerError()
|
||||
.json(ErrorResponse::<()>::error("数据库错误"));
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// 创建或获取测试用户
|
||||
let test_openid = format!("mock_test_{}", Utc::now().timestamp_millis());
|
||||
match sqlx::query_as::<_, (i32,)>(
|
||||
r#"INSERT INTO users (openid, name, type) VALUES ($1, $2, 2)
|
||||
ON CONFLICT (openid) DO UPDATE SET id = users.id RETURNING id"#,
|
||||
)
|
||||
.bind(&test_openid)
|
||||
.bind(&test_openid[..8.min(test_openid.len())])
|
||||
.fetch_one(pool.get_ref())
|
||||
.await
|
||||
{
|
||||
Ok((id,)) => id,
|
||||
Err(e) => {
|
||||
error!("创建测试用户失败: {}", e);
|
||||
return HttpResponse::InternalServerError()
|
||||
.json(ErrorResponse::<()>::error("创建测试用户失败"));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 获取 openid
|
||||
let openid: String = match sqlx::query_as::<_, (String,)>("SELECT openid FROM users WHERE id = $1")
|
||||
.bind(user_id)
|
||||
.fetch_optional(pool.get_ref())
|
||||
.await
|
||||
{
|
||||
Ok(Some((o,))) => o,
|
||||
Ok(None) => {
|
||||
return HttpResponse::InternalServerError()
|
||||
.json(ErrorResponse::<()>::error("用户无 openid"));
|
||||
}
|
||||
Err(e) => {
|
||||
error!("查询 openid 失败: {}", e);
|
||||
return HttpResponse::InternalServerError()
|
||||
.json(ErrorResponse::<()>::error("数据库错误"));
|
||||
}
|
||||
};
|
||||
|
||||
// 生成 token
|
||||
let token = match generate_token(user_id, &openid, 2, &app_state.jwt_secret) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
error!("JWT 生成失败: {}", e);
|
||||
return HttpResponse::InternalServerError()
|
||||
.json(ErrorResponse::<()>::error("生成令牌失败"));
|
||||
}
|
||||
};
|
||||
|
||||
// 生成 refresh_token
|
||||
let refresh_token_str = match generate_refresh_token(user_id, &app_state.jwt_secret) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
error!("refresh_token 生成失败: {}", e);
|
||||
return HttpResponse::InternalServerError()
|
||||
.json(ErrorResponse::<()>::error("生成刷新令牌失败"));
|
||||
}
|
||||
};
|
||||
|
||||
let refresh_expires_at = Utc::now() + Duration::days(7);
|
||||
if let Err(e) =
|
||||
db::create_refresh_token(pool.get_ref(), user_id, &refresh_token_str, refresh_expires_at).await
|
||||
{
|
||||
warn!("保存 refresh_token 失败(继续): {}", e);
|
||||
}
|
||||
|
||||
info!("[MOCK LOGIN] user_id={} 登录成功", user_id);
|
||||
HttpResponse::Ok().json(MockLoginResponse {
|
||||
success: true,
|
||||
token,
|
||||
refresh_token: refresh_token_str,
|
||||
user_id,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user