1
1
use crate :: config:: AppConfig ;
2
2
use axum:: {
3
+ body:: Body ,
3
4
extract:: State ,
5
+ http:: { header, Response , StatusCode } ,
4
6
response:: { Html , IntoResponse } ,
5
7
routing:: get,
6
8
Json , Router ,
7
9
} ;
10
+ use chrono:: Local ;
8
11
use std:: path:: PathBuf ;
9
12
use std:: sync:: { Arc , Mutex } ;
13
+ use tokio:: fs;
10
14
11
15
const TEMPLATE : & str = include_str ! ( "../static/index.html" ) ;
12
16
const PICO_CSS : & str = include_str ! ( "../static/pico.min.css" ) ;
@@ -21,6 +25,7 @@ pub fn app(state: Arc<AppState>) -> Router {
21
25
Router :: new ( )
22
26
. route ( "/" , get ( index) )
23
27
. route ( "/config" , get ( get_config) . post ( set_config) )
28
+ . route ( "/download" , get ( download_handler) )
24
29
. with_state ( state)
25
30
}
26
31
@@ -33,6 +38,32 @@ async fn index() -> impl IntoResponse {
33
38
Html ( html)
34
39
}
35
40
41
+ fn generate_filename ( ) -> String {
42
+ let now = Local :: now ( ) ;
43
+ now. format ( "%Y%m%d%H%M%S_aa-proxy-rs.log" ) . to_string ( )
44
+ }
45
+
46
+ async fn download_handler ( State ( state) : State < Arc < AppState > > ) -> impl IntoResponse {
47
+ let file_path = state. config . lock ( ) . unwrap ( ) . logfile . clone ( ) ;
48
+ let filename = generate_filename ( ) ;
49
+
50
+ match fs:: read ( file_path) . await {
51
+ Ok ( content) => Response :: builder ( )
52
+ . status ( StatusCode :: OK )
53
+ . header ( header:: CONTENT_TYPE , "application/octet-stream" )
54
+ . header (
55
+ header:: CONTENT_DISPOSITION ,
56
+ format ! ( "attachment; filename=\" {}\" " , filename) ,
57
+ )
58
+ . body ( Body :: from ( content) )
59
+ . unwrap ( ) ,
60
+ Err ( _) => Response :: builder ( )
61
+ . status ( StatusCode :: NOT_FOUND )
62
+ . body ( Body :: from ( "Cannot access log file" ) )
63
+ . unwrap ( ) ,
64
+ }
65
+ }
66
+
36
67
async fn get_config ( State ( state) : State < Arc < AppState > > ) -> impl IntoResponse {
37
68
let cfg = state. config . lock ( ) . unwrap ( ) . clone ( ) ;
38
69
Json ( cfg)
0 commit comments