use actix_cloud::{tokio::sync::mpsc::UnboundedSender, utils};
use derivative::Derivative;
use entity::agents;
use enum_as_inner::EnumAsInner;
use ffi_rpc::{
self, abi_stable, async_trait,
ffi_rpc_macro::{self, plugin_api},
rmp_serde,
};
use message::Data;
use semver::Version;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use skynet_api::{HyUuid, service::SResult, uuid};
use std::{collections::HashMap, net::SocketAddr};
pub use prost;
pub use semver;
pub mod entity;
pub mod viewer;
include!(concat!(env!("OUT_DIR"), "/msg.rs"));
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const ID: HyUuid = HyUuid(uuid!("2eb2e1a5-66b4-45f9-ad24-3c4f05c858aa"));
#[plugin_api(MonitorService)]
pub trait Service {
async fn api_version() -> Version;
async fn get_agents() -> Vec<Agent>;
async fn find_agent(id: HyUuid) -> Option<Agent>;
async fn run_command(id: HyUuid, cmd: String) -> SResult<HyUuid>;
async fn get_command_output(id: HyUuid, cid: HyUuid) -> Option<AgentCommand>;
async fn kill_command(id: HyUuid, cid: HyUuid, force: bool) -> SResult<()>;
async fn send_file(id: HyUuid, path: String, data: Vec<u8>) -> SResult<HyUuid>;
async fn get_file_result(id: HyUuid, fid: HyUuid) -> Option<AgentFile>;
}
#[derive(
Default, EnumAsInner, Debug, Serialize_repr, Deserialize_repr, PartialEq, Eq, Hash, Clone, Copy,
)]
#[repr(u8)]
pub enum AgentStatus {
#[default]
Offline = 0,
Online,
Updating,
}
#[derive(Clone, Debug, Derivative, Serialize, Deserialize)]
#[derivative(Default(new = "true"))]
pub struct AgentCommand {
pub code: Option<i32>,
pub output: Vec<u8>,
}
#[derive(Clone, Debug, Derivative, Serialize, Deserialize)]
#[derivative(Default(new = "true"))]
pub struct AgentFile {
pub code: u32,
pub message: String,
}
#[derive(Derivative, Serialize, Deserialize, Clone, Debug)]
#[derivative(Default(new = "true"))]
pub struct Agent {
pub id: HyUuid,
pub uid: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub os: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hostname: Option<String>,
pub ip: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub system: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub arch: Option<String>,
pub last_login: i64,
pub status: AgentStatus,
#[serde(skip)]
pub message: Option<UnboundedSender<Data>>,
#[serde(skip)]
pub command: HashMap<HyUuid, Option<AgentCommand>>,
#[serde(skip)]
pub file: HashMap<HyUuid, Option<AgentFile>>,
#[serde(skip_serializing_if = "utils::is_default")]
pub report_rate: u32,
#[serde(skip_serializing_if = "utils::is_default")]
pub disable_shell: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub address: Option<SocketAddr>,
#[serde(skip_serializing_if = "String::is_empty")]
pub endpoint: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_rsp: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cpu: Option<f32>, #[serde(skip_serializing_if = "Option::is_none")]
pub memory: Option<u64>, #[serde(skip_serializing_if = "Option::is_none")]
pub total_memory: Option<u64>, #[serde(skip_serializing_if = "Option::is_none")]
pub disk: Option<u64>, #[serde(skip_serializing_if = "Option::is_none")]
pub total_disk: Option<u64>, #[serde(skip_serializing_if = "Option::is_none")]
pub latency: Option<i64>, #[serde(skip_serializing_if = "Option::is_none")]
pub net_up: Option<u64>, #[serde(skip_serializing_if = "Option::is_none")]
pub net_down: Option<u64>, #[serde(skip_serializing_if = "Option::is_none")]
pub band_up: Option<u64>, #[serde(skip_serializing_if = "Option::is_none")]
pub band_down: Option<u64>, }
impl From<agents::Model> for Agent {
fn from(v: agents::Model) -> Self {
Self {
id: v.id,
uid: v.uid,
name: v.name,
os: v.os,
hostname: v.hostname,
ip: v.ip,
system: v.system,
arch: v.arch,
last_login: v.last_login,
message: None,
command: HashMap::new(),
file: HashMap::new(),
endpoint: String::new(),
..Default::default()
}
}
}