talk support + prometheus
This commit is contained in:
parent
c1a620c040
commit
d722ea9ede
|
@ -21,6 +21,12 @@ dependencies = [
|
||||||
"pretty",
|
"pretty",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler32"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.13"
|
version = "0.7.13"
|
||||||
|
@ -255,6 +261,15 @@ version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
|
checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dhall"
|
name = "dhall"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
@ -776,6 +791,24 @@ version = "0.2.72"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9f8082297d534141b30c8d39e9b1773713ab50fdbe4ff30f750d063b3bfd701"
|
checksum = "a9f8082297d534141b30c8d39e9b1773713ab50fdbe4ff30f750d063b3bfd701"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libflate"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e9bac9023e1db29c084f9f8cd9d3852e5e8fddf98fb47c4964a0ea4663d95949"
|
||||||
|
dependencies = [
|
||||||
|
"adler32",
|
||||||
|
"crc32fast",
|
||||||
|
"libflate_lz77",
|
||||||
|
"rle-decode-fast",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libflate_lz77"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3286f09f7d4926fc486334f28d8d2e6ebe4f7f9994494b6dab27ddfad2c9b11b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linked-hash-map"
|
name = "linked-hash-map"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
@ -1237,6 +1270,35 @@ dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "procfs"
|
||||||
|
version = "0.7.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c434e93ef69c216e68e4f417c927b4f31502c3560b72cfdb6827e2321c5c6b3e"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"byteorder",
|
||||||
|
"hex",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"libflate",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prometheus"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd0ced56dee39a6e960c15c74dc48849d614586db2eaada6497477af7c7811cd"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"fnv",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"procfs",
|
||||||
|
"spin",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-error"
|
name = "quick-error"
|
||||||
version = "1.2.3"
|
version = "1.2.3"
|
||||||
|
@ -1475,6 +1537,12 @@ dependencies = [
|
||||||
"winreg",
|
"winreg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rle-decode-fast"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ructe"
|
name = "ructe"
|
||||||
version = "0.11.4"
|
version = "0.11.4"
|
||||||
|
@ -1712,6 +1780,12 @@ dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "static_assertions"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -2262,9 +2336,12 @@ dependencies = [
|
||||||
"comrak",
|
"comrak",
|
||||||
"envy",
|
"envy",
|
||||||
"glob",
|
"glob",
|
||||||
|
"hyper",
|
||||||
|
"lazy_static",
|
||||||
"log 0.4.8",
|
"log 0.4.8",
|
||||||
"mime 0.3.16",
|
"mime 0.3.16",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
|
"prometheus",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
"ructe",
|
"ructe",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
@ -13,9 +13,12 @@ chrono = "0.4"
|
||||||
comrak = "0.8"
|
comrak = "0.8"
|
||||||
envy = "0.4"
|
envy = "0.4"
|
||||||
glob = "0.3"
|
glob = "0.3"
|
||||||
|
hyper = "0.13"
|
||||||
|
lazy_static = "1.4"
|
||||||
log = "0"
|
log = "0"
|
||||||
mime = "0.3.0"
|
mime = "0.3.0"
|
||||||
pretty_env_logger = "0"
|
pretty_env_logger = "0"
|
||||||
|
prometheus = { version = "0.9", default-features = false, features = ["process"] }
|
||||||
rand = "0"
|
rand = "0"
|
||||||
ructe = "0.11"
|
ructe = "0.11"
|
||||||
serde_dhall = "0.5.3"
|
serde_dhall = "0.5.3"
|
||||||
|
|
|
@ -4,9 +4,17 @@ use crate::{
|
||||||
post::Post,
|
post::Post,
|
||||||
templates::{self, Html, RenderRucte},
|
templates::{self, Html, RenderRucte},
|
||||||
};
|
};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use prometheus::{IntCounterVec, register_int_counter_vec, opts};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use warp::{http::Response, Rejection, Reply};
|
use warp::{http::Response, Rejection, Reply};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref HIT_COUNTER: IntCounterVec =
|
||||||
|
register_int_counter_vec!(opts!("blogpost_hits", "Number of hits to blogposts"), &["name"])
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn index(state: Arc<State>) -> Result<impl Reply, Rejection> {
|
pub async fn index(state: Arc<State>) -> Result<impl Reply, Rejection> {
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
Response::builder().html(|o| templates::blogindex_html(o, state.blog.clone()))
|
Response::builder().html(|o| templates::blogindex_html(o, state.blog.clone()))
|
||||||
|
@ -61,6 +69,7 @@ pub async fn post_view(name: String, state: Arc<State>) -> Result<impl Reply, Re
|
||||||
match want {
|
match want {
|
||||||
None => Err(PostNotFound("blog".into(), name).into()),
|
None => Err(PostNotFound("blog".into(), name).into()),
|
||||||
Some(post) => {
|
Some(post) => {
|
||||||
|
HIT_COUNTER.with_label_values(&[name.clone().as_str()]).inc();
|
||||||
let body = Html(post.body_html.clone());
|
let body = Html(post.body_html.clone());
|
||||||
Response::builder().html(|o| templates::blogpost_html(o, post, body))
|
Response::builder().html(|o| templates::blogpost_html(o, post, body))
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,17 @@ use crate::{
|
||||||
post::Post,
|
post::Post,
|
||||||
templates::{self, Html, RenderRucte},
|
templates::{self, Html, RenderRucte},
|
||||||
};
|
};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use prometheus::{IntCounterVec, register_int_counter_vec, opts};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use warp::{http::Response, Rejection, Reply};
|
use warp::{http::Response, Rejection, Reply};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref HIT_COUNTER: IntCounterVec =
|
||||||
|
register_int_counter_vec!(opts!("gallery_hits", "Number of hits to gallery images"), &["name"])
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn index(state: Arc<State>) -> Result<impl Reply, Rejection> {
|
pub async fn index(state: Arc<State>) -> Result<impl Reply, Rejection> {
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
Response::builder().html(|o| templates::galleryindex_html(o, state.gallery.clone()))
|
Response::builder().html(|o| templates::galleryindex_html(o, state.gallery.clone()))
|
||||||
|
@ -22,8 +30,9 @@ pub async fn post_view(name: String, state: Arc<State>) -> Result<impl Reply, Re
|
||||||
}
|
}
|
||||||
|
|
||||||
match want {
|
match want {
|
||||||
None => Err(PostNotFound("blog".into(), name).into()),
|
None => Err(PostNotFound("gallery".into(), name).into()),
|
||||||
Some(post) => {
|
Some(post) => {
|
||||||
|
HIT_COUNTER.with_label_values(&[name.clone().as_str()]).inc();
|
||||||
let body = Html(post.body_html.clone());
|
let body = Html(post.body_html.clone());
|
||||||
Response::builder().html(|o| templates::gallerypost_html(o, post, body))
|
Response::builder().html(|o| templates::gallerypost_html(o, post, body))
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,40 +2,55 @@ use crate::{
|
||||||
app::State,
|
app::State,
|
||||||
templates::{self, Html, RenderRucte},
|
templates::{self, Html, RenderRucte},
|
||||||
};
|
};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use prometheus::{IntCounterVec, register_int_counter_vec, opts};
|
||||||
use std::{convert::Infallible, fmt, sync::Arc};
|
use std::{convert::Infallible, fmt, sync::Arc};
|
||||||
use warp::{
|
use warp::{
|
||||||
http::{Response, StatusCode},
|
http::{Response, StatusCode},
|
||||||
Rejection, Reply,
|
Rejection, Reply,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref HIT_COUNTER: IntCounterVec =
|
||||||
|
register_int_counter_vec!(opts!("hits", "Number of hits to various pages"), &["page"])
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn index() -> Result<impl Reply, Rejection> {
|
pub async fn index() -> Result<impl Reply, Rejection> {
|
||||||
|
HIT_COUNTER.with_label_values(&["index"]).inc();
|
||||||
Response::builder().html(|o| templates::index_html(o))
|
Response::builder().html(|o| templates::index_html(o))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn contact() -> Result<impl Reply, Rejection> {
|
pub async fn contact() -> Result<impl Reply, Rejection> {
|
||||||
|
HIT_COUNTER.with_label_values(&["contact"]).inc();
|
||||||
Response::builder().html(|o| templates::contact_html(o))
|
Response::builder().html(|o| templates::contact_html(o))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn feeds() -> Result<impl Reply, Rejection> {
|
pub async fn feeds() -> Result<impl Reply, Rejection> {
|
||||||
|
HIT_COUNTER.with_label_values(&["feeds"]).inc();
|
||||||
Response::builder().html(|o| templates::feeds_html(o))
|
Response::builder().html(|o| templates::feeds_html(o))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn resume(state: Arc<State>) -> Result<impl Reply, Rejection> {
|
pub async fn resume(state: Arc<State>) -> Result<impl Reply, Rejection> {
|
||||||
|
HIT_COUNTER.with_label_values(&["resume"]).inc();
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
Response::builder().html(|o| templates::resume_html(o, Html(state.resume.clone())))
|
Response::builder().html(|o| templates::resume_html(o, Html(state.resume.clone())))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn signalboost(state: Arc<State>) -> Result<impl Reply, Rejection> {
|
pub async fn signalboost(state: Arc<State>) -> Result<impl Reply, Rejection> {
|
||||||
|
HIT_COUNTER.with_label_values(&["signalboost"]).inc();
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
Response::builder().html(|o| templates::signalboost_html(o, state.signalboost.clone()))
|
Response::builder().html(|o| templates::signalboost_html(o, state.signalboost.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn not_found() -> Result<impl Reply, Rejection> {
|
pub async fn not_found() -> Result<impl Reply, Rejection> {
|
||||||
|
HIT_COUNTER.with_label_values(&["not_found"]).inc();
|
||||||
Response::builder().html(|o| templates::notfound_html(o, "some path".into()))
|
Response::builder().html(|o| templates::notfound_html(o, "some path".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod blog;
|
pub mod blog;
|
||||||
pub mod gallery;
|
pub mod gallery;
|
||||||
|
pub mod talks;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
struct PostNotFound(String, String);
|
struct PostNotFound(String, String);
|
||||||
|
@ -71,24 +86,34 @@ impl From<SeriesNotFound> for warp::reject::Rejection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref REJECTION_COUNTER: IntCounterVec =
|
||||||
|
register_int_counter_vec!(opts!("rejections", "Number of rejections by kind"), &["kind"])
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn rejection(err: Rejection) -> Result<impl Reply, Infallible> {
|
pub async fn rejection(err: Rejection) -> Result<impl Reply, Infallible> {
|
||||||
let path: String;
|
let path: String;
|
||||||
let code;
|
let code;
|
||||||
|
|
||||||
if err.is_not_found() {
|
if err.is_not_found() {
|
||||||
|
REJECTION_COUNTER.with_label_values(&["404"]).inc();
|
||||||
path = "".into();
|
path = "".into();
|
||||||
code = StatusCode::NOT_FOUND;
|
code = StatusCode::NOT_FOUND;
|
||||||
} else if let Some(SeriesNotFound(series)) = err.find() {
|
} else if let Some(SeriesNotFound(series)) = err.find() {
|
||||||
|
REJECTION_COUNTER.with_label_values(&["SeriesNotFound"]).inc();
|
||||||
log::error!("invalid series {}", series);
|
log::error!("invalid series {}", series);
|
||||||
path = format!("/blog/series/{}", series);
|
path = format!("/blog/series/{}", series);
|
||||||
code = StatusCode::NOT_FOUND;
|
code = StatusCode::NOT_FOUND;
|
||||||
} else if let Some(PostNotFound(kind, name)) = err.find() {
|
} else if let Some(PostNotFound(kind, name)) = err.find() {
|
||||||
|
REJECTION_COUNTER.with_label_values(&["PostNotFound"]).inc();
|
||||||
log::error!("unknown post {}/{}", kind, name);
|
log::error!("unknown post {}/{}", kind, name);
|
||||||
path = format!("/{}/{}", kind, name);
|
path = format!("/{}/{}", kind, name);
|
||||||
code = StatusCode::NOT_FOUND;
|
code = StatusCode::NOT_FOUND;
|
||||||
} else {
|
} else {
|
||||||
|
REJECTION_COUNTER.with_label_values(&["Other"]).inc();
|
||||||
log::error!("unhandled rejection: {:?}", err);
|
log::error!("unhandled rejection: {:?}", err);
|
||||||
path = "wut".into();
|
path = format!("weird rejection: {:?}", err);
|
||||||
code = StatusCode::INTERNAL_SERVER_ERROR;
|
code = StatusCode::INTERNAL_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
use super::PostNotFound;
|
||||||
|
use crate::{
|
||||||
|
app::State,
|
||||||
|
post::Post,
|
||||||
|
templates::{self, Html, RenderRucte},
|
||||||
|
};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use prometheus::{IntCounterVec, register_int_counter_vec, opts};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use warp::{http::Response, Rejection, Reply};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref HIT_COUNTER: IntCounterVec =
|
||||||
|
register_int_counter_vec!(opts!("talks_hits", "Number of hits to talks images"), &["name"])
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn index(state: Arc<State>) -> Result<impl Reply, Rejection> {
|
||||||
|
let state = state.clone();
|
||||||
|
Response::builder().html(|o| templates::talkindex_html(o, state.talks.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn post_view(name: String, state: Arc<State>) -> Result<impl Reply, Rejection> {
|
||||||
|
let mut want: Option<Post> = None;
|
||||||
|
|
||||||
|
for post in &state.talks {
|
||||||
|
if post.link == format!("talks/{}", name) {
|
||||||
|
want = Some(post.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match want {
|
||||||
|
None => Err(PostNotFound("talks".into(), name).into()),
|
||||||
|
Some(post) => {
|
||||||
|
HIT_COUNTER.with_label_values(&[name.clone().as_str()]).inc();
|
||||||
|
let body = Html(post.body_html.clone());
|
||||||
|
Response::builder().html(|o| templates::talkpost_html(o, post, body))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
src/main.rs
47
src/main.rs
|
@ -1,4 +1,6 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use hyper::{header::CONTENT_TYPE, Body, Response};
|
||||||
|
use prometheus::{Encoder, TextEncoder};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use warp::{path, Filter};
|
use warp::{path, Filter};
|
||||||
|
|
||||||
|
@ -72,6 +74,22 @@ async fn main() -> Result<()> {
|
||||||
index.or(post_view)
|
index.or(post_view)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let talks = {
|
||||||
|
let base = warp::path!("talks" / ..);
|
||||||
|
let index = base
|
||||||
|
.and(warp::path::end())
|
||||||
|
.and(with_state(state.clone()))
|
||||||
|
.and_then(handlers::talks::index);
|
||||||
|
let post_view = base.and(
|
||||||
|
warp::path!(String)
|
||||||
|
.and(with_state(state.clone()))
|
||||||
|
.and(warp::get())
|
||||||
|
.and_then(handlers::talks::post_view),
|
||||||
|
);
|
||||||
|
|
||||||
|
index.or(post_view)
|
||||||
|
};
|
||||||
|
|
||||||
let static_pages = {
|
let static_pages = {
|
||||||
let contact = warp::path!("contact").and_then(handlers::contact);
|
let contact = warp::path!("contact").and_then(handlers::contact);
|
||||||
let feeds = warp::path!("feeds").and_then(handlers::feeds);
|
let feeds = warp::path!("feeds").and_then(handlers::feeds);
|
||||||
|
@ -82,15 +100,9 @@ async fn main() -> Result<()> {
|
||||||
.and(with_state(state.clone()))
|
.and(with_state(state.clone()))
|
||||||
.and_then(handlers::signalboost);
|
.and_then(handlers::signalboost);
|
||||||
|
|
||||||
contact.or(feeds.or(resume.or(signalboost)))
|
contact.or(feeds).or(resume).or(signalboost)
|
||||||
};
|
};
|
||||||
|
|
||||||
let routes = warp::get()
|
|
||||||
.and(path::end().and_then(handlers::index))
|
|
||||||
.or(static_pages)
|
|
||||||
.or(blog)
|
|
||||||
.or(gallery);
|
|
||||||
|
|
||||||
let files = {
|
let files = {
|
||||||
let files = warp::path("static").and(warp::fs::dir("./static"));
|
let files = warp::path("static").and(warp::fs::dir("./static"));
|
||||||
let css = warp::path("css").and(warp::fs::dir("./css"));
|
let css = warp::path("css").and(warp::fs::dir("./css"));
|
||||||
|
@ -100,8 +112,26 @@ async fn main() -> Result<()> {
|
||||||
files.or(css).or(sw).or(robots)
|
files.or(css).or(sw).or(robots)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let metrics_endpoint = warp::path("metrics").and(warp::path::end()).map(move || {
|
||||||
|
let encoder = TextEncoder::new();
|
||||||
|
let metric_families = prometheus::gather();
|
||||||
|
let mut buffer = vec![];
|
||||||
|
encoder.encode(&metric_families, &mut buffer).unwrap();
|
||||||
|
Response::builder()
|
||||||
|
.status(200)
|
||||||
|
.header(CONTENT_TYPE, encoder.format_type())
|
||||||
|
.body(Body::from(buffer))
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
let site = files
|
let site = files
|
||||||
.or(routes)
|
.or(warp::get().and(path::end().and_then(handlers::index)))
|
||||||
|
.or(static_pages)
|
||||||
|
.or(blog)
|
||||||
|
.or(gallery)
|
||||||
|
.or(talks)
|
||||||
|
.or(healthcheck)
|
||||||
|
.or(metrics_endpoint)
|
||||||
.map(|reply| {
|
.map(|reply| {
|
||||||
warp::reply::with_header(
|
warp::reply::with_header(
|
||||||
reply,
|
reply,
|
||||||
|
@ -109,7 +139,6 @@ async fn main() -> Result<()> {
|
||||||
"If you are reading this, check out /signalboost to find people for your team",
|
"If you are reading this, check out /signalboost to find people for your team",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.or(healthcheck)
|
|
||||||
.map(|reply| warp::reply::with_header(reply, "X-Clacks-Overhead", "GNU Ashlynn"))
|
.map(|reply| warp::reply::with_header(reply, "X-Clacks-Overhead", "GNU Ashlynn"))
|
||||||
.with(warp::log(APPLICATION_NAME))
|
.with(warp::log(APPLICATION_NAME))
|
||||||
.recover(handlers::rejection);
|
.recover(handlers::rejection);
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
|
||||||
|
@use crate::post::Post;
|
||||||
|
@use super::{header_html, footer_html};
|
||||||
|
|
||||||
|
@(posts: Vec<Post>)
|
||||||
|
|
||||||
|
@:header_html(Some("Talks"), None)
|
||||||
|
|
||||||
|
<h1>Talks</h1>
|
||||||
|
|
||||||
|
<p>Here is a link to all of the talks I have done at conferences. Each of these will have links to the slides (PDF) as well as some brief information about them.</p>
|
||||||
|
|
||||||
|
<p>If you have a compatible reader, be sure to check out my <a href="/blog.rss">RSS Feed</a> for automatic updates. Also check out the <a href="/blog.json">JSONFeed</a>.</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<ul>
|
||||||
|
@for post in posts {
|
||||||
|
<li>@post.date - <a href="@post.link">@post.front_matter.title</a></li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
@:footer_html()
|
|
@ -0,0 +1,118 @@
|
||||||
|
@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
|
||||||
|
|
||||||
|
<a href="@post.front_matter.slides_link.as_ref().unwrap()">Link to the slides</a>
|
||||||
|
|
||||||
|
<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 = "#talk";
|
||||||
|
|
||||||
|
@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