mirror of
https://github.com/veeso/termscp.git
synced 2026-09-27 14:31:18 -07:00
feat(gcs): add Google Cloud Storage support (#443)
* feat(gcs): add Google Cloud Storage support Closes #436 * fix: honor configured protocol and finalization errors * ci: remove TruffleHog checks
This commit is contained in:
@@ -31,9 +31,10 @@ const HOST_BRIDGE_RADIO_PROTOCOL_SCP: usize = 2;
|
||||
const HOST_BRIDGE_RADIO_PROTOCOL_FTP: usize = 3;
|
||||
const HOST_BRIDGE_RADIO_PROTOCOL_FTPS: usize = 4;
|
||||
const HOST_BRIDGE_RADIO_PROTOCOL_S3: usize = 5;
|
||||
const HOST_BRIDGE_RADIO_PROTOCOL_KUBE: usize = 6;
|
||||
const HOST_BRIDGE_RADIO_PROTOCOL_WEBDAV: usize = 7;
|
||||
const HOST_BRIDGE_RADIO_PROTOCOL_SMB: usize = 8; // Keep as last
|
||||
const HOST_BRIDGE_RADIO_PROTOCOL_GCS: usize = 6;
|
||||
const HOST_BRIDGE_RADIO_PROTOCOL_KUBE: usize = 7;
|
||||
const HOST_BRIDGE_RADIO_PROTOCOL_WEBDAV: usize = 8;
|
||||
const HOST_BRIDGE_RADIO_PROTOCOL_SMB: usize = 9; // Keep as last
|
||||
|
||||
// remote protocol radio
|
||||
const REMOTE_RADIO_PROTOCOL_SFTP: usize = 0;
|
||||
@@ -41,9 +42,10 @@ const REMOTE_RADIO_PROTOCOL_SCP: usize = 1;
|
||||
const REMOTE_RADIO_PROTOCOL_FTP: usize = 2;
|
||||
const REMOTE_RADIO_PROTOCOL_FTPS: usize = 3;
|
||||
const REMOTE_RADIO_PROTOCOL_S3: usize = 4;
|
||||
const REMOTE_RADIO_PROTOCOL_KUBE: usize = 5;
|
||||
const REMOTE_RADIO_PROTOCOL_WEBDAV: usize = 6;
|
||||
const REMOTE_RADIO_PROTOCOL_SMB: usize = 7; // Keep as last
|
||||
const REMOTE_RADIO_PROTOCOL_GCS: usize = 5;
|
||||
const REMOTE_RADIO_PROTOCOL_KUBE: usize = 6;
|
||||
const REMOTE_RADIO_PROTOCOL_WEBDAV: usize = 7;
|
||||
const REMOTE_RADIO_PROTOCOL_SMB: usize = 8; // Keep as last
|
||||
|
||||
// -- components
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||
@@ -74,6 +76,9 @@ pub enum Id {
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||
pub enum AuthFormId {
|
||||
Address,
|
||||
GcsBucket,
|
||||
GcsEndpoint,
|
||||
GcsServiceAccountKey,
|
||||
KubeNamespace,
|
||||
KubeClusterUrl,
|
||||
KubeUsername,
|
||||
@@ -153,6 +158,12 @@ pub enum UiAuthFormMsg {
|
||||
AddressBlurDown,
|
||||
AddressBlurUp,
|
||||
ChangeFormTab,
|
||||
GcsBucketBlurDown,
|
||||
GcsBucketBlurUp,
|
||||
GcsEndpointBlurDown,
|
||||
GcsEndpointBlurUp,
|
||||
GcsServiceAccountKeyBlurDown,
|
||||
GcsServiceAccountKeyBlurUp,
|
||||
KubeNamespaceBlurDown,
|
||||
KubeNamespaceBlurUp,
|
||||
KubeClusterUrlBlurDown,
|
||||
@@ -209,6 +220,7 @@ pub enum UiAuthFormMsg {
|
||||
enum InputMask {
|
||||
Generic,
|
||||
AwsS3,
|
||||
Gcs,
|
||||
Kube,
|
||||
Localhost,
|
||||
Smb,
|
||||
@@ -301,6 +313,10 @@ impl AuthActivity {
|
||||
Self::file_transfer_protocol_input_mask(self.remote_protocol)
|
||||
}
|
||||
|
||||
fn set_remote_protocol(&mut self, protocol: FileTransferProtocol) {
|
||||
self.remote_protocol = protocol;
|
||||
}
|
||||
|
||||
/// Get current input mask to show
|
||||
fn host_bridge_input_mask(&self) -> InputMask {
|
||||
match self.host_bridge_protocol {
|
||||
@@ -315,6 +331,7 @@ impl AuthActivity {
|
||||
fn file_transfer_protocol_input_mask(protocol: FileTransferProtocol) -> InputMask {
|
||||
match protocol {
|
||||
FileTransferProtocol::AwsS3 => InputMask::AwsS3,
|
||||
FileTransferProtocol::GoogleCloudStorage => InputMask::Gcs,
|
||||
FileTransferProtocol::Ftp(_)
|
||||
| FileTransferProtocol::Scp
|
||||
| FileTransferProtocol::Sftp => InputMask::Generic,
|
||||
@@ -411,3 +428,20 @@ impl Activity for AuthActivity {
|
||||
self.context.take()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_set_configured_remote_protocol() {
|
||||
let mut activity = AuthActivity::new(Duration::ZERO);
|
||||
|
||||
activity.set_remote_protocol(FileTransferProtocol::GoogleCloudStorage);
|
||||
|
||||
assert_eq!(
|
||||
activity.remote_protocol,
|
||||
FileTransferProtocol::GoogleCloudStorage
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
use super::{AuthActivity, FileTransferParams, FormTab, HostBridgeProtocol};
|
||||
use crate::filetransfer::HostBridgeParams;
|
||||
use crate::filetransfer::params::{
|
||||
AwsS3Params, GenericProtocolParams, KubeProtocolParams, ProtocolParams, SmbParams,
|
||||
WebDAVProtocolParams,
|
||||
AwsS3Params, DEFAULT_GCS_ENDPOINT, GenericProtocolParams, GoogleCloudStorageParams,
|
||||
KubeProtocolParams, ProtocolParams, SmbParams, WebDAVProtocolParams,
|
||||
};
|
||||
|
||||
impl AuthActivity {
|
||||
@@ -201,6 +201,9 @@ impl AuthActivity {
|
||||
ProtocolParams::AwsS3(params) => {
|
||||
self.load_bookmark_s3_into_gui(FormTab::HostBridge, params)
|
||||
}
|
||||
ProtocolParams::GoogleCloudStorage(params) => {
|
||||
self.load_bookmark_gcs_into_gui(FormTab::HostBridge, params)
|
||||
}
|
||||
ProtocolParams::Kube(params) => {
|
||||
self.load_bookmark_kube_into_gui(FormTab::HostBridge, params)
|
||||
}
|
||||
@@ -240,6 +243,9 @@ impl AuthActivity {
|
||||
ProtocolParams::AwsS3(params) => {
|
||||
self.load_bookmark_s3_into_gui(FormTab::Remote, params)
|
||||
}
|
||||
ProtocolParams::GoogleCloudStorage(params) => {
|
||||
self.load_bookmark_gcs_into_gui(FormTab::Remote, params)
|
||||
}
|
||||
ProtocolParams::Kube(params) => {
|
||||
self.load_bookmark_kube_into_gui(FormTab::Remote, params)
|
||||
}
|
||||
@@ -276,6 +282,22 @@ impl AuthActivity {
|
||||
self.mount_s3_new_path_style(form_tab, params.new_path_style);
|
||||
}
|
||||
|
||||
fn load_bookmark_gcs_into_gui(&mut self, form_tab: FormTab, params: GoogleCloudStorageParams) {
|
||||
self.mount_gcs_bucket(form_tab, ¶ms.bucket_name);
|
||||
self.mount_gcs_endpoint(
|
||||
form_tab,
|
||||
if params.endpoint.is_empty() {
|
||||
DEFAULT_GCS_ENDPOINT
|
||||
} else {
|
||||
¶ms.endpoint
|
||||
},
|
||||
);
|
||||
self.mount_gcs_service_account_key(
|
||||
form_tab,
|
||||
params.service_account_key.as_deref().unwrap_or(""),
|
||||
);
|
||||
}
|
||||
|
||||
fn load_bookmark_kube_into_gui(&mut self, form_tab: FormTab, params: KubeProtocolParams) {
|
||||
self.mount_kube_cluster_url(form_tab, params.cluster_url.as_deref().unwrap_or(""));
|
||||
self.mount_kube_namespace(form_tab, params.namespace.as_deref().unwrap_or(""));
|
||||
|
||||
@@ -16,12 +16,12 @@ pub use bookmarks::{
|
||||
#[cfg(posix)]
|
||||
pub use form::InputSmbWorkgroup;
|
||||
pub use form::{
|
||||
HostBridgeProtocolRadio, InputAddress, InputKubeClientCert, InputKubeClientKey,
|
||||
InputKubeClusterUrl, InputKubeNamespace, InputKubeUsername, InputLocalDirectory, InputPassword,
|
||||
InputPort, InputRemoteDirectory, InputS3AccessKey, InputS3Bucket, InputS3Endpoint,
|
||||
InputS3Profile, InputS3Region, InputS3SecretAccessKey, InputS3SecurityToken,
|
||||
InputS3SessionToken, InputSmbShare, InputUsername, InputWebDAVUri, RadioS3NewPathStyle,
|
||||
RemoteProtocolRadio,
|
||||
HostBridgeProtocolRadio, InputAddress, InputGcsBucket, InputGcsEndpoint,
|
||||
InputGcsServiceAccountKey, InputKubeClientCert, InputKubeClientKey, InputKubeClusterUrl,
|
||||
InputKubeNamespace, InputKubeUsername, InputLocalDirectory, InputPassword, InputPort,
|
||||
InputRemoteDirectory, InputS3AccessKey, InputS3Bucket, InputS3Endpoint, InputS3Profile,
|
||||
InputS3Region, InputS3SecretAccessKey, InputS3SecurityToken, InputS3SessionToken,
|
||||
InputSmbShare, InputUsername, InputWebDAVUri, RadioS3NewPathStyle, RemoteProtocolRadio,
|
||||
};
|
||||
pub use popup::{
|
||||
ErrorPopup, InfoPopup, InstallUpdatePopup, Keybindings, QuitPopup, ReleaseNotes, WaitPopup,
|
||||
|
||||
@@ -13,14 +13,18 @@ use tuirealm::props::{
|
||||
use super::{FileTransferProtocol, FormMsg, Msg, UiMsg};
|
||||
use crate::ui::activities::auth::{
|
||||
FormTab, HOST_BRIDGE_RADIO_PROTOCOL_FTP, HOST_BRIDGE_RADIO_PROTOCOL_FTPS,
|
||||
HOST_BRIDGE_RADIO_PROTOCOL_KUBE, HOST_BRIDGE_RADIO_PROTOCOL_LOCALHOST,
|
||||
HOST_BRIDGE_RADIO_PROTOCOL_S3, HOST_BRIDGE_RADIO_PROTOCOL_SCP, HOST_BRIDGE_RADIO_PROTOCOL_SFTP,
|
||||
HOST_BRIDGE_RADIO_PROTOCOL_GCS, HOST_BRIDGE_RADIO_PROTOCOL_KUBE,
|
||||
HOST_BRIDGE_RADIO_PROTOCOL_LOCALHOST, HOST_BRIDGE_RADIO_PROTOCOL_S3,
|
||||
HOST_BRIDGE_RADIO_PROTOCOL_SCP, HOST_BRIDGE_RADIO_PROTOCOL_SFTP,
|
||||
HOST_BRIDGE_RADIO_PROTOCOL_SMB, HOST_BRIDGE_RADIO_PROTOCOL_WEBDAV, HostBridgeProtocol,
|
||||
REMOTE_RADIO_PROTOCOL_FTP, REMOTE_RADIO_PROTOCOL_FTPS, REMOTE_RADIO_PROTOCOL_KUBE,
|
||||
REMOTE_RADIO_PROTOCOL_S3, REMOTE_RADIO_PROTOCOL_SCP, REMOTE_RADIO_PROTOCOL_SFTP,
|
||||
REMOTE_RADIO_PROTOCOL_SMB, REMOTE_RADIO_PROTOCOL_WEBDAV, UiAuthFormMsg,
|
||||
REMOTE_RADIO_PROTOCOL_FTP, REMOTE_RADIO_PROTOCOL_FTPS, REMOTE_RADIO_PROTOCOL_GCS,
|
||||
REMOTE_RADIO_PROTOCOL_KUBE, REMOTE_RADIO_PROTOCOL_S3, REMOTE_RADIO_PROTOCOL_SCP,
|
||||
REMOTE_RADIO_PROTOCOL_SFTP, REMOTE_RADIO_PROTOCOL_SMB, REMOTE_RADIO_PROTOCOL_WEBDAV,
|
||||
UiAuthFormMsg,
|
||||
};
|
||||
|
||||
#[path = "form/gcs.rs"]
|
||||
mod gcs;
|
||||
#[path = "form/generic.rs"]
|
||||
mod generic;
|
||||
#[path = "form/kube.rs"]
|
||||
@@ -36,6 +40,7 @@ mod smb;
|
||||
#[path = "form/webdav.rs"]
|
||||
mod webdav;
|
||||
|
||||
pub use gcs::{InputGcsBucket, InputGcsEndpoint, InputGcsServiceAccountKey};
|
||||
pub use generic::{InputAddress, InputPassword, InputPort, InputUsername};
|
||||
pub use kube::{
|
||||
InputKubeClientCert, InputKubeClientKey, InputKubeClusterUrl, InputKubeNamespace,
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
//! ## Google Cloud Storage Form
|
||||
//!
|
||||
//! Input components for Google Cloud Storage authentication parameters.
|
||||
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct InputGcsBucket {
|
||||
component: Input,
|
||||
form_tab: FormTab,
|
||||
}
|
||||
|
||||
impl InputGcsBucket {
|
||||
pub fn new(bucket: &str, form_tab: FormTab, color: Color) -> Self {
|
||||
Self {
|
||||
component: Input::default()
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.placeholder(tuirealm::props::SpanStatic::styled(
|
||||
"my-bucket",
|
||||
Style::default().fg(Color::Rgb(128, 128, 128)),
|
||||
))
|
||||
.title(Title::from("Bucket").alignment(HorizontalAlignment::Left))
|
||||
.input_type(InputType::Text)
|
||||
.value(bucket),
|
||||
form_tab,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppComponent<Msg, NoUserEvent> for InputGcsBucket {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
let (on_key_down, on_key_up) = match self.form_tab {
|
||||
FormTab::Remote => (
|
||||
Msg::Ui(UiMsg::Remote(UiAuthFormMsg::GcsBucketBlurDown)),
|
||||
Msg::Ui(UiMsg::Remote(UiAuthFormMsg::GcsBucketBlurUp)),
|
||||
),
|
||||
FormTab::HostBridge => (
|
||||
Msg::Ui(UiMsg::HostBridge(UiAuthFormMsg::GcsBucketBlurDown)),
|
||||
Msg::Ui(UiMsg::HostBridge(UiAuthFormMsg::GcsBucketBlurUp)),
|
||||
),
|
||||
};
|
||||
let form_tab = self.form_tab;
|
||||
handle_input_ev(self, ev, on_key_down, on_key_up, form_tab)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct InputGcsEndpoint {
|
||||
component: Input,
|
||||
form_tab: FormTab,
|
||||
}
|
||||
|
||||
impl InputGcsEndpoint {
|
||||
pub fn new(endpoint: &str, form_tab: FormTab, color: Color) -> Self {
|
||||
Self {
|
||||
component: Input::default()
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.placeholder(tuirealm::props::SpanStatic::styled(
|
||||
"https://storage.googleapis.com",
|
||||
Style::default().fg(Color::Rgb(128, 128, 128)),
|
||||
))
|
||||
.title(Title::from("Endpoint").alignment(HorizontalAlignment::Left))
|
||||
.input_type(InputType::Text)
|
||||
.value(endpoint),
|
||||
form_tab,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppComponent<Msg, NoUserEvent> for InputGcsEndpoint {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
let (on_key_down, on_key_up) = match self.form_tab {
|
||||
FormTab::Remote => (
|
||||
Msg::Ui(UiMsg::Remote(UiAuthFormMsg::GcsEndpointBlurDown)),
|
||||
Msg::Ui(UiMsg::Remote(UiAuthFormMsg::GcsEndpointBlurUp)),
|
||||
),
|
||||
FormTab::HostBridge => (
|
||||
Msg::Ui(UiMsg::HostBridge(UiAuthFormMsg::GcsEndpointBlurDown)),
|
||||
Msg::Ui(UiMsg::HostBridge(UiAuthFormMsg::GcsEndpointBlurUp)),
|
||||
),
|
||||
};
|
||||
let form_tab = self.form_tab;
|
||||
handle_input_ev(self, ev, on_key_down, on_key_up, form_tab)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct InputGcsServiceAccountKey {
|
||||
component: Input,
|
||||
form_tab: FormTab,
|
||||
}
|
||||
|
||||
impl InputGcsServiceAccountKey {
|
||||
pub fn new(path: &str, form_tab: FormTab, color: Color) -> Self {
|
||||
Self {
|
||||
component: Input::default()
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.placeholder(tuirealm::props::SpanStatic::styled(
|
||||
"Optional service-account JSON path",
|
||||
Style::default().fg(Color::Rgb(128, 128, 128)),
|
||||
))
|
||||
.title(Title::from("Service account JSON").alignment(HorizontalAlignment::Left))
|
||||
.input_type(InputType::Text)
|
||||
.value(path),
|
||||
form_tab,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppComponent<Msg, NoUserEvent> for InputGcsServiceAccountKey {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
let (on_key_down, on_key_up) = match self.form_tab {
|
||||
FormTab::Remote => (
|
||||
Msg::Ui(UiMsg::Remote(UiAuthFormMsg::GcsServiceAccountKeyBlurDown)),
|
||||
Msg::Ui(UiMsg::Remote(UiAuthFormMsg::GcsServiceAccountKeyBlurUp)),
|
||||
),
|
||||
FormTab::HostBridge => (
|
||||
Msg::Ui(UiMsg::HostBridge(
|
||||
UiAuthFormMsg::GcsServiceAccountKeyBlurDown,
|
||||
)),
|
||||
Msg::Ui(UiMsg::HostBridge(UiAuthFormMsg::GcsServiceAccountKeyBlurUp)),
|
||||
),
|
||||
};
|
||||
let form_tab = self.form_tab;
|
||||
handle_input_ev(self, ev, on_key_down, on_key_up, form_tab)
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,12 @@ impl RemoteProtocolRadio {
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.choices(if cfg!(smb) {
|
||||
vec!["SFTP", "SCP", "FTP", "FTPS", "S3", "Kube", "WebDAV", "SMB"].into_iter()
|
||||
vec![
|
||||
"SFTP", "SCP", "FTP", "FTPS", "S3", "GCS", "Kube", "WebDAV", "SMB",
|
||||
]
|
||||
.into_iter()
|
||||
} else {
|
||||
vec!["SFTP", "SCP", "FTP", "FTPS", "S3", "Kube", "WebDAV"].into_iter()
|
||||
vec!["SFTP", "SCP", "FTP", "FTPS", "S3", "GCS", "Kube", "WebDAV"].into_iter()
|
||||
})
|
||||
.rewind(true)
|
||||
.title(Title::from("Protocol").alignment(HorizontalAlignment::Left))
|
||||
@@ -44,6 +47,7 @@ impl RemoteProtocolRadio {
|
||||
REMOTE_RADIO_PROTOCOL_FTP => FileTransferProtocol::Ftp(false),
|
||||
REMOTE_RADIO_PROTOCOL_FTPS => FileTransferProtocol::Ftp(true),
|
||||
REMOTE_RADIO_PROTOCOL_S3 => FileTransferProtocol::AwsS3,
|
||||
REMOTE_RADIO_PROTOCOL_GCS => FileTransferProtocol::GoogleCloudStorage,
|
||||
REMOTE_RADIO_PROTOCOL_SMB => FileTransferProtocol::Smb,
|
||||
REMOTE_RADIO_PROTOCOL_KUBE => FileTransferProtocol::Kube,
|
||||
REMOTE_RADIO_PROTOCOL_WEBDAV => FileTransferProtocol::WebDAV,
|
||||
@@ -58,6 +62,7 @@ impl RemoteProtocolRadio {
|
||||
FileTransferProtocol::Ftp(false) => REMOTE_RADIO_PROTOCOL_FTP,
|
||||
FileTransferProtocol::Ftp(true) => REMOTE_RADIO_PROTOCOL_FTPS,
|
||||
FileTransferProtocol::AwsS3 => REMOTE_RADIO_PROTOCOL_S3,
|
||||
FileTransferProtocol::GoogleCloudStorage => REMOTE_RADIO_PROTOCOL_GCS,
|
||||
FileTransferProtocol::Kube => REMOTE_RADIO_PROTOCOL_KUBE,
|
||||
FileTransferProtocol::Smb => REMOTE_RADIO_PROTOCOL_SMB,
|
||||
FileTransferProtocol::WebDAV => REMOTE_RADIO_PROTOCOL_WEBDAV,
|
||||
@@ -128,6 +133,7 @@ impl HostBridgeProtocolRadio {
|
||||
"FTP",
|
||||
"FTPS",
|
||||
"S3",
|
||||
"GCS",
|
||||
"Kube",
|
||||
"WebDAV",
|
||||
"SMB",
|
||||
@@ -141,6 +147,7 @@ impl HostBridgeProtocolRadio {
|
||||
"FTP",
|
||||
"FTPS",
|
||||
"S3",
|
||||
"GCS",
|
||||
"Kube",
|
||||
"WebDAV",
|
||||
]
|
||||
@@ -168,6 +175,9 @@ impl HostBridgeProtocolRadio {
|
||||
HostBridgeProtocol::Remote(FileTransferProtocol::AwsS3) => {
|
||||
HOST_BRIDGE_RADIO_PROTOCOL_S3
|
||||
}
|
||||
HostBridgeProtocol::Remote(FileTransferProtocol::GoogleCloudStorage) => {
|
||||
HOST_BRIDGE_RADIO_PROTOCOL_GCS
|
||||
}
|
||||
HostBridgeProtocol::Remote(FileTransferProtocol::Smb) => HOST_BRIDGE_RADIO_PROTOCOL_SMB,
|
||||
HostBridgeProtocol::Remote(FileTransferProtocol::Kube) => {
|
||||
HOST_BRIDGE_RADIO_PROTOCOL_KUBE
|
||||
@@ -194,6 +204,9 @@ impl HostBridgeProtocolRadio {
|
||||
HOST_BRIDGE_RADIO_PROTOCOL_S3 => {
|
||||
HostBridgeProtocol::Remote(FileTransferProtocol::AwsS3)
|
||||
}
|
||||
HOST_BRIDGE_RADIO_PROTOCOL_GCS => {
|
||||
HostBridgeProtocol::Remote(FileTransferProtocol::GoogleCloudStorage)
|
||||
}
|
||||
HOST_BRIDGE_RADIO_PROTOCOL_SMB => HostBridgeProtocol::Remote(FileTransferProtocol::Smb),
|
||||
HOST_BRIDGE_RADIO_PROTOCOL_KUBE => {
|
||||
HostBridgeProtocol::Remote(FileTransferProtocol::Kube)
|
||||
|
||||
@@ -17,6 +17,7 @@ impl AuthActivity {
|
||||
FileTransferProtocol::Sftp | FileTransferProtocol::Scp => 22,
|
||||
FileTransferProtocol::Ftp(_) => 21,
|
||||
FileTransferProtocol::AwsS3 => 22, // Doesn't matter, since not used
|
||||
FileTransferProtocol::GoogleCloudStorage => 22, // Doesn't matter, since not used
|
||||
FileTransferProtocol::Kube => 22, // Doesn't matter, since not used
|
||||
FileTransferProtocol::Smb => 445,
|
||||
FileTransferProtocol::WebDAV => 80, // Doesn't matter, since not used
|
||||
@@ -45,6 +46,9 @@ impl AuthActivity {
|
||||
HostBridgeProtocol::Remote(remote) => {
|
||||
let transfer_params = match remote {
|
||||
FileTransferProtocol::AwsS3 => self.collect_s3_host_params(FormTab::HostBridge),
|
||||
FileTransferProtocol::GoogleCloudStorage => {
|
||||
self.collect_gcs_host_params(FormTab::HostBridge)
|
||||
}
|
||||
FileTransferProtocol::Kube => {
|
||||
self.collect_kube_host_params(FormTab::HostBridge)
|
||||
}
|
||||
@@ -71,6 +75,9 @@ impl AuthActivity {
|
||||
pub(super) fn collect_remote_host_params(&self) -> Result<FileTransferParams, &'static str> {
|
||||
match self.remote_protocol {
|
||||
FileTransferProtocol::AwsS3 => self.collect_s3_host_params(FormTab::Remote),
|
||||
FileTransferProtocol::GoogleCloudStorage => {
|
||||
self.collect_gcs_host_params(FormTab::Remote)
|
||||
}
|
||||
FileTransferProtocol::Kube => self.collect_kube_host_params(FormTab::Remote),
|
||||
FileTransferProtocol::Smb => self.collect_smb_host_params(FormTab::Remote),
|
||||
FileTransferProtocol::Ftp(_)
|
||||
@@ -136,6 +143,26 @@ impl AuthActivity {
|
||||
})
|
||||
}
|
||||
|
||||
/// Get input values from fields or return an error if fields are invalid to work as GCS.
|
||||
pub(super) fn collect_gcs_host_params(
|
||||
&self,
|
||||
form_tab: FormTab,
|
||||
) -> Result<FileTransferParams, &'static str> {
|
||||
let params = self.get_gcs_params_input(form_tab);
|
||||
if params.bucket_name.is_empty() {
|
||||
return Err("Invalid bucket");
|
||||
}
|
||||
if params.endpoint.is_empty() {
|
||||
return Err("Invalid endpoint");
|
||||
}
|
||||
Ok(FileTransferParams {
|
||||
protocol: FileTransferProtocol::GoogleCloudStorage,
|
||||
params: ProtocolParams::GoogleCloudStorage(params),
|
||||
local_path: self.get_input_local_directory(form_tab),
|
||||
remote_path: self.get_input_remote_directory(form_tab),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get input values from fields or return an error if fields are invalid to work as aws s3
|
||||
pub(super) fn collect_kube_host_params(
|
||||
&self,
|
||||
|
||||
@@ -86,6 +86,7 @@ impl AuthActivity {
|
||||
InputMask::Generic => &Id::Remote(AuthFormId::Password),
|
||||
InputMask::Smb => &Id::Remote(AuthFormId::Password),
|
||||
InputMask::AwsS3 => &Id::Remote(AuthFormId::S3Bucket),
|
||||
InputMask::Gcs => &Id::Remote(AuthFormId::GcsBucket),
|
||||
InputMask::Kube => &Id::Remote(AuthFormId::KubeNamespace),
|
||||
InputMask::WebDAV => &Id::Remote(AuthFormId::Password),
|
||||
},
|
||||
@@ -94,6 +95,7 @@ impl AuthActivity {
|
||||
InputMask::Generic => &Id::HostBridge(AuthFormId::Password),
|
||||
InputMask::Smb => &Id::HostBridge(AuthFormId::Password),
|
||||
InputMask::AwsS3 => &Id::HostBridge(AuthFormId::S3Bucket),
|
||||
InputMask::Gcs => &Id::HostBridge(AuthFormId::GcsBucket),
|
||||
InputMask::Kube => &Id::HostBridge(AuthFormId::KubeNamespace),
|
||||
InputMask::WebDAV => &Id::HostBridge(AuthFormId::Password),
|
||||
},
|
||||
@@ -112,6 +114,7 @@ impl AuthActivity {
|
||||
InputMask::Generic => &Id::Remote(AuthFormId::Password),
|
||||
InputMask::Smb => &Id::Remote(AuthFormId::Password),
|
||||
InputMask::AwsS3 => &Id::Remote(AuthFormId::S3Bucket),
|
||||
InputMask::Gcs => &Id::Remote(AuthFormId::GcsBucket),
|
||||
InputMask::Kube => &Id::Remote(AuthFormId::KubeNamespace),
|
||||
InputMask::WebDAV => &Id::Remote(AuthFormId::Password),
|
||||
},
|
||||
@@ -120,6 +123,7 @@ impl AuthActivity {
|
||||
InputMask::Generic => &Id::HostBridge(AuthFormId::Password),
|
||||
InputMask::Smb => &Id::HostBridge(AuthFormId::Password),
|
||||
InputMask::AwsS3 => &Id::HostBridge(AuthFormId::S3Bucket),
|
||||
InputMask::Gcs => &Id::HostBridge(AuthFormId::GcsBucket),
|
||||
InputMask::Kube => &Id::HostBridge(AuthFormId::KubeNamespace),
|
||||
InputMask::WebDAV => &Id::HostBridge(AuthFormId::Password),
|
||||
},
|
||||
@@ -280,6 +284,7 @@ impl AuthActivity {
|
||||
#[cfg(win)]
|
||||
InputMask::Smb => Id::HostBridge(AuthFormId::RemoteDirectory),
|
||||
InputMask::AwsS3 => unreachable!("this shouldn't happen (password on s3)"),
|
||||
InputMask::Gcs => unreachable!("this shouldn't happen (password on gcs)"),
|
||||
InputMask::Kube => unreachable!("this shouldn't happen (password on kube)"),
|
||||
InputMask::WebDAV => Id::HostBridge(AuthFormId::RemoteDirectory),
|
||||
};
|
||||
@@ -294,6 +299,7 @@ impl AuthActivity {
|
||||
InputMask::Smb => Id::HostBridge(AuthFormId::SmbShare),
|
||||
InputMask::Localhost
|
||||
| InputMask::AwsS3
|
||||
| InputMask::Gcs
|
||||
| InputMask::Kube
|
||||
| InputMask::WebDAV => {
|
||||
unreachable!("this shouldn't happen (port on s3/kube/webdav)")
|
||||
@@ -310,6 +316,7 @@ impl AuthActivity {
|
||||
InputMask::Generic => Id::HostBridge(AuthFormId::Address),
|
||||
InputMask::Smb => Id::HostBridge(AuthFormId::Address),
|
||||
InputMask::AwsS3 => Id::HostBridge(AuthFormId::S3Bucket),
|
||||
InputMask::Gcs => Id::HostBridge(AuthFormId::GcsBucket),
|
||||
InputMask::Kube => Id::HostBridge(AuthFormId::KubeNamespace),
|
||||
InputMask::WebDAV => Id::HostBridge(AuthFormId::WebDAVUri),
|
||||
};
|
||||
@@ -331,6 +338,7 @@ impl AuthActivity {
|
||||
InputMask::Smb => Id::HostBridge(AuthFormId::Password),
|
||||
InputMask::Kube => Id::HostBridge(AuthFormId::KubeClientKey),
|
||||
InputMask::AwsS3 => Id::HostBridge(AuthFormId::S3NewPathStyle),
|
||||
InputMask::Gcs => Id::HostBridge(AuthFormId::GcsServiceAccountKey),
|
||||
InputMask::WebDAV => Id::HostBridge(AuthFormId::Password),
|
||||
};
|
||||
self.activate_component(id);
|
||||
@@ -389,6 +397,24 @@ impl AuthActivity {
|
||||
UiAuthFormMsg::S3NewPathStyleBlurUp => {
|
||||
self.activate_component(Id::HostBridge(AuthFormId::S3SessionToken))
|
||||
}
|
||||
UiAuthFormMsg::GcsBucketBlurDown => {
|
||||
self.activate_component(Id::HostBridge(AuthFormId::GcsEndpoint))
|
||||
}
|
||||
UiAuthFormMsg::GcsBucketBlurUp => {
|
||||
self.activate_component(Id::HostBridge(AuthFormId::Protocol))
|
||||
}
|
||||
UiAuthFormMsg::GcsEndpointBlurDown => {
|
||||
self.activate_component(Id::HostBridge(AuthFormId::GcsServiceAccountKey))
|
||||
}
|
||||
UiAuthFormMsg::GcsEndpointBlurUp => {
|
||||
self.activate_component(Id::HostBridge(AuthFormId::GcsBucket))
|
||||
}
|
||||
UiAuthFormMsg::GcsServiceAccountKeyBlurDown => {
|
||||
self.activate_component(Id::HostBridge(AuthFormId::RemoteDirectory))
|
||||
}
|
||||
UiAuthFormMsg::GcsServiceAccountKeyBlurUp => {
|
||||
self.activate_component(Id::HostBridge(AuthFormId::GcsEndpoint))
|
||||
}
|
||||
UiAuthFormMsg::KubeClientCertBlurDown => {
|
||||
self.activate_component(Id::HostBridge(AuthFormId::KubeClientKey))
|
||||
}
|
||||
@@ -448,6 +474,7 @@ impl AuthActivity {
|
||||
InputMask::Smb => Id::HostBridge(AuthFormId::SmbShare),
|
||||
InputMask::Kube => unreachable!("this shouldn't happen (username on kube)"),
|
||||
InputMask::AwsS3 => unreachable!("this shouldn't happen (username on s3)"),
|
||||
InputMask::Gcs => unreachable!("this shouldn't happen (username on gcs)"),
|
||||
InputMask::WebDAV => Id::HostBridge(AuthFormId::WebDAVUri),
|
||||
};
|
||||
self.activate_component(id);
|
||||
@@ -496,6 +523,7 @@ impl AuthActivity {
|
||||
#[cfg(win)]
|
||||
InputMask::Smb => Id::Remote(AuthFormId::RemoteDirectory),
|
||||
InputMask::AwsS3 => unreachable!("this shouldn't happen (password on s3)"),
|
||||
InputMask::Gcs => unreachable!("this shouldn't happen (password on gcs)"),
|
||||
InputMask::Kube => unreachable!("this shouldn't happen (password on kube)"),
|
||||
InputMask::WebDAV => Id::Remote(AuthFormId::RemoteDirectory),
|
||||
};
|
||||
@@ -510,6 +538,7 @@ impl AuthActivity {
|
||||
InputMask::Smb => Id::Remote(AuthFormId::SmbShare),
|
||||
InputMask::Localhost
|
||||
| InputMask::AwsS3
|
||||
| InputMask::Gcs
|
||||
| InputMask::Kube
|
||||
| InputMask::WebDAV => {
|
||||
unreachable!("this shouldn't happen (port on s3/kube/webdav)")
|
||||
@@ -526,6 +555,7 @@ impl AuthActivity {
|
||||
InputMask::Generic => Id::Remote(AuthFormId::Address),
|
||||
InputMask::Smb => Id::Remote(AuthFormId::Address),
|
||||
InputMask::AwsS3 => Id::Remote(AuthFormId::S3Bucket),
|
||||
InputMask::Gcs => Id::Remote(AuthFormId::GcsBucket),
|
||||
InputMask::Kube => Id::Remote(AuthFormId::KubeNamespace),
|
||||
InputMask::WebDAV => Id::Remote(AuthFormId::WebDAVUri),
|
||||
};
|
||||
@@ -547,6 +577,7 @@ impl AuthActivity {
|
||||
InputMask::Smb => Id::Remote(AuthFormId::Password),
|
||||
InputMask::Kube => Id::Remote(AuthFormId::KubeClientKey),
|
||||
InputMask::AwsS3 => Id::Remote(AuthFormId::S3NewPathStyle),
|
||||
InputMask::Gcs => Id::Remote(AuthFormId::GcsServiceAccountKey),
|
||||
InputMask::WebDAV => Id::Remote(AuthFormId::Password),
|
||||
};
|
||||
self.activate_component(id);
|
||||
@@ -605,6 +636,24 @@ impl AuthActivity {
|
||||
UiAuthFormMsg::S3NewPathStyleBlurUp => {
|
||||
self.activate_component(Id::Remote(AuthFormId::S3SessionToken))
|
||||
}
|
||||
UiAuthFormMsg::GcsBucketBlurDown => {
|
||||
self.activate_component(Id::Remote(AuthFormId::GcsEndpoint))
|
||||
}
|
||||
UiAuthFormMsg::GcsBucketBlurUp => {
|
||||
self.activate_component(Id::Remote(AuthFormId::Protocol))
|
||||
}
|
||||
UiAuthFormMsg::GcsEndpointBlurDown => {
|
||||
self.activate_component(Id::Remote(AuthFormId::GcsServiceAccountKey))
|
||||
}
|
||||
UiAuthFormMsg::GcsEndpointBlurUp => {
|
||||
self.activate_component(Id::Remote(AuthFormId::GcsBucket))
|
||||
}
|
||||
UiAuthFormMsg::GcsServiceAccountKeyBlurDown => {
|
||||
self.activate_component(Id::Remote(AuthFormId::RemoteDirectory))
|
||||
}
|
||||
UiAuthFormMsg::GcsServiceAccountKeyBlurUp => {
|
||||
self.activate_component(Id::Remote(AuthFormId::GcsEndpoint))
|
||||
}
|
||||
UiAuthFormMsg::KubeClientCertBlurDown => {
|
||||
self.activate_component(Id::Remote(AuthFormId::KubeClientKey))
|
||||
}
|
||||
@@ -664,6 +713,7 @@ impl AuthActivity {
|
||||
InputMask::Smb => Id::Remote(AuthFormId::SmbShare),
|
||||
InputMask::Kube => unreachable!("this shouldn't happen (username on kube)"),
|
||||
InputMask::AwsS3 => unreachable!("this shouldn't happen (username on s3)"),
|
||||
InputMask::Gcs => unreachable!("this shouldn't happen (username on gcs)"),
|
||||
InputMask::WebDAV => Id::Remote(AuthFormId::WebDAVUri),
|
||||
};
|
||||
self.activate_component(id);
|
||||
|
||||
@@ -11,6 +11,7 @@ use super::{
|
||||
AuthActivity, AuthFormId, Context, FileTransferProtocol, FormTab, HostBridgeProtocol, Id,
|
||||
InputMask, components,
|
||||
};
|
||||
use crate::filetransfer::params::DEFAULT_GCS_ENDPOINT;
|
||||
use crate::utils::ui::{Popup, Size};
|
||||
|
||||
#[path = "view/mounting.rs"]
|
||||
@@ -52,6 +53,9 @@ impl AuthActivity {
|
||||
self.mount_port(FormTab::HostBridge, 22);
|
||||
self.mount_username(FormTab::HostBridge, "");
|
||||
self.mount_password(FormTab::HostBridge, "");
|
||||
self.mount_gcs_bucket(FormTab::HostBridge, "");
|
||||
self.mount_gcs_endpoint(FormTab::HostBridge, DEFAULT_GCS_ENDPOINT);
|
||||
self.mount_gcs_service_account_key(FormTab::HostBridge, "");
|
||||
self.mount_s3_bucket(FormTab::HostBridge, "");
|
||||
self.mount_s3_profile(FormTab::HostBridge, "");
|
||||
self.mount_s3_region(FormTab::HostBridge, "");
|
||||
@@ -72,6 +76,7 @@ impl AuthActivity {
|
||||
self.mount_webdav_uri(FormTab::HostBridge, "");
|
||||
|
||||
let remote_default_protocol = self.context().config().get_default_protocol();
|
||||
self.set_remote_protocol(remote_default_protocol);
|
||||
self.mount_remote_protocol(remote_default_protocol);
|
||||
self.mount_remote_directory(FormTab::Remote, "");
|
||||
self.mount_local_directory(FormTab::Remote, "");
|
||||
@@ -82,6 +87,9 @@ impl AuthActivity {
|
||||
);
|
||||
self.mount_username(FormTab::Remote, "");
|
||||
self.mount_password(FormTab::Remote, "");
|
||||
self.mount_gcs_bucket(FormTab::Remote, "");
|
||||
self.mount_gcs_endpoint(FormTab::Remote, DEFAULT_GCS_ENDPOINT);
|
||||
self.mount_gcs_service_account_key(FormTab::Remote, "");
|
||||
self.mount_s3_bucket(FormTab::Remote, "");
|
||||
self.mount_s3_profile(FormTab::Remote, "");
|
||||
self.mount_s3_region(FormTab::Remote, "");
|
||||
@@ -264,6 +272,7 @@ impl AuthActivity {
|
||||
.split(protocol_and_mask_chunks[1]);
|
||||
match self.host_bridge_input_mask() {
|
||||
InputMask::AwsS3 => self.render_view_ids(f, input_mask, self.get_host_bridge_s3_view()),
|
||||
InputMask::Gcs => self.render_view_ids(f, input_mask, self.get_host_bridge_gcs_view()),
|
||||
InputMask::Generic => {
|
||||
self.render_view_ids(f, input_mask, self.get_host_bridge_generic_params_view())
|
||||
}
|
||||
@@ -311,6 +320,7 @@ impl AuthActivity {
|
||||
.split(protocol_and_mask_chunks[1]);
|
||||
match self.remote_input_mask() {
|
||||
InputMask::AwsS3 => self.render_view_ids(f, input_mask, self.get_remote_s3_view()),
|
||||
InputMask::Gcs => self.render_view_ids(f, input_mask, self.get_remote_gcs_view()),
|
||||
InputMask::Generic => {
|
||||
self.render_view_ids(f, input_mask, self.get_remote_generic_params_view())
|
||||
}
|
||||
|
||||
@@ -383,6 +383,56 @@ impl AuthActivity {
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::ui::activities::auth) fn mount_gcs_bucket(
|
||||
&mut self,
|
||||
form_tab: FormTab,
|
||||
bucket: &str,
|
||||
) {
|
||||
let color = self.theme().auth_address;
|
||||
let id = Self::form_tab_id(form_tab, AuthFormId::GcsBucket);
|
||||
if let Err(err) = self.app.remount(
|
||||
id,
|
||||
Box::new(components::InputGcsBucket::new(bucket, form_tab, color)),
|
||||
vec![],
|
||||
) {
|
||||
error!("Failed to remount component: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::ui::activities::auth) fn mount_gcs_endpoint(
|
||||
&mut self,
|
||||
form_tab: FormTab,
|
||||
endpoint: &str,
|
||||
) {
|
||||
let color = self.theme().auth_username;
|
||||
let id = Self::form_tab_id(form_tab, AuthFormId::GcsEndpoint);
|
||||
if let Err(err) = self.app.remount(
|
||||
id,
|
||||
Box::new(components::InputGcsEndpoint::new(endpoint, form_tab, color)),
|
||||
vec![],
|
||||
) {
|
||||
error!("Failed to remount component: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::ui::activities::auth) fn mount_gcs_service_account_key(
|
||||
&mut self,
|
||||
form_tab: FormTab,
|
||||
path: &str,
|
||||
) {
|
||||
let color = self.theme().auth_password;
|
||||
let id = Self::form_tab_id(form_tab, AuthFormId::GcsServiceAccountKey);
|
||||
if let Err(err) = self.app.remount(
|
||||
id,
|
||||
Box::new(components::InputGcsServiceAccountKey::new(
|
||||
path, form_tab, color,
|
||||
)),
|
||||
vec![],
|
||||
) {
|
||||
error!("Failed to remount component: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::ui::activities::auth) fn mount_s3_bucket(
|
||||
&mut self,
|
||||
form_tab: FormTab,
|
||||
|
||||
@@ -6,8 +6,8 @@ use tuirealm::state::{State, StateValue};
|
||||
use super::*;
|
||||
use crate::filetransfer::FileTransferParams;
|
||||
use crate::filetransfer::params::{
|
||||
AwsS3Params, GenericProtocolParams, KubeProtocolParams, ProtocolParams, SmbParams,
|
||||
WebDAVProtocolParams,
|
||||
AwsS3Params, GenericProtocolParams, GoogleCloudStorageParams, KubeProtocolParams,
|
||||
ProtocolParams, SmbParams, WebDAVProtocolParams,
|
||||
};
|
||||
|
||||
impl AuthActivity {
|
||||
@@ -48,6 +48,15 @@ impl AuthActivity {
|
||||
.new_path_style(new_path_style)
|
||||
}
|
||||
|
||||
pub(in crate::ui::activities::auth) fn get_gcs_params_input(
|
||||
&self,
|
||||
form_tab: FormTab,
|
||||
) -> GoogleCloudStorageParams {
|
||||
GoogleCloudStorageParams::new(self.get_input_gcs_bucket(form_tab))
|
||||
.endpoint(self.get_input_gcs_endpoint(form_tab))
|
||||
.service_account_key(self.get_input_gcs_service_account_key(form_tab))
|
||||
}
|
||||
|
||||
pub(in crate::ui::activities::auth) fn get_kube_params_input(
|
||||
&self,
|
||||
form_tab: FormTab,
|
||||
@@ -213,6 +222,45 @@ impl AuthActivity {
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::ui::activities::auth) fn get_input_gcs_bucket(
|
||||
&self,
|
||||
form_tab: FormTab,
|
||||
) -> String {
|
||||
match self
|
||||
.app
|
||||
.state(&Self::form_tab_id(form_tab, AuthFormId::GcsBucket))
|
||||
{
|
||||
Ok(State::Single(StateValue::String(value))) => value,
|
||||
_ => String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::ui::activities::auth) fn get_input_gcs_endpoint(
|
||||
&self,
|
||||
form_tab: FormTab,
|
||||
) -> String {
|
||||
match self
|
||||
.app
|
||||
.state(&Self::form_tab_id(form_tab, AuthFormId::GcsEndpoint))
|
||||
{
|
||||
Ok(State::Single(StateValue::String(value))) => value,
|
||||
_ => String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::ui::activities::auth) fn get_input_gcs_service_account_key(
|
||||
&self,
|
||||
form_tab: FormTab,
|
||||
) -> Option<String> {
|
||||
match self.app.state(&Self::form_tab_id(
|
||||
form_tab,
|
||||
AuthFormId::GcsServiceAccountKey,
|
||||
)) {
|
||||
Ok(State::Single(StateValue::String(value))) if !value.is_empty() => Some(value),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::ui::activities::auth) fn get_input_s3_region(
|
||||
&self,
|
||||
form_tab: FormTab,
|
||||
@@ -428,6 +476,7 @@ impl AuthActivity {
|
||||
fn input_mask_size(input_mask: InputMask) -> u16 {
|
||||
match input_mask {
|
||||
InputMask::AwsS3
|
||||
| InputMask::Gcs
|
||||
| InputMask::Generic
|
||||
| InputMask::Kube
|
||||
| InputMask::Smb
|
||||
@@ -446,6 +495,11 @@ impl AuthActivity {
|
||||
|
||||
pub(in crate::ui::activities::auth) fn fmt_recent(b: FileTransferParams) -> String {
|
||||
let protocol = b.protocol.to_string().to_lowercase();
|
||||
let remote_path = b
|
||||
.remote_path
|
||||
.as_ref()
|
||||
.map(|path| format!(" {}", path.display()))
|
||||
.unwrap_or_default();
|
||||
match b.params {
|
||||
ProtocolParams::AwsS3(s3) => {
|
||||
let profile = match s3.profile {
|
||||
@@ -471,6 +525,15 @@ impl AuthActivity {
|
||||
protocol, username, params.address, params.port
|
||||
)
|
||||
}
|
||||
ProtocolParams::GoogleCloudStorage(params) => format!(
|
||||
"{protocol}://{} ({}){remote_path}",
|
||||
params.bucket_name,
|
||||
if params.endpoint.is_empty() {
|
||||
crate::filetransfer::params::DEFAULT_GCS_ENDPOINT
|
||||
} else {
|
||||
params.endpoint.as_str()
|
||||
},
|
||||
),
|
||||
ProtocolParams::Kube(params) => {
|
||||
format!(
|
||||
"{}://{}{}",
|
||||
|
||||
@@ -159,6 +159,40 @@ impl AuthActivity {
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::ui::activities::auth) fn get_host_bridge_gcs_view(&self) -> [Id; 4] {
|
||||
match self.app.focus() {
|
||||
Some(&Id::HostBridge(AuthFormId::LocalDirectory)) => [
|
||||
Id::HostBridge(AuthFormId::GcsEndpoint),
|
||||
Id::HostBridge(AuthFormId::GcsServiceAccountKey),
|
||||
Id::HostBridge(AuthFormId::RemoteDirectory),
|
||||
Id::HostBridge(AuthFormId::LocalDirectory),
|
||||
],
|
||||
_ => [
|
||||
Id::HostBridge(AuthFormId::GcsBucket),
|
||||
Id::HostBridge(AuthFormId::GcsEndpoint),
|
||||
Id::HostBridge(AuthFormId::GcsServiceAccountKey),
|
||||
Id::HostBridge(AuthFormId::RemoteDirectory),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::ui::activities::auth) fn get_remote_gcs_view(&self) -> [Id; 4] {
|
||||
match self.app.focus() {
|
||||
Some(&Id::Remote(AuthFormId::LocalDirectory)) => [
|
||||
Id::Remote(AuthFormId::GcsEndpoint),
|
||||
Id::Remote(AuthFormId::GcsServiceAccountKey),
|
||||
Id::Remote(AuthFormId::RemoteDirectory),
|
||||
Id::Remote(AuthFormId::LocalDirectory),
|
||||
],
|
||||
_ => [
|
||||
Id::Remote(AuthFormId::GcsBucket),
|
||||
Id::Remote(AuthFormId::GcsEndpoint),
|
||||
Id::Remote(AuthFormId::GcsServiceAccountKey),
|
||||
Id::Remote(AuthFormId::RemoteDirectory),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::ui::activities::auth) fn get_host_bridge_kube_view(&self) -> [Id; 4] {
|
||||
match self.app.focus() {
|
||||
Some(&Id::HostBridge(AuthFormId::KubeClientCert)) => [
|
||||
|
||||
@@ -64,6 +64,7 @@ impl FileTransferActivity {
|
||||
match params {
|
||||
ProtocolParams::Generic(params) => params.address.clone(),
|
||||
ProtocolParams::AwsS3(params) => params.bucket_name.clone(),
|
||||
ProtocolParams::GoogleCloudStorage(params) => params.bucket_name.clone(),
|
||||
ProtocolParams::Kube(params) => {
|
||||
params.namespace.clone().unwrap_or("default".to_string())
|
||||
}
|
||||
@@ -93,6 +94,11 @@ impl FileTransferActivity {
|
||||
);
|
||||
format!("Connecting to {}…", params.bucket_name)
|
||||
}
|
||||
ProtocolParams::GoogleCloudStorage(params) => format!(
|
||||
"Connecting to GCS bucket '{bucket}' at {endpoint}…",
|
||||
bucket = params.bucket_name,
|
||||
endpoint = params.endpoint,
|
||||
),
|
||||
ProtocolParams::Kube(params) => {
|
||||
let namespace = params.namespace.as_deref().unwrap_or("default");
|
||||
info!("Client is not connected to remote; connecting to namespace {namespace}",);
|
||||
|
||||
@@ -32,6 +32,10 @@ enum TransferErrorReason {
|
||||
RemoteHostError(HostError),
|
||||
}
|
||||
|
||||
fn handle_remote_finalize_result(result: Result<(), HostError>) -> Result<(), TransferErrorReason> {
|
||||
result.map_err(TransferErrorReason::RemoteHostError)
|
||||
}
|
||||
|
||||
/// Represents the entity to send or receive during a transfer.
|
||||
/// - File: describes an individual `File` to send
|
||||
/// - Any: Can be any kind of `File`, but just one
|
||||
@@ -525,12 +529,7 @@ impl FileTransferActivity {
|
||||
}
|
||||
}
|
||||
// Finalize stream
|
||||
if let Err(err) = self.browser.remote_pane_mut().fs.finalize_write(writer) {
|
||||
self.log(
|
||||
LogLevel::Warn,
|
||||
format!("Could not finalize remote stream: \"{err}\""),
|
||||
);
|
||||
}
|
||||
handle_remote_finalize_result(self.browser.remote_pane_mut().fs.finalize_write(writer))?;
|
||||
// if upload was abrupted, return error
|
||||
if self.transfer.aborted() {
|
||||
return Err(TransferErrorReason::Abrupted);
|
||||
@@ -922,6 +921,24 @@ impl FileTransferActivity {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod finalize_test {
|
||||
use super::*;
|
||||
use crate::host::HostErrorType;
|
||||
|
||||
#[test]
|
||||
fn should_propagate_remote_finalize_error() {
|
||||
let error = HostError::from(HostErrorType::CouldNotCreateFile);
|
||||
|
||||
let result = handle_remote_finalize_result(Err(error));
|
||||
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(TransferErrorReason::RemoteHostError(_))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod worklist_test {
|
||||
use std::time::SystemTime;
|
||||
|
||||
@@ -32,8 +32,9 @@ const RADIO_PROTOCOL_FTP: usize = 2;
|
||||
const RADIO_PROTOCOL_FTPS: usize = 3;
|
||||
const RADIO_PROTOCOL_KUBE: usize = 4;
|
||||
const RADIO_PROTOCOL_S3: usize = 5;
|
||||
const RADIO_PROTOCOL_SMB: usize = 6;
|
||||
const RADIO_PROTOCOL_WEBDAV: usize = 7;
|
||||
const RADIO_PROTOCOL_GCS: usize = 6;
|
||||
const RADIO_PROTOCOL_SMB: usize = 7;
|
||||
const RADIO_PROTOCOL_WEBDAV: usize = 8;
|
||||
|
||||
// -- components
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||
|
||||
@@ -14,8 +14,9 @@ use super::{ConfigMsg, Msg};
|
||||
use crate::explorer::GroupDirs as GroupDirsEnum;
|
||||
use crate::filetransfer::FileTransferProtocol;
|
||||
use crate::ui::activities::setup::{
|
||||
RADIO_PROTOCOL_FTP, RADIO_PROTOCOL_FTPS, RADIO_PROTOCOL_KUBE, RADIO_PROTOCOL_S3,
|
||||
RADIO_PROTOCOL_SCP, RADIO_PROTOCOL_SFTP, RADIO_PROTOCOL_SMB, RADIO_PROTOCOL_WEBDAV,
|
||||
RADIO_PROTOCOL_FTP, RADIO_PROTOCOL_FTPS, RADIO_PROTOCOL_GCS, RADIO_PROTOCOL_KUBE,
|
||||
RADIO_PROTOCOL_S3, RADIO_PROTOCOL_SCP, RADIO_PROTOCOL_SFTP, RADIO_PROTOCOL_SMB,
|
||||
RADIO_PROTOCOL_WEBDAV,
|
||||
};
|
||||
use crate::utils::parser::parse_bytesize;
|
||||
|
||||
@@ -73,7 +74,9 @@ impl DefaultProtocol {
|
||||
.color(Color::Cyan)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.choices(["SFTP", "SCP", "FTP", "FTPS", "Kube", "S3", "SMB", "WebDAV"])
|
||||
.choices([
|
||||
"SFTP", "SCP", "FTP", "FTPS", "Kube", "S3", "GCS", "SMB", "WebDAV",
|
||||
])
|
||||
.foreground(Color::Cyan)
|
||||
.rewind(true)
|
||||
.title(Title::from("Default protocol").alignment(HorizontalAlignment::Left))
|
||||
@@ -84,6 +87,7 @@ impl DefaultProtocol {
|
||||
FileTransferProtocol::Ftp(true) => RADIO_PROTOCOL_FTPS,
|
||||
FileTransferProtocol::Kube => RADIO_PROTOCOL_KUBE,
|
||||
FileTransferProtocol::AwsS3 => RADIO_PROTOCOL_S3,
|
||||
FileTransferProtocol::GoogleCloudStorage => RADIO_PROTOCOL_GCS,
|
||||
FileTransferProtocol::Smb => RADIO_PROTOCOL_SMB,
|
||||
FileTransferProtocol::WebDAV => RADIO_PROTOCOL_WEBDAV,
|
||||
}),
|
||||
|
||||
@@ -12,8 +12,8 @@ use tuirealm::state::{State, StateValue};
|
||||
use tuirealm::terminal::TerminalAdapter;
|
||||
|
||||
use super::{
|
||||
Context, Id, IdCommon, IdConfig, RADIO_PROTOCOL_KUBE, RADIO_PROTOCOL_WEBDAV, SetupActivity,
|
||||
ViewLayout, components,
|
||||
Context, Id, IdCommon, IdConfig, RADIO_PROTOCOL_GCS, RADIO_PROTOCOL_KUBE,
|
||||
RADIO_PROTOCOL_WEBDAV, SetupActivity, ViewLayout, components,
|
||||
};
|
||||
use crate::explorer::GroupDirs;
|
||||
use crate::filetransfer::FileTransferProtocol;
|
||||
@@ -272,6 +272,7 @@ impl SetupActivity {
|
||||
RADIO_PROTOCOL_FTPS => FileTransferProtocol::Ftp(true),
|
||||
RADIO_PROTOCOL_KUBE => FileTransferProtocol::Kube,
|
||||
RADIO_PROTOCOL_S3 => FileTransferProtocol::AwsS3,
|
||||
RADIO_PROTOCOL_GCS => FileTransferProtocol::GoogleCloudStorage,
|
||||
RADIO_PROTOCOL_SMB => FileTransferProtocol::Smb,
|
||||
RADIO_PROTOCOL_WEBDAV => FileTransferProtocol::WebDAV,
|
||||
_ => FileTransferProtocol::Sftp,
|
||||
|
||||
Reference in New Issue
Block a user