mod schema; extern crate diesel; #[macro_use] extern crate rocket; use codigocomentado::*; use comrak::{markdown_to_html, Options}; use controllers::posts; use dotenv::dotenv; use isbot::Bots; use rocket::fs::{FileServer, NamedFile}; use rocket::http::Status; use rocket::request::{FromRequest, Outcome, Request}; use rocket::response::content::RawXml; use rocket::tokio::sync::OnceCell; use rocket_dyn_templates::Template; use std::path::Path; use std::vec::Vec; static BOTS: OnceCell<Bots> = OnceCell::const_new(); async fn is_bot(ua: &str) -> bool { let bots = BOTS.get_or_init(|| async { Bots::default() }).await; bots.is_bot(ua) } struct Headers { user_agent: String, } #[rocket::async_trait] impl<'r> FromRequest<'r> for Headers { type Error = (); async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> { let token = request.headers().get_one("User-Agent"); match token { Some(token) => Outcome::Success(Headers { user_agent: token.to_string(), }), None => Outcome::Error((Status::Unauthorized, ())), } } } #[get("/?<page>")] fn index(page: Option<u64>) -> Template { let page: u64 = page.unwrap_or(1); let (posts, n_posts) = posts::get_posts(Some(page)); let context = contexts::PageOfPosts::new(posts, page, n_posts); Template::render("index", context) } #[get("/feed.xml")] fn rss_feed() -> RawXml<Template> { let (mut posts, _) = posts::get_posts(None); let mut comrak_options = Options::default(); comrak_options.extension.table = true; comrak_options.extension.autolink = true; comrak_options.extension.tasklist = true; comrak_options.render.unsafe_ = true; posts.iter_mut().for_each(|post| { let content = markdown_to_html(&post.content, &comrak_options); post.content = content; }); let context = contexts::XmlFeed::new(posts); RawXml(Template::render("rss", context)) } #[get("/atom.xml")] fn atom_feed() -> RawXml<Template> { let (mut posts, _) = posts::get_posts(None); let mut comrak_options = Options::default(); comrak_options.extension.table = true; comrak_options.extension.autolink = true; comrak_options.extension.tasklist = true; comrak_options.render.unsafe_ = true; posts.iter_mut().for_each(|post| { let content = markdown_to_html(&post.content, &comrak_options); post.content = content; }); let context = contexts::XmlFeed::new(posts); RawXml(Template::render("atom", context)) } #[get("/post/<title>")] async fn show_post(title: &str, headers: Headers) -> Template { let title_splited: Vec<&str> = title.split('-').collect(); let id = title_splited.last().unwrap().parse().unwrap_or(-1); match posts::get_post(id) { Ok(mut post) => { if !is_bot(&headers.user_agent).await { posts::add_visit(id); } let mut comrak_options = Options::default(); comrak_options.extension.table = true; comrak_options.extension.autolink = true; comrak_options.extension.tasklist = true; comrak_options.render.unsafe_ = true; comrak_options.extension.header_ids = Some("id-".to_string()); let content = markdown_to_html(&post.content, &comrak_options); post.content = content; let context = contexts::SinglePost::new(post); Template::render("post", context) } Err(_) => { let url = format!("/post/{}", title_splited.join("-")); let context = contexts::NotFound::new(url); Template::render("404", context) } } } #[catch(404)] fn not_found_404(req: &Request) -> Template { let uri = format!("{}", req.uri()); let context = contexts::NotFound::new(uri); Template::render("404", context) } #[get("/favicon.ico")] async fn favicon() -> Option<NamedFile> { NamedFile::open(Path::new("static/favicon.ico")).await.ok() } #[launch] fn rocket() -> _ { dotenv().ok(); rocket::build() .attach(Template::fairing()) .mount("/", routes![index, show_post, favicon, rss_feed, atom_feed]) .mount("/admin", admin::get_routes()) .mount("/static", FileServer::from("static")) .register("/", catchers![not_found_404]) }