forked from cadey/xesite
move poking services into app boot after systemd notify
Signed-off-by: Christine Dodrill <me@christine.website>
This commit is contained in:
parent
17af42bc69
commit
4bcc848bb1
|
@ -212,6 +212,21 @@ version = "1.0.66"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
|
||||
|
||||
[[package]]
|
||||
name = "cfcache"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"eyre",
|
||||
"kankyo",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
|
@ -1068,12 +1083,35 @@ dependencies = [
|
|||
"kernel32-sys",
|
||||
"libc",
|
||||
"log",
|
||||
"miow",
|
||||
"miow 0.2.2",
|
||||
"net2",
|
||||
"slab",
|
||||
"winapi 0.2.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio-named-pipes"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mio",
|
||||
"miow 0.3.6",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio-uds"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
|
||||
dependencies = [
|
||||
"iovec",
|
||||
"libc",
|
||||
"mio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.2.2"
|
||||
|
@ -1086,6 +1124,16 @@ dependencies = [
|
|||
"ws2_32-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
|
||||
dependencies = [
|
||||
"socket2",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multipart"
|
||||
version = "0.17.1"
|
||||
|
@ -1913,6 +1961,15 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6fa3938c99da4914afedd13bf3d79bcb6c277d1b2c398d23257a304d9e1b074"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sitemap"
|
||||
version = "0.4.1"
|
||||
|
@ -2069,12 +2126,17 @@ dependencies = [
|
|||
"futures-core",
|
||||
"iovec",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio",
|
||||
"mio-named-pipes",
|
||||
"mio-uds",
|
||||
"num_cpus",
|
||||
"pin-project-lite 0.1.11",
|
||||
"signal-hook-registry",
|
||||
"slab",
|
||||
"tokio-macros",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2603,6 +2665,7 @@ checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57"
|
|||
name = "xesite"
|
||||
version = "2.2.0"
|
||||
dependencies = [
|
||||
"cfcache",
|
||||
"chrono",
|
||||
"color-eyre",
|
||||
"comrak",
|
||||
|
@ -2621,7 +2684,7 @@ dependencies = [
|
|||
"pfacts",
|
||||
"pretty_env_logger",
|
||||
"prometheus",
|
||||
"rand 0.7.3",
|
||||
"rand 0.8.1",
|
||||
"reqwest",
|
||||
"ructe",
|
||||
"sdnotify",
|
||||
|
|
|
@ -21,6 +21,7 @@ log = "0.4"
|
|||
mime = "0.3.0"
|
||||
prometheus = { version = "0.10", default-features = false, features = ["process"] }
|
||||
rand = "0"
|
||||
reqwest = { version = "0.10", features = ["json"] }
|
||||
sdnotify = { version = "0.1", default-features = false }
|
||||
serde_dhall = "0.8.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
@ -37,6 +38,7 @@ url = "2"
|
|||
uuid = { version = "0.8", features = ["serde", "v4"] }
|
||||
|
||||
# workspace dependencies
|
||||
cfcache = { path = "./lib/cfcache" }
|
||||
go_vanity = { path = "./lib/go_vanity" }
|
||||
jsonfeed = { path = "./lib/jsonfeed" }
|
||||
mi = { path = "./lib/mi" }
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "cfcache"
|
||||
version = "0.1.0"
|
||||
authors = ["Christine Dodrill <me@christine.website>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
reqwest = { version = "0.10", features = ["json"] }
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
tracing = "0.1"
|
||||
tracing-futures = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
eyre = "0.6.5"
|
||||
kankyo = "0.3"
|
||||
tokio = { version = "0.2", features = ["full"] }
|
|
@ -0,0 +1,15 @@
|
|||
use eyre::Result;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
kankyo::init()?;
|
||||
|
||||
let key = std::env::var("CF_TOKEN")?;
|
||||
let zone_id = std::env::var("CF_ZONE_ID")?;
|
||||
|
||||
let cli = cfcache::Client::new(key, zone_id)?;
|
||||
cli.purge(vec!["https://christine.website/.within/health".to_string()])
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
use reqwest::header;
|
||||
use tracing::instrument;
|
||||
|
||||
pub type Result<T = ()> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("json error: {0}")]
|
||||
Json(#[from] serde_json::Error),
|
||||
|
||||
#[error("request error: {0}")]
|
||||
Request(#[from] reqwest::Error),
|
||||
|
||||
#[error("invalid header value: {0}")]
|
||||
InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
|
||||
}
|
||||
|
||||
pub struct Client {
|
||||
zone_id: String,
|
||||
cli: reqwest::Client,
|
||||
}
|
||||
|
||||
static USER_AGENT: &str = concat!(
|
||||
"xesite ",
|
||||
env!("CARGO_PKG_NAME"),
|
||||
"/",
|
||||
env!("CARGO_PKG_VERSION")
|
||||
);
|
||||
|
||||
impl Client {
|
||||
pub fn new(api_key: String, zone_id: String) -> Result<Self> {
|
||||
let mut headers = header::HeaderMap::new();
|
||||
headers.insert(
|
||||
header::AUTHORIZATION,
|
||||
header::HeaderValue::from_str(&format!("Bearer {}", api_key))?,
|
||||
);
|
||||
|
||||
let cli = reqwest::Client::builder()
|
||||
.user_agent(USER_AGENT)
|
||||
.default_headers(headers)
|
||||
.build()?;
|
||||
|
||||
Ok(Self { zone_id, cli })
|
||||
}
|
||||
|
||||
#[instrument(skip(self), err)]
|
||||
pub async fn purge(&self, urls: Vec<String>) -> Result {
|
||||
#[derive(serde::Serialize)]
|
||||
struct Files {
|
||||
files: Vec<String>,
|
||||
}
|
||||
|
||||
self.cli
|
||||
.post(&format!(
|
||||
"https://api.cloudflare.com/client/v4/zones/{}/purge_cache",
|
||||
self.zone_id
|
||||
))
|
||||
.json(&Files { files: urls })
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ impl Client {
|
|||
})
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
#[instrument(skip(self), err)]
|
||||
pub async fn mentioners(&self, url: String) -> Result<Vec<WebMention>> {
|
||||
Ok(self
|
||||
.cli
|
||||
|
@ -46,6 +46,16 @@ impl Client {
|
|||
.json()
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[instrument(skip(self), err)]
|
||||
pub async fn refresh(&self) -> Result<()> {
|
||||
self.cli
|
||||
.post("https://mi.within.website/api/blog/refresh")
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Eq, PartialEq, Clone)]
|
||||
|
|
|
@ -5,6 +5,7 @@ use std::{fs, path::PathBuf};
|
|||
use tracing::{error, instrument};
|
||||
|
||||
pub mod markdown;
|
||||
pub mod poke;
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct Config {
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
use color_eyre::eyre::Result;
|
||||
use std::{env, time::Duration};
|
||||
use tokio::time::delay_for;
|
||||
|
||||
#[instrument(err)]
|
||||
pub async fn the_cloud() -> Result<()> {
|
||||
info!("waiting for things to settle");
|
||||
delay_for(Duration::from_secs(10)).await;
|
||||
|
||||
info!("purging cloudflare cache");
|
||||
cloudflare().await?;
|
||||
|
||||
info!("waiting for the cloudflare cache to purge globally");
|
||||
delay_for(Duration::from_secs(45)).await;
|
||||
|
||||
info!("poking mi");
|
||||
mi().await?;
|
||||
|
||||
info!("poking bing");
|
||||
bing().await?;
|
||||
|
||||
info!("poking google");
|
||||
google().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(err)]
|
||||
async fn bing() -> Result<()> {
|
||||
let cli = reqwest::Client::new();
|
||||
cli.get("https://www.bing.com/ping")
|
||||
.query(&[("sitemap", "https://christine.website/sitemap.xml")])
|
||||
.header("User-Agent", crate::APPLICATION_NAME)
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(err)]
|
||||
async fn google() -> Result<()> {
|
||||
let cli = reqwest::Client::new();
|
||||
cli.get("https://www.google.com/ping")
|
||||
.query(&[("sitemap", "https://christine.website/sitemap.xml")])
|
||||
.header("User-Agent", crate::APPLICATION_NAME)
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(err)]
|
||||
async fn cloudflare() -> Result<()> {
|
||||
let cli = cfcache::Client::new(env::var("CF_TOKEN")?, env::var("CF_ZONE_ID")?)?;
|
||||
cli.purge(
|
||||
vec![
|
||||
"https://christine.website/sitemap.xml",
|
||||
"https://christine.website",
|
||||
"https://christine.website/blog",
|
||||
"https://christine.website/blog.atom",
|
||||
"https://christine.website/blog.json",
|
||||
"https://christine.website/blog.rss",
|
||||
"https://christine.website/gallery",
|
||||
"https://christine.website/talks",
|
||||
"https://christine.website/resume",
|
||||
"https://christine.website/signalboost",
|
||||
"https://christine.website/feeds",
|
||||
]
|
||||
.into_iter()
|
||||
.map(|i| i.to_string())
|
||||
.collect(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(err)]
|
||||
async fn mi() -> Result<()> {
|
||||
let cli = mi::Client::new(env::var("MI_TOKEN")?, crate::APPLICATION_NAME.to_string())?;
|
||||
cli.refresh().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
37
src/main.rs
37
src/main.rs
|
@ -39,21 +39,6 @@ async fn main() -> Result<()> {
|
|||
.await?,
|
||||
);
|
||||
|
||||
match sdnotify::SdNotify::from_env() {
|
||||
Ok(ref mut n) => {
|
||||
n.notify_ready().map_err(|why| {
|
||||
error!("can't signal readiness to systemd: {}", why);
|
||||
why
|
||||
})?;
|
||||
n.set_status(format!("hosting {} posts", state.clone().everything.len()))
|
||||
.map_err(|why| {
|
||||
error!("can't signal status to systemd: {}", why);
|
||||
why
|
||||
})?;
|
||||
}
|
||||
Err(why) => error!("not running under systemd with Type=notify: {}", why),
|
||||
}
|
||||
|
||||
let healthcheck = warp::get().and(warp::path(".within").and(warp::path("health")).map(|| "OK"));
|
||||
|
||||
let base = warp::path!("blog" / ..);
|
||||
|
@ -222,6 +207,28 @@ async fn main() -> Result<()> {
|
|||
.with(warp::log(APPLICATION_NAME))
|
||||
.recover(handlers::rejection);
|
||||
|
||||
match sdnotify::SdNotify::from_env() {
|
||||
Ok(ref mut n) => {
|
||||
// shitty heuristic for detecting if we're running in prod
|
||||
tokio::spawn(async {
|
||||
if let Err(why) = app::poke::the_cloud().await {
|
||||
error!("Unable to poke the cloud: {}", why);
|
||||
}
|
||||
});
|
||||
|
||||
n.notify_ready().map_err(|why| {
|
||||
error!("can't signal readiness to systemd: {}", why);
|
||||
why
|
||||
})?;
|
||||
n.set_status(format!("hosting {} posts", state.clone().everything.len()))
|
||||
.map_err(|why| {
|
||||
error!("can't signal status to systemd: {}", why);
|
||||
why
|
||||
})?;
|
||||
}
|
||||
Err(why) => error!("not running under systemd with Type=notify: {}", why),
|
||||
}
|
||||
|
||||
warp::serve(site)
|
||||
.run((
|
||||
[0, 0, 0, 0],
|
||||
|
|
Loading…
Reference in New Issue