2025-03-12 12:32:15 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								/* 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  *   asklyphe - frontend   main . rs 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  *   -   entrypoint   for   the   asklyphe   frontend 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  * 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  *   Copyright   ( C )   2025   Real   Microsoft ,   LLC 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  * 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  *   This   program   is   free   software : you   can   redistribute   it   and / or   modify   it   under   the   terms   of   the   GNU   Affero   General   Public   License   as   published   by   the   Free   Software   Foundation ,   version   3. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  * 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  *   This   program   is   distributed   in   the   hope   that   it   will   be   useful ,   but   WITHOUT   ANY   WARRANTY ;   without   even   the   implied   warranty   of   MERCHANTABILITY   or   FITNESS   FOR   A   PARTICULAR   PURPOSE .   See   the   GNU   Affero   General   Public   License   for   more   details . 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  * 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  *   You   should   have   received   a   copy   of   the   GNU   Affero   General   Public   License   along   with   this   program .   If   not ,   see   < https ://www.gnu.org/licenses/>.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								* / 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								pub   mod  searchbot ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								pub   mod  wikipedia ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								pub   mod  unit_converter ; 
 
							 
						 
					
						
							
								
									
										
										
										
											2025-05-08 15:03:00 +12:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								pub   mod  bangs ; 
 
							 
						 
					
						
							
								
									
										
										
										
											2025-03-12 12:32:15 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								pub   mod  routes ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   std ::{ env ,   process } ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   std ::collections ::HashMap ; 
 
							 
						 
					
						
							
								
									
										
										
										
											2025-09-04 15:53:53 +12:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								use   std ::net ::{ SocketAddr ,   ToSocketAddrs } ; 
 
							 
						 
					
						
							
								
									
										
										
										
											2025-03-12 12:32:15 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								use   std ::ops ::Deref ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   std ::sync ::Arc ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   std ::sync ::atomic ::{ AtomicU64 ,   Ordering } ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   std ::time ::Duration ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   askama ::Template ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   asklyphe_common ::nats ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   asklyphe_common ::nats ::comms ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   asklyphe_common ::nats ::searchservice ::{ SearchSrvcQuery ,   SearchSrvcRequest ,   SearchSrvcResponse } ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   async_nats ::jetstream ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   axum ::{ Extension ,   Router } ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   axum ::extract ::Query ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   axum ::http ::{ HeaderValue ,   Method } ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   axum ::response ::IntoResponse ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   axum ::routing ::{ get ,   post } ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   serde ::Serialize ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   tokio ::sync ::Mutex ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   tower_http ::cors ::{ AllowOrigin ,   CorsLayer } ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   tracing ::error ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   tracing_loki ::url ::Url ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   tracing_subscriber ::layer ::SubscriberExt ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   tracing_subscriber ::util ::SubscriberInitExt ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   crate ::routes ::admin ::{ admin_announcement ,   admin_announcement_post ,   admin_home ,   admin_invitecode ,   admin_invitecode_post ,   admin_user_list } ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   crate ::routes ::announcement ::announcement_full ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   crate ::routes ::index ::{ frontpage ,   index ,   logout } ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   crate ::routes ::search ::{ image_proxy ,   search ,   search_json } ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   crate ::routes ::semaphore ::semaphore ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   crate ::routes ::user_settings ::{ theme_change_post ,   user_settings } ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   crate ::unit_converter ::UnitConversion ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								use   crate ::wikipedia ::WikipediaSummary ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								const   ALPHA : bool  =   { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     let   alpha   =   option_env! ( " ALPHA " ) ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     alpha . is_some ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								const   VERSION : & str  =   env! ( " CARGO_PKG_VERSION " ) ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								const   GIT_COMMIT : & str  =   env! ( " GIT_COMMIT " ) ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								const   BUILT_ON : & str  =   env! ( " BUILT_ON " ) ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								const   YEAR : & str  =   env! ( " YEAR " ) ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								#[ derive(Clone) ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								pub   struct  Opts   { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     pub   bind_addr : SocketAddr , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     pub   nats_addr : SocketAddr , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     nats_cert : String , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     nats_key : String , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     auth_url : String , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     pub   loki_addr : Option < Url > , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     pub   datacenter_id : i64 , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     pub   emergency : bool , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								pub   static   WEBSITE_COUNT : AtomicU64   =   AtomicU64 ::new ( 0 ) ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								async   fn  website_count_update_thread ( nats : Arc < jetstream ::Context > )   { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     loop   { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         searchbot ::update_website_counter ( nats . clone ( ) ) . await ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         tokio ::time ::sleep ( Duration ::from_secs ( 30 ) ) . await ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     } 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								#[ tokio::main ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								async   fn  main ( )   { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     env_logger ::init ( ) ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     let   opts   =   Opts   { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         bind_addr : env ::var ( " BIND_ADDR " ) . unwrap_or ( " 0.0.0.0:5842 " . to_string ( ) ) . parse ( ) . expect ( " Badly formed BIND_ADDR (Needs to be SocketAddr) " ) , 
 
							 
						 
					
						
							
								
									
										
										
										
											2025-09-04 15:53:53 +12:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								         nats_addr : env ::var ( " NATS_ADDR " ) . unwrap_or ( " 127.0.0.1:4222 " . to_string ( ) ) . to_socket_addrs ( ) . expect ( " Badly formed NATS_ADDR (Needs to be SocketAddr) " ) . nth ( 0 ) . unwrap ( ) , 
 
							 
						 
					
						
							
								
									
										
										
										
											2025-03-12 12:32:15 -07:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								         nats_cert : env ::var ( " NATS_CERT " ) . expect ( " NATS_CERT needs to be set " ) , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         nats_key : env ::var ( " NATS_KEY " ) . expect ( " NATS_KEY needs to be set " ) , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         auth_url : env ::var ( " AUTH_URL " ) . unwrap_or ( " https://auth.asklyphe.com " . to_string ( ) ) , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         loki_addr : match   env ::var ( " LOKI_ADDR " )   { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								             Ok ( url )   = >   { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                 Some ( Url ::parse ( & url ) . expect ( " Badly formed LOKI_ADDR (Needs to be Url) " ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								             } 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								             Err ( _ )   = >   { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                 None 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								             } 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         } , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         datacenter_id : env ::var ( " DATACENTER_ID " ) . unwrap_or ( " 1 " . to_string ( ) ) . parse ( ) . expect ( " Badly formed DATACENTER_ID (Need to be i64) " ) , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         emergency : env ::var ( " EMERGENCY " ) . unwrap_or ( " 0 " . to_string ( ) ) . eq ( " 1 " ) , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     } ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     if   opts . loki_addr . is_some ( )   { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         //let (layer, task) = tracing_loki::builder()
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         //    .label("environment", "dev").unwrap()
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         //    .extra_field("pid", process::id().to_string()).unwrap()
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         //    .extra_field("run_id", snowflake_factory.generate().to_string()).unwrap()
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         //    .build_url(opts.loki_addr.clone().unwrap()).unwrap();
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         //// Register Loki layer
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         //tracing_subscriber::registry()
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         //    .with(layer)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         //    .init();
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         //// Spawn Loki background task
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         //tokio::spawn(task);
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     }   else   { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         //tracing_subscriber::fmt::init()
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     } 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     let   nats   =   async_nats ::ConnectOptions ::new ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . add_client_certificate ( opts . nats_cert . as_str ( ) . into ( ) ,   opts . nats_key . as_str ( ) . into ( ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . connect ( opts . nats_addr . to_string ( ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . await ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     if   let   Err ( e )   =   nats   { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         eprintln! ( " FATAL ERROR, COULDN'T CONNECT TO NATS:  {} " ,   e ) ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         return ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     } 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     let   nats   =   nats . unwrap ( ) ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     let   nats   =   jetstream ::new ( nats ) ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     let   nats   =   Arc ::new ( nats ) ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     tokio ::spawn ( website_count_update_thread ( nats . clone ( ) ) ) ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     let   app   =   Router ::new ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . route ( " / " ,   get ( index ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . route ( " /semaphore " ,   get ( semaphore ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . route ( " /frontpage " ,   get ( frontpage ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . route ( " /ask " ,   get ( search ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . route ( " /ask_json " ,   get ( search_json ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . route ( " /imgproxy " ,   get ( image_proxy ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . route ( " /logout " ,   post ( logout ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . route ( " /user_settings " ,   get ( user_settings ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . route ( " /user_settings/set_theme " ,   post ( theme_change_post ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . route ( " /admin " ,   get ( admin_home ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . route ( " /admin/invitecode " ,   get ( admin_invitecode ) . post ( admin_invitecode_post ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . route ( " /admin/announcement " ,   get ( admin_announcement ) . post ( admin_announcement_post ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . route ( " /admin/allusers " ,   get ( admin_user_list ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . route ( " /announcements/:slug " ,   get ( announcement_full ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . layer ( CorsLayer ::new ( ) . allow_methods ( [ Method ::GET ,   Method ::POST ] ) . allow_origin ( AllowOrigin ::exact ( HeaderValue ::from_str ( " localhost " ) . unwrap ( ) ) ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . layer ( Extension ( nats . clone ( ) ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . layer ( Extension ( opts . clone ( ) ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								         . fallback ( routes ::not_found ) ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     let   listener   =   tokio ::net ::TcpListener ::bind ( opts . bind_addr ) . await . expect ( " Failed to get listener " ) ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								     axum ::serve ( listener ,   app ) . await . expect ( " Failed to serve " ) ; 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								}