添加管理员API端点
- .env 添加 FREE_USER_DATA_LIMIT 配置
- 新增 GET /api/admin/users/{id} 管理员获取用户信息
- 新增 PUT /api/admin/users/{id}/payment 更新用户付费状态
- post_weather_data 集成 JWT 认证,使用 claims.user_id
- 管理员接口通过数据库查询验证 is_admin
This commit is contained in:
1
.env
1
.env
@@ -7,3 +7,4 @@ SSL_KEY_PATH=/etc/ssl/private/private.key
|
|||||||
SSL_CERT_PATH=/etc/ssl/certs/full_chain.pem
|
SSL_CERT_PATH=/etc/ssl/certs/full_chain.pem
|
||||||
RUST_LOG=info
|
RUST_LOG=info
|
||||||
APP_VERSION="0.2.0"
|
APP_VERSION="0.2.0"
|
||||||
|
FREE_USER_DATA_LIMIT=20
|
||||||
133
src/main.rs
133
src/main.rs
@@ -1,5 +1,5 @@
|
|||||||
use actix_web::middleware::from_fn;
|
use actix_web::middleware::from_fn;
|
||||||
use actix_web::{App, HttpResponse, HttpServer, Responder, delete, get, post, web};
|
use actix_web::{App, HttpResponse, HttpServer, Responder, delete, get, post, put, web};
|
||||||
use include_dir::{Dir, include_dir};
|
use include_dir::{Dir, include_dir};
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod};
|
use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod};
|
||||||
@@ -14,7 +14,7 @@ use auth::{generate_token, jwt_middleware};
|
|||||||
use db::{create_pool, insert_weather_data};
|
use db::{create_pool, insert_weather_data};
|
||||||
use models::{ErrorResponse, WeChatApiResponse, WeChatLoginRequest, WeatherData};
|
use models::{ErrorResponse, WeChatApiResponse, WeChatLoginRequest, WeatherData};
|
||||||
|
|
||||||
use crate::models::{Claims, TokenResponse};
|
use crate::models::{Claims, TokenResponse, UpdatePaymentRequest};
|
||||||
|
|
||||||
// 编译时嵌入整个 static 目录
|
// 编译时嵌入整个 static 目录
|
||||||
static STATIC_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/static");
|
static STATIC_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/static");
|
||||||
@@ -214,11 +214,14 @@ async fn login(
|
|||||||
async fn post_weather_data(
|
async fn post_weather_data(
|
||||||
data: web::Json<WeatherData>,
|
data: web::Json<WeatherData>,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
|
claims: web::ReqData<Claims>,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
info!("Received weather data, preparing to insert into DB...");
|
info!("Received weather data, preparing to insert into DB...");
|
||||||
|
|
||||||
|
let user_id = claims.user_id;
|
||||||
|
|
||||||
// 调用 db.rs 中的函数来处理数据库逻辑
|
// 调用 db.rs 中的函数来处理数据库逻辑
|
||||||
match insert_weather_data(pool.get_ref(), &data).await {
|
match insert_weather_data(pool.get_ref(), &data, user_id).await {
|
||||||
Ok(inserted_id) => {
|
Ok(inserted_id) => {
|
||||||
info!(
|
info!(
|
||||||
"Successfully inserted weather data with id: {}",
|
"Successfully inserted weather data with id: {}",
|
||||||
@@ -233,14 +236,9 @@ async fn post_weather_data(
|
|||||||
}
|
}
|
||||||
Err(error_msg) => {
|
Err(error_msg) => {
|
||||||
error!("Database operation failed: {}", error_msg);
|
error!("Database operation failed: {}", error_msg);
|
||||||
// 根据错误信息返回统一格式的错误响应
|
|
||||||
let mut errcode = 500;
|
|
||||||
if error_msg.starts_with("未找到openid") {
|
|
||||||
errcode = 404;
|
|
||||||
}
|
|
||||||
HttpResponse::Ok().json(serde_json::json!({
|
HttpResponse::Ok().json(serde_json::json!({
|
||||||
"success": false,
|
"success": false,
|
||||||
"errcode": errcode,
|
"errcode": 500,
|
||||||
"errmsg": error_msg
|
"errmsg": error_msg
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -547,6 +545,119 @@ async fn delete_weather(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 管理员获取用户信息
|
||||||
|
#[get("/api/admin/users/{id}")]
|
||||||
|
async fn admin_get_user(
|
||||||
|
path: web::Path<i32>,
|
||||||
|
pool: web::Data<PgPool>,
|
||||||
|
claims: web::ReqData<Claims>,
|
||||||
|
) -> impl Responder {
|
||||||
|
let target_user_id = path.into_inner();
|
||||||
|
info!("管理员获取用户信息, 目标用户ID: {}", target_user_id);
|
||||||
|
|
||||||
|
// 验证当前用户是否为管理员
|
||||||
|
let current_user = match db::get_user_by_id(pool.get_ref(), claims.user_id).await {
|
||||||
|
Ok(user) => user,
|
||||||
|
Err(e) => {
|
||||||
|
error!("获取当前用户信息失败: {}", e);
|
||||||
|
return HttpResponse::Ok().json(serde_json::json!({
|
||||||
|
"success": false,
|
||||||
|
"errcode": 500,
|
||||||
|
"errmsg": "获取用户信息失败"
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !current_user.is_admin {
|
||||||
|
return HttpResponse::Forbidden().json(serde_json::json!({
|
||||||
|
"success": false,
|
||||||
|
"errcode": 403,
|
||||||
|
"errmsg": "无权限执行此操作"
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取目标用户信息
|
||||||
|
match db::get_user_by_id(pool.get_ref(), target_user_id).await {
|
||||||
|
Ok(user) => HttpResponse::Ok().json(serde_json::json!({
|
||||||
|
"success": true,
|
||||||
|
"data": user
|
||||||
|
})),
|
||||||
|
Err(e) => {
|
||||||
|
error!("获取目标用户信息失败: {}", e);
|
||||||
|
let errcode = if e.starts_with("未找到") { 404 } else { 500 };
|
||||||
|
HttpResponse::Ok().json(serde_json::json!({
|
||||||
|
"success": false,
|
||||||
|
"errcode": errcode,
|
||||||
|
"errmsg": e
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 管理员更新用户付费状态
|
||||||
|
#[put("/api/admin/users/{id}/payment")]
|
||||||
|
async fn admin_update_user_payment(
|
||||||
|
path: web::Path<i32>,
|
||||||
|
pool: web::Data<PgPool>,
|
||||||
|
claims: web::ReqData<Claims>,
|
||||||
|
body: web::Json<UpdatePaymentRequest>,
|
||||||
|
) -> impl Responder {
|
||||||
|
let target_user_id = path.into_inner();
|
||||||
|
info!("管理员更新用户付费状态, 目标用户ID: {}", target_user_id);
|
||||||
|
|
||||||
|
// 验证当前用户是否为管理员
|
||||||
|
let current_user = match db::get_user_by_id(pool.get_ref(), claims.user_id).await {
|
||||||
|
Ok(user) => user,
|
||||||
|
Err(e) => {
|
||||||
|
error!("获取当前用户信息失败: {}", e);
|
||||||
|
return HttpResponse::Ok().json(serde_json::json!({
|
||||||
|
"success": false,
|
||||||
|
"errcode": 500,
|
||||||
|
"errmsg": "获取用户信息失败"
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !current_user.is_admin {
|
||||||
|
return HttpResponse::Forbidden().json(serde_json::json!({
|
||||||
|
"success": false,
|
||||||
|
"errcode": 403,
|
||||||
|
"errmsg": "无权限执行此操作"
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 paid_expires_at
|
||||||
|
let paid_expires_at = match &body.paid_expires_at {
|
||||||
|
Some(date_str) => match chrono::DateTime::parse_from_rfc3339(date_str) {
|
||||||
|
Ok(dt) => Some(dt.with_timezone(&chrono::Utc)),
|
||||||
|
Err(e) => {
|
||||||
|
return HttpResponse::BadRequest().json(serde_json::json!({
|
||||||
|
"success": false,
|
||||||
|
"errcode": 400,
|
||||||
|
"errmsg": format!("日期格式错误: {}", e)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新用户付费状态
|
||||||
|
match db::update_user_payment_status(pool.get_ref(), target_user_id, body.is_paid, paid_expires_at).await {
|
||||||
|
Ok(_) => HttpResponse::Ok().json(serde_json::json!({
|
||||||
|
"success": true,
|
||||||
|
"message": "用户付费状态已更新"
|
||||||
|
})),
|
||||||
|
Err(e) => {
|
||||||
|
error!("更新用户付费状态失败: {}", e);
|
||||||
|
HttpResponse::Ok().json(serde_json::json!({
|
||||||
|
"success": false,
|
||||||
|
"errcode": 500,
|
||||||
|
"errmsg": e
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 在 main.rs 中添加以下函数,用于处理静态文件请求
|
// 在 main.rs 中添加以下函数,用于处理静态文件请求
|
||||||
async fn serve_static_files(path: web::Path<String>) -> impl Responder {
|
async fn serve_static_files(path: web::Path<String>) -> impl Responder {
|
||||||
// 获取请求的文件路径(例如 "css/style.css")
|
// 获取请求的文件路径(例如 "css/style.css")
|
||||||
@@ -615,7 +726,9 @@ fn create_server_config(
|
|||||||
.service(post_weather_data)
|
.service(post_weather_data)
|
||||||
.service(get_weather_brief) // 新增
|
.service(get_weather_brief) // 新增
|
||||||
.service(generate_temp_token_handler)
|
.service(generate_temp_token_handler)
|
||||||
.service(delete_weather),
|
.service(delete_weather)
|
||||||
|
.service(admin_get_user)
|
||||||
|
.service(admin_update_user_payment),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user