@@ -18,6 +18,7 @@ struct Assets;
1818pub struct AppState {
1919 pub stats : Arc < RwLock < TunnelStats > > ,
2020 pub proxy_info : Arc < RwLock < Option < ProxyInfo > > > ,
21+ pub cloudfront_info : Arc < RwLock < Option < CloudFrontInfo > > > ,
2122}
2223
2324#[ derive( Clone , Default ) ]
@@ -72,6 +73,31 @@ fn format_bytes(bytes: u64) -> String {
7273 }
7374}
7475
76+ /// Calculate uptime from a launch time string in RFC3339 format
77+ pub fn calculate_uptime ( launch_time : & str ) -> String {
78+ use chrono:: { DateTime , Utc } ;
79+
80+ // Try to parse the launch time
81+ if let Ok ( launch) = DateTime :: parse_from_rfc3339 ( launch_time) {
82+ let now = Utc :: now ( ) ;
83+ let duration = now. signed_duration_since ( launch. with_timezone ( & Utc ) ) ;
84+
85+ let days = duration. num_days ( ) ;
86+ let hours = duration. num_hours ( ) % 24 ;
87+ let minutes = duration. num_minutes ( ) % 60 ;
88+
89+ if days > 0 {
90+ format ! ( "{}d {}h {}m" , days, hours, minutes)
91+ } else if hours > 0 {
92+ format ! ( "{}h {}m" , hours, minutes)
93+ } else {
94+ format ! ( "{}m" , minutes)
95+ }
96+ } else {
97+ "Unknown" . to_string ( )
98+ }
99+ }
100+
75101#[ derive( Clone ) ]
76102pub enum ProxyInfo {
77103 Aws {
@@ -82,6 +108,7 @@ pub enum ProxyInfo {
82108 private_ip : String ,
83109 state : String ,
84110 launch_time : String ,
111+ uptime : String ,
85112 } ,
86113 Cloudflare {
87114 hostname : String ,
@@ -101,6 +128,7 @@ impl ProxyInfo {
101128 private_ip : "172.17.0.1" . to_string ( ) ,
102129 state : "running" . to_string ( ) ,
103130 launch_time : "2025-11-11 19:30:00 UTC" . to_string ( ) ,
131+ uptime : "2h 15m" . to_string ( ) ,
104132 }
105133 }
106134
@@ -114,11 +142,30 @@ impl ProxyInfo {
114142 }
115143}
116144
145+ #[ derive( Clone , Default ) ]
146+ pub struct CloudFrontInfo {
147+ pub distribution_id : String ,
148+ pub distribution_domain : String ,
149+ pub status : String ,
150+ }
151+
152+ impl CloudFrontInfo {
153+ /// Create example CloudFront info for demo mode
154+ pub fn example ( ) -> Self {
155+ CloudFrontInfo {
156+ distribution_id : "E1234ABCDEFGHI" . to_string ( ) ,
157+ distribution_domain : "d1234abcdefghi.cloudfront.net" . to_string ( ) ,
158+ status : "Deployed" . to_string ( ) ,
159+ }
160+ }
161+ }
162+
117163#[ derive( Template ) ]
118164#[ template( path = "index.html" ) ]
119165pub struct IndexTemplate {
120166 pub tunnel_stats : TunnelStats ,
121167 pub proxy_info : Option < ProxyInfo > ,
168+ pub cloudfront_info : Option < CloudFrontInfo > ,
122169}
123170
124171pub async fn assets ( axum:: extract:: Path ( file) : axum:: extract:: Path < String > ) -> Response {
@@ -148,11 +195,23 @@ pub fn router(state: AppState) -> Router {
148195pub async fn index ( State ( state) : State < AppState > ) -> impl IntoResponse {
149196 let mut stats = state. stats . read ( ) . await . clone ( ) ;
150197 stats. format_sizes ( ) ;
151- let proxy_info = state. proxy_info . read ( ) . await . clone ( ) ;
198+ let mut proxy_info = state. proxy_info . read ( ) . await . clone ( ) ;
199+ let cloudfront_info = state. cloudfront_info . read ( ) . await . clone ( ) ;
200+
201+ // Calculate uptime dynamically for AWS proxy
202+ if let Some ( ProxyInfo :: Aws {
203+ launch_time,
204+ uptime,
205+ ..
206+ } ) = & mut proxy_info
207+ {
208+ * uptime = calculate_uptime ( launch_time) ;
209+ }
152210
153211 let template = IndexTemplate {
154212 tunnel_stats : stats,
155213 proxy_info,
214+ cloudfront_info,
156215 } ;
157216
158217 Html ( template. render ( ) . unwrap ( ) )
0 commit comments