aboutsummaryrefslogtreecommitdiffstats
path: root/src/command
diff options
context:
space:
mode:
Diffstat (limited to 'src/command')
-rw-r--r--src/command/history.rs30
-rw-r--r--src/command/login.rs48
-rw-r--r--src/command/mod.rs34
-rw-r--r--src/command/register.rs54
-rw-r--r--src/command/search.rs3
-rw-r--r--src/command/server.rs4
-rw-r--r--src/command/sync.rs15
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(())
+}