@@ -7,7 +7,11 @@ use file_classification_core::utils::database::establish_connection;
77use file_classification_core:: utils:: database:: run_pending_migrations;
88use rust_embed:: RustEmbed ;
99use std:: path:: Path ;
10+ use std:: path:: PathBuf ;
1011use utils:: database:: establish_connection_pool;
12+ use log;
13+ use fern;
14+ use chrono;
1115
1216// 嵌入静态资源
1317#[ derive( RustEmbed ) ]
@@ -32,33 +36,181 @@ fn handle_embedded_file(path: &str) -> HttpResponse {
3236 }
3337}
3438
39+ /// 查找静态文件目录,支持从不同目录运行程序
40+ fn find_static_directory ( ) -> std:: io:: Result < PathBuf > {
41+ // 首先尝试从可执行文件所在目录查找
42+ if let Ok ( exe_path) = std:: env:: current_exe ( ) {
43+ if let Some ( exe_dir) = exe_path. parent ( ) {
44+ let static_dir = exe_dir. join ( "static" ) ;
45+ if Path :: new ( & static_dir) . exists ( ) {
46+ log:: info!( "找到静态资源目录: {:?}" , static_dir) ;
47+ return Ok ( static_dir) ;
48+ }
49+ }
50+ }
51+
52+ // 然后尝试从当前工作目录查找
53+ if let Ok ( current_dir) = std:: env:: current_dir ( ) {
54+ let static_dir = current_dir. join ( "static" ) ;
55+ if Path :: new ( & static_dir) . exists ( ) {
56+ log:: info!( "找到静态资源目录: {:?}" , static_dir) ;
57+ return Ok ( static_dir) ;
58+ }
59+
60+ // 尝试从当前工作目录的子目录 file_classification_webapi 中查找
61+ let static_dir = current_dir. join ( "file_classification_webapi" ) . join ( "static" ) ;
62+ if Path :: new ( & static_dir) . exists ( ) {
63+ log:: info!( "找到静态资源目录: {:?}" , static_dir) ;
64+ return Ok ( static_dir) ;
65+ }
66+ }
67+
68+ // 如果都没找到,则返回默认路径并让后续逻辑处理错误
69+ if let Ok ( current_dir) = std:: env:: current_dir ( ) {
70+ let static_dir = current_dir. join ( "static" ) ;
71+ log:: warn!( "静态文件目录不存在: {:?}" , static_dir) ;
72+ Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: NotFound , format ! ( "静态文件目录不存在: {:?}" , static_dir) ) )
73+ } else {
74+ log:: error!( "无法确定静态文件目录位置" ) ;
75+ Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: NotFound , "无法确定静态文件目录位置" ) )
76+ }
77+ }
78+
3579async fn index_handler ( ) -> HttpResponse {
80+ // 首先尝试从物理目录提供文件
81+ match find_static_directory ( ) {
82+ Ok ( static_dir) => {
83+ let index_path = static_dir. join ( "index.html" ) ;
84+ if index_path. exists ( ) {
85+ log:: info!( "从物理目录提供 index.html 文件: {:?}" , index_path) ;
86+ return HttpResponse :: Ok ( )
87+ . content_type ( "text/html; charset=utf-8" )
88+ . body ( std:: fs:: read ( index_path) . unwrap_or_else ( |_| Vec :: new ( ) ) ) ;
89+ }
90+ }
91+ Err ( _) => {
92+ // 物理目录不存在,回退到嵌入资源
93+ log:: info!( "物理目录中未找到 index.html,回退到嵌入资源" ) ;
94+ }
95+ }
96+
97+ // 回退到嵌入资源
98+ log:: info!( "从嵌入资源提供 index.html 文件" ) ;
3699 handle_embedded_file ( "index.html" )
37100}
38101
39102async fn static_handler ( path : web:: Path < String > ) -> HttpResponse {
40103 let path = path. into_inner ( ) ;
104+
105+ // 首先尝试从物理目录提供文件
106+ match find_static_directory ( ) {
107+ Ok ( static_dir) => {
108+ let file_path = static_dir. join ( & path) ;
109+ log:: debug!( "尝试从物理目录提供文件: {:?}, 请求路径: {}" , file_path, path) ;
110+
111+ if file_path. exists ( ) && file_path. is_file ( ) {
112+ // 确保请求的文件在 static 目录内,防止路径遍历攻击
113+ if let Ok ( abs_file_path) = file_path. canonicalize ( ) {
114+ if abs_file_path. starts_with ( & static_dir. canonicalize ( ) . unwrap_or ( static_dir. clone ( ) ) ) {
115+ log:: info!( "从物理目录提供文件: {:?}" , file_path) ;
116+ let content = std:: fs:: read ( & file_path) ;
117+ match content {
118+ Ok ( data) => {
119+ let mime = mime_guess:: from_path ( & path) . first_or_octet_stream ( ) ;
120+ return HttpResponse :: Ok ( )
121+ . content_type ( mime. as_ref ( ) )
122+ . body ( data) ;
123+ }
124+ Err ( e) => {
125+ log:: error!( "读取文件失败 {:?}: {}" , file_path, e) ;
126+ }
127+ }
128+ } else {
129+ log:: warn!( "文件路径不在静态目录内: {:?}" , file_path) ;
130+ }
131+ }
132+ } else {
133+ log:: debug!( "文件不存在或不是文件: {:?}" , file_path) ;
134+ }
135+ }
136+ Err ( e) => {
137+ // 物理目录不存在,回退到嵌入资源
138+ log:: info!( "查找物理目录失败: {},回退到嵌入资源,请求路径: {}" , e, path) ;
139+ }
140+ }
141+
142+ // 回退到嵌入资源
143+ log:: info!( "从嵌入资源提供文件: {}" , path) ;
41144 handle_embedded_file ( & path)
42145}
43146
147+ /// 初始化日志系统,同时输出到终端和文件
148+ fn setup_logger ( ) -> Result < ( ) , fern:: InitError > {
149+ // 创建 logs 目录(如果不存在)
150+ std:: fs:: create_dir_all ( "logs" ) ?;
151+
152+ // 获取当前日期时间作为日志文件名
153+ let local_time = chrono:: Local :: now ( ) ;
154+ let date_str = local_time. format ( "%Y-%m-%d" ) . to_string ( ) ;
155+ let log_file_path = format ! ( "logs/{}.log" , date_str) ;
156+
157+ let log_level = std:: env:: var ( "RUST_LOG" )
158+ . ok ( )
159+ . and_then ( |s| s. parse ( ) . ok ( ) )
160+ . unwrap_or ( log:: LevelFilter :: Info ) ;
161+
162+ fern:: Dispatch :: new ( )
163+ . format ( |out, message, record| {
164+ out. finish ( format_args ! (
165+ "[{}][{}][{}] {}" ,
166+ chrono:: Local :: now( ) . format( "%Y-%m-%d %H:%M:%S" ) ,
167+ record. level( ) ,
168+ record. target( ) ,
169+ message
170+ ) )
171+ } )
172+ // 同时输出到终端和文件
173+ . chain ( std:: io:: stdout ( ) )
174+ . chain ( fern:: log_file ( log_file_path) ?)
175+ . level ( log_level)
176+ . apply ( ) ?;
177+
178+ Ok ( ( ) )
179+ }
180+
44181#[ actix_web:: main]
45182async fn main ( ) -> std:: io:: Result < ( ) > {
46- env_logger:: init ( ) ;
183+ // 初始化日志记录器
184+ setup_logger ( ) . expect ( "日志系统初始化失败" ) ;
185+
186+ // 输出日志等级信息
187+ let log_level = std:: env:: var ( "RUST_LOG" ) . unwrap_or_else ( |_| "info" . to_string ( ) ) ;
188+ log:: info!( "日志等级设置为: {}" , log_level) ;
189+
190+ log:: info!( "正在启动文件分类 Web API..." ) ;
47191
48- println ! ( "正在启动文件分类 Web API..." ) ;
192+ // 运行待处理的数据库迁移
193+ let mut conn = establish_connection ( ) ;
194+ if let Err ( e) = run_pending_migrations ( & mut conn) {
195+ log:: error!( "数据库迁移失败: {}" , e) ;
196+ return Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , e) ) ;
197+ }
49198
50- // 运行待处理的数据库迁移
51- let mut conn = establish_connection ( ) ;
52- if let Err ( e) = run_pending_migrations ( & mut conn) {
53- eprintln ! ( "数据库迁移失败: {}" , e) ;
54- return Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , e) ) ;
55- }
199+ let pool = establish_connection_pool ( ) ;
56200
57- let pool = establish_connection_pool ( ) ;
201+ // 输出当前工作目录
202+ if let Ok ( current_dir) = std:: env:: current_dir ( ) {
203+ log:: info!( "当前工作目录: {:?}" , current_dir) ;
204+ }
205+
206+ // 输出可执行文件路径
207+ if let Ok ( exe_path) = std:: env:: current_exe ( ) {
208+ log:: info!( "可执行文件路径: {:?}" , exe_path) ;
209+ }
58210
59- // 在HttpServer::new中添加新的路由
60- HttpServer :: new ( move || {
61- App :: new ( )
211+ // 在HttpServer::new中添加新的路由
212+ HttpServer :: new ( move || {
213+ App :: new ( )
62214 . app_data ( web:: Data :: new ( pool. clone ( ) ) )
63215 . wrap ( Logger :: default ( ) )
64216 // API路由 - 放在静态文件服务之前以确保优先匹配
@@ -121,8 +273,8 @@ async fn main() -> std::io::Result<()> {
121273 // 静态文件服务 - 使用嵌入的资源
122274 . route ( "/" , web:: get ( ) . to ( index_handler) )
123275 . route ( "/{filename:.*}" , web:: get ( ) . to ( static_handler) )
124- } )
125- . bind ( "127.0.0.1:8082" ) ?
126- . run ( )
127- . await
276+ } )
277+ . bind ( "127.0.0.1:8082" ) ?
278+ . run ( )
279+ . await
128280}
0 commit comments