diff --git a/Cargo.lock b/Cargo.lock index d80f2830..7f70e83c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5414,6 +5414,7 @@ dependencies = [ "atoi", "axum", "bytes", + "chrono", "clap", "common", "const-str", diff --git a/rustfs/Cargo.toml b/rustfs/Cargo.toml index 0c7d7ebb..b6fa08d1 100644 --- a/rustfs/Cargo.toml +++ b/rustfs/Cargo.toml @@ -75,6 +75,7 @@ tower-http = { version = "0.6.2", features = ["cors"] } mime_guess = "2.0.5" rust-embed = { version = "8.5.0", features = ["interpolate-folder-path"] } local-ip-address = "0.6.3" +chrono = "0.4" [build-dependencies] prost-build.workspace = true diff --git a/rustfs/src/console.rs b/rustfs/src/console.rs index f4e9bb75..6c7d64bd 100644 --- a/rustfs/src/console.rs +++ b/rustfs/src/console.rs @@ -48,11 +48,12 @@ async fn static_handler(uri: axum::http::Uri) -> impl IntoResponse { } #[derive(Debug, Serialize)] -struct Config { +pub(crate) struct Config { api: Api, s3: S3, release: Release, license: License, + doc: String } impl Config { @@ -73,12 +74,30 @@ impl Config { name: "Apache-2.0".to_string(), url: "https://www.apache.org/licenses/LICENSE-2.0".to_string(), }, + doc: "https://rustfs.com/docs/".to_string() } } fn to_json(&self) -> String { serde_json::to_string(self).unwrap_or_default() } + + pub(crate) fn version(&self) -> String { + format!( + "RELEASE.{} (rust {} {})", + self.release.date.clone(), + build::RUST_VERSION, + build::BUILD_TARGET + ) + } + + pub(crate) fn license(&self) -> String { + format!("{} {}", self.license.name.clone(), self.license.url.clone()) + } + + pub(crate) fn doc(&self) -> String { + self.doc.clone() + } } #[derive(Debug, Serialize)] @@ -105,9 +124,9 @@ struct License { url: String, } -static CONSOLE_CONFIG: OnceLock = OnceLock::new(); +pub(crate) static CONSOLE_CONFIG: OnceLock = OnceLock::new(); -fn initialize_config(fs_addr: &str) { +pub(crate) fn init_console_cfg(fs_addr: &str) { CONSOLE_CONFIG.get_or_init(|| { let ver = { if !build::TAG.is_empty() { @@ -134,9 +153,7 @@ async fn config_handler() -> impl IntoResponse { .unwrap() } -pub async fn start_static_file_server(addrs: &str, local_ip: Ipv4Addr, server_port: u16) { - let srv_addr = format!("http://{}:{}", local_ip, server_port); - initialize_config(&srv_addr); +pub async fn start_static_file_server(addrs: &str, local_ip: Ipv4Addr, access_key: &str, secret_key: &str) { // 创建路由 let app = Router::new() .route("/config.json", get(config_handler)) @@ -145,7 +162,10 @@ pub async fn start_static_file_server(addrs: &str, local_ip: Ipv4Addr, server_po let listener = tokio::net::TcpListener::bind(addrs).await.unwrap(); let local_addr = listener.local_addr().unwrap(); - info!("console running on: http://{}:{} with s3 api {}", local_ip, local_addr.port(), srv_addr); - + info!("WebUI: http://{}:{} http://127.0.0.1:{}", + local_ip, local_addr.port(), local_addr.port()); + info!(" RootUser: {}", access_key); + info!(" RootPass: {}", secret_key); + axum::serve(listener, app).await.unwrap(); } diff --git a/rustfs/src/main.rs b/rustfs/src/main.rs index ae1b6173..3c2d121c 100644 --- a/rustfs/src/main.rs +++ b/rustfs/src/main.rs @@ -7,11 +7,13 @@ mod service; mod storage; mod utils; use crate::auth::IAMAuth; +use crate::console::{init_console_cfg, CONSOLE_CONFIG}; use clap::Parser; use common::{ error::{Error, Result}, globals::set_global_addr, }; +use config::{DEFAULT_ACCESS_KEY, DEFAULT_SECRET_KEY}; use ecstore::heal::background_heal_ops::init_auto_heal; use ecstore::utils::net::{self, get_available_port}; use ecstore::{ @@ -37,6 +39,7 @@ use tokio::net::TcpListener; use tonic::{metadata::MetadataValue, Request, Status}; use tower_http::cors::CorsLayer; use tracing::{debug, error, info, warn}; +use chrono::Datelike; use tracing_error::ErrorLayer; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; @@ -69,6 +72,16 @@ fn check_auth(req: Request<()>) -> Result, Status> { } } +fn print_server_info() { + let cfg = CONSOLE_CONFIG.get().unwrap(); + let current_year = chrono::Utc::now().year(); + info!("RustFS Object Storage Server"); + info!("Copyright: 2024-{} RustFS, Inc", current_year); + info!("License: {}", cfg.license()); + info!("Version: {}", cfg.version()); + info!("Docs: {}", cfg.doc()) +} + fn main() -> Result<()> { //解析获得到的参数 let opt = config::Opt::parse(); @@ -133,8 +146,8 @@ async fn run(opt: config::Opt) -> Result<()> { let api_endpoints = format!("http://{}:{}", local_ip, server_port); let localhost_endpoint = format!("http://127.0.0.1:{}", server_port); info!("API: {} {}", api_endpoints, localhost_endpoint); - info!(" RootUser: {}", opt.access_key); - info!(" RootPass: {}", opt.secret_key); + info!(" RootUser: {}", opt.access_key.clone()); + info!(" RootPass: {}", opt.secret_key.clone()); for (i, eps) in endpoint_pools.as_ref().iter().enumerate() { info!( @@ -160,10 +173,12 @@ async fn run(opt: config::Opt) -> Result<()> { // let mut b = S3ServiceBuilder::new(storage::ecfs::FS::new(server_address.clone(), endpoint_pools).await?); let mut b = S3ServiceBuilder::new(store.clone()); + let access_key = opt.access_key.clone(); + let secret_key = opt.secret_key.clone(); //显示info信息 - info!("authentication is enabled {}, {}", &opt.access_key, &opt.secret_key); + debug!("authentication is enabled {}, {}", &access_key, &secret_key); - b.set_auth(IAMAuth::new(opt.access_key, opt.secret_key)); + b.set_auth(IAMAuth::new(access_key, secret_key)); b.set_access(store.clone()); @@ -251,7 +266,7 @@ async fn run(opt: config::Opt) -> Result<()> { error!("ECStore init faild {:?}", &err); Error::from_string(err.to_string()) })?; - warn!(" init store success!"); + debug!("init store success!"); init_iam_sys(store.clone()).await.unwrap(); @@ -265,10 +280,20 @@ async fn run(opt: config::Opt) -> Result<()> { // init auto heal init_auto_heal().await; + let srv_addr = format!("http://{}:{}", local_ip, server_port); + init_console_cfg(&srv_addr); + print_server_info(); + if DEFAULT_ACCESS_KEY.eq(&opt.access_key) && DEFAULT_SECRET_KEY.eq(&opt.secret_key) { + warn!("Detected default credentials '{}:{}', we recommend that you change these values with 'RUSTFS_ACCESS_KEY' and 'RUSTFS_SECRET_KEY' environment variables", DEFAULT_ACCESS_KEY, DEFAULT_SECRET_KEY); + } + if opt.console_enable { debug!("console is enabled"); + let access_key = opt.access_key.clone(); + let secret_key = opt.secret_key.clone(); + let console_address = opt.console_address.clone(); tokio::spawn(async move { - console::start_static_file_server(&opt.console_address, local_ip, server_port).await; + console::start_static_file_server(&console_address, local_ip, &access_key, &secret_key).await; }); }