Better logging and error handling
This commit is contained in:
		
							parent
							
								
									76b1f53120
								
							
						
					
					
						commit
						5950fa7370
					
				
							
								
								
									
										539
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										539
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -25,7 +25,7 @@ dotenv = "0.15.0" | ||||
| actix-identity = "0.3" | ||||
| chrono = { version = "0.4", features = ["serde"] } | ||||
| argonautica = "0.2" | ||||
| slog = "2" | ||||
| slog = { version = "2", features = ["max_level_trace", "release_max_level_info"] } | ||||
| slog-term = "2" | ||||
| slog-async = "2" | ||||
| qrcode = "0.12" | ||||
|  | ||||
| @ -223,7 +223,7 @@ pub(crate) async fn setup() -> Result<Option<crate::ServerConfig>, ServerError> | ||||
| 
 | ||||
|     // Print launch info
 | ||||
|     slog_info!(log, "Launching Pslink a 'Private short link generator'"); | ||||
|     slog_info!(log, "logging initialized"); | ||||
|     slog_trace!(log, "logging initialized"); | ||||
| 
 | ||||
|     let app = generate_cli(); | ||||
| 
 | ||||
| @ -239,6 +239,7 @@ pub(crate) async fn setup() -> Result<Option<crate::ServerConfig>, ServerError> | ||||
|         .parse::<PathBuf>() | ||||
|         .expect("Failed to parse Database path."); | ||||
|     if !db.exists() { | ||||
|         slog_trace!(log, "No database file found {}", db.display()); | ||||
|         if config.subcommand_matches("migrate-database").is_none() { | ||||
|             let msg = format!( | ||||
|                 concat!( | ||||
| @ -252,8 +253,9 @@ pub(crate) async fn setup() -> Result<Option<crate::ServerConfig>, ServerError> | ||||
|             eprintln!("{}", msg); | ||||
|             return Ok(None); | ||||
|         } | ||||
|         slog_trace!(log, "Creating database: {}", db.display()); | ||||
| 
 | ||||
|         // create an empty database file the if above makes sure that this file does not exist.
 | ||||
|         // create an empty database file. The if above makes sure that this file does not exist.
 | ||||
|         File::create(db)?; | ||||
|     }; | ||||
| 
 | ||||
| @ -290,8 +292,10 @@ pub(crate) async fn setup() -> Result<Option<crate::ServerConfig>, ServerError> | ||||
|                     " Create a user with `pslink create-admin`" | ||||
|                 ) | ||||
|             ); | ||||
|         } else { | ||||
|             slog_trace!(&server_config.log, "At least one admin user is found."); | ||||
|         } | ||||
|         slog_info!( | ||||
|         slog_trace!( | ||||
|             &server_config.log, | ||||
|             "Initialization finished starting the service." | ||||
|         ); | ||||
|  | ||||
| @ -2,6 +2,7 @@ extern crate sqlx; | ||||
| #[allow(unused_imports)] | ||||
| #[macro_use(
 | ||||
|     slog_o, | ||||
|     slog_trace, | ||||
|     slog_info, | ||||
|     slog_warn, | ||||
|     slog_error, | ||||
| @ -82,25 +83,26 @@ fn build_tera() -> Result<Tera> { | ||||
|     Ok(tera) | ||||
| } | ||||
| 
 | ||||
| #[allow(clippy::future_not_send)] | ||||
| #[allow(clippy::future_not_send, clippy::too_many_lines)] | ||||
| async fn webservice(server_config: ServerConfig) -> Result<()> { | ||||
|     let host_port = format!("{}:{}", &server_config.internal_ip, &server_config.port); | ||||
| 
 | ||||
|     let cfg = server_config.clone(); | ||||
|     slog_info!( | ||||
|         server_config.log, | ||||
|         cfg.log, | ||||
|         "Running on: {}://{}/admin/login/", | ||||
|         &server_config.protocol, | ||||
|         host_port | ||||
|     ); | ||||
|     slog_info!( | ||||
|         server_config.log, | ||||
|         cfg.log, | ||||
|         "If the public url is set up correctly it should be accessible via: {}://{}/admin/login/", | ||||
|         &server_config.protocol, | ||||
|         &server_config.public_url | ||||
|     ); | ||||
|     let tera = build_tera()?; | ||||
|     slog_trace!(cfg.log, "The tera templates are ready"); | ||||
| 
 | ||||
|     HttpServer::new(move || { | ||||
|         let tera = build_tera(); //Tera::new("templates/**/*").expect("failed to initialize the templates");
 | ||||
|         let generated = generate(); | ||||
|         App::new() | ||||
|             .data(server_config.clone()) | ||||
| @ -112,7 +114,7 @@ async fn webservice(server_config: ServerConfig) -> Result<()> { | ||||
|                     .name("auth-cookie") | ||||
|                     .secure(false), | ||||
|             )) | ||||
|             .data(tera) | ||||
|             .data(tera.clone()) | ||||
|             .service(actix_web_static_files::ResourceFiles::new( | ||||
|                 "/static", generated, | ||||
|             )) | ||||
| @ -187,7 +189,11 @@ async fn webservice(server_config: ServerConfig) -> Result<()> { | ||||
|             .route("/{redirect_id}", web::get().to(views::redirect)) | ||||
|     }) | ||||
|     .bind(host_port) | ||||
|     .context("Failed to bind to port")? | ||||
|     .context("Failed to bind to port") | ||||
|     .map_err(|e| { | ||||
|         slog_error!(cfg.log, "Failed to bind to port!"); | ||||
|         e | ||||
|     })? | ||||
|     .run() | ||||
|     .await | ||||
|     .context("Failed to run the webservice") | ||||
|  | ||||
| @ -13,6 +13,7 @@ use fluent_langneg::{ | ||||
| use fluent_templates::LanguageIdentifier; | ||||
| use image::{DynamicImage, ImageOutputFormat, Luma}; | ||||
| use qrcode::{render::svg, QrCode}; | ||||
| use queries::{authenticate, Role}; | ||||
| use tera::{Context, Tera}; | ||||
| 
 | ||||
| use pslink::forms::LinkForm; | ||||
| @ -57,14 +58,13 @@ fn detect_language(request: &HttpRequest) -> Result<String, ServerError> { | ||||
|     let languagecode = supported | ||||
|         .get(0) | ||||
|         .map_or("en".to_string(), std::string::ToString::to_string); | ||||
|     println!("Detected the language: {}", &languagecode); | ||||
|     Ok(languagecode) | ||||
| } | ||||
| 
 | ||||
| /// Show the list of all available links if a user is authenticated
 | ||||
| pub async fn index( | ||||
|     tera: web::Data<Tera>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     id: Identity, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
|     if let Ok(links) = queries::list_all_allowed(&id, &config).await { | ||||
| @ -82,7 +82,7 @@ pub async fn index( | ||||
| /// Show the list of all available links if a user is authenticated
 | ||||
| pub async fn index_users( | ||||
|     tera: web::Data<Tera>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     id: Identity, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
|     if let Ok(users) = queries::list_users(&id, &config).await { | ||||
| @ -99,7 +99,7 @@ pub async fn index_users( | ||||
| } | ||||
| pub async fn view_link_empty( | ||||
|     tera: web::Data<Tera>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     id: Identity, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
|     view_link(tera, config, id, web::Path::from("".to_owned())).await | ||||
| @ -107,7 +107,7 @@ pub async fn view_link_empty( | ||||
| 
 | ||||
| pub async fn view_link( | ||||
|     tera: web::Data<Tera>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     id: Identity, | ||||
|     link_id: web::Path<String>, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
| @ -146,7 +146,7 @@ pub async fn view_link( | ||||
| 
 | ||||
| pub async fn view_profile( | ||||
|     tera: web::Data<Tera>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     id: Identity, | ||||
|     user_id: web::Path<String>, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
| @ -173,7 +173,7 @@ pub async fn view_profile( | ||||
| 
 | ||||
| pub async fn edit_profile( | ||||
|     tera: web::Data<Tera>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     id: Identity, | ||||
|     user_id: web::Path<String>, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
| @ -199,7 +199,7 @@ pub async fn edit_profile( | ||||
| 
 | ||||
| pub async fn process_edit_profile( | ||||
|     data: web::Form<NewUser>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     id: Identity, | ||||
|     user_id: web::Path<String>, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
| @ -215,7 +215,7 @@ pub async fn process_edit_profile( | ||||
| 
 | ||||
| pub async fn download_png( | ||||
|     id: Identity, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     link_code: web::Path<String>, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
|     match queries::get_link(&id, &link_code.0, &config).await { | ||||
| @ -239,7 +239,7 @@ pub async fn download_png( | ||||
| 
 | ||||
| pub async fn signup( | ||||
|     tera: web::Data<Tera>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     id: Identity, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
|     match queries::authenticate(&id, &config).await? { | ||||
| @ -259,7 +259,7 @@ pub async fn signup( | ||||
| 
 | ||||
| pub async fn process_signup( | ||||
|     data: web::Form<NewUser>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     id: Identity, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
|     slog_info!(config.log, "Creating a User: {:?}", &data); | ||||
| @ -272,7 +272,7 @@ pub async fn process_signup( | ||||
| 
 | ||||
| pub async fn toggle_admin( | ||||
|     data: web::Path<String>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     id: Identity, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
|     let update = queries::toggle_admin(&id, &data.0, &config).await?; | ||||
| @ -284,7 +284,7 @@ pub async fn toggle_admin( | ||||
| 
 | ||||
| pub async fn set_language( | ||||
|     data: web::Path<String>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     id: Identity, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
|     queries::set_language(&id, &data.0, &config).await?; | ||||
| @ -294,7 +294,7 @@ pub async fn set_language( | ||||
| pub async fn login( | ||||
|     tera: web::Data<Tera>, | ||||
|     id: Identity, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     req: HttpRequest, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
|     let language_code = detect_language(&req)?; | ||||
| @ -303,9 +303,23 @@ pub async fn login( | ||||
|     data.insert("title", "Login"); | ||||
|     data.insert("language", &language_code); | ||||
| 
 | ||||
|     if let Some(_id) = id.identity() { | ||||
|     if id.identity().is_some() { | ||||
|         if let Ok(r) = authenticate(&id, &config).await { | ||||
|             match r { | ||||
|                 Role::Admin { user } | Role::Regular { user } => { | ||||
|                     slog_trace!( | ||||
|                         config.log, | ||||
|                         "This user ({}) is already logged in redirecting to /admin/index/", | ||||
|                         user.username | ||||
|                     ); | ||||
|                     return Ok(redirect_builder("/admin/index/")); | ||||
|                 } | ||||
|                 Role::Disabled | Role::NotAuthenticated => (), | ||||
|             } | ||||
|         } | ||||
|         slog_warn!(config.log, "Invalid user session. The user might be deleted or something tampered with the cookies."); | ||||
|         id.forget(); | ||||
|     } | ||||
| 
 | ||||
|     let rendered = tera.render("login.html", &data)?; | ||||
|     Ok(HttpResponse::Ok().body(rendered)) | ||||
| @ -313,7 +327,7 @@ pub async fn login( | ||||
| 
 | ||||
| pub async fn process_login( | ||||
|     data: web::Form<LoginUser>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     id: Identity, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
|     let user = queries::get_user_by_name(&data.username, &config).await; | ||||
| @ -350,7 +364,7 @@ pub async fn logout(id: Identity) -> Result<HttpResponse, ServerError> { | ||||
| 
 | ||||
| pub async fn redirect( | ||||
|     tera: web::Data<Tera>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     data: web::Path<String>, | ||||
|     req: HttpRequest, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
| @ -382,14 +396,14 @@ pub async fn redirect( | ||||
| } | ||||
| 
 | ||||
| pub async fn redirect_empty( | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
|     Ok(redirect_builder(&config.empty_forward_url)) | ||||
| } | ||||
| 
 | ||||
| pub async fn create_link( | ||||
|     tera: web::Data<Tera>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     id: Identity, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
|     match queries::authenticate(&id, &config).await? { | ||||
| @ -409,7 +423,7 @@ pub async fn create_link( | ||||
| 
 | ||||
| pub async fn process_link_creation( | ||||
|     data: web::Form<LinkForm>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     id: Identity, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
|     let new_link = queries::create_link(&id, data, &config).await?; | ||||
| @ -421,7 +435,7 @@ pub async fn process_link_creation( | ||||
| 
 | ||||
| pub async fn edit_link( | ||||
|     tera: web::Data<Tera>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     id: Identity, | ||||
|     link_id: web::Path<String>, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
| @ -438,7 +452,7 @@ pub async fn edit_link( | ||||
| } | ||||
| pub async fn process_link_edit( | ||||
|     data: web::Form<LinkForm>, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     id: Identity, | ||||
|     link_code: web::Path<String>, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
| @ -453,7 +467,7 @@ pub async fn process_link_edit( | ||||
| 
 | ||||
| pub async fn process_link_delete( | ||||
|     id: Identity, | ||||
|     config: web::Data<crate::ServerConfig>, | ||||
|     config: web::Data<pslink::ServerConfig>, | ||||
|     link_code: web::Path<String>, | ||||
| ) -> Result<HttpResponse, ServerError> { | ||||
|     queries::delete_link(&id, &link_code.0, &config).await?; | ||||
|  | ||||
							
								
								
									
										79
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								src/lib.rs
									
									
									
									
									
								
							| @ -51,25 +51,86 @@ impl From<argonautica::Error> for ServerError { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ServerError { | ||||
|     fn render_error(title: &str, content: &str) -> String { | ||||
|         format!( | ||||
|             "<!DOCTYPE html>
 | ||||
|         <html lang=\"en\">
 | ||||
|         <head> | ||||
|             <meta charset=\"utf-8\">
 | ||||
|             <title>{0}</title> | ||||
|             <meta name=\"author\" content=\"Franz Dietrich\">
 | ||||
|             <meta http-equiv=\"robots\" content=\"[noindex|nofollow]\">
 | ||||
|             <link rel=\"stylesheet\" href=\"/static/style.css\">
 | ||||
|         </head> | ||||
|         <body> | ||||
|         <section class=\"centered\">
 | ||||
|         <h1>{0}</h1> | ||||
|         <div class=\"container\">
 | ||||
|           {1} | ||||
|         </div> | ||||
|       </section> | ||||
|       </body> | ||||
|       </html>",
 | ||||
|             title, content | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl actix_web::error::ResponseError for ServerError { | ||||
|     fn error_response(&self) -> HttpResponse { | ||||
|         match self { | ||||
|             Self::Argonautica(_) => HttpResponse::InternalServerError().json("Argonautica Error"), | ||||
|             Self::Database(e) => { | ||||
|                 HttpResponse::InternalServerError().json(format!("Database Error: {:?}", e)) | ||||
|             Self::Argonautica(e) => { | ||||
|                 eprintln!("Argonautica Error happened: {:?}", e); | ||||
|                 HttpResponse::InternalServerError() | ||||
|                     .body("Failed to encrypt the password - Aborting!") | ||||
|             } | ||||
|             Self::DatabaseMigration(_) => { | ||||
|             Self::Database(e) => { | ||||
|                 eprintln!("Database Error happened: {:?}", e); | ||||
|                 HttpResponse::InternalServerError().body(&Self::render_error( | ||||
|                     "Server Error", | ||||
|                     "Database could not be accessed! - It could be that this value already was in the database! If you are the admin look into the logs for a more detailed error.", | ||||
|                 )) | ||||
|             } | ||||
|             Self::DatabaseMigration(e) => { | ||||
|                 eprintln!("Migration Error happened: {:?}", e); | ||||
|                 unimplemented!("A migration error should never be rendered") | ||||
|             } | ||||
|             Self::Environment(_) => HttpResponse::InternalServerError().json("Environment Error"), | ||||
|             Self::Environment(e) => { | ||||
|                 eprintln!("Environment Error happened: {:?}", e); | ||||
|                 HttpResponse::InternalServerError().body(&Self::render_error( | ||||
|                   "Server Error", | ||||
|                   "This Server is not properly configured, if you are the admin look into the installation- or update instructions!", | ||||
|               )) | ||||
|             } | ||||
|             Self::Template(e) => { | ||||
|                 HttpResponse::InternalServerError().json(format!("Template Error: {:?}", e)) | ||||
|                 eprintln!("Template Error happened: {:?}", e); | ||||
|                 HttpResponse::InternalServerError().body(&Self::render_error( | ||||
|                     "Server Error", | ||||
|                     "The templates could not be rendered.", | ||||
|                 )) | ||||
|             } | ||||
|             Self::Qr(e) => { | ||||
|                 HttpResponse::InternalServerError().json(format!("Qr Code Error: {:?}", e)) | ||||
|                 eprintln!("QR Error happened: {:?}", e); | ||||
|                 HttpResponse::InternalServerError().body(&Self::render_error( | ||||
|                     "Server Error", | ||||
|                     "Could not generate the QR-code!", | ||||
|                 )) | ||||
|             } | ||||
|             Self::Io(e) => { | ||||
|                 eprintln!("Io Error happened: {:?}", e); | ||||
|                 HttpResponse::InternalServerError().body(&Self::render_error( | ||||
|                     "Server Error", | ||||
|                     "Some Files could not be read or written. If you are the admin look into the logfiles for more details.", | ||||
|                 )) | ||||
|             } | ||||
|             Self::User(data) => { | ||||
|                 eprintln!("User Error happened: {:?}", data); | ||||
|                 HttpResponse::InternalServerError().body(&Self::render_error( | ||||
|                     "Server Error", | ||||
|                     &format!("An error happened: {}", data), | ||||
|                 )) | ||||
|             } | ||||
|             Self::Io(e) => HttpResponse::InternalServerError().json(format!("IO Error: {:?}", e)), | ||||
|             Self::User(data) => HttpResponse::InternalServerError().json(data), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user