diff --git a/.env b/.env index b38307c..e1ce95c 100644 --- a/.env +++ b/.env @@ -3,4 +3,7 @@ DATABASE_URL=postgres://milkydata:44n6FdB8CdDAk5rk@154.37.213.24:5432/milkydata WECHAT_APPID="wx5b00eb90621802f7" WECHAT_SECRET="494efc513faa310bfba588bda2849bfd" JWT_SECRET="your_super_secret_key" -RUST_LOG=info \ No newline at end of file +SSL_KEY_PATH=/etc/ssl/private/private.key +SSL_CERT_PATH=/etc/ssl/certs/full_chain.pem +RUST_LOG=info +APP_VERSION="0.1.9" \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 22f2d90..1f52136 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,6 +51,7 @@ dependencies = [ "actix-codec", "actix-rt", "actix-service", + "actix-tls", "actix-utils", "base64", "bitflags", @@ -143,6 +144,25 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "actix-tls" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac453898d866cdbecdbc2334fe1738c747b4eba14a677261f2b768ba05329389" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "impl-more", + "openssl", + "pin-project-lite", + "tokio", + "tokio-openssl", + "tokio-util", + "tracing", +] + [[package]] name = "actix-utils" version = "3.0.1" @@ -166,6 +186,7 @@ dependencies = [ "actix-rt", "actix-server", "actix-service", + "actix-tls", "actix-utils", "actix-web-codegen", "bytes", @@ -2053,6 +2074,7 @@ dependencies = [ "include_dir", "jsonwebtoken", "log", + "openssl", "reqwest", "serde", "serde_json", @@ -2709,6 +2731,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-openssl" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59df6849caa43bb7567f9a36f863c447d95a11d5903c9cc334ba32576a27eadd" +dependencies = [ + "openssl", + "openssl-sys", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.2" diff --git a/Cargo.toml b/Cargo.toml index 3f3ba96..84115f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] actix-files = "0.6.8" -actix-web = "4.11.0" +actix-web = {version = "4.11.0", features=["openssl"]} chrono = {version = "0.4.41", features=["serde"]} dotenvy = "0.15.7" env_logger = "0.11.8" @@ -13,6 +13,7 @@ error = "0.1.9" include_dir = "0.7.4" jsonwebtoken = "9.3.1" log = "0.4.28" +openssl = "0.10.73" reqwest = { version = "0.12.23", features=["json"]} serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.143" diff --git a/src/main.rs b/src/main.rs index 9b446d8..9f2c56c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,10 @@ +use std::pin::Pin; + 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 openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod}; use reqwest::Client; use sqlx::postgres::PgPool; mod auth; @@ -18,6 +21,23 @@ use crate::models::{Claims, TokenResponse}; static STATIC_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/static"); static TEMPLATES_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates"); +// 加载TLS证书和私钥 +fn create_ssl_acceptor() -> Result> { + let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls())?; + + // 从环境变量获取证书和私钥路径 + let key_path = + std::env::var("SSL_KEY_PATH").unwrap_or_else(|_| "path/to/private.key".to_string()); + let cert_path = + std::env::var("SSL_CERT_PATH").unwrap_or_else(|_| "path/to/certificate.crt".to_string()); + + builder.set_private_key_file(&key_path, SslFiletype::PEM)?; + builder.set_certificate_chain_file(&cert_path)?; + + // 返回构建器而不是构建后的接受器 + Ok(builder) +} + #[post("/api/login")] async fn login( pool: web::Data, @@ -610,18 +630,19 @@ async fn main() -> std::io::Result<()> { eprintln!("警告:加载.env文件失败,使用系统环境变量: {}", e); } - // 2. 打印当前 RUST_LOG 的值(关键:验证配置是否生效) + // 打印当前 RUST_LOG 的值 let rust_log = std::env::var("RUST_LOG").unwrap_or_else(|_| "未设置".to_string()); - eprintln!("当前 RUST_LOG 级别:{}", rust_log); // 这行用 eprintln!,不受日志级别影响 + eprintln!("当前 RUST_LOG 级别:{}", rust_log); - env_logger::init(); // 添加这一行 + env_logger::init(); info!("logger init successful"); + // 初始化数据库连接池 let pool = match create_pool().await { Ok(pool) => pool, Err(e) => { - eprintln!("Failed to create database pool: {}", e); - eprintln!("Please check your database connection configuration in .env file"); + error!("Failed to create database pool: {}", e); + error!("Please check your database connection configuration in .env file"); std::process::exit(1); } }; @@ -629,45 +650,99 @@ async fn main() -> std::io::Result<()> { // 初始化HTTP客户端 let http_client = Client::new(); - println!("Attempting to start server..."); - // 尝试多个端口 - let ports = vec![8080, 3000, 8000, 8888]; - let mut server = None; + info!("Attempting to start server..."); - for port in ports { + // 尝试多个端口 + let ports = vec![443, 8443, 8080, 3000, 8000, 8888]; + let mut server: Option< + Pin> + Unpin>>, + > = None; + let mut bound_port = 0; // 记录绑定的端口 + + // 使用&ports创建引用迭代器,而不是获取所有权 + for port in &ports { let addr = format!("0.0.0.0:{}", port); - println!("Trying to bind to {}", addr); + info!("Trying to bind to {}", addr); + // 为每个服务器创建克隆的连接池和HTTP客户端 let pool_clone = pool.clone(); let http_client_clone = http_client.clone(); - match HttpServer::new(move || { - create_server_config(pool_clone.clone(), http_client_clone.clone()) - }) - .bind(&addr) - { - Ok(s) => { - println!("Successfully bound to {}", addr); - server = Some(s); - break; - } - Err(e) => { - eprintln!("Failed to bind to {}: {}", addr, e); - if e.kind() == std::io::ErrorKind::PermissionDenied { - eprintln!(" -> Permission denied. Try running with sudo or use a port > 1024"); + // 根据端口选择是否使用SSL + if *port == 443 || *port == 8443 { + // 对于443和8443端口,使用HTTPS + // 为每个端口创建新的SSL构建器 + let ssl_builder = match create_ssl_acceptor() { + Ok(builder) => builder, + Err(e) => { + eprintln!("Failed to create SSL acceptor: {}", e); + continue; + } + }; + + match HttpServer::new(move || { + create_server_config(pool_clone.clone(), http_client_clone.clone()) + }) + .bind_openssl(&addr, ssl_builder) // 直接传递构建器 + { + Ok(s) => { + info!("Successfully bound to {} with HTTPS", addr); + // 将服务器运行Future转换为正确的类型 + let server_future = s.run(); + // 使用Box::pin将Pin>转换为Pin> + Unpin>> + server = Some(Box::pin(server_future)); + bound_port = *port; // 记录绑定的端口 + break; + } + Err(e) => { + eprintln!("Failed to bind to {} with HTTPS: {}", addr, e); + if e.kind() == std::io::ErrorKind::PermissionDenied { + eprintln!(" -> Permission denied. Try running with sudo or use a port > 1024"); + } + continue; + } + } + } else { + // 对于其他端口,使用HTTP + match HttpServer::new(move || { + create_server_config(pool_clone.clone(), http_client_clone.clone()) + }) + .bind(&addr) + { + Ok(s) => { + info!("Successfully bound to {} with HTTP", addr); + // 将服务器运行Future转换为正确的类型 + let server_future = s.run(); + // 使用Box::pin将Pin>转换为Pin> + Unpin>> + server = Some(Box::pin(server_future)); + bound_port = *port; // 记录绑定的端口 + break; + } + Err(e) => { + error!("Failed to bind to {} with HTTP: {}", addr, e); + if e.kind() == std::io::ErrorKind::PermissionDenied { + error!( + " -> Permission denied. Try running with sudo or use a port > 1024" + ); + } + continue; } - continue; } } } match server { Some(s) => { - println!("Server started successfully"); - s.run().await + // 使用记录的端口判断是否为HTTPS + if bound_port == 443 || bound_port == 8443 { + info!("Server started successfully with HTTPS"); + } else { + info!("Server started successfully with HTTP"); + } + s.await } None => { - eprintln!("Failed to bind to any port. Please check your system configuration."); + error!("Failed to bind to any port. Please check your system configuration."); std::process::exit(1); } }