ui sync version
This commit is contained in:
16
src/app.rs
16
src/app.rs
@@ -1,18 +1,12 @@
|
||||
use std::io;
|
||||
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
|
||||
use ratatui::{DefaultTerminal, Frame};
|
||||
|
||||
use crate::{
|
||||
session::{connection::ConnectionManager, credential::CredentialManager},
|
||||
view::{home::Home, View},
|
||||
};
|
||||
use crate::view::{home::Home, View};
|
||||
|
||||
pub struct App {
|
||||
running: bool,
|
||||
view: Home,
|
||||
connection_manager: ConnectionManager,
|
||||
credential_manager: CredentialManager,
|
||||
}
|
||||
|
||||
impl Default for App {
|
||||
@@ -20,14 +14,12 @@ impl Default for App {
|
||||
App {
|
||||
running: true,
|
||||
view: Home::default(),
|
||||
connection_manager: ConnectionManager::default(),
|
||||
credential_manager: CredentialManager::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn run(&mut self, terminal: &mut DefaultTerminal) -> io::Result<()> {
|
||||
pub fn run(&mut self, terminal: &mut DefaultTerminal) -> Result<()> {
|
||||
while self.running {
|
||||
terminal.draw(|frame| self.draw(frame))?;
|
||||
self.handle_event()?;
|
||||
@@ -39,7 +31,7 @@ impl App {
|
||||
self.view.draw(frame);
|
||||
}
|
||||
|
||||
fn handle_event(&mut self) -> io::Result<()> {
|
||||
fn handle_event(&mut self) -> Result<()> {
|
||||
match event::read()? {
|
||||
Event::Key(KeyEvent {
|
||||
kind: KeyEventKind::Press,
|
||||
|
||||
59
src/main.rs
59
src/main.rs
@@ -1,13 +1,5 @@
|
||||
use std::{io, path::Path};
|
||||
|
||||
use app::App;
|
||||
use base64::{engine::general_purpose::STANDARD, Engine as _};
|
||||
use session::{
|
||||
connection::{Connection, ConnectionManager, Connections, ShConnection, SshConnection},
|
||||
credential::{Credential, CredentialManager, CredentialType},
|
||||
SessionManager,
|
||||
};
|
||||
use util::file;
|
||||
use color_eyre::Result;
|
||||
|
||||
mod app;
|
||||
mod global;
|
||||
@@ -15,55 +7,10 @@ mod session;
|
||||
mod util;
|
||||
mod view;
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
let mut terminal = ratatui::init();
|
||||
let app_result = App::default().run(&mut terminal);
|
||||
ratatui::restore();
|
||||
app_result
|
||||
}
|
||||
|
||||
// fn main() -> io::Result<()> {
|
||||
// // let sh = ShConnection::new("zsh".to_string(), "/usr/bin/nu".to_string(), None);
|
||||
// // let _ = sh.exec_cmd();
|
||||
// let ssh = SshConnection::new("".to_string(), "myhost.fallen-angle.com".to_string(), 7044, "rainbus".to_string(), "".to_string());
|
||||
// ssh.exec_cmd();
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
/* fn main() -> io::Result<()> {
|
||||
let mut cnm = ConnectionManager::default();
|
||||
let sh = Connections::Sh(ShConnection::new(
|
||||
"zsh".to_string(),
|
||||
"/usr/bin/zsh".to_string(),
|
||||
None,
|
||||
));
|
||||
let ssh_sec = Connections::Ssh(SshConnection::new(
|
||||
"Tencent".to_string(),
|
||||
"myhost.fallen-angle.com".to_string(),
|
||||
22,
|
||||
"rainbus".to_string(),
|
||||
"Server".to_string(),
|
||||
));
|
||||
let ssh_pwd = Connections::Ssh(SshConnection::new(
|
||||
"Aliyun".to_string(),
|
||||
"myweb.fallen-angle.com".to_string(),
|
||||
22,
|
||||
"rainbus".to_string(),
|
||||
"AliPwd".to_string(),
|
||||
));
|
||||
cnm.connections().extend([sh, ssh_sec, ssh_pwd]);
|
||||
let mut crm = CredentialManager::default();
|
||||
let ser_crd = Credential::new(
|
||||
"Server".to_string(),
|
||||
CredentialType::Secret(STANDARD.encode(file::read_file(Path::new("")))),
|
||||
);
|
||||
let pwd_crd = Credential::new(
|
||||
"Alipwd".to_string(),
|
||||
CredentialType::Password("20001001".to_string()),
|
||||
);
|
||||
crm.credentials().extend([ser_crd, pwd_crd]);
|
||||
let sem = SessionManager::new(cnm, crm);
|
||||
// println!("{}", toml::to_string_pretty(&sem).unwrap());
|
||||
file::write_file(&global::CONFIG_DIR.join("tethers.toml"), toml::to_string_pretty(&sem).unwrap().as_str());
|
||||
Ok(())
|
||||
} */
|
||||
|
||||
@@ -1,21 +1,2 @@
|
||||
use connection::ConnectionManager;
|
||||
use credential::CredentialManager;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod connection;
|
||||
pub mod credential;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SessionManager {
|
||||
connection_manager: ConnectionManager,
|
||||
credential_manager: CredentialManager,
|
||||
}
|
||||
|
||||
impl SessionManager {
|
||||
pub fn new(cnm: ConnectionManager, crm: CredentialManager) -> Self {
|
||||
Self {
|
||||
connection_manager: cnm,
|
||||
credential_manager: crm,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
use std::{env, fs, io, path::Path, process::Command};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::util::file;
|
||||
|
||||
use super::credential::{Credential, CredentialType};
|
||||
|
||||
pub trait Connection {
|
||||
fn exec_cmd(&self) -> io::Result<()>;
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ShConnection {
|
||||
name: String,
|
||||
path: String,
|
||||
args: Vec<String>,
|
||||
}
|
||||
|
||||
impl Connection for ShConnection {
|
||||
fn exec_cmd(&self) -> io::Result<()> {
|
||||
Command::new(&self.path).args(&self.args).spawn()?.wait()?;
|
||||
Ok(())
|
||||
|
||||
// if output.status.success() {
|
||||
// Ok(String::from_utf8_lossy(&output.stdout).into_owned())
|
||||
// } else {
|
||||
// Err(io::Error::new(
|
||||
// io::ErrorKind::Other,
|
||||
// String::from_utf8_lossy(&output.stderr).into_owned(),
|
||||
// ))
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
impl ShConnection {
|
||||
pub fn new(name: String, path: String, args: Option<Vec<String>>) -> Self {
|
||||
ShConnection {
|
||||
name,
|
||||
path,
|
||||
args: args.unwrap_or_else(Vec::new),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SshConnection {
|
||||
name: String,
|
||||
host: String,
|
||||
port: u16,
|
||||
user: String,
|
||||
credential_name: String,
|
||||
}
|
||||
|
||||
impl SshConnection {
|
||||
pub fn new(
|
||||
name: String,
|
||||
host: String,
|
||||
port: u16,
|
||||
user: String,
|
||||
credential_name: String,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
host,
|
||||
port,
|
||||
user,
|
||||
credential_name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Connection for SshConnection {
|
||||
fn exec_cmd(&self) -> io::Result<()> {
|
||||
let credential = Credential::new(
|
||||
"zsh".to_string(),
|
||||
CredentialType::Secret(file::read_file(Path::new(""))),
|
||||
);
|
||||
let mut ssh_command = Command::new("ssh");
|
||||
let secret_path = env::temp_dir().join("tethers").join("secret.tmp");
|
||||
let _ = fs::create_dir_all(secret_path.parent().unwrap());
|
||||
ssh_command
|
||||
.args(["-p", &self.port.to_string()])
|
||||
.args(["-l", &self.user]);
|
||||
match credential.credential() {
|
||||
CredentialType::Password(pwd) => {
|
||||
println!("{}", pwd);
|
||||
}
|
||||
CredentialType::Secret(secret) => {
|
||||
let _ = credential.write_secret(&secret_path);
|
||||
ssh_command.args(["-i", secret_path.to_str().unwrap()]);
|
||||
println!("{}", secret);
|
||||
}
|
||||
}
|
||||
ssh_command.arg(&self.host);
|
||||
ssh_command.spawn()?.wait()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum Connections {
|
||||
Sh(ShConnection),
|
||||
Ssh(SshConnection),
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct ConnectionManager {
|
||||
connections: Vec<Connections>,
|
||||
}
|
||||
|
||||
impl ConnectionManager {
|
||||
pub fn connections(&mut self) -> &mut Vec<Connections> {
|
||||
&mut self.connections
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
use std::{
|
||||
fs::OpenOptions,
|
||||
io::{self, Write},
|
||||
os::unix::fs::OpenOptionsExt,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use base64::{engine::general_purpose::STANDARD, Engine};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum CredentialType {
|
||||
Password(String),
|
||||
Secret(String),
|
||||
}
|
||||
|
||||
impl CredentialType {}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Credential {
|
||||
credential: CredentialType,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Credential {
|
||||
pub fn write_secret(&self, secret_path: &Path) -> io::Result<()> {
|
||||
match &self.credential {
|
||||
CredentialType::Secret(secret) => {
|
||||
let mut secret_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.mode(0o600)
|
||||
.open(secret_path)?;
|
||||
let _ = secret_file.set_len(0);
|
||||
let _ = secret_file.write_all(secret.as_bytes());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Credential {
|
||||
pub fn new(name: String, credential: CredentialType) -> Self {
|
||||
// TODO: modify this to Type
|
||||
let credential = match credential {
|
||||
CredentialType::Password(password) => {
|
||||
CredentialType::Password(STANDARD.encode(password))
|
||||
}
|
||||
CredentialType::Secret(secret) => CredentialType::Secret(STANDARD.encode(secret)),
|
||||
};
|
||||
Self { name, credential }
|
||||
}
|
||||
|
||||
pub fn credential(&self) -> &CredentialType {
|
||||
&self.credential
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct CredentialManager {
|
||||
credentials: Vec<Credential>,
|
||||
}
|
||||
|
||||
impl CredentialManager {
|
||||
pub fn credentials(&mut self) -> &mut Vec<Credential> {
|
||||
&mut self.credentials
|
||||
}
|
||||
|
||||
pub fn find_by_name(&self, name: &str) -> io::Result<&Credential> {
|
||||
self.credentials
|
||||
.iter()
|
||||
.find(|credential| credential.name == name)
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "Credential not found"))
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
pub mod file;
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{Read, Write},
|
||||
path::{self, Path},
|
||||
};
|
||||
|
||||
pub fn read_file(path: &Path) -> String {
|
||||
let path = dirs::home_dir().unwrap().join(".ssh/Server");
|
||||
let mut file = File::open(path).unwrap();
|
||||
let mut content = String::new();
|
||||
file.read_to_string(&mut content).unwrap();
|
||||
content
|
||||
}
|
||||
|
||||
pub fn write_file(path: &Path, content: &str) {
|
||||
let mut file = File::create(path).unwrap();
|
||||
let res = file.write_all(content.as_bytes());
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::io;
|
||||
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::Event;
|
||||
use ratatui::{
|
||||
layout::{Flex, Layout, Rect},
|
||||
@@ -12,7 +11,7 @@ pub mod setting;
|
||||
|
||||
pub trait View {
|
||||
fn draw(&self, frame: &mut Frame);
|
||||
fn handle_event(&mut self, event: &Event) -> io::Result<()>;
|
||||
fn handle_event(&mut self, event: &Event) -> Result<()>;
|
||||
}
|
||||
|
||||
pub trait PopupView {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::io;
|
||||
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::Event;
|
||||
use ratatui::widgets::Widget;
|
||||
|
||||
@@ -14,6 +13,6 @@ pub mod list;
|
||||
pub trait Component {
|
||||
type EventResult;
|
||||
fn widget(&self) -> impl Widget;
|
||||
fn event_handler(&mut self, event: &Event) -> io::Result<Self::EventResult>;
|
||||
fn event_handler(&mut self, event: &Event) -> Result<Self::EventResult>;
|
||||
fn active_state(&mut self) -> &mut ActiveState;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use ratatui::{symbols::block, widgets::Paragraph};
|
||||
use color_eyre::Result;
|
||||
use ratatui::widgets::Paragraph;
|
||||
|
||||
use crate::view::ActiveState;
|
||||
|
||||
@@ -14,6 +15,7 @@ pub struct ButtonComponent {
|
||||
active_state: ActiveState,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl ButtonComponent {
|
||||
pub fn text(mut self, text: String) -> Self {
|
||||
self.text = text;
|
||||
@@ -31,8 +33,8 @@ impl Component for ButtonComponent {
|
||||
}
|
||||
fn event_handler(
|
||||
&mut self,
|
||||
event: &crossterm::event::Event,
|
||||
) -> std::io::Result<Self::EventResult> {
|
||||
_event: &crossterm::event::Event,
|
||||
) -> Result<ButtonEvent> {
|
||||
Ok(ButtonEvent::Click)
|
||||
}
|
||||
fn active_state(&mut self) -> &mut ActiveState {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::io;
|
||||
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::Event;
|
||||
use ratatui::widgets::{Paragraph, Widget};
|
||||
|
||||
@@ -19,7 +18,7 @@ impl Component for HelpComponent {
|
||||
let help = Paragraph::new("Temp").block(block);
|
||||
help
|
||||
}
|
||||
fn event_handler(&mut self, _event: &Event) -> io::Result<()> {
|
||||
fn event_handler(&mut self, _event: &Event) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn active_state(&mut self) -> &mut ActiveState {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::io;
|
||||
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
|
||||
use ratatui::{
|
||||
layout::{Position, Rect},
|
||||
@@ -11,6 +10,7 @@ use crate::view::ActiveState;
|
||||
|
||||
use super::{block::BlockComponent, Component};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub enum InputEvent {
|
||||
Compelete,
|
||||
None,
|
||||
@@ -47,7 +47,7 @@ impl Component for InputComponent {
|
||||
input
|
||||
}
|
||||
|
||||
fn event_handler(&mut self, event: &Event) -> io::Result<InputEvent> {
|
||||
fn event_handler(&mut self, event: &Event) -> Result<InputEvent> {
|
||||
if !self.active_state.is_active() {
|
||||
return Ok(InputEvent::None);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use color_eyre::Result;
|
||||
use crossterm::{
|
||||
event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
|
||||
style::Color,
|
||||
@@ -26,8 +27,9 @@ pub struct ListComponent {
|
||||
}
|
||||
|
||||
impl ListComponent {
|
||||
pub fn add_item(&mut self, _item_keys: Vec<String>) {
|
||||
self.item_keys.extend(vec!["S".to_string()]);
|
||||
#[allow(dead_code)]
|
||||
pub fn add_item(&mut self, item_keys: Vec<String>) {
|
||||
self.item_keys.extend(item_keys);
|
||||
}
|
||||
pub fn name(mut self, name: String) -> Self {
|
||||
self.name = name;
|
||||
@@ -43,7 +45,7 @@ impl Default for ListComponent {
|
||||
fn default() -> Self {
|
||||
ListComponent {
|
||||
name: String::default(),
|
||||
item_keys: vec!["1".to_string(), "2".to_string(), "3".to_string()],
|
||||
item_keys: vec![],
|
||||
cursor: 0,
|
||||
selectable: false,
|
||||
active_state: Default::default(),
|
||||
@@ -74,7 +76,10 @@ impl Component for ListComponent {
|
||||
list
|
||||
}
|
||||
|
||||
fn event_handler(&mut self, event: &Event) -> std::io::Result<ListEvent> {
|
||||
fn event_handler(&mut self, event: &Event) -> Result<ListEvent> {
|
||||
if self.item_keys.is_empty() {
|
||||
return Ok(ListEvent::None);
|
||||
}
|
||||
if !self.active_state.is_active() {
|
||||
return Ok(ListEvent::None);
|
||||
}
|
||||
@@ -88,6 +93,7 @@ impl Component for ListComponent {
|
||||
use KeyCode::*;
|
||||
match code {
|
||||
Up => {
|
||||
if self.item_keys.len() == 0 {}
|
||||
self.cursor = if self.cursor == 0 {
|
||||
self.item_keys.len() - 1
|
||||
} else {
|
||||
@@ -112,6 +118,7 @@ impl Component for ListComponent {
|
||||
}
|
||||
Ok(ListEvent::None)
|
||||
}
|
||||
|
||||
fn active_state(&mut self) -> &mut ActiveState {
|
||||
&mut self.active_state
|
||||
}
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
use std::io;
|
||||
|
||||
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::Event;
|
||||
use ratatui::{
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
style::{Color, Style},
|
||||
widgets::{Block, BorderType, Borders},
|
||||
layout::{Constraint, Direction, Layout},
|
||||
Frame,
|
||||
};
|
||||
|
||||
use super::{
|
||||
component::{
|
||||
help::HelpComponent,
|
||||
input::{self, InputComponent, InputEvent},
|
||||
input::{InputComponent, InputEvent},
|
||||
list::{ListComponent, ListEvent},
|
||||
Component,
|
||||
},
|
||||
@@ -60,7 +57,7 @@ impl View for Home {
|
||||
self.setting.draw(frame);
|
||||
}
|
||||
|
||||
fn handle_event(&mut self, event: &Event) -> io::Result<()> {
|
||||
fn handle_event(&mut self, event: &Event) -> Result<()> {
|
||||
match self.input.event_handler(event) {
|
||||
Ok(InputEvent::None) => {}
|
||||
_ => {}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
use std::io;
|
||||
|
||||
use ratatui::{
|
||||
layout::{Constraint, Layout, Rect},
|
||||
widgets::{Block, Clear},
|
||||
};
|
||||
use color_eyre::Result;
|
||||
use ratatui::layout::{Constraint, Layout, Rect};
|
||||
|
||||
use super::{
|
||||
center_rect,
|
||||
component::{
|
||||
block::BlockComponent,
|
||||
list::{ListComponent, ListEvent},
|
||||
Component,
|
||||
},
|
||||
@@ -47,7 +42,7 @@ impl View for SettingView {
|
||||
frame.render_widget(self.menu.widget(), layout[0]);
|
||||
}
|
||||
|
||||
fn handle_event(&mut self, event: &crossterm::event::Event) -> io::Result<()> {
|
||||
fn handle_event(&mut self, event: &crossterm::event::Event) -> Result<()> {
|
||||
match self.menu.event_handler(event) {
|
||||
Ok(ListEvent::Select(idx)) => {
|
||||
self.activate_state.set_active(false);
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
|
||||
|
||||
Reference in New Issue
Block a user