diff options
Diffstat (limited to '')
| -rw-r--r-- | src/command/history.rs | 30 | ||||
| -rw-r--r-- | src/command/login.rs | 48 | ||||
| -rw-r--r-- | src/command/mod.rs | 34 | ||||
| -rw-r--r-- | src/command/register.rs | 54 | ||||
| -rw-r--r-- | src/command/search.rs | 3 | ||||
| -rw-r--r-- | src/command/server.rs | 4 | ||||
| -rw-r--r-- | src/command/sync.rs | 15 |
7 files changed, 173 insertions, 15 deletions
diff --git a/src/command/history.rs b/src/command/history.rs index 05aed4b9..3b4a717c 100644 --- a/src/command/history.rs +++ b/src/command/history.rs @@ -1,10 +1,13 @@ use std::env; use eyre::Result; +use fork::{fork, Fork}; use structopt::StructOpt; use crate::local::database::Database; use crate::local::history::History; +use crate::local::sync; +use crate::settings::Settings; #[derive(StructOpt)] pub enum Cmd { @@ -50,21 +53,13 @@ fn print_list(h: &[History]) { } impl Cmd { - pub fn run(&self, db: &mut impl Database) -> Result<()> { + pub fn run(&self, settings: &Settings, db: &mut impl Database) -> Result<()> { match self { Self::Start { command: words } => { let command = words.join(" "); let cwd = env::current_dir()?.display().to_string(); - let h = History::new( - chrono::Utc::now().timestamp_nanos(), - command, - cwd, - -1, - -1, - None, - None, - ); + let h = History::new(chrono::Utc::now(), command, cwd, -1, -1, None, None); // print the ID // we use this as the key for calling end @@ -76,10 +71,23 @@ impl Cmd { Self::End { id, exit } => { let mut h = db.load(id)?; h.exit = *exit; - h.duration = chrono::Utc::now().timestamp_nanos() - h.timestamp; + h.duration = chrono::Utc::now().timestamp_nanos() - h.timestamp.timestamp_nanos(); db.update(&h)?; + if settings.local.should_sync()? { + match fork() { + Ok(Fork::Parent(child)) => { + debug!("launched sync background process with PID {}", child); + } + Ok(Fork::Child) => { + debug!("running periodic background sync"); + sync::sync(settings, false, db)?; + } + Err(_) => println!("Fork failed"), + } + } + Ok(()) } diff --git a/src/command/login.rs b/src/command/login.rs new file mode 100644 index 00000000..4f58b77f --- /dev/null +++ b/src/command/login.rs @@ -0,0 +1,48 @@ +use std::collections::HashMap; +use std::fs::File; +use std::io::prelude::*; + +use eyre::Result; +use structopt::StructOpt; + +use crate::settings::Settings; + +#[derive(StructOpt)] +#[structopt(setting(structopt::clap::AppSettings::DeriveDisplayOrder))] +pub struct Cmd { + #[structopt(long, short)] + pub username: String, + + #[structopt(long, short)] + pub password: String, + + #[structopt(long, short, about = "the encryption key for your account")] + pub key: String, +} + +impl Cmd { + pub fn run(&self, settings: &Settings) -> Result<()> { + let mut map = HashMap::new(); + map.insert("username", self.username.clone()); + map.insert("password", self.password.clone()); + + let url = format!("{}/login", settings.local.sync_address); + let client = reqwest::blocking::Client::new(); + let resp = client.post(url).json(&map).send()?; + + let session = resp.json::<HashMap<String, String>>()?; + let session = session["session"].clone(); + + let session_path = settings.local.session_path.as_str(); + let mut file = File::create(session_path)?; + file.write_all(session.as_bytes())?; + + let key_path = settings.local.key_path.as_str(); + let mut file = File::create(key_path)?; + file.write_all(&base64::decode(self.key.clone())?)?; + + println!("Logged in!"); + + Ok(()) + } +} diff --git a/src/command/mod.rs b/src/command/mod.rs index a5ea0228..eeb11a87 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -9,9 +9,12 @@ mod event; mod history; mod import; mod init; +mod login; +mod register; mod search; mod server; mod stats; +mod sync; #[derive(StructOpt)] pub enum AtuinCmd { @@ -38,6 +41,21 @@ pub enum AtuinCmd { #[structopt(about = "interactive history search")] Search { query: Vec<String> }, + + #[structopt(about = "sync with the configured server")] + Sync { + #[structopt(long, short, about = "force re-download everything")] + force: bool, + }, + + #[structopt(about = "login to the configured server")] + Login(login::Cmd), + + #[structopt(about = "register with the configured server")] + Register(register::Cmd), + + #[structopt(about = "print the encryption key for transfer to another machine")] + Key, } pub fn uuid_v4() -> String { @@ -47,13 +65,27 @@ pub fn uuid_v4() -> String { impl AtuinCmd { pub fn run(self, db: &mut impl Database, settings: &Settings) -> Result<()> { match self { - Self::History(history) => history.run(db), + Self::History(history) => history.run(settings, db), Self::Import(import) => import.run(db), Self::Server(server) => server.run(settings), Self::Stats(stats) => stats.run(db, settings), Self::Init => init::init(), Self::Search { query } => search::run(&query, db), + Self::Sync { force } => sync::run(settings, force, db), + Self::Login(l) => l.run(settings), + Self::Register(r) => register::run( + settings, + r.username.as_str(), + r.email.as_str(), + r.password.as_str(), + ), + Self::Key => { + let key = std::fs::read(settings.local.key_path.as_str())?; + println!("{}", base64::encode(key)); + Ok(()) + } + Self::Uuid => { println!("{}", uuid_v4()); Ok(()) diff --git a/src/command/register.rs b/src/command/register.rs new file mode 100644 index 00000000..62bbeaeb --- /dev/null +++ b/src/command/register.rs @@ -0,0 +1,54 @@ +use std::collections::HashMap; +use std::fs::File; +use std::io::prelude::*; + +use eyre::{eyre, Result}; +use structopt::StructOpt; + +use crate::settings::Settings; + +#[derive(StructOpt)] +#[structopt(setting(structopt::clap::AppSettings::DeriveDisplayOrder))] +pub struct Cmd { + #[structopt(long, short)] + pub username: String, + + #[structopt(long, short)] + pub email: String, + + #[structopt(long, short)] + pub password: String, +} + +pub fn run(settings: &Settings, username: &str, email: &str, password: &str) -> Result<()> { + let mut map = HashMap::new(); + map.insert("username", username); + map.insert("email", email); + map.insert("password", password); + + let url = format!("{}/user/{}", settings.local.sync_address, username); + let resp = reqwest::blocking::get(url)?; + + if resp.status().is_success() { + println!("Username is already in use! Please try another."); + return Ok(()); + } + + let url = format!("{}/register", settings.local.sync_address); + let client = reqwest::blocking::Client::new(); + let resp = client.post(url).json(&map).send()?; + + if !resp.status().is_success() { + println!("Failed to register user - please check your details and try again"); + return Err(eyre!("failed to register user")); + } + + let session = resp.json::<HashMap<String, String>>()?; + let session = session["session"].clone(); + + let path = settings.local.session_path.as_str(); + let mut file = File::create(path)?; + file.write_all(session.as_bytes())?; + + Ok(()) +} diff --git a/src/command/search.rs b/src/command/search.rs index d51e29ef..b9f3987c 100644 --- a/src/command/search.rs +++ b/src/command/search.rs @@ -171,7 +171,8 @@ fn select_history(query: &[String], db: &mut impl Database) -> Result<String> { .iter() .enumerate() .map(|(i, m)| { - let mut content = Span::raw(m.command.to_string()); + let mut content = + Span::raw(m.command.to_string().replace("\n", " ").replace("\t", " ")); if let Some(selected) = app.results_state.selected() { if selected == i { diff --git a/src/command/server.rs b/src/command/server.rs index 5156f409..ba2a9a2f 100644 --- a/src/command/server.rs +++ b/src/command/server.rs @@ -24,10 +24,10 @@ impl Cmd { match self { Self::Start { host, port } => { let host = host.as_ref().map_or( - settings.remote.host.clone(), + settings.server.host.clone(), std::string::ToString::to_string, ); - let port = port.map_or(settings.remote.port, |p| p); + let port = port.map_or(settings.server.port, |p| p); server::launch(settings, host, port); } diff --git a/src/command/sync.rs b/src/command/sync.rs new file mode 100644 index 00000000..facbe578 --- /dev/null +++ b/src/command/sync.rs @@ -0,0 +1,15 @@ +use eyre::Result; + +use crate::local::database::Database; +use crate::local::sync; +use crate::settings::Settings; + +pub fn run(settings: &Settings, force: bool, db: &mut impl Database) -> Result<()> { + sync::sync(settings, force, db)?; + println!( + "Sync complete! {} items in database, force: {}", + db.history_count()?, + force + ); + Ok(()) +} |
