diff --git a/.env b/.env index 4c365d4..b38307c 100644 --- a/.env +++ b/.env @@ -2,4 +2,5 @@ DATABASE_URL=postgres://milkydata:44n6FdB8CdDAk5rk@154.37.213.24:5432/milkydata WECHAT_APPID="wx5b00eb90621802f7" WECHAT_SECRET="494efc513faa310bfba588bda2849bfd" -JWT_SECRET="your_super_secret_key" \ No newline at end of file +JWT_SECRET="your_super_secret_key" +RUST_LOG=info \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 2590835..22f2d90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -268,6 +268,56 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +dependencies = [ + "windows-sys 0.60.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.60.2", +] + [[package]] name = "atoi" version = "2.0.0" @@ -415,6 +465,12 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -603,6 +659,29 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1266,12 +1345,42 @@ dependencies = [ "serde", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jiff" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "jobserver" version = "0.1.34" @@ -1548,6 +1657,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "openssl" version = "0.10.73" @@ -1685,6 +1800,21 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" version = "0.1.3" @@ -1918,9 +2048,11 @@ dependencies = [ "actix-web", "chrono", "dotenvy", + "env_logger", "error", "include_dir", "jsonwebtoken", + "log", "reqwest", "serde", "serde_json", @@ -2775,6 +2907,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.18.0" @@ -3046,6 +3184,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.4", +] + [[package]] name = "windows-sys" version = "0.61.0" @@ -3079,13 +3226,30 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b" +dependencies = [ + "windows-link 0.2.0", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -3098,6 +3262,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -3110,6 +3280,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -3122,12 +3298,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -3140,6 +3328,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -3152,6 +3346,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -3164,6 +3364,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -3176,6 +3382,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "wit-bindgen" version = "0.45.0" diff --git a/Cargo.toml b/Cargo.toml index d0cf83b..3f3ba96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,11 @@ actix-files = "0.6.8" actix-web = "4.11.0" chrono = {version = "0.4.41", features=["serde"]} dotenvy = "0.15.7" +env_logger = "0.11.8" error = "0.1.9" include_dir = "0.7.4" jsonwebtoken = "9.3.1" +log = "0.4.28" reqwest = { version = "0.12.23", features=["json"]} serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.143" diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..2a0fb72 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,32 @@ +use std::env; +use dotenvy::dotenv; + +#[derive(Debug, Clone)] +pub struct Config { + pub database_url: String, + pub jwt_secret: String, + pub wechat_appid: String, + pub wechat_secret: String, + pub server_ports: Vec, +} + +impl Config { + pub fn load() -> Result { + // 加载.env文件 + if let Err(e) = dotenv() { + eprintln!("警告: 无法加载.env文件 - {}", e); + } + + Ok(Self { + database_url: env::var("DATABASE_URL") + .map_err(|_| "环境变量DATABASE_URL未设置".to_string())?, + jwt_secret: env::var("JWT_SECRET") + .map_err(|_| "环境变量JWT_SECRET未设置".to_string())?, + wechat_appid: env::var("WECHAT_APPID") + .map_err(|_| "环境变量WECHAT_APPID未设置".to_string())?, + wechat_secret: env::var("WECHAT_SECRET") + .map_err(|_| "环境变量WECHAT_SECRET未设置".to_string())?, + server_ports: vec![8080, 3000, 8000, 8888], + }) + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index f30e602..9b446d8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use actix_web::middleware::from_fn; use actix_web::{App, HttpResponse, HttpServer, Responder, delete, get, post, web}; use include_dir::{Dir, include_dir}; +use log::{debug, error, info, warn}; use reqwest::Client; use sqlx::postgres::PgPool; mod auth; @@ -11,13 +12,12 @@ use auth::{generate_token, jwt_middleware}; use db::{create_pool, insert_weather_data}; use models::{ErrorResponse, WeChatApiResponse, WeChatLoginRequest, WeatherData}; -use crate::models::Claims; +use crate::models::{Claims, TokenResponse}; // 编译时嵌入整个 static 目录 static STATIC_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/static"); static TEMPLATES_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates"); -// 登录的API端点 #[post("/api/login")] async fn login( pool: web::Data, @@ -26,8 +26,12 @@ async fn login( ) -> impl Responder { // 从环境变量获取微信小程序配置 let appid = match std::env::var("WECHAT_APPID") { - Ok(id) => id, + Ok(id) => { + debug!("成功加载微信小程序appid"); + id + } Err(_) => { + error!("环境变量中未配置WECHAT_APPID"); return HttpResponse::InternalServerError().json(ErrorResponse { error: "服务器配置错误:缺少微信小程序appid".to_string(), errcode: None, @@ -37,8 +41,12 @@ async fn login( }; let secret = match std::env::var("WECHAT_SECRET") { - Ok(secret) => secret, + Ok(secret) => { + debug!("成功加载微信小程序secret"); + secret + } Err(_) => { + error!("环境变量中未配置WECHAT_SECRET"); return HttpResponse::InternalServerError().json(ErrorResponse { error: "服务器配置错误:缺少微信小程序secret".to_string(), errcode: None, @@ -52,12 +60,16 @@ async fn login( "https://api.weixin.qq.com/sns/jscode2session?appid={}&secret={}&js_code={}&grant_type=authorization_code", appid, secret, req.code ); + debug!("准备调用微信API: {}", url.replace(&secret, "***")); // 日志中隐藏secret // 调用微信API let wechat_response = match http_client.get(&url).send().await { - Ok(response) => response, + Ok(response) => { + debug!("微信API请求成功,状态码: {}", response.status()); + response + } Err(e) => { - eprintln!("请求微信API失败: {}", e); + error!("微信API请求失败: {}", e); return HttpResponse::InternalServerError().json(ErrorResponse { error: "请求微信服务失败".to_string(), errcode: None, @@ -68,9 +80,12 @@ async fn login( // 解析微信API响应 let wechat_data: WeChatApiResponse = match wechat_response.json().await { - Ok(data) => data, + Ok(data) => { + debug!("微信API响应解析成功"); + data + } Err(e) => { - eprintln!("解析微信API响应失败: {}", e); + error!("微信API响应解析失败: {}", e); return HttpResponse::InternalServerError().json(ErrorResponse { error: "解析微信响应失败".to_string(), errcode: None, @@ -81,6 +96,10 @@ async fn login( // 检查微信API响应中的错误 if let Some(errcode) = wechat_data.errcode { + warn!( + "微信登录验证失败 - 错误码: {}, 错误信息: {:?}", + errcode, wechat_data.errmsg + ); return HttpResponse::BadRequest().json(ErrorResponse { error: "微信登录失败".to_string(), errcode: Some(errcode), @@ -90,8 +109,14 @@ async fn login( // 获取openid let openid = match wechat_data.openid { - Some(id) => id, + Some(id) => { + // 日志中只显示openid的前4位和后4位,保护用户隐私 + let masked_openid = format!("{}***{}", &id[0..4], &id[id.len() - 4..]); + debug!("获取到用户openid: {}", masked_openid); + id + } None => { + error!("微信API响应中未包含openid"); return HttpResponse::InternalServerError().json(ErrorResponse { error: "微信API未返回openid".to_string(), errcode: None, @@ -100,7 +125,7 @@ async fn login( } }; - // 3. 检查用户是否已存在,不存在则创建(显式处理数据库错误) + // 检查用户是否已存在,不存在则创建 let query = r#" INSERT INTO users (openid, name, type) VALUES ($1, left($1, 8), 2) @@ -114,21 +139,22 @@ async fn login( .await { Ok(Some((id,))) => { - // 已存在用户,直接获取ID - println!("用户已存在,ID: {}", id); + info!("用户已存在,用户ID: {}", id); id } Ok(None) => { - // 新用户创建成功,需再次查询ID(因ON CONFLICT DO NOTHING不返回值) - println!("新用户已创建,openid: {}", openid); + // 新用户创建成功,查询ID match sqlx::query_as::<_, (i32,)>("SELECT id FROM users WHERE openid = $1") .bind(&openid) .fetch_one(pool.get_ref()) .await { - Ok((id,)) => id, + Ok((id,)) => { + info!("新用户创建成功,用户ID: {}", id); + id + } Err(e) => { - eprintln!("查询新用户ID失败: {}", e); + error!("新用户创建后查询ID失败: {}", e); return HttpResponse::InternalServerError().json(ErrorResponse { error: "创建用户后查询ID失败".to_string(), errcode: Some(500), @@ -138,8 +164,7 @@ async fn login( } } Err(e) => { - // 数据库操作失败 - eprintln!("用户查询/创建失败: {}", e); + error!("用户查询/创建失败: {}", e); return HttpResponse::InternalServerError().json(ErrorResponse { error: "用户信息处理失败".to_string(), errcode: Some(500), @@ -148,11 +173,11 @@ async fn login( } }; - // 4. 生成JWT token(显式处理所有可能的错误) - // 4.1 获取JWT密钥 + // 生成JWT token let jwt_secret = match std::env::var("JWT_SECRET") { Ok(secret) => secret, Err(_) => { + error!("环境变量中未配置JWT_SECRET"); return HttpResponse::InternalServerError().json(ErrorResponse { error: "服务器配置错误:缺少JWT_SECRET".to_string(), errcode: Some(500), @@ -161,11 +186,26 @@ async fn login( } }; - // 4.2 编码生成token - let token = generate_token(user_id, &openid, 2, &jwt_secret); + // 生成token + let token = match generate_token(user_id, &openid, 2, &jwt_secret) { + Ok(t) => { + // 日志中只显示token的前8位,保护安全 + debug!("JWT生成成功: {}", &t[0..8]); + t + } + Err(e) => { + error!("JWT生成失败: {}", e); + return HttpResponse::InternalServerError().json(ErrorResponse { + error: "生成身份令牌失败".to_string(), + errcode: Some(500), + errmsg: Some(e.to_string()), + }); + } + }; - // 5. 返回成功响应(包含token) - HttpResponse::Ok().json(token) + info!("用户登录成功,用户ID: {}", user_id); + let token_response = TokenResponse::new(token); + HttpResponse::Ok().json(token_response) } #[post("/api/post-weather-data")] @@ -566,6 +606,16 @@ fn create_server_config( #[actix_web::main] async fn main() -> std::io::Result<()> { + if let Err(e) = dotenvy::dotenv() { + eprintln!("警告:加载.env文件失败,使用系统环境变量: {}", e); + } + + // 2. 打印当前 RUST_LOG 的值(关键:验证配置是否生效) + let rust_log = std::env::var("RUST_LOG").unwrap_or_else(|_| "未设置".to_string()); + eprintln!("当前 RUST_LOG 级别:{}", rust_log); // 这行用 eprintln!,不受日志级别影响 + + env_logger::init(); // 添加这一行 + info!("logger init successful"); // 初始化数据库连接池 let pool = match create_pool().await { Ok(pool) => pool, diff --git a/src/models.rs b/src/models.rs index 1bab4d8..02f12af 100644 --- a/src/models.rs +++ b/src/models.rs @@ -28,6 +28,36 @@ pub struct TempTokenClaims { pub resource_id: i32, // 允许访问的资源ID(如天气数据id) } +// 登录成功后的令牌响应结构体 +/// 仅包含操作状态和令牌信息 +#[derive(Debug, Serialize, Clone)] +pub struct TokenResponse { + /// 操作状态:true表示登录成功,false表示失败 + pub success: bool, + + /// 登录成功后生成的JWT令牌,客户端后续请求需携带此令牌 + pub token: String, +} + +impl TokenResponse { + /// 快速创建登录成功的令牌响应 + /// - token: 生成的JWT令牌字符串 + pub fn new(token: String) -> Self { + Self { + success: true, // 登录成功场景固定为true + token, + } + } + + /// 创建登录失败的响应(可选,用于统一错误响应格式) + pub fn failed() -> Self { + Self { + success: false, + token: String::new(), // 失败时令牌为空 + } + } +} + #[derive(Debug, Deserialize)] pub struct WeChatLoginRequest { pub code: String,