feat: add store push (#1649)

* feat: add store push

* only push for the current host unless specified

* tidy up

* tidy up some more

* sort features
This commit is contained in:
Ellie Huxtable 2024-01-30 13:41:01 +00:00 committed by GitHub
parent 366b8ea97b
commit 9597080825
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 143 additions and 72 deletions

1
Cargo.lock generated
View File

@ -202,6 +202,7 @@ dependencies = [
"tracing-subscriber",
"tracing-tree",
"unicode-width",
"uuid",
"whoami",
]

View File

@ -1,5 +1,6 @@
pub mod encryption;
pub mod sqlite_store;
pub mod store;
#[cfg(feature = "sync")]
pub mod sync;

View File

@ -76,6 +76,7 @@ colored = "2.0.4"
ratatui = "0.25"
tracing = "0.1"
cli-clipboard = { version = "0.4.0", optional = true }
uuid = { workspace = true }
[dependencies.tracing-subscriber]

View File

@ -13,7 +13,7 @@ use atuin_client::{
database::{current_context, Database},
encryption,
history::{store::HistoryStore, History},
record::{self, sqlite_store::SqliteStore},
record::sqlite_store::SqliteStore,
settings::{
FilterMode::{Directory, Global, Session},
Settings,
@ -21,7 +21,8 @@ use atuin_client::{
};
#[cfg(feature = "sync")]
use atuin_client::sync;
use atuin_client::{record, sync};
use log::{debug, warn};
use time::{macros::format_description, OffsetDateTime};
@ -278,6 +279,7 @@ impl Cmd {
Ok(())
}
#[allow(unused_variables)]
async fn handle_end(
db: &impl Database,
store: SqliteStore,

View File

@ -1,65 +1,26 @@
use clap::{Args, Subcommand};
use eyre::{bail, Result};
use clap::Subcommand;
use eyre::Result;
use atuin_client::{
database::Database,
encryption,
history::store::HistoryStore,
record::{sqlite_store::SqliteStore, store::Store},
settings::Settings,
};
use time::OffsetDateTime;
#[derive(Args, Debug)]
pub struct Rebuild {
pub tag: String,
}
#[cfg(feature = "sync")]
mod push;
impl Rebuild {
pub async fn run(
&self,
settings: &Settings,
store: SqliteStore,
database: &dyn Database,
) -> Result<()> {
// keep it as a string and not an enum atm
// would be super cool to build this dynamically in the future
// eg register handles for rebuilding various tags without having to make this part of the
// binary big
match self.tag.as_str() {
"history" => {
self.rebuild_history(settings, store.clone(), database)
.await?;
}
tag => bail!("unknown tag: {tag}"),
}
Ok(())
}
async fn rebuild_history(
&self,
settings: &Settings,
store: SqliteStore,
database: &dyn Database,
) -> Result<()> {
let encryption_key: [u8; 32] = encryption::load_key(settings)?.into();
let host_id = Settings::host_id().expect("failed to get host_id");
let history_store = HistoryStore::new(store, host_id, encryption_key);
history_store.build(database).await?;
Ok(())
}
}
mod rebuild;
#[derive(Subcommand, Debug)]
#[command(infer_subcommands = true)]
pub enum Cmd {
Status,
Rebuild(Rebuild),
Rebuild(rebuild::Rebuild),
#[cfg(feature = "sync")]
Push(push::Push),
}
impl Cmd {
@ -72,6 +33,9 @@ impl Cmd {
match self {
Self::Status => self.status(store).await,
Self::Rebuild(rebuild) => rebuild.run(settings, store, database).await,
#[cfg(feature = "sync")]
Self::Push(push) => push.run(settings, store).await,
}
}

View File

@ -0,0 +1,68 @@
use atuin_common::record::HostId;
use clap::Args;
use eyre::Result;
use uuid::Uuid;
use atuin_client::{
record::sync::Operation,
record::{sqlite_store::SqliteStore, sync},
settings::Settings,
};
#[derive(Args, Debug)]
pub struct Push {
/// The tag to push (eg, 'history'). Defaults to all tags
#[arg(long, short)]
pub tag: Option<String>,
/// The host to push, in the form of a UUID host ID. Defaults to the current host.
#[arg(long)]
pub host: Option<Uuid>,
}
impl Push {
pub async fn run(&self, settings: &Settings, store: SqliteStore) -> Result<()> {
let host_id = Settings::host_id().expect("failed to get host_id");
// We can actually just use the existing diff/etc to push
// 1. Diff
// 2. Get operations
// 3. Filter operations by
// a) are they an upload op?
// b) are they for the host/tag we are pushing here?
let (diff, _) = sync::diff(settings, &store).await?;
let operations = sync::operations(diff, &store).await?;
let operations = operations
.into_iter()
.filter(|op| match op {
// No noops or downloads thx
Operation::Noop { .. } | Operation::Download { .. } => false,
// push, so yes plz to uploads!
Operation::Upload { host, tag, .. } => {
if let Some(h) = self.host {
if HostId(h) != *host {
return false;
}
} else if *host != host_id {
return false;
}
if let Some(t) = self.tag.clone() {
if t != *tag {
return false;
}
}
true
}
})
.collect();
let (uploaded, _) = sync::sync_remote(operations, &store, settings).await?;
println!("Uploaded {uploaded} records");
Ok(())
}
}

View File

@ -0,0 +1,52 @@
use clap::Args;
use eyre::{bail, Result};
use atuin_client::{
database::Database, encryption, history::store::HistoryStore,
record::sqlite_store::SqliteStore, settings::Settings,
};
#[derive(Args, Debug)]
pub struct Rebuild {
pub tag: String,
}
impl Rebuild {
pub async fn run(
&self,
settings: &Settings,
store: SqliteStore,
database: &dyn Database,
) -> Result<()> {
// keep it as a string and not an enum atm
// would be super cool to build this dynamically in the future
// eg register handles for rebuilding various tags without having to make this part of the
// binary big
match self.tag.as_str() {
"history" => {
self.rebuild_history(settings, store.clone(), database)
.await?;
}
tag => bail!("unknown tag: {tag}"),
}
Ok(())
}
async fn rebuild_history(
&self,
settings: &Settings,
store: SqliteStore,
database: &dyn Database,
) -> Result<()> {
let encryption_key: [u8; 32] = encryption::load_key(settings)?.into();
let host_id = Settings::host_id().expect("failed to get host_id");
let history_store = HistoryStore::new(store, host_id, encryption_key);
history_store.build(database).await?;
Ok(())
}
}

View File

@ -1,11 +1,10 @@
use std::{env, time::Duration};
use atuin_client::api_client;
use atuin_common::{api::AddHistoryRequest, utils::uuid_v7};
use atuin_common::utils::uuid_v7;
use atuin_server::{launch_with_tcp_listener, Settings as ServerSettings};
use atuin_server_postgres::{Postgres, PostgresSettings};
use futures_util::TryFutureExt;
use time::OffsetDateTime;
use tokio::{net::TcpListener, sync::oneshot, task::JoinHandle};
use tracing::{dispatcher, Dispatch};
use tracing_subscriber::{layer::SubscriberExt, EnvFilter};
@ -80,6 +79,7 @@ pub async fn register_inner<'a>(
api_client::Client::new(address, &registration_response.session, 5, 30).unwrap()
}
#[allow(dead_code)]
pub async fn login(address: &str, username: String, password: String) -> api_client::Client<'_> {
// registration works
let login_respose = api_client::login(
@ -92,6 +92,7 @@ pub async fn login(address: &str, username: String, password: String) -> api_cli
api_client::Client::new(address, &login_respose.session, 5, 30).unwrap()
}
#[allow(dead_code)]
pub async fn register(address: &str) -> api_client::Client<'_> {
let username = uuid_v7().as_simple().to_string();
let password = uuid_v7().as_simple().to_string();

View File

@ -1,14 +1,5 @@
use std::{env, time::Duration};
use atuin_client::api_client;
use atuin_common::{api::AddHistoryRequest, utils::uuid_v7};
use atuin_server::{launch_with_tcp_listener, Settings as ServerSettings};
use atuin_server_postgres::{Postgres, PostgresSettings};
use futures_util::TryFutureExt;
use time::OffsetDateTime;
use tokio::{net::TcpListener, sync::oneshot, task::JoinHandle};
use tracing::{dispatcher, Dispatch};
use tracing_subscriber::{layer::SubscriberExt, EnvFilter};
mod common;

View File

@ -1,14 +1,4 @@
use std::{env, time::Duration};
use atuin_client::api_client;
use atuin_common::{api::AddHistoryRequest, utils::uuid_v7};
use atuin_server::{launch_with_tcp_listener, Settings as ServerSettings};
use atuin_server_postgres::{Postgres, PostgresSettings};
use futures_util::TryFutureExt;
use time::OffsetDateTime;
use tokio::{net::TcpListener, sync::oneshot, task::JoinHandle};
use tracing::{dispatcher, Dispatch};
use tracing_subscriber::{layer::SubscriberExt, EnvFilter};
use atuin_common::utils::uuid_v7;
mod common;