refactor: remove sync_usage option, enhance SSH connection error messages

This commit is contained in:
2026-06-03 23:31:42 +08:00
parent 5e45bbbaf6
commit 24af4d0f95
5 changed files with 38 additions and 33 deletions
+1 -8
View File
@@ -13,7 +13,6 @@ pub enum SettingsField {
S3Bucket,
S3AccessKey,
S3SecretKey,
SyncUsage,
}
impl SettingsField {
@@ -37,7 +36,6 @@ impl SettingsField {
]);
}
}
fields.push(SettingsField::SyncUsage);
fields
}
@@ -53,12 +51,11 @@ impl SettingsField {
Self::S3Bucket => "Bucket",
Self::S3AccessKey => "Access Key",
Self::S3SecretKey => "Secret Key",
Self::SyncUsage => "Sync Usage",
}
}
pub fn is_toggle(self) -> bool {
matches!(self, Self::Backend | Self::SyncUsage)
matches!(self, Self::Backend)
}
pub fn is_text(self) -> bool {
@@ -78,7 +75,6 @@ pub struct SettingsState {
pub s3_bucket: String,
pub s3_access_key: String,
pub s3_secret_key: String,
pub sync_usage: bool,
pub active: SettingsField,
pub cursor: usize,
}
@@ -148,7 +144,6 @@ impl Default for SettingsState {
s3_bucket: String::new(),
s3_access_key: String::new(),
s3_secret_key: String::new(),
sync_usage: false,
active: SettingsField::SyncPassword,
cursor: 0,
}
@@ -225,7 +220,6 @@ impl App {
self.config.settings.s3_access_key.clone().unwrap_or_default();
self.session.settings.s3_secret_key =
self.config.settings.s3_secret_key.clone().unwrap_or_default();
self.session.settings.sync_usage = self.config.settings.sync_usage_count;
self.session.settings.active = SettingsField::SyncPassword;
self.session.settings.cursor = char_len(self.session.settings.active_text());
self.session.mode = Mode::Settings;
@@ -259,7 +253,6 @@ impl App {
} else {
Some(s3_sk)
};
self.config.settings.sync_usage_count = self.session.settings.sync_usage;
self.config.save()?;
self.session.mode = Mode::Home;
Ok(())
-2
View File
@@ -64,8 +64,6 @@ pub struct Settings {
pub s3_bucket: Option<String>,
pub s3_access_key: Option<String>,
pub s3_secret_key: Option<String>,
#[serde(default)]
pub sync_usage_count: bool,
pub sync_password: Option<String>,
}
+33 -2
View File
@@ -1,9 +1,38 @@
use crate::config::{ConnectionType, CredentialEntry, SshellConfig};
use crate::config::{ConnectionType, CredentialEntry, SshellConfig, find_binary};
use anyhow::{Context, Result, bail};
use std::io::Write;
use std::process::Command;
use tempfile::NamedTempFile;
fn require_binary(name: &str) -> Result<()> {
if find_binary(name).is_some() {
return Ok(());
}
let hint = match name {
"ssh" => "\n\
sshell requires `ssh` to connect via SSH.\n\
\n\
Install it with:\n\
macOS: pre-installed (or: xcode-select --install)\n\
Debian: sudo apt install openssh-client\n\
Arch: sudo pacman -S openssh\n\
Fedora: sudo dnf install openssh-clients\n\
Windows: Settings → Apps → Optional Features → OpenSSH Client"
,
"sshpass" => "\n\
Password-based SSH login requires `sshpass`.\n\
Consider switching to private-key auth instead, or install it:\n\
macOS: brew install hudochenkov/sshpass/sshpass\n\
Debian: sudo apt install sshpass\n\
Arch: sudo pacman -S sshpass\n\
Fedora: sudo dnf install sshpass\n\
Windows: not available — use private-key auth"
,
_ => "",
};
bail!("command not found: `{name}`{hint}");
}
pub fn connect(name: &str, cfg: &SshellConfig) -> Result<()> {
let profile = cfg
.connections
@@ -41,8 +70,10 @@ fn connect_ssh(
if auth_ref.is_empty() {
bail!("this connection has no credential; edit it and set password or private key");
}
require_binary("ssh")?;
match cfg.credential(auth_ref) {
Some(CredentialEntry::Password { value, .. }) => {
require_binary("sshpass")?;
let args = vec![
"ssh".to_string(),
"-o".to_string(),
@@ -87,7 +118,7 @@ fn run_sshpass(args: &[String], password: &str) -> Result<()> {
.args(args)
.env("SSHPASS", password)
.status()
.context("failed to run sshpass — is it installed?")?;
.context("failed to run sshpass")?;
std::process::exit(status.code().unwrap_or(1));
}
-7
View File
@@ -60,9 +60,6 @@ pub(crate) fn build_sync_payload(
sync_password: Option<&str>,
) -> Result<toml::Value> {
let mut payload = cfg.clone();
payload.settings.sync_password = None;
payload.settings.webdav_password = None;
payload.settings.s3_secret_key = None;
let synced_refs: Vec<String> = payload
.connections
@@ -90,14 +87,10 @@ pub(crate) fn build_sync_payload(
"version".to_string(),
toml::Value::Integer(payload.version as i64),
);
table.insert("settings".to_string(), to_toml_value(&payload.settings)?);
let mut conns = toml::map::Map::new();
for (name, profile) in &mut payload.connections {
profile.local_tags.clear();
if !payload.settings.sync_usage_count {
profile.usage_count = 0;
}
match &mut profile.kind {
ConnectionType::Shell {
auth_ref,
+2 -12
View File
@@ -1,7 +1,7 @@
use crate::app::{App, FormAction, Mode, SettingsField, SettingsState, char_len};
use crate::config::SyncBackend;
use crate::ui::component::{FormRow, badge_span};
use crate::ui::{ACCENT, GREEN, ORANGE, PURPLE, RED};
use crate::ui::{ACCENT, ORANGE, PURPLE};
use super::{View, handle_form_nav};
@@ -31,7 +31,7 @@ impl View for SettingsView {
let active = settings.active == field;
let label = field.label().to_string();
if i == 2 || matches!(field, SettingsField::SyncUsage) {
if i == 2 {
rows.push(FormRow::Separator);
}
@@ -42,13 +42,6 @@ impl View for SettingsView {
SyncBackend::Webdav => badge_span("WebDAV", ORANGE),
SyncBackend::S3 => badge_span("S3", PURPLE),
},
SettingsField::SyncUsage => {
if settings.sync_usage {
badge_span("Yes", GREEN)
} else {
badge_span("No", RED)
}
}
_ => unreachable!(),
};
rows.push(FormRow::Toggle {
@@ -131,9 +124,6 @@ fn settings_toggle(settings: &mut SettingsState) {
};
settings.ensure_active_visible();
}
SettingsField::SyncUsage => {
settings.sync_usage = !settings.sync_usage;
}
_ => {}
}
}