mirror of
https://github.com/atuinsh/atuin.git
synced 2025-06-25 04:11:53 +02:00
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:
parent
366b8ea97b
commit
9597080825
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -202,6 +202,7 @@ dependencies = [
|
|||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"tracing-tree",
|
"tracing-tree",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
|
"uuid",
|
||||||
"whoami",
|
"whoami",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
pub mod encryption;
|
pub mod encryption;
|
||||||
pub mod sqlite_store;
|
pub mod sqlite_store;
|
||||||
pub mod store;
|
pub mod store;
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
|
@ -76,6 +76,7 @@ colored = "2.0.4"
|
|||||||
ratatui = "0.25"
|
ratatui = "0.25"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
cli-clipboard = { version = "0.4.0", optional = true }
|
cli-clipboard = { version = "0.4.0", optional = true }
|
||||||
|
uuid = { workspace = true }
|
||||||
|
|
||||||
|
|
||||||
[dependencies.tracing-subscriber]
|
[dependencies.tracing-subscriber]
|
||||||
|
@ -13,7 +13,7 @@ use atuin_client::{
|
|||||||
database::{current_context, Database},
|
database::{current_context, Database},
|
||||||
encryption,
|
encryption,
|
||||||
history::{store::HistoryStore, History},
|
history::{store::HistoryStore, History},
|
||||||
record::{self, sqlite_store::SqliteStore},
|
record::sqlite_store::SqliteStore,
|
||||||
settings::{
|
settings::{
|
||||||
FilterMode::{Directory, Global, Session},
|
FilterMode::{Directory, Global, Session},
|
||||||
Settings,
|
Settings,
|
||||||
@ -21,7 +21,8 @@ use atuin_client::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
use atuin_client::sync;
|
use atuin_client::{record, sync};
|
||||||
|
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
use time::{macros::format_description, OffsetDateTime};
|
use time::{macros::format_description, OffsetDateTime};
|
||||||
|
|
||||||
@ -278,6 +279,7 @@ impl Cmd {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
async fn handle_end(
|
async fn handle_end(
|
||||||
db: &impl Database,
|
db: &impl Database,
|
||||||
store: SqliteStore,
|
store: SqliteStore,
|
||||||
|
@ -1,65 +1,26 @@
|
|||||||
use clap::{Args, Subcommand};
|
use clap::Subcommand;
|
||||||
use eyre::{bail, Result};
|
use eyre::Result;
|
||||||
|
|
||||||
use atuin_client::{
|
use atuin_client::{
|
||||||
database::Database,
|
database::Database,
|
||||||
encryption,
|
|
||||||
history::store::HistoryStore,
|
|
||||||
record::{sqlite_store::SqliteStore, store::Store},
|
record::{sqlite_store::SqliteStore, store::Store},
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
};
|
};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
#[derive(Args, Debug)]
|
#[cfg(feature = "sync")]
|
||||||
pub struct Rebuild {
|
mod push;
|
||||||
pub tag: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rebuild {
|
mod 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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
#[command(infer_subcommands = true)]
|
#[command(infer_subcommands = true)]
|
||||||
pub enum Cmd {
|
pub enum Cmd {
|
||||||
Status,
|
Status,
|
||||||
Rebuild(Rebuild),
|
Rebuild(rebuild::Rebuild),
|
||||||
|
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
Push(push::Push),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cmd {
|
impl Cmd {
|
||||||
@ -72,6 +33,9 @@ impl Cmd {
|
|||||||
match self {
|
match self {
|
||||||
Self::Status => self.status(store).await,
|
Self::Status => self.status(store).await,
|
||||||
Self::Rebuild(rebuild) => rebuild.run(settings, store, database).await,
|
Self::Rebuild(rebuild) => rebuild.run(settings, store, database).await,
|
||||||
|
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
Self::Push(push) => push.run(settings, store).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
68
atuin/src/command/client/store/push.rs
Normal file
68
atuin/src/command/client/store/push.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
52
atuin/src/command/client/store/rebuild.rs
Normal file
52
atuin/src/command/client/store/rebuild.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,10 @@
|
|||||||
use std::{env, time::Duration};
|
use std::{env, time::Duration};
|
||||||
|
|
||||||
use atuin_client::api_client;
|
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::{launch_with_tcp_listener, Settings as ServerSettings};
|
||||||
use atuin_server_postgres::{Postgres, PostgresSettings};
|
use atuin_server_postgres::{Postgres, PostgresSettings};
|
||||||
use futures_util::TryFutureExt;
|
use futures_util::TryFutureExt;
|
||||||
use time::OffsetDateTime;
|
|
||||||
use tokio::{net::TcpListener, sync::oneshot, task::JoinHandle};
|
use tokio::{net::TcpListener, sync::oneshot, task::JoinHandle};
|
||||||
use tracing::{dispatcher, Dispatch};
|
use tracing::{dispatcher, Dispatch};
|
||||||
use tracing_subscriber::{layer::SubscriberExt, EnvFilter};
|
use tracing_subscriber::{layer::SubscriberExt, EnvFilter};
|
||||||
@ -80,6 +79,7 @@ pub async fn register_inner<'a>(
|
|||||||
api_client::Client::new(address, ®istration_response.session, 5, 30).unwrap()
|
api_client::Client::new(address, ®istration_response.session, 5, 30).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub async fn login(address: &str, username: String, password: String) -> api_client::Client<'_> {
|
pub async fn login(address: &str, username: String, password: String) -> api_client::Client<'_> {
|
||||||
// registration works
|
// registration works
|
||||||
let login_respose = api_client::login(
|
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()
|
api_client::Client::new(address, &login_respose.session, 5, 30).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub async fn register(address: &str) -> api_client::Client<'_> {
|
pub async fn register(address: &str) -> api_client::Client<'_> {
|
||||||
let username = uuid_v7().as_simple().to_string();
|
let username = uuid_v7().as_simple().to_string();
|
||||||
let password = uuid_v7().as_simple().to_string();
|
let password = uuid_v7().as_simple().to_string();
|
||||||
|
@ -1,14 +1,5 @@
|
|||||||
use std::{env, time::Duration};
|
|
||||||
|
|
||||||
use atuin_client::api_client;
|
|
||||||
use atuin_common::{api::AddHistoryRequest, utils::uuid_v7};
|
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 time::OffsetDateTime;
|
||||||
use tokio::{net::TcpListener, sync::oneshot, task::JoinHandle};
|
|
||||||
use tracing::{dispatcher, Dispatch};
|
|
||||||
use tracing_subscriber::{layer::SubscriberExt, EnvFilter};
|
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
|
@ -1,14 +1,4 @@
|
|||||||
use std::{env, time::Duration};
|
use atuin_common::utils::uuid_v7;
|
||||||
|
|
||||||
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;
|
mod common;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user