Page MenuHomePhorge

internal.rs
No OneTemporary

Size
25 KB
Referenced Files
None
Subscribers
None

internal.rs

use colored::Colorize;
use macros_rs::{crashln, string, ternary, then};
use psutil::process::{MemoryInfo, Process};
use regex::Regex;
use serde::Serialize;
use serde_json::json;
use pmc::{
config, file,
helpers::{self, ColoredString},
log,
process::{http, ItemSingle, Runner},
};
use tabled::{
settings::{
object::{Columns, Rows},
style::{BorderColor, Style},
themes::Colorization,
Color, Modify, Rotate, Width,
},
Table, Tabled,
};
pub struct Internal<'i> {
pub id: usize,
pub runner: Runner,
pub kind: String,
pub server_name: &'i str,
}
impl<'i> Internal<'i> {
pub fn create(mut self, script: &String, name: &Option<String>, watch: &Option<String>) {
let config = config::read();
let name = match name {
Some(name) => string!(name),
None => string!(script.split_whitespace().next().unwrap_or_default()),
};
if matches!(self.server_name, "internal" | "local") {
let pattern = Regex::new(r"(?m)^[a-zA-Z0-9]+(/[a-zA-Z0-9]+)*(\.js|\.ts)?$").unwrap();
if pattern.is_match(script) {
let script = format!("{} {script}", config.runner.node);
self.runner.start(&name, &script, file::cwd(), watch).save();
} else {
self.runner.start(&name, script, file::cwd(), watch).save();
}
} else {
let Some(servers) = config::servers().servers else {
crashln!("{} Failed to read servers", *helpers::FAIL)
};
if let Some(server) = servers.get(self.server_name) {
match Runner::connect(self.server_name.into(), server.get(), false) {
Some(mut remote) => remote.start(&name, script, file::cwd(), watch),
None => crashln!("{} Failed to connect (name={}, address={})", *helpers::FAIL, self.server_name, server.address),
};
} else {
crashln!("{} Server '{}' does not exist", *helpers::FAIL, self.server_name,)
};
}
println!("{} Creating {}process with ({name})", *helpers::SUCCESS, self.kind);
println!("{} {}Created ({name}) ✓", *helpers::SUCCESS, self.kind);
}
pub fn restart(mut self, name: &Option<String>, watch: &Option<String>, silent: bool) -> Runner {
then!(!silent, println!("{} Applying {}action restartProcess on ({})", *helpers::SUCCESS, self.kind, self.id));
if matches!(self.server_name, "internal" | "local") {
let mut item = self.runner.get(self.id);
match watch {
Some(path) => item.watch(path),
None => item.disable_watch(),
}
name.as_ref().map(|n| item.rename(n.trim().replace("\n", "")));
item.restart();
self.runner = item.get_runner().clone();
} else {
let Some(servers) = config::servers().servers else {
crashln!("{} Failed to read servers", *helpers::FAIL)
};
if let Some(server) = servers.get(self.server_name) {
match Runner::connect(self.server_name.into(), server.get(), false) {
Some(remote) => {
let mut item = remote.get(self.id);
name.as_ref().map(|n| item.rename(n.trim().replace("\n", "")));
item.restart();
}
None => crashln!("{} Failed to connect (name={}, address={})", *helpers::FAIL, self.server_name, server.address),
}
} else {
crashln!("{} Server '{}' does not exist", *helpers::FAIL, self.server_name)
};
}
if !silent {
println!("{} Restarted {}({}) ✓", *helpers::SUCCESS, self.kind, self.id);
log!("process started (id={})", self.id);
}
return self.runner;
}
pub fn stop(mut self, silent: bool) -> Runner {
then!(!silent, println!("{} Applying {}action stopProcess on ({})", *helpers::SUCCESS, self.kind, self.id));
if !matches!(self.server_name, "internal" | "local") {
let Some(servers) = config::servers().servers else {
crashln!("{} Failed to read servers", *helpers::FAIL)
};
if let Some(server) = servers.get(self.server_name) {
self.runner = match Runner::connect(self.server_name.into(), server.get(), false) {
Some(remote) => remote,
None => crashln!("{} Failed to connect (name={}, address={})", *helpers::FAIL, self.server_name, server.address),
};
} else {
crashln!("{} Server '{}' does not exist", *helpers::FAIL, self.server_name)
};
}
let mut item = self.runner.get(self.id);
item.stop();
self.runner = item.get_runner().clone();
if !silent {
println!("{} Stopped {}({}) ✓", *helpers::SUCCESS, self.kind, self.id);
log!("process stopped {}(id={})", self.kind, self.id);
}
return self.runner;
}
pub fn remove(mut self) {
println!("{} Applying {}action removeProcess on ({})", *helpers::SUCCESS, self.kind, self.id);
if !matches!(self.server_name, "internal" | "local") {
let Some(servers) = config::servers().servers else {
crashln!("{} Failed to read servers", *helpers::FAIL)
};
if let Some(server) = servers.get(self.server_name) {
self.runner = match Runner::connect(self.server_name.into(), server.get(), false) {
Some(remote) => remote,
None => crashln!("{} Failed to remove (name={}, address={})", *helpers::FAIL, self.server_name, server.address),
};
} else {
crashln!("{} Server '{}' does not exist", *helpers::FAIL, self.server_name)
};
}
self.runner.remove(self.id);
println!("{} Removed {}({}) ✓", *helpers::SUCCESS, self.kind, self.id);
log!("process removed (id={})", self.id);
}
pub fn info(&self, format: &String) {
#[derive(Clone, Debug, Tabled)]
struct Info {
#[tabled(rename = "error log path ")]
log_error: String,
#[tabled(rename = "out log path")]
log_out: String,
#[tabled(rename = "cpu percent")]
cpu_percent: String,
#[tabled(rename = "memory usage")]
memory_usage: String,
#[tabled(rename = "path hash")]
hash: String,
#[tabled(rename = "watching")]
watch: String,
children: String,
#[tabled(rename = "exec cwd")]
path: String,
#[tabled(rename = "script command ")]
command: String,
#[tabled(rename = "script id")]
id: String,
restarts: u64,
uptime: String,
pid: String,
name: String,
status: ColoredString,
}
impl Serialize for Info {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let trimmed_json = json!({
"id": &self.id.trim(),
"pid": &self.pid.trim(),
"name": &self.name.trim(),
"path": &self.path.trim(),
"restarts": &self.restarts,
"hash": &self.hash.trim(),
"watch": &self.watch.trim(),
"children": &self.children,
"uptime": &self.uptime.trim(),
"status": &self.status.0.trim(),
"log_out": &self.log_out.trim(),
"cpu": &self.cpu_percent.trim(),
"command": &self.command.trim(),
"mem": &self.memory_usage.trim(),
"log_error": &self.log_error.trim(),
});
trimmed_json.serialize(serializer)
}
}
let render_info = |data: Vec<Info>| {
let table = Table::new(data.clone())
.with(Rotate::Left)
.with(Style::rounded().remove_horizontals())
.with(Colorization::exact([Color::FG_CYAN], Columns::first()))
.with(BorderColor::filled(Color::FG_BRIGHT_BLACK))
.to_string();
if let Ok(json) = serde_json::to_string(&data[0]) {
match format.as_str() {
"raw" => println!("{:?}", data[0]),
"json" => println!("{json}"),
_ => {
println!("{}\n{table}\n", format!("Describing {}process with id ({})", self.kind, self.id).on_bright_white().black());
println!(" {}", format!("Use `pmc logs {} [--lines <num>]` to display logs", self.id).white());
println!(" {}", format!("Use `pmc env {}` to display environment variables", self.id).white());
}
};
};
};
if matches!(self.server_name, "internal" | "local") {
if let Some(home) = home::home_dir() {
let config = config::read().runner;
let mut runner = Runner::new();
let item = runner.process(self.id);
let mut memory_usage: Option<MemoryInfo> = None;
let mut cpu_percent: Option<f32> = None;
let path = file::make_relative(&item.path, &home).to_string_lossy().into_owned();
let children = if item.children.is_empty() { "none".to_string() } else { format!("{:?}", item.children) };
if let Ok(mut process) = Process::new(item.pid as u32) {
memory_usage = process.memory_info().ok();
cpu_percent = process.cpu_percent().ok();
}
let cpu_percent = match cpu_percent {
Some(percent) => format!("{:.2}%", percent),
None => string!("0%"),
};
let memory_usage = match memory_usage {
Some(usage) => helpers::format_memory(usage.rss()),
None => string!("0b"),
};
let status = if item.running {
"online ".green().bold()
} else {
match item.crash.crashed {
true => "crashed ",
false => "stopped ",
}
.red()
.bold()
};
let data = vec![Info {
children,
cpu_percent,
memory_usage,
id: string!(self.id),
restarts: item.restarts,
name: item.name.clone(),
log_out: item.logs().out,
path: format!("{} ", path),
log_error: item.logs().error,
status: ColoredString(status),
pid: ternary!(item.running, format!("{}", item.pid), string!("n/a")),
command: format!("{} {} '{}'", config.shell, config.args.join(" "), item.script),
hash: ternary!(item.watch.enabled, format!("{} ", item.watch.hash), string!("none ")),
watch: ternary!(item.watch.enabled, format!("{path}/{} ", item.watch.path), string!("disabled ")),
uptime: ternary!(item.running, format!("{}", helpers::format_duration(item.started)), string!("none")),
}];
render_info(data)
} else {
crashln!("{} Impossible to get your home directory", *helpers::FAIL);
}
} else {
let data: (pmc::process::Process, Runner);
let Some(servers) = config::servers().servers else {
crashln!("{} Failed to read servers", *helpers::FAIL)
};
if let Some(server) = servers.get(self.server_name) {
data = match Runner::connect(self.server_name.into(), server.get(), false) {
Some(mut remote) => (remote.process(self.id).clone(), remote),
None => crashln!("{} Failed to connect (name={}, address={})", *helpers::FAIL, self.server_name, server.address),
};
} else {
crashln!("{} Server '{}' does not exist", *helpers::FAIL, self.server_name)
};
let (item, remote) = data;
let remote = remote.remote.unwrap();
let info = http::info(&remote, self.id);
let path = item.path.to_string_lossy().into_owned();
let status = if item.running {
"online ".green().bold()
} else {
match item.crash.crashed {
true => "crashed ",
false => "stopped ",
}
.red()
.bold()
};
if let Ok(info) = info {
let stats = info.json::<ItemSingle>().unwrap().stats;
let children = if item.children.is_empty() { "none".to_string() } else { format!("{:?}", item.children) };
let cpu_percent = match stats.cpu_percent {
Some(percent) => format!("{percent:.2}%"),
None => string!("0%"),
};
let memory_usage = match stats.memory_usage {
Some(usage) => helpers::format_memory(usage.rss),
None => string!("0b"),
};
let data = vec![Info {
children,
cpu_percent,
memory_usage,
id: string!(self.id),
path: path.clone(),
status: status.into(),
restarts: item.restarts,
name: item.name.clone(),
pid: ternary!(item.running, format!("{pid}", pid = item.pid), string!("n/a")),
log_out: format!("{}/{}-out.log", remote.config.log_path, item.name),
log_error: format!("{}/{}-error.log", remote.config.log_path, item.name),
hash: ternary!(item.watch.enabled, format!("{} ", item.watch.hash), string!("none ")),
command: format!("{} {} '{}'", remote.config.shell, remote.config.args.join(" "), item.script),
watch: ternary!(item.watch.enabled, format!("{path}/{} ", item.watch.path), string!("disabled ")),
uptime: ternary!(item.running, format!("{}", helpers::format_duration(item.started)), string!("none")),
}];
render_info(data)
}
}
}
pub fn logs(mut self, lines: &usize) {
if !matches!(self.server_name, "internal" | "local") {
let Some(servers) = config::servers().servers else {
crashln!("{} Failed to read servers", *helpers::FAIL)
};
if let Some(server) = servers.get(self.server_name) {
self.runner = match Runner::connect(self.server_name.into(), server.get(), false) {
Some(remote) => remote,
None => crashln!("{} Failed to connect (name={}, address={})", *helpers::FAIL, self.server_name, server.address),
};
} else {
crashln!("{} Server '{}' does not exist", *helpers::FAIL, self.server_name)
};
let item = self.runner.info(self.id).unwrap_or_else(|| crashln!("{} Process ({}) not found", *helpers::FAIL, self.id));
println!(
"{}",
format!("Showing last {lines} lines for {}process [{}] (change the value with --lines option)", self.kind, self.id).yellow()
);
for kind in vec!["error", "out"] {
let logs = http::logs(&self.runner.remote.as_ref().unwrap(), self.id, kind);
if let Ok(log) = logs {
if log.lines.is_empty() {
println!("{} No logs found for {}/{kind}", *helpers::FAIL, item.name);
continue;
}
file::logs_internal(log.lines, *lines, log.path, self.id, kind, &item.name)
}
}
} else {
let item = self.runner.info(self.id).unwrap_or_else(|| crashln!("{} Process ({}) not found", *helpers::FAIL, self.id));
println!(
"{}",
format!("Showing last {lines} lines for {}process [{}] (change the value with --lines option)", self.kind, self.id).yellow()
);
file::logs(item, *lines, "error");
file::logs(item, *lines, "out");
}
}
pub fn env(mut self) {
println!("{}", format!("Showing env for {}process {}:\n", self.kind, self.id).bright_yellow());
if !matches!(self.server_name, "internal" | "local") {
let Some(servers) = config::servers().servers else {
crashln!("{} Failed to read servers", *helpers::FAIL)
};
if let Some(server) = servers.get(self.server_name) {
self.runner = match Runner::connect(self.server_name.into(), server.get(), false) {
Some(remote) => remote,
None => crashln!("{} Failed to connect (name={}, address={})", *helpers::FAIL, self.server_name, server.address),
};
} else {
crashln!("{} Server '{}' does not exist", *helpers::FAIL, self.server_name)
};
}
let item = self.runner.process(self.id);
item.env.iter().for_each(|(key, value)| println!("{}: {}", key, value.green()));
}
pub fn save(server_name: &String) {
if !matches!(&**server_name, "internal" | "local") {
crashln!("{} Cannot force save on remote servers", *helpers::FAIL)
}
println!("{} Saved current processes to dumpfile", *helpers::SUCCESS);
Runner::new().save();
}
pub fn restore(server_name: &String) {
let mut runner = Runner::new();
let (kind, list_name) = super::format(server_name);
if !matches!(&**server_name, "internal" | "local") {
crashln!("{} Cannot restore on remote servers", *helpers::FAIL)
}
Runner::new().list().for_each(|(id, p)| {
if p.running == true {
runner = Internal {
id: *id,
server_name,
kind: kind.clone(),
runner: runner.clone(),
}
.restart(&None, &None, true);
}
});
println!("{} Restored process statuses from dumpfile", *helpers::SUCCESS);
Internal::list(&string!("default"), &list_name);
}
pub fn list(format: &String, server_name: &String) {
let render_list = |runner: &mut Runner, internal: bool| {
let mut processes: Vec<ProcessItem> = Vec::new();
#[derive(Tabled, Debug)]
struct ProcessItem {
id: ColoredString,
name: String,
pid: String,
uptime: String,
#[tabled(rename = "↺")]
restarts: String,
status: ColoredString,
cpu: String,
mem: String,
#[tabled(rename = "watching")]
watch: String,
}
impl serde::Serialize for ProcessItem {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let trimmed_json = json!({
"cpu": &self.cpu.trim(),
"mem": &self.mem.trim(),
"id": &self.id.0.trim(),
"pid": &self.pid.trim(),
"name": &self.name.trim(),
"watch": &self.watch.trim(),
"uptime": &self.uptime.trim(),
"status": &self.status.0.trim(),
"restarts": &self.restarts.trim(),
});
trimmed_json.serialize(serializer)
}
}
if runner.is_empty() {
println!("{} Process table empty", *helpers::SUCCESS);
} else {
for (id, item) in runner.items() {
let mut cpu_percent: String = string!("0%");
let mut memory_usage: String = string!("0b");
if internal {
let mut usage_internals: (Option<f32>, Option<MemoryInfo>) = (None, None);
if let Ok(mut process) = Process::new(item.pid as u32) {
usage_internals = (process.cpu_percent().ok(), process.memory_info().ok());
}
cpu_percent = match usage_internals.0 {
Some(percent) => format!("{:.0}%", percent),
None => string!("0%"),
};
memory_usage = match usage_internals.1 {
Some(usage) => helpers::format_memory(usage.rss()),
None => string!("0b"),
};
} else {
let info = http::info(&runner.remote.as_ref().unwrap(), id);
if let Ok(info) = info {
let stats = info.json::<ItemSingle>().unwrap().stats;
cpu_percent = match stats.cpu_percent {
Some(percent) => format!("{:.2}%", percent),
None => string!("0%"),
};
memory_usage = match stats.memory_usage {
Some(usage) => helpers::format_memory(usage.rss),
None => string!("0b"),
};
}
}
let status = if item.running {
"online ".green().bold()
} else {
match item.crash.crashed {
true => "crashed ",
false => "stopped ",
}
.red()
.bold()
};
processes.push(ProcessItem {
status: status.into(),
cpu: format!("{cpu_percent} "),
mem: format!("{memory_usage} "),
id: id.to_string().cyan().bold().into(),
restarts: format!("{} ", item.restarts),
name: format!("{} ", item.name.clone()),
pid: ternary!(item.running, format!("{} ", item.pid), string!("n/a ")),
watch: ternary!(item.watch.enabled, format!("{} ", item.watch.path), string!("disabled ")),
uptime: ternary!(item.running, format!("{} ", helpers::format_duration(item.started)), string!("none ")),
});
}
let table = Table::new(&processes)
.with(Style::rounded().remove_verticals())
.with(BorderColor::filled(Color::FG_BRIGHT_BLACK))
.with(Colorization::exact([Color::FG_BRIGHT_CYAN], Rows::first()))
.with(Modify::new(Columns::single(1)).with(Width::truncate(35).suffix("... ")))
.to_string();
if let Ok(json) = serde_json::to_string(&processes) {
match format.as_str() {
"raw" => println!("{:?}", processes),
"json" => println!("{json}"),
"default" => println!("{table}"),
_ => {}
};
};
}
};
if let Some(servers) = config::servers().servers {
let mut failed: Vec<(String, String)> = vec![];
if let Some(server) = servers.get(server_name) {
match Runner::connect(server_name.clone(), server.get(), true) {
Some(mut remote) => render_list(&mut remote, false),
None => println!("{} Failed to fetch (name={server_name}, address={})", *helpers::FAIL, server.address),
}
} else {
if matches!(&**server_name, "internal" | "all" | "global" | "local") {
if *server_name == "all" || *server_name == "global" {
println!("{} Internal daemon", *helpers::SUCCESS);
}
render_list(&mut Runner::new(), true);
} else {
crashln!("{} Server '{server_name}' does not exist", *helpers::FAIL);
}
}
if *server_name == "all" || *server_name == "global" {
for (name, server) in servers {
match Runner::connect(name.clone(), server.get(), true) {
Some(mut remote) => render_list(&mut remote, false),
None => failed.push((name, server.address)),
}
}
}
if !failed.is_empty() {
println!("{} Failed servers:", *helpers::FAIL);
failed
.iter()
.for_each(|server| println!(" {} {} {}", "-".yellow(), format!("{}", server.0), format!("[{}]", server.1).white()));
}
} else {
render_list(&mut Runner::new(), true);
}
}
}

File Metadata

Mime Type
text/plain
Expires
Tue, Dec 16, 10:08 AM (21 h, 4 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
484937
Default Alt Text
internal.rs (25 KB)

Event Timeline