Commit 30816d3c authored by Stavros Korokithakis's avatar Stavros Korokithakis

Accept an Eternum API key and notify on hash change

parent a827a230
...@@ -8,6 +8,7 @@ clap = "^2.30" ...@@ -8,6 +8,7 @@ clap = "^2.30"
lazy_static = "^0.2" lazy_static = "^0.2"
log = "^0.4" log = "^0.4"
notify = "^4.0" notify = "^4.0"
reqwest = "^0.8"
rouille = "^1.0" rouille = "^1.0"
rustc-serialize = "^0.3" rustc-serialize = "^0.3"
simple_logger = "^0.5" simple_logger = "^0.5"
......
...@@ -7,6 +7,7 @@ extern crate notify; ...@@ -7,6 +7,7 @@ extern crate notify;
#[macro_use] #[macro_use]
extern crate rouille; extern crate rouille;
extern crate rustc_serialize; extern crate rustc_serialize;
extern crate reqwest;
extern crate simple_logger; extern crate simple_logger;
extern crate xdg; extern crate xdg;
...@@ -26,16 +27,19 @@ use std::process::Command; ...@@ -26,16 +27,19 @@ use std::process::Command;
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
use std::sync::Mutex; use std::sync::Mutex;
use std::thread; use std::thread;
use std::time::{Duration, SystemTime, Instant}; use std::time::{Duration, Instant, SystemTime};
use views::start_api; use views::start_api;
/// The time of the last filesystem event in the Hearth folder, or None if the files were already /// The time of the last filesystem event in the Hearth folder, or None if the files were already
/// added to IPFS. /// added to IPFS.
lazy_static! { lazy_static! {
static ref LAST_EVENT_TIME: Mutex<Option<Instant>> = Mutex::new(None); static ref LAST_EVENT_TIME: Mutex<Option<Instant>> = Mutex::new(None);
} }
lazy_static! {
static ref ETERNUM_KEY: Mutex<String> = Mutex::new(String::from(""));
}
lazy_static! { lazy_static! {
static ref IPFS_PID: Mutex<u32> = Mutex::new(0); static ref IPFS_PID: Mutex<u32> = Mutex::new(0);
} }
...@@ -109,6 +113,40 @@ fn add_files_to_ipfs() { ...@@ -109,6 +113,40 @@ fn add_files_to_ipfs() {
})); }));
} }
/// Send the latest root hash to Eternum.
fn notify_eternum() {
let hash = {
let hashes = IPFS_HASHES.lock().unwrap();
hashes.get("").unwrap_or(&String::from("")).clone()
};
let eternum_key = {
let eternum_key = ETERNUM_KEY.lock().unwrap();
eternum_key.clone()
};
if hash == "" || eternum_key == "" {
debug!(
"No key ({}) or hash ({}) found, will not notify Eternum...",
eternum_key, hash
);
return;
}
debug!("Notifying Eternum with hash {}...", hash);
let mut payload = HashMap::new();
payload.insert("site_hash", hash);
let eternum_url = format!("https://www.eternum.io/api/site/?key={}", eternum_key);
let client = reqwest::Client::new();
let _ = client.put(eternum_url.as_str())
.json(&payload)
.send();
debug!("Successfully notified Eterunm.");
}
/// Receive a filesystem event and act on it. /// Receive a filesystem event and act on it.
/// ///
/// This does the proper debouncing (on top of the inotify debouncer) to emit the necessary /// This does the proper debouncing (on top of the inotify debouncer) to emit the necessary
...@@ -135,7 +173,11 @@ fn notify_ipfs(fs_event: bool) { ...@@ -135,7 +173,11 @@ fn notify_ipfs(fs_event: bool) {
return; return;
} else { } else {
// This is a timer event, so we should check when the last timer event was. // This is a timer event, so we should check when the last timer event was.
if last_event_time.is_none() || Instant::now().duration_since(last_event_time.unwrap()).as_secs() < 5 { if last_event_time.is_none()
|| Instant::now()
.duration_since(last_event_time.unwrap())
.as_secs() < 5
{
// It there was no event, or there hasn't been long enough since the last one, return. // It there was no event, or there hasn't been long enough since the last one, return.
return; return;
} }
...@@ -145,22 +187,9 @@ fn notify_ipfs(fs_event: bool) { ...@@ -145,22 +187,9 @@ fn notify_ipfs(fs_event: bool) {
// We added files to IPFS, so we'll set the last_event_time to None. // We added files to IPFS, so we'll set the last_event_time to None.
*last_event_time = None; *last_event_time = None;
}
}
/// This function gets triggered when inotify notices files having changed. // Send the updated hash to Eternum.
/// thread::spawn(notify_eternum);
/// add_to_ipfs will be true when we actually need to add the files to IPFS (ie when enough time
/// has passed after debouncing).
fn files_changed(op: notify::Op, path: std::path::PathBuf) {
debug!("Got new event: {:?}, {:?}", op, path);
if op.contains(notify::op::CREATE) ||
op.contains(notify::op::REMOVE) ||
op.contains(notify::op::RENAME) ||
op.contains(notify::op::WRITE) ||
op.contains(notify::op::RESCAN) ||
op.contains(notify::op::CLOSE_WRITE) {
notify_ipfs(true);
} }
} }
...@@ -238,7 +267,6 @@ fn start_ipfs() { ...@@ -238,7 +267,6 @@ fn start_ipfs() {
process::exit(1) process::exit(1)
} }
/// Run notify_ipfs every second, for debouncing. /// Run notify_ipfs every second, for debouncing.
/// ///
/// notify_ipfs stores the last time an event occurred, and only adds files to IPFS if there's no /// notify_ipfs stores the last time an event occurred, and only adds files to IPFS if there's no
...@@ -252,6 +280,17 @@ fn cron_thread() { ...@@ -252,6 +280,17 @@ fn cron_thread() {
} }
} }
/// Handle the inotify events in the Hearth directory.
fn files_changed(op: notify::Op, path: std::path::PathBuf) {
debug!("Got new event: {:?}, {:?}", op, path);
if op.contains(notify::op::CREATE) || op.contains(notify::op::REMOVE)
|| op.contains(notify::op::RENAME) || op.contains(notify::op::WRITE)
|| op.contains(notify::op::RESCAN) || op.contains(notify::op::CLOSE_WRITE)
{
notify_ipfs(true);
}
}
fn create_watcher() { fn create_watcher() {
let (tx, rx) = channel(); let (tx, rx) = channel();
let mut watcher: RecommendedWatcher = Watcher::new_raw(tx).unwrap(); let mut watcher: RecommendedWatcher = Watcher::new_raw(tx).unwrap();
...@@ -265,7 +304,7 @@ fn create_watcher() { ...@@ -265,7 +304,7 @@ fn create_watcher() {
Ok(notify::RawEvent { Ok(notify::RawEvent {
path: Some(path), path: Some(path),
op: Ok(op), op: Ok(op),
cookie: _cookie cookie: _cookie,
}) => files_changed(op, path), }) => files_changed(op, path),
Ok(event) => debug!("Broken event: {:?}", event), Ok(event) => debug!("Broken event: {:?}", event),
Err(event) => debug!("Watch error: {:?}", event), Err(event) => debug!("Watch error: {:?}", event),
...@@ -274,9 +313,17 @@ fn create_watcher() { ...@@ -274,9 +313,17 @@ fn create_watcher() {
} }
fn main() { fn main() {
let matches = App::new("hearth") let matches = App::new("Hearth")
.author("Stavros and Stelios") .author("by Stavros and Stelios")
.about("A personal website publisher") .about("A personal website publisher")
.arg(
Arg::with_name("eternum_key")
.short("k")
.long("api_key")
.value_name("APIKEY")
.help("Your Eternum API key, to automatically update your personal site hash")
.takes_value(true),
)
.arg( .arg(
Arg::with_name("debug") Arg::with_name("debug")
.short("d") .short("d")
...@@ -291,6 +338,11 @@ fn main() { ...@@ -291,6 +338,11 @@ fn main() {
simple_logger::init_with_level(Level::Info).unwrap(); simple_logger::init_with_level(Level::Info).unwrap();
} }
{
let mut eternum_key = ETERNUM_KEY.lock().unwrap();
*eternum_key = String::from(matches.value_of("eternum_key").unwrap_or(""));
}
info!("Starting hearth..."); info!("Starting hearth...");
thread::spawn(start_ipfs); thread::spawn(start_ipfs);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment