init repo
This commit is contained in:
commit
b0cd77f68a
|
@ -0,0 +1,2 @@
|
||||||
|
target
|
||||||
|
/secret.key
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,19 @@
|
||||||
|
[package]
|
||||||
|
name = "blek-heartbeat"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
obfstr = "0.4.3"
|
||||||
|
reqwest = { version = "0.11.23", features = ["blocking", "serde_json"] }
|
||||||
|
serde = "1.0.195"
|
||||||
|
serde_json = "1.0.111"
|
||||||
|
users = "0.11.0"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip = "debuginfo"
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
opt-level = "z"
|
|
@ -0,0 +1,58 @@
|
||||||
|
use reqwest::{blocking, Method};
|
||||||
|
use std::{result::Result, error::Error, process::exit, io::{Write, stderr}, fs, path::Path};
|
||||||
|
|
||||||
|
fn write_metrics(add: u32) -> Result<(), Box<dyn Error>> {
|
||||||
|
let user = users::get_user_by_uid(users::get_current_uid());
|
||||||
|
if let Some(user) = user.clone() {
|
||||||
|
let path = format!("/home/{}/.local/share/bHeartbeat", user.name().to_str().unwrap());
|
||||||
|
let file_path = path.clone() + "/data_spent.txt";
|
||||||
|
let _ = fs::create_dir_all(path);
|
||||||
|
if ! Path::new(&file_path).is_file() {
|
||||||
|
fs::write(&file_path, add.to_string())?;
|
||||||
|
} else {
|
||||||
|
let f = fs::File::open(&file_path)?;
|
||||||
|
let meta = f.metadata()?;
|
||||||
|
if meta.permissions().readonly() {
|
||||||
|
return Err("Can't write to the metrics file".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut data = fs::read_to_string(&file_path)?.parse::<u32>().unwrap_or(0);
|
||||||
|
data += add;
|
||||||
|
fs::write(&file_path, data.to_string())?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err("No user data available. Metrics will not be saved.".into())
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let key = include_bytes!("key.bin");
|
||||||
|
let cli = blocking::Client::new();
|
||||||
|
let res = cli.request(Method::POST, "https://hb.blek.codes/beat")
|
||||||
|
.body(key.to_vec())
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
if res.status() != 200 {
|
||||||
|
eprintln!("HTTP heartbeat returned {}", res.status());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = write_metrics((1024 * 10) + key.len() as u32);
|
||||||
|
if let Err(data) = data {
|
||||||
|
eprintln!("Can't write metrics:\n{}", data.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = res.bytes()?.to_vec();
|
||||||
|
let status = status.as_slice();
|
||||||
|
let ok_status = &status[0..2];
|
||||||
|
|
||||||
|
if ok_status != "OK".as_bytes() {
|
||||||
|
eprintln!("ERR: Server returned this:");
|
||||||
|
eprintln!("-----BEGIN BLOCK-----");
|
||||||
|
stderr().write(status)?;
|
||||||
|
eprintln!("\n-----END BLOCK-----");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
../../secret.key
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "blek-heartbeat-server"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bytes = "1.5.0"
|
||||||
|
chrono = "0.4.31"
|
||||||
|
tokio = { version = "1.35.1", features = ["full"] }
|
||||||
|
warp = "0.3.6"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip = "debuginfo"
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
opt-level = "z"
|
|
@ -0,0 +1,100 @@
|
||||||
|
#![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;
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 575 B |
Binary file not shown.
After Width: | Height: | Size: 630 B |
|
@ -0,0 +1 @@
|
||||||
|
../../secret.key
|
Loading…
Reference in New Issue