render blogposts
This commit is contained in:
parent
512e6eb9a5
commit
611baba0ce
|
@ -3,8 +3,11 @@ use crate::{
|
||||||
post::Post,
|
post::Post,
|
||||||
templates::{self, Html, RenderRucte},
|
templates::{self, Html, RenderRucte},
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::{convert::Infallible, fmt, sync::Arc};
|
||||||
use warp::{http::Response, Rejection, Reply};
|
use warp::{
|
||||||
|
http::{Response, StatusCode},
|
||||||
|
Rejection, Reply,
|
||||||
|
};
|
||||||
|
|
||||||
pub async fn index() -> Result<impl Reply, Rejection> {
|
pub async fn index() -> Result<impl Reply, Rejection> {
|
||||||
Response::builder().html(|o| templates::index_html(o))
|
Response::builder().html(|o| templates::index_html(o))
|
||||||
|
@ -67,5 +70,91 @@ pub async fn blog_series_view(series: String, state: Arc<State>) -> Result<impl
|
||||||
posts.push(post.clone());
|
posts.push(post.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
Response::builder().html(|o| templates::series_posts_html(o, series, &posts))
|
if posts.len() == 0 {
|
||||||
|
Err(SeriesNotFound(series).into())
|
||||||
|
} else {
|
||||||
|
Response::builder().html(|o| templates::series_posts_html(o, series, &posts))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn blog_post_view(name: String, state: Arc<State>) -> Result<impl Reply, Rejection> {
|
||||||
|
let mut want: Option<Post> = None;
|
||||||
|
|
||||||
|
for post in &state.blog {
|
||||||
|
log::debug!("{}", post.link);
|
||||||
|
if post.link == format!("blog/{}", name) {
|
||||||
|
want = Some(post.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match want {
|
||||||
|
None => Err(PostNotFound("blog".into(), name).into()),
|
||||||
|
Some(post) => {
|
||||||
|
let body = Html(post.body_html.clone());
|
||||||
|
Response::builder().html(|o| templates::blogpost_html(o, post, body))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
struct PostNotFound(String, String);
|
||||||
|
|
||||||
|
impl fmt::Display for PostNotFound {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "not found: {}/{}", self.0, self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl warp::reject::Reject for PostNotFound {}
|
||||||
|
|
||||||
|
impl From<PostNotFound> for warp::reject::Rejection {
|
||||||
|
fn from(error: PostNotFound) -> Self {
|
||||||
|
warp::reject::custom(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
struct SeriesNotFound(String);
|
||||||
|
|
||||||
|
impl fmt::Display for SeriesNotFound {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl warp::reject::Reject for SeriesNotFound {}
|
||||||
|
|
||||||
|
impl From<SeriesNotFound> for warp::reject::Rejection {
|
||||||
|
fn from(error: SeriesNotFound) -> Self {
|
||||||
|
warp::reject::custom(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn rejection(err: Rejection) -> Result<impl Reply, Infallible> {
|
||||||
|
let path: String;
|
||||||
|
let code;
|
||||||
|
|
||||||
|
if err.is_not_found() {
|
||||||
|
path = "".into();
|
||||||
|
code = StatusCode::NOT_FOUND;
|
||||||
|
} else if let Some(SeriesNotFound(series)) = err.find() {
|
||||||
|
log::error!("invalid series {}", series);
|
||||||
|
path = format!("/blog/series/{}", series);
|
||||||
|
code = StatusCode::NOT_FOUND;
|
||||||
|
} else if let Some(PostNotFound(kind, name)) = err.find() {
|
||||||
|
log::error!("unknown post {}/{}", kind, name);
|
||||||
|
path = format!("/{}/{}", kind, name);
|
||||||
|
code = StatusCode::NOT_FOUND;
|
||||||
|
} else {
|
||||||
|
log::error!("unhandled rejection: {:?}", err);
|
||||||
|
path = "wut".into();
|
||||||
|
code = StatusCode::INTERNAL_SERVER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(warp::reply::with_status(
|
||||||
|
Response::builder()
|
||||||
|
.html(|o| templates::notfound_html(o, path))
|
||||||
|
.unwrap(),
|
||||||
|
code,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
63
src/main.rs
63
src/main.rs
|
@ -39,35 +39,51 @@ async fn main() -> Result<()> {
|
||||||
let series = base.and(
|
let series = base.and(
|
||||||
warp::path!("series").and(with_state(state.clone()).and_then(handlers::blog_series)),
|
warp::path!("series").and(with_state(state.clone()).and_then(handlers::blog_series)),
|
||||||
);
|
);
|
||||||
|
let series_view = base.and(
|
||||||
|
warp::path!("series" / String)
|
||||||
|
.and(with_state(state.clone()))
|
||||||
|
.and(warp::get())
|
||||||
|
.and_then(handlers::blog_series_view),
|
||||||
|
);
|
||||||
|
let post_view = base.and(
|
||||||
|
warp::path!(String)
|
||||||
|
.and(with_state(state.clone()))
|
||||||
|
.and(warp::get())
|
||||||
|
.and_then(handlers::blog_post_view),
|
||||||
|
);
|
||||||
|
|
||||||
index.or(series)
|
index.or(series.or(series_view)).or(post_view)
|
||||||
};
|
};
|
||||||
|
|
||||||
// let blog_series_view = warp::path!("blog" / "series" / String)
|
let static_pages = {
|
||||||
// .and(warp::get())
|
let contact = warp::path!("contact").and_then(handlers::contact);
|
||||||
// .and(with_state(state.clone())
|
let feeds = warp::path!("feeds").and_then(handlers::feeds);
|
||||||
// .and_then( move | series: String, state: Arc<State> | handlers::blog_series_view(series, state)));
|
let resume = warp::path!("resume")
|
||||||
|
.and(with_state(state.clone()))
|
||||||
|
.and_then(handlers::resume);
|
||||||
|
let signalboost = warp::path!("signalboost")
|
||||||
|
.and(with_state(state.clone()))
|
||||||
|
.and_then(handlers::signalboost);
|
||||||
|
|
||||||
|
contact.or(feeds.or(resume.or(signalboost)))
|
||||||
|
};
|
||||||
|
|
||||||
let routes = warp::get()
|
let routes = warp::get()
|
||||||
.and(path::end().and_then(handlers::index))
|
.and(path::end().and_then(handlers::index))
|
||||||
.or(warp::path!("contact").and_then(handlers::contact))
|
.or(static_pages)
|
||||||
.or(warp::path!("feeds").and_then(handlers::feeds))
|
.or(blog);
|
||||||
.or(warp::path!("resume")
|
|
||||||
.and(with_state(state.clone()))
|
|
||||||
.and_then(handlers::resume))
|
|
||||||
.or(warp::path!("signalboost")
|
|
||||||
.and(with_state(state.clone()))
|
|
||||||
.and_then(handlers::signalboost))
|
|
||||||
.or(blog)
|
|
||||||
.or(warp::any().and_then(handlers::not_found));
|
|
||||||
let files = warp::path("static")
|
|
||||||
.and(warp::fs::dir("./static"))
|
|
||||||
.or(warp::path("css").and(warp::fs::dir("./css")))
|
|
||||||
.or(warp::path("sw.js").and(warp::fs::file("./static/js/sw.js")))
|
|
||||||
.or(warp::path("robots.txt").and(warp::fs::file("./static/robots.txt")));
|
|
||||||
|
|
||||||
let site = routes
|
let files = {
|
||||||
.or(files)
|
let files = warp::path("static").and(warp::fs::dir("./static"));
|
||||||
|
let css = warp::path("css").and(warp::fs::dir("./css"));
|
||||||
|
let sw = warp::path("sw.js").and(warp::fs::file("./static/js/sw.js"));
|
||||||
|
let robots = warp::path("robots.txt").and(warp::fs::file("./static/robots.txt"));
|
||||||
|
|
||||||
|
files.or(css).or(sw).or(robots)
|
||||||
|
};
|
||||||
|
|
||||||
|
let site = files
|
||||||
|
.or(routes)
|
||||||
.map(|reply| {
|
.map(|reply| {
|
||||||
warp::reply::with_header(
|
warp::reply::with_header(
|
||||||
reply,
|
reply,
|
||||||
|
@ -76,7 +92,8 @@ async fn main() -> Result<()> {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.or(healthcheck)
|
.or(healthcheck)
|
||||||
.with(warp::log(APPLICATION_NAME));
|
.with(warp::log(APPLICATION_NAME))
|
||||||
|
.recover(handlers::rejection);
|
||||||
|
|
||||||
warp::serve(site).run(([127, 0, 0, 1], 3030)).await;
|
warp::serve(site).run(([127, 0, 0, 1], 3030)).await;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
@use super::{header_html, footer_html};
|
||||||
|
@use crate::post::Post;
|
||||||
|
|
||||||
|
@(post: Post, body: impl ToHtml)
|
||||||
|
|
||||||
|
@:header_html(Some(&post.front_matter.title.clone()), None)
|
||||||
|
|
||||||
|
<!-- Twitter -->
|
||||||
|
<meta name="twitter:card" content="summary" />
|
||||||
|
<meta name="twitter:site" content="@@theprincessxena" />
|
||||||
|
<meta name="twitter:title" content="@post.front_matter.title" />
|
||||||
|
<meta name="twitter:description" content="Posted on @post.date" />
|
||||||
|
|
||||||
|
<!-- Facebook -->
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:title" content="@post.front_matter.title" />
|
||||||
|
<meta property="og:site_name" content="Christine Dodrill's Blog" />
|
||||||
|
|
||||||
|
<!-- Description -->
|
||||||
|
<meta name="description" content="@post.front_matter.title - Christine Dodrill's Blog" />
|
||||||
|
<meta name="author" content="Christine Dodrill">
|
||||||
|
|
||||||
|
<link rel="canonical" href="https://christine.website/@post.link">
|
||||||
|
|
||||||
|
<script type="application/ld+json">
|
||||||
|
@{
|
||||||
|
"@@context": "http://schema.org",
|
||||||
|
"@@type": "Article",
|
||||||
|
"headline": "@post.front_matter.title",
|
||||||
|
"image": "https://christine.website/static/img/avatar.png",
|
||||||
|
"url": "https://christine.website/@post.link",
|
||||||
|
"datePublished": "@post.date",
|
||||||
|
"mainEntityOfPage": @{
|
||||||
|
"@@type": "WebPage",
|
||||||
|
"@@id": "https://christine.website/@post.link"
|
||||||
|
@},
|
||||||
|
"author": @{
|
||||||
|
"@@type": "Person",
|
||||||
|
"name": "Christine Dodrill"
|
||||||
|
@},
|
||||||
|
"publisher": @{
|
||||||
|
"@@type": "Person",
|
||||||
|
"name": "Christine Dodrill"
|
||||||
|
@}
|
||||||
|
@}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
@body
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<!-- The button that should be clicked. -->
|
||||||
|
<button onclick="share_on_mastodon()">Share on Mastodon</button>
|
||||||
|
|
||||||
|
@if post.front_matter.series.is_some() {
|
||||||
|
<p>Series: <a href="/blog/series/@post.front_matter.series.as_ref().unwrap()">@post.front_matter.series.as_ref().unwrap()</a></p>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if post.front_matter.tags.is_some() {
|
||||||
|
<p>Tags: @for tag in post.front_matter.tags.as_ref().unwrap() { <code>@tag</code> }</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
// The actual function. Set this as an onclick function for your "Share on Mastodon" button
|
||||||
|
function share_on_mastodon() @{
|
||||||
|
// Prefill the form with the user's previously-specified Mastodon instance, if applicable
|
||||||
|
var default_url = localStorage['mastodon_instance'];
|
||||||
|
|
||||||
|
// If there is no cached instance/domain, then insert a "https://" with no domain at the start of the prompt.
|
||||||
|
if (!default_url)
|
||||||
|
default_url = "https://";
|
||||||
|
|
||||||
|
var instance = prompt("Enter your instance's address: (ex: https://linuxrocks.online)", default_url);
|
||||||
|
if (instance) @{
|
||||||
|
// Handle URL formats
|
||||||
|
if ( !instance.startsWith("https://") && !instance.startsWith("http://") )
|
||||||
|
instance = "https://" + instance;
|
||||||
|
|
||||||
|
// get the current page's url
|
||||||
|
var url = window.location.href;
|
||||||
|
|
||||||
|
// get the page title from the og:title meta tag, if it exists.
|
||||||
|
var title = document.querySelectorAll('meta[property="og:title"]')[0].getAttribute("content");
|
||||||
|
|
||||||
|
// Otherwise, use the <title> tag as the title
|
||||||
|
if (!title) var title = document.getElementsByTagName("title")[0].innerHTML;
|
||||||
|
|
||||||
|
// Handle slash
|
||||||
|
if ( !instance.endsWith("/") )
|
||||||
|
instance = instance + "/";
|
||||||
|
|
||||||
|
// Cache the instance/domain for future requests
|
||||||
|
localStorage['mastodon_instance'] = instance;
|
||||||
|
|
||||||
|
// Hashtags
|
||||||
|
var hashtags = "#blogpost";
|
||||||
|
|
||||||
|
@if post.front_matter.series.is_some() {
|
||||||
|
hashtags += "#@post.front_matter.series.as_ref().unwrap()";
|
||||||
|
}
|
||||||
|
|
||||||
|
@if post.front_matter.tags.is_some() {
|
||||||
|
hashtags += "@for tag in post.front_matter.tags.as_ref().unwrap() { #@tag }";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tagging users, such as offical accounts or the author of the post
|
||||||
|
var author = "@@cadey@@mst3k.interlinked.me";
|
||||||
|
|
||||||
|
// Create the Share URL
|
||||||
|
// https://someinstance.tld/share?text=URL%20encoded%20text
|
||||||
|
mastodon_url = instance + "share?text=" + encodeURIComponent(title + "\n\n" + url + "\n\n" + hashtags + " " + author);
|
||||||
|
|
||||||
|
// Open a new window at the share location
|
||||||
|
window.open(mastodon_url, '_blank');
|
||||||
|
@}
|
||||||
|
@}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
@:footer_html()
|
Loading…
Reference in New Issue