101 lines
2.5 KiB
Rust
101 lines
2.5 KiB
Rust
#![forbid(unsafe_code)]
|
|
|
|
use std::sync::{Mutex, Arc};
|
|
use bytes::Bytes;
|
|
use warp::{Filter, reject::{Rejection, Reject}, reply::{Reply, with_header, with_status}};
|
|
|
|
use chrono::{Local, DateTime};
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
struct State {
|
|
pub last_heartbeat: DateTime<Local>
|
|
}
|
|
impl Default for State {
|
|
fn default() -> Self {
|
|
Self {
|
|
last_heartbeat: DateTime::UNIX_EPOCH.into()
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
enum HttpReject {
|
|
String(String)
|
|
}
|
|
|
|
impl Reject for HttpReject {}
|
|
|
|
async fn beat_f(state: Arc<Mutex<State>>, body: Bytes) -> Result<Box<dyn Reply>, Rejection> {
|
|
|
|
if body != include_bytes!("secret.key").to_vec() {
|
|
return Ok(
|
|
Box::new(
|
|
with_status("Invalid key", warp::http::StatusCode::FORBIDDEN)
|
|
)
|
|
)
|
|
}
|
|
|
|
let mut state = state.lock().map_err(|x| HttpReject::String(x.to_string()))?;
|
|
state.last_heartbeat = Local::now().into();
|
|
|
|
Ok(
|
|
Box::new("OK")
|
|
)
|
|
}
|
|
|
|
async fn gif_f(state: Arc<Mutex<State>>) -> Result<Box<dyn Reply>, Rejection> {
|
|
let state = state.lock().map_err(|x| HttpReject::String(x.to_string()))?;
|
|
|
|
let online_gif = include_bytes!("me_online.gif");
|
|
let offline_gif = include_bytes!("me_offline.gif");
|
|
|
|
let duration = (Local::now() - state.last_heartbeat).num_minutes();
|
|
|
|
Ok(
|
|
Box::new(
|
|
with_header(
|
|
with_header(
|
|
{
|
|
if duration < 2 {
|
|
online_gif.to_vec()
|
|
} else {
|
|
offline_gif.to_vec()
|
|
}
|
|
},
|
|
"Content-Type",
|
|
"image/gif"
|
|
),
|
|
"X-Duration",
|
|
duration.to_string()
|
|
)
|
|
)
|
|
)
|
|
}
|
|
|
|
fn beat(state: Arc<Mutex<State>>) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
|
warp::path!("beat")
|
|
.and(warp::path::end())
|
|
.and(warp::post())
|
|
.map(move || state.clone())
|
|
.and(warp::body::bytes())
|
|
.and_then(beat_f)
|
|
}
|
|
|
|
fn gif(state: Arc<Mutex<State>>) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
|
warp::path!("gif")
|
|
.and(warp::path::end())
|
|
.and(warp::get())
|
|
.map(move || state.clone())
|
|
.and_then(gif_f)
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let state = Arc::new(Mutex::new(State::default()));
|
|
|
|
warp::serve(
|
|
beat(state.clone())
|
|
.or(gif(state))
|
|
).run(([0, 0, 0, 0], 80)).await;
|
|
}
|