dummy ssh connect
This commit is contained in:
parent
c87b60a738
commit
bec3952861
@ -23,7 +23,7 @@ actix-web-actors = "3.0.0"
|
|||||||
urlencoding = "2.1.0"
|
urlencoding = "2.1.0"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
trust-dns-resolver = "0.20"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
|
|
||||||
# log systems
|
# log systems
|
||||||
|
64
backend/src/agent/agent.rs
Normal file
64
backend/src/agent/agent.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
use actix::{Actor, Addr, Context, Handler, Message, MessageResponse};
|
||||||
|
use actix_web::web::Bytes;
|
||||||
|
use std::net::*;
|
||||||
|
|
||||||
|
use log::info;
|
||||||
|
#[derive(MessageResponse)]
|
||||||
|
pub enum AgentResp {
|
||||||
|
Success,
|
||||||
|
Failed,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Message)]
|
||||||
|
#[rtype(result = "AgentResp")]
|
||||||
|
pub enum AgentMsg {
|
||||||
|
ConnectServer(SocketAddr),
|
||||||
|
SendToServer(Bytes),
|
||||||
|
SendToClient(Bytes),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Agent {
|
||||||
|
id: u32,
|
||||||
|
server_info: Option<SocketAddr>,
|
||||||
|
server_stream: Option<TcpStream>,
|
||||||
|
// client_info: SocketAddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Actor for Agent {
|
||||||
|
type Context = Context<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<AgentMsg> for Agent {
|
||||||
|
type Result = AgentResp;
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: AgentMsg, _ctx: &mut Context<Self>) -> Self::Result {
|
||||||
|
match msg {
|
||||||
|
AgentMsg::ConnectServer(addr) => {
|
||||||
|
info!("connect to server: {}", addr);
|
||||||
|
self.server_info = Some(addr);
|
||||||
|
if let Ok(stream) = TcpStream::connect(addr) {
|
||||||
|
stream
|
||||||
|
.set_nonblocking(true)
|
||||||
|
.expect("set_nonblocking call failed");
|
||||||
|
self.server_stream = Some(stream);
|
||||||
|
AgentResp::Success
|
||||||
|
} else {
|
||||||
|
AgentResp::Failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AgentMsg::SendToServer(_data) => AgentResp::Success,
|
||||||
|
AgentMsg::SendToClient(_data) => AgentResp::Success,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Agent {
|
||||||
|
pub fn new(id: u32) -> Addr<Agent> {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
server_info: None,
|
||||||
|
server_stream: None,
|
||||||
|
}
|
||||||
|
.start()
|
||||||
|
}
|
||||||
|
}
|
4
backend/src/agent/mod.rs
Normal file
4
backend/src/agent/mod.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub mod remote;
|
||||||
|
pub mod ws;
|
||||||
|
pub mod resolver;
|
||||||
|
pub mod agent;
|
88
backend/src/agent/remote.rs
Normal file
88
backend/src/agent/remote.rs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
use actix_session::Session;
|
||||||
|
use actix_web::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
use log::info;
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
use crate::agent::resolver::*;
|
||||||
|
use crate::AppData;
|
||||||
|
|
||||||
|
use super::agent;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct RemoteInfo {
|
||||||
|
#[serde(default)]
|
||||||
|
host: String,
|
||||||
|
#[serde(default)]
|
||||||
|
ip: String,
|
||||||
|
#[serde(default)]
|
||||||
|
port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/target/validate")]
|
||||||
|
pub async fn target_validate(
|
||||||
|
data: web::Data<AppData>,
|
||||||
|
params: web::Json<RemoteInfo>,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let remote = params.into_inner();
|
||||||
|
info!("{:?}", remote);
|
||||||
|
let resolved = data.resolver.send(ResolveMsg::Resolve(remote.host)).await;
|
||||||
|
|
||||||
|
match resolved.unwrap() {
|
||||||
|
ResolveResp::Success(ipaddr) => {
|
||||||
|
let json = json!({
|
||||||
|
"status": "success",
|
||||||
|
"ip": ipaddr
|
||||||
|
});
|
||||||
|
Ok(HttpResponse::Ok().json(json))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let json = json!({
|
||||||
|
"status": "failed",
|
||||||
|
"message": "Failed to resolve the target name"
|
||||||
|
});
|
||||||
|
Ok(HttpResponse::Ok().json(json))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/target/ssh")]
|
||||||
|
pub async fn target_ssh(
|
||||||
|
session: Session,
|
||||||
|
data: web::Data<AppData>,
|
||||||
|
params: web::Json<RemoteInfo>,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let aid = rand::thread_rng().gen::<u32>();
|
||||||
|
let remote = params.into_inner();
|
||||||
|
let agent = agent::Agent::new(aid);
|
||||||
|
|
||||||
|
match agent
|
||||||
|
.send(agent::AgentMsg::ConnectServer(
|
||||||
|
format!("{}:{}", remote.ip, remote.port).parse().unwrap(),
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(agent::AgentResp::Success) => {
|
||||||
|
// add to agent list
|
||||||
|
data.agents.write().unwrap().insert(aid, agent);
|
||||||
|
|
||||||
|
// add session, so that the websocket can send message to the agent
|
||||||
|
let _ = session.set::<u32>("aid", aid);
|
||||||
|
|
||||||
|
// send response
|
||||||
|
let json = json!({
|
||||||
|
"status": "success",
|
||||||
|
});
|
||||||
|
Ok(HttpResponse::Ok().json(json))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let json = json!({
|
||||||
|
"status": "failed",
|
||||||
|
"message": "Failed to connect to the target"
|
||||||
|
});
|
||||||
|
Ok(HttpResponse::Ok().json(json))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
backend/src/agent/resolver.rs
Normal file
75
backend/src/agent/resolver.rs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
use actix::{Actor, Addr, Context, Handler, Message, MessageResponse};
|
||||||
|
|
||||||
|
use std::net::*;
|
||||||
|
use trust_dns_resolver::config::*;
|
||||||
|
use trust_dns_resolver::Resolver;
|
||||||
|
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
#[derive(MessageResponse)]
|
||||||
|
pub enum ResolveResp {
|
||||||
|
Success(IpAddr),
|
||||||
|
Failed,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Message)]
|
||||||
|
#[rtype(result = "ResolveResp")]
|
||||||
|
pub enum ResolveMsg {
|
||||||
|
Resolve(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DnsResolver {
|
||||||
|
resolver: Resolver,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Actor for DnsResolver {
|
||||||
|
type Context = Context<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<ResolveMsg> for DnsResolver {
|
||||||
|
type Result = ResolveResp;
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: ResolveMsg, _: &mut Context<Self>) -> Self::Result {
|
||||||
|
match msg {
|
||||||
|
ResolveMsg::Resolve(name) => {
|
||||||
|
if let Ok(response) = self.resolver.lookup_ip(name.clone()) {
|
||||||
|
if let Some(address) = response.iter().next() {
|
||||||
|
info!("Resolved {} to {}", name, address);
|
||||||
|
ResolveResp::Success(address)
|
||||||
|
} else {
|
||||||
|
ResolveResp::Failed
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!("Failed to resolve {}", name);
|
||||||
|
ResolveResp::Failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DnsResolver {
|
||||||
|
pub fn new() -> Addr<Self> {
|
||||||
|
let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap();
|
||||||
|
|
||||||
|
DnsResolver { resolver }.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct a new Resolver with default configuration options
|
||||||
|
// let mut resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap();
|
||||||
|
|
||||||
|
// On Unix/Posix systems, this will read the /etc/resolv.conf
|
||||||
|
// let mut resolver = Resolver::from_system_conf().unwrap();
|
||||||
|
|
||||||
|
// Lookup the IP addresses associated with a name.
|
||||||
|
// let mut response = resolver.lookup_ip("www.example.com.").unwrap();
|
||||||
|
|
||||||
|
// There can be many addresses associated with the name,
|
||||||
|
// this can return IPv4 and/or IPv6 addresses
|
||||||
|
// let address = response.iter().next().expect("no addresses returned!");
|
||||||
|
// if address.is_ipv4() {
|
||||||
|
// assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
|
||||||
|
// } else {
|
||||||
|
// assert_eq!(address, IpAddr::V6(Ipv6Addr::new(0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946)));
|
||||||
|
// }
|
@ -1,18 +1,25 @@
|
|||||||
use actix::{Actor, StreamHandler};
|
use actix::{Actor, Addr, StreamHandler};
|
||||||
|
use actix_session::Session;
|
||||||
use actix_web::*;
|
use actix_web::*;
|
||||||
use actix_web::{web, Error, HttpRequest, HttpResponse};
|
use actix_web::{web, Error, HttpRequest, HttpResponse};
|
||||||
use actix_web_actors::ws;
|
use actix_web_actors::ws;
|
||||||
use log::*;
|
use log::*;
|
||||||
|
|
||||||
/// Define HTTP actor
|
use crate::AppData;
|
||||||
struct MyWs;
|
|
||||||
|
|
||||||
impl Actor for MyWs {
|
use super::agent::Agent;
|
||||||
|
|
||||||
|
/// Define HTTP actor
|
||||||
|
struct WsSession {
|
||||||
|
agent: Addr<Agent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Actor for WsSession {
|
||||||
type Context = ws::WebsocketContext<Self>;
|
type Context = ws::WebsocketContext<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handler for ws::Message message
|
/// Handler for ws::Message message
|
||||||
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWs {
|
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WsSession {
|
||||||
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
|
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
|
||||||
match msg {
|
match msg {
|
||||||
Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),
|
Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),
|
||||||
@ -24,8 +31,16 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/ws")]
|
#[get("/ws")]
|
||||||
pub async fn ws_index(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
|
pub async fn ws_index(
|
||||||
let resp = ws::start(MyWs {}, &req, stream);
|
req: HttpRequest,
|
||||||
|
session: Session,
|
||||||
|
data: web::Data<AppData>,
|
||||||
|
stream: web::Payload,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let aid = session.get::<u32>("aid").unwrap_or(Some(0)).unwrap();
|
||||||
|
let agent = data.agents.read().unwrap().get(&aid).unwrap().clone();
|
||||||
|
let resp = ws::start(WsSession { agent }, &req, stream);
|
||||||
|
|
||||||
match &resp {
|
match &resp {
|
||||||
Ok(resp) => info!("{:?}", resp),
|
Ok(resp) => info!("{:?}", resp),
|
||||||
Err(e) => error!("{:?}", e),
|
Err(e) => error!("{:?}", e),
|
@ -1,24 +1,43 @@
|
|||||||
|
use std::{collections::HashMap, sync::RwLock};
|
||||||
|
|
||||||
|
use actix::Addr;
|
||||||
use actix_files as fs;
|
use actix_files as fs;
|
||||||
use actix_session::CookieSession;
|
use actix_session::CookieSession;
|
||||||
use actix_web::http::{ContentEncoding, StatusCode};
|
use actix_web::http::{ContentEncoding, StatusCode};
|
||||||
use actix_web::*;
|
use actix_web::*;
|
||||||
|
|
||||||
|
use agent::{agent::Agent, resolver::DnsResolver};
|
||||||
use log::info;
|
use log::info;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
|
mod agent;
|
||||||
mod user;
|
mod user;
|
||||||
mod ws;
|
|
||||||
|
|
||||||
// pub struct AppState ;
|
|
||||||
|
|
||||||
// impl Actor for AppState {
|
|
||||||
// type Context = actix::Context<Self>;
|
|
||||||
// }
|
|
||||||
|
|
||||||
const STATIC_DIR: &str = "./static/";
|
const STATIC_DIR: &str = "./static/";
|
||||||
const PAGE_INDEX: &str = "./static/index.html";
|
const PAGE_INDEX: &str = "./static/index.html";
|
||||||
const PAGE_NOT_FOUND: &str = "./static/p404.html";
|
const PAGE_NOT_FOUND: &str = "./static/p404.html";
|
||||||
|
|
||||||
|
pub struct AppData {
|
||||||
|
// session: CookieSession,
|
||||||
|
resolver: Addr<DnsResolver>,
|
||||||
|
agents: RwLock<HashMap<u32, Addr<Agent>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppData {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
resolver: DnsResolver::new(),
|
||||||
|
agents: RwLock::new(HashMap::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AppData {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn setup_logger() {
|
fn setup_logger() {
|
||||||
let logger = femme::pretty::Logger::new();
|
let logger = femme::pretty::Logger::new();
|
||||||
async_log::Logger::wrap(logger, || 12)
|
async_log::Logger::wrap(logger, || 12)
|
||||||
@ -43,12 +62,14 @@ async fn main() -> std::io::Result<()> {
|
|||||||
let private_key = rand::thread_rng().gen::<[u8; 32]>();
|
let private_key = rand::thread_rng().gen::<[u8; 32]>();
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
// .data(AppState)
|
.data(AppData::new())
|
||||||
.wrap(CookieSession::signed(&private_key).secure(false))
|
.wrap(CookieSession::signed(&private_key).secure(false))
|
||||||
.wrap(middleware::Compress::new(ContentEncoding::Gzip))
|
.wrap(middleware::Compress::new(ContentEncoding::Gzip))
|
||||||
.service(index)
|
.service(index)
|
||||||
.service(ws::ws_index)
|
|
||||||
.service(user::auth::auth)
|
.service(user::auth::auth)
|
||||||
|
.service(agent::remote::target_validate)
|
||||||
|
.service(agent::remote::target_ssh)
|
||||||
|
.service(agent::ws::ws_index)
|
||||||
.service(
|
.service(
|
||||||
fs::Files::new("/static", STATIC_DIR)
|
fs::Files::new("/static", STATIC_DIR)
|
||||||
.prefer_utf8(true)
|
.prefer_utf8(true)
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
use actix::{Actor, Context, Handler, Message, MessageResponse};
|
use actix::{Actor, Context, Handler, Message, MessageResponse};
|
||||||
|
use actix_session::Session;
|
||||||
use actix_web::*;
|
use actix_web::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
#[derive(Message)]
|
|
||||||
#[rtype(result = "Self")]
|
|
||||||
#[derive(MessageResponse)]
|
#[derive(MessageResponse)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
enum AuthMessage {
|
enum AuthResp {
|
||||||
DoAuth,
|
|
||||||
AuthSuccess,
|
AuthSuccess,
|
||||||
AuthFailure,
|
AuthFailure,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Message)]
|
||||||
|
#[rtype(result = "AuthResp")]
|
||||||
|
enum AuthMsg {
|
||||||
|
DoAuth,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct AuthInfo {
|
pub struct AuthInfo {
|
||||||
username: String,
|
username: String,
|
||||||
@ -34,12 +38,12 @@ impl Actor for AuthInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handler<AuthMessage> for AuthInfo {
|
impl Handler<AuthMsg> for AuthInfo {
|
||||||
type Result = AuthMessage;
|
type Result = AuthResp;
|
||||||
|
|
||||||
fn handle(&mut self, _msg: AuthMessage, _ctx: &mut Context<Self>) -> Self::Result {
|
fn handle(&mut self, _msg: AuthMsg, _ctx: &mut Context<Self>) -> Self::Result {
|
||||||
info!("AuthInfo handle");
|
info!("AuthInfo handle");
|
||||||
AuthMessage::AuthSuccess
|
AuthResp::AuthSuccess
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,15 +51,12 @@ impl Handler<AuthMessage> for AuthInfo {
|
|||||||
pub async fn auth(params: web::Json<AuthInfo>) -> Result<HttpResponse, Error> {
|
pub async fn auth(params: web::Json<AuthInfo>) -> Result<HttpResponse, Error> {
|
||||||
let auth = params.into_inner();
|
let auth = params.into_inner();
|
||||||
let auth_addr = auth.start();
|
let auth_addr = auth.start();
|
||||||
let res = auth_addr.send(AuthMessage::DoAuth).await;
|
let res = auth_addr.send(AuthMsg::DoAuth).await;
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(AuthMessage::AuthSuccess) => Ok(HttpResponse::Ok().json(json!({
|
Ok(AuthResp::AuthSuccess) => Ok(HttpResponse::Ok().json(json!({
|
||||||
"status": "success",
|
"status": "success",
|
||||||
}))),
|
}))),
|
||||||
Ok(AuthMessage::AuthFailure) => Ok(HttpResponse::Ok().json(json!({
|
|
||||||
"status": "failure",
|
|
||||||
}))),
|
|
||||||
_ => Ok(HttpResponse::Ok().json(json!({
|
_ => Ok(HttpResponse::Ok().json(json!({
|
||||||
"status": "failure",
|
"status": "failure",
|
||||||
}))),
|
}))),
|
||||||
|
142
frontend/src/components/host.rs
Normal file
142
frontend/src/components/host.rs
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
use serde_json::{json, Value};
|
||||||
|
use yew::{
|
||||||
|
format::Json,
|
||||||
|
prelude::*,
|
||||||
|
services::{
|
||||||
|
fetch::{FetchTask, Request, Response},
|
||||||
|
ConsoleService, FetchService,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum HostMsg {
|
||||||
|
UpdateHost(String),
|
||||||
|
UpdatePort(String),
|
||||||
|
ValidateResponse(Result<Value, anyhow::Error>),
|
||||||
|
ConnectHost,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Host {
|
||||||
|
link: ComponentLink<Self>,
|
||||||
|
host: String,
|
||||||
|
port: u16,
|
||||||
|
error_msg: String,
|
||||||
|
onsubmit: Callback<(String, u16)>,
|
||||||
|
fetch_task: Option<FetchTask>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Props
|
||||||
|
#[derive(Clone, PartialEq, Properties)]
|
||||||
|
pub struct HostProps {
|
||||||
|
#[prop_or_default]
|
||||||
|
pub onsubmit: Callback<(String, u16)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Host {
|
||||||
|
type Message = HostMsg;
|
||||||
|
type Properties = HostProps;
|
||||||
|
|
||||||
|
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||||
|
Host {
|
||||||
|
link,
|
||||||
|
host: "".to_string(),
|
||||||
|
port: 0,
|
||||||
|
error_msg: "".to_string(),
|
||||||
|
onsubmit: props.onsubmit,
|
||||||
|
fetch_task: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||||
|
match msg {
|
||||||
|
HostMsg::UpdateHost(host) => {
|
||||||
|
self.host = host;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
HostMsg::UpdatePort(port) => match port.parse::<u16>() {
|
||||||
|
Ok(port) => {
|
||||||
|
self.port = port;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
self.error_msg = "Port must be a number".to_string();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
HostMsg::ValidateResponse(response) => {
|
||||||
|
if let Ok(response) = response {
|
||||||
|
self.error_msg = response["status"].to_string();
|
||||||
|
|
||||||
|
if "\"success\"" == self.error_msg {
|
||||||
|
let mut ip = response["ip"].to_string();
|
||||||
|
let _ = ip.pop();
|
||||||
|
let _ = ip.remove(0);
|
||||||
|
self.onsubmit.emit((ip, self.port));
|
||||||
|
} else {
|
||||||
|
self.error_msg = response["message"].to_string();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.error_msg = String::from("Valid host failed with unknown reason");
|
||||||
|
ConsoleService::error(&format!("{:?}", response.unwrap_err().to_string()));
|
||||||
|
}
|
||||||
|
// release resources
|
||||||
|
self.fetch_task = None;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
HostMsg::ConnectHost => {
|
||||||
|
let to_post = json!({
|
||||||
|
"host": self.host,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 1. build the request
|
||||||
|
let request = Request::post("/target/validate")
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(Json(&to_post))
|
||||||
|
.expect("Could not build auth request.");
|
||||||
|
// 2. construct a callback
|
||||||
|
let callback =
|
||||||
|
self.link
|
||||||
|
.callback(|response: Response<Json<Result<Value, anyhow::Error>>>| {
|
||||||
|
// ConsoleService::error(&format!("{:?}", response));
|
||||||
|
let Json(data) = response.into_body();
|
||||||
|
HostMsg::ValidateResponse(data)
|
||||||
|
});
|
||||||
|
// 3. pass the request and callback to the fetch service
|
||||||
|
let task = FetchService::fetch(request, callback).expect("failed to start request");
|
||||||
|
// 4. store the task so it isn't canceled immediately
|
||||||
|
self.fetch_task = Some(task);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self) -> Html {
|
||||||
|
let updatehost = self.link.callback(|e: ChangeData| match e {
|
||||||
|
ChangeData::Value(val) => HostMsg::UpdateHost(val),
|
||||||
|
_ => panic!("unexpected message"),
|
||||||
|
});
|
||||||
|
|
||||||
|
let updateport = self.link.callback(|e: ChangeData| match e {
|
||||||
|
ChangeData::Value(val) => HostMsg::UpdatePort(val),
|
||||||
|
_ => panic!("unexpected message"),
|
||||||
|
});
|
||||||
|
|
||||||
|
let connecthost = self.link.callback(|_| HostMsg::ConnectHost);
|
||||||
|
|
||||||
|
html! {
|
||||||
|
<div class="horizontal-centre vertical-centre">
|
||||||
|
<label for="hostname">{"Hostname: "}</label>
|
||||||
|
<input id="hostname" type="text" placeholder="hostname" onchange={updatehost} />
|
||||||
|
<br />
|
||||||
|
<input id="port" type="text" placeholder="port" onchange={updateport}/>
|
||||||
|
<br />
|
||||||
|
<button onclick={connecthost}>{"Connect"}</button>
|
||||||
|
<br />
|
||||||
|
{self.error_msg.clone()}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1,2 @@
|
|||||||
pub mod auth;
|
pub mod auth;
|
||||||
|
pub mod host;
|
@ -1,19 +1,94 @@
|
|||||||
use yew::prelude::*;
|
use serde_json::{json, Value};
|
||||||
|
use yew::{
|
||||||
|
format::Json,
|
||||||
|
prelude::*,
|
||||||
|
services::{
|
||||||
|
fetch::{FetchTask, Request, Response},
|
||||||
|
ConsoleService, FetchService,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
pub struct PageSsh {}
|
use crate::components;
|
||||||
|
|
||||||
pub enum Msg {}
|
pub struct PageSsh {
|
||||||
|
link: ComponentLink<Self>,
|
||||||
|
target: (String, u16),
|
||||||
|
error_msg: String,
|
||||||
|
fetch_task: Option<FetchTask>,
|
||||||
|
connected: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SshMsg {
|
||||||
|
SshConnect((String, u16)),
|
||||||
|
SshConnectResp(Result<Value, anyhow::Error>),
|
||||||
|
SshConnected,
|
||||||
|
}
|
||||||
|
|
||||||
impl Component for PageSsh {
|
impl Component for PageSsh {
|
||||||
type Message = Msg;
|
type Message = SshMsg;
|
||||||
type Properties = ();
|
type Properties = ();
|
||||||
|
|
||||||
fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
|
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||||
PageSsh {}
|
PageSsh {
|
||||||
|
link,
|
||||||
|
target: (String::from(""), 0),
|
||||||
|
error_msg: String::from(""),
|
||||||
|
fetch_task: None,
|
||||||
|
connected: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
|
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||||
true
|
match msg {
|
||||||
|
SshMsg::SshConnect(target) => {
|
||||||
|
self.target = target;
|
||||||
|
// ConsoleService::log(&self.target);
|
||||||
|
let to_post = json!({
|
||||||
|
"ip": self.target.0,
|
||||||
|
"port": self.target.1,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 1. build the request
|
||||||
|
let request = Request::post("/target/ssh")
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(Json(&to_post))
|
||||||
|
.expect("Could not build auth request.");
|
||||||
|
// 2. construct a callback
|
||||||
|
let callback =
|
||||||
|
self.link
|
||||||
|
.callback(|response: Response<Json<Result<Value, anyhow::Error>>>| {
|
||||||
|
// ConsoleService::error(&format!("{:?}", response));
|
||||||
|
let Json(data) = response.into_body();
|
||||||
|
SshMsg::SshConnectResp(data)
|
||||||
|
});
|
||||||
|
// 3. pass the request and callback to the fetch service
|
||||||
|
let task = FetchService::fetch(request, callback).expect("failed to start request");
|
||||||
|
// 4. store the task so it isn't canceled immediately
|
||||||
|
self.fetch_task = Some(task);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
SshMsg::SshConnectResp(response) => {
|
||||||
|
if let Ok(response) = response {
|
||||||
|
self.error_msg = response["status"].to_string();
|
||||||
|
|
||||||
|
if "\"success\"" == self.error_msg {
|
||||||
|
self.link.send_message(SshMsg::SshConnected);
|
||||||
|
} else {
|
||||||
|
self.error_msg = response["message"].to_string();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.error_msg = String::from("Connect host failed with unknown reason");
|
||||||
|
ConsoleService::error(&format!("{:?}", response.unwrap_err().to_string()));
|
||||||
|
}
|
||||||
|
// release resources
|
||||||
|
self.fetch_task = None;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
SshMsg::SshConnected => {
|
||||||
|
self.connected = true;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change(&mut self, _: Self::Properties) -> ShouldRender {
|
fn change(&mut self, _: Self::Properties) -> ShouldRender {
|
||||||
@ -21,8 +96,18 @@ impl Component for PageSsh {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self) -> Html {
|
fn view(&self) -> Html {
|
||||||
html! {
|
let connect_ssh = self.link.callback(SshMsg::SshConnect);
|
||||||
<p>{ "Hello ssh!\n\n\n\n" }</p>
|
if !self.connected {
|
||||||
|
html! {
|
||||||
|
<>
|
||||||
|
<components::host::Host onsubmit=connect_ssh/>
|
||||||
|
{self.error_msg.clone()}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html! {
|
||||||
|
<></>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user