|
@@ -4,12 +4,17 @@ mod config;
|
|
|
mod repository;
|
|
|
mod theme;
|
|
|
|
|
|
-use actix_web::middleware::Logger;
|
|
|
+use actix_web::body::BoxBody;
|
|
|
+use actix_web::dev::ServiceResponse;
|
|
|
+use actix_web::error::ParseError::Status;
|
|
|
+use actix_web::http::{header::ContentType, StatusCode};
|
|
|
+use actix_web::middleware::{ErrorHandlerResponse, ErrorHandlers, Logger};
|
|
|
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};
|
|
|
use fern;
|
|
|
use fluent_templates::{static_loader, FluentLoader, Loader as _};
|
|
|
use handlebars::Handlebars;
|
|
|
use serde_json;
|
|
|
+use serde_json::json;
|
|
|
use std::{env, io};
|
|
|
|
|
|
use crate::config::Configuration;
|
|
@@ -30,55 +35,6 @@ static_loader! {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
-#[get("")]
|
|
|
-async fn show_articles_list(hb: web::Data<Handlebars<'_>>) -> impl Responder {
|
|
|
- let repo = GitRepository {};
|
|
|
-
|
|
|
- let exe_dir = env::current_dir()
|
|
|
- .and_then(|dir| Ok(dir.to_str().unwrap_or("").to_string()))
|
|
|
- .unwrap();
|
|
|
- log::trace!("Serving article from repository: {exe_dir}/{ARTICLE_BASE_PATH}");
|
|
|
-
|
|
|
- let paths = repo.get_directory_listing(ARTICLE_BASE_PATH);
|
|
|
-
|
|
|
- let data = serde_json::json!({
|
|
|
- "page-title": "Article",
|
|
|
- "entries": &paths
|
|
|
- });
|
|
|
- let body = hb.render("articles_list", &data).unwrap();
|
|
|
-
|
|
|
- HttpResponse::Ok().body(body)
|
|
|
-}
|
|
|
-
|
|
|
-#[get("/{path:.+}.html")]
|
|
|
-async fn show_article(hb: web::Data<Handlebars<'_>>, path: web::Path<(String,)>) -> impl Responder {
|
|
|
- let p = format!("{ARTICLE_BASE_PATH}/{}", &path.0);
|
|
|
- let exe_dir = env::current_dir()
|
|
|
- .and_then(|dir| Ok(dir.to_str().unwrap_or("").to_string()))
|
|
|
- .unwrap();
|
|
|
- log::trace!("Estimated content repository: {p}");
|
|
|
-
|
|
|
- let theme: Theme = Theme::new(format!("{}/{}", exe_dir, ARTICLE_BASE_PATH));
|
|
|
-
|
|
|
- let rendered = theme.markdown_as_html(&p);
|
|
|
-
|
|
|
- match rendered {
|
|
|
- Ok(content) => {
|
|
|
- let data = serde_json::json!({
|
|
|
- "page-title": "Article",
|
|
|
- "article": &content
|
|
|
- });
|
|
|
- let body = hb.render("article", &data).unwrap();
|
|
|
-
|
|
|
- HttpResponse::Ok().body(body)
|
|
|
- }
|
|
|
- Err(e) => {
|
|
|
- log::error!("Couldn't find article located at '{}'. Error: {}", p, e);
|
|
|
- HttpResponse::NotFound().finish()
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
#[actix_web::main]
|
|
|
async fn main() -> io::Result<()> {
|
|
|
if !env::vars().any(|(k, _)| k == "RUST_LOG") {
|
|
@@ -113,6 +69,7 @@ async fn main() -> io::Result<()> {
|
|
|
|
|
|
HttpServer::new(move || {
|
|
|
App::new()
|
|
|
+ .wrap(error_handlers())
|
|
|
.wrap(Logger::default())
|
|
|
.app_data(web::Data::clone(&handlebars))
|
|
|
.service(actix_files::Files::new("/css", "./www/themes/default/css"))
|
|
@@ -135,3 +92,97 @@ async fn main() -> io::Result<()> {
|
|
|
.run()
|
|
|
.await
|
|
|
}
|
|
|
+
|
|
|
+#[get("")]
|
|
|
+async fn show_articles_list(hb: web::Data<Handlebars<'_>>) -> impl Responder {
|
|
|
+ let repo = GitRepository {};
|
|
|
+
|
|
|
+ let exe_dir = env::current_dir()
|
|
|
+ .and_then(|dir| Ok(dir.to_str().unwrap_or("").to_string()))
|
|
|
+ .unwrap();
|
|
|
+ log::trace!("Serving article from repository: {exe_dir}/{ARTICLE_BASE_PATH}");
|
|
|
+
|
|
|
+ let paths = repo.get_directory_listing(ARTICLE_BASE_PATH);
|
|
|
+
|
|
|
+ let data = serde_json::json!({
|
|
|
+ "page-title": "Article",
|
|
|
+ "entries": &paths
|
|
|
+ });
|
|
|
+ let body = hb.render("articles_list", &data).unwrap();
|
|
|
+
|
|
|
+ HttpResponse::Ok().body(body)
|
|
|
+}
|
|
|
+
|
|
|
+#[get("/{path:.+}.html")]
|
|
|
+async fn show_article(hb: web::Data<Handlebars<'_>>, path: web::Path<(String,)>) -> impl Responder {
|
|
|
+ let p = format!("{ARTICLE_BASE_PATH}/{}", &path.0);
|
|
|
+ let exe_dir = env::current_dir()
|
|
|
+ .and_then(|dir| Ok(dir.to_str().unwrap_or("").to_string()))
|
|
|
+ .unwrap();
|
|
|
+ log::trace!("Estimated content repository: {p}");
|
|
|
+
|
|
|
+ let theme: Theme = Theme::new(format!("{}/{}", exe_dir, ARTICLE_BASE_PATH));
|
|
|
+
|
|
|
+ let rendered = theme.markdown_as_html(&p);
|
|
|
+
|
|
|
+ match rendered {
|
|
|
+ Ok(content) => {
|
|
|
+ let data = serde_json::json!({
|
|
|
+ "page-title": "Article",
|
|
|
+ "article": &content
|
|
|
+ });
|
|
|
+ let body = hb.render("article", &data).unwrap();
|
|
|
+
|
|
|
+ HttpResponse::Ok().body(body)
|
|
|
+ }
|
|
|
+ Err(e) => {
|
|
|
+ log::error!("Couldn't find article located at '{}'. Error: {}", p, e);
|
|
|
+ HttpResponse::NotFound().finish()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Custom error handlers, to return HTML responses when an error occurs.
|
|
|
+fn error_handlers() -> ErrorHandlers<BoxBody> {
|
|
|
+ ErrorHandlers::new().handler(StatusCode::NOT_FOUND, not_found)
|
|
|
+}
|
|
|
+
|
|
|
+fn not_found<B>(
|
|
|
+ res: ServiceResponse<B>,
|
|
|
+) -> Result<ErrorHandlerResponse<BoxBody>, actix_web::Error> {
|
|
|
+ let response = get_error_response(&res, "La page web demandée est indisponible.");
|
|
|
+
|
|
|
+ Ok(ErrorHandlerResponse::Response(ServiceResponse::new(
|
|
|
+ res.into_parts().0,
|
|
|
+ response.map_into_left_body(),
|
|
|
+ )))
|
|
|
+}
|
|
|
+
|
|
|
+// Generic error handler.
|
|
|
+fn get_error_response<B>(res: &ServiceResponse<B>, error: &str) -> HttpResponse<BoxBody> {
|
|
|
+ let req = res.request();
|
|
|
+
|
|
|
+ // Provide a fallback to a simple plain text response in case an error occurs during the
|
|
|
+ // rendering of the error page.
|
|
|
+
|
|
|
+ let hb = req
|
|
|
+ .app_data::<web::Data<Handlebars>>()
|
|
|
+ .expect("correctly set up handlebars in app data");
|
|
|
+
|
|
|
+ let data = json!({
|
|
|
+ "error": error,
|
|
|
+ "status_code": res.status().as_str()
|
|
|
+ });
|
|
|
+
|
|
|
+ let body = hb.render("error", &data);
|
|
|
+
|
|
|
+ match body {
|
|
|
+ Ok(body) => HttpResponse::build(res.status())
|
|
|
+ .content_type(ContentType::html())
|
|
|
+ .body(body),
|
|
|
+
|
|
|
+ Err(_) => HttpResponse::build(res.status())
|
|
|
+ .content_type(ContentType::plaintext())
|
|
|
+ .body(error.to_string()),
|
|
|
+ }
|
|
|
+}
|