Merge pull request #442 from jb-alvarado/master
set public path, fix #439, add system stat route
This commit is contained in:
commit
0e8b8e92f3
456
Cargo.lock
generated
456
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@ default-members = ["ffplayout-api", "ffplayout-engine", "tests"]
|
|||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.20.0-beta4"
|
version = "0.20.0-beta5"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
repository = "https://github.com/ffplayout/ffplayout"
|
repository = "https://github.com/ffplayout/ffplayout"
|
||||||
authors = ["Jonathan Baecker <jonbae77@gmail.com>"]
|
authors = ["Jonathan Baecker <jonbae77@gmail.com>"]
|
||||||
|
1
debian/postinst
vendored
1
debian/postinst
vendored
@ -13,6 +13,7 @@ fi
|
|||||||
|
|
||||||
if [ ! -d "/usr/share/ffplayout/db" ]; then
|
if [ ! -d "/usr/share/ffplayout/db" ]; then
|
||||||
mkdir "/usr/share/ffplayout/db"
|
mkdir "/usr/share/ffplayout/db"
|
||||||
|
mkdir -p "/usr/share/ffplayout/public/live"
|
||||||
mkdir -p "/var/lib/ffplayout/playlists"
|
mkdir -p "/var/lib/ffplayout/playlists"
|
||||||
mkdir -p "/var/lib/ffplayout/tv-media"
|
mkdir -p "/var/lib/ffplayout/tv-media"
|
||||||
|
|
||||||
|
@ -23,7 +23,9 @@ derive_more = "0.99"
|
|||||||
faccess = "0.2"
|
faccess = "0.2"
|
||||||
futures-util = { version = "0.3", default-features = false, features = ["std"] }
|
futures-util = { version = "0.3", default-features = false, features = ["std"] }
|
||||||
jsonwebtoken = "8"
|
jsonwebtoken = "8"
|
||||||
|
lazy_static = "1.4"
|
||||||
lexical-sort = "0.3"
|
lexical-sort = "0.3"
|
||||||
|
local-ip-address = "0.5"
|
||||||
once_cell = "1.18"
|
once_cell = "1.18"
|
||||||
path-clean = "1.0"
|
path-clean = "1.0"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
@ -37,6 +39,7 @@ serde_json = "1.0"
|
|||||||
serde_yaml = "0.9"
|
serde_yaml = "0.9"
|
||||||
simplelog = { version = "0.12", features = ["paris"] }
|
simplelog = { version = "0.12", features = ["paris"] }
|
||||||
static-files = "0.2"
|
static-files = "0.2"
|
||||||
|
sysinfo = "0.29"
|
||||||
sqlx = { version = "0.7", features = ["runtime-tokio", "sqlite"] }
|
sqlx = { version = "0.7", features = ["runtime-tokio", "sqlite"] }
|
||||||
tokio = { version = "1.29", features = ["full"] }
|
tokio = { version = "1.29", features = ["full"] }
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ use argon2::{
|
|||||||
Argon2, PasswordHasher, PasswordVerifier,
|
Argon2, PasswordHasher, PasswordVerifier,
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, Datelike, Duration, Local, NaiveDateTime, TimeZone, Utc};
|
use chrono::{DateTime, Datelike, Duration, Local, NaiveDateTime, TimeZone, Utc};
|
||||||
|
use path_clean::PathClean;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
@ -46,10 +47,10 @@ use crate::utils::{
|
|||||||
},
|
},
|
||||||
naive_date_time_from_str,
|
naive_date_time_from_str,
|
||||||
playlist::{delete_playlist, generate_playlist, read_playlist, write_playlist},
|
playlist::{delete_playlist, generate_playlist, read_playlist, write_playlist},
|
||||||
playout_config, read_log_file, read_playout_config, Role,
|
playout_config, public_path, read_log_file, read_playout_config, system, Role,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
auth::{create_jwt, Claims},
|
api::auth::{create_jwt, Claims},
|
||||||
utils::control::ProcessControl,
|
utils::control::ProcessControl,
|
||||||
};
|
};
|
||||||
use ffplayout_lib::{
|
use ffplayout_lib::{
|
||||||
@ -955,6 +956,35 @@ async fn get_file(
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// **Get Public**
|
||||||
|
///
|
||||||
|
/// Can be used for HLS Playlist and other static files in public folder
|
||||||
|
///
|
||||||
|
/// ```BASH
|
||||||
|
/// curl -X GET http://127.0.0.1:8787/live/stream.m3u8
|
||||||
|
/// ```
|
||||||
|
#[get("/{public:((live|preview|public).*|.*(ts|m3u8))}")]
|
||||||
|
async fn get_public(public: web::Path<String>) -> Result<actix_files::NamedFile, ServiceError> {
|
||||||
|
let public_path = public_path();
|
||||||
|
|
||||||
|
let absolute_path = if public_path.is_absolute() {
|
||||||
|
public_path.to_path_buf()
|
||||||
|
} else {
|
||||||
|
env::current_dir()?.join(public_path)
|
||||||
|
}
|
||||||
|
.clean();
|
||||||
|
|
||||||
|
let path = absolute_path.join(public.as_str());
|
||||||
|
let file = actix_files::NamedFile::open(path)?;
|
||||||
|
|
||||||
|
Ok(file
|
||||||
|
.use_last_modified(true)
|
||||||
|
.set_content_disposition(ContentDisposition {
|
||||||
|
disposition: DispositionType::Attachment,
|
||||||
|
parameters: vec![],
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
/// **Import playlist**
|
/// **Import playlist**
|
||||||
///
|
///
|
||||||
/// Import text/m3u file and convert it to a playlist
|
/// Import text/m3u file and convert it to a playlist
|
||||||
@ -1084,3 +1114,24 @@ async fn get_program(
|
|||||||
|
|
||||||
Ok(web::Json(program))
|
Ok(web::Json(program))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ### System Statistics
|
||||||
|
///
|
||||||
|
/// Get statistics about CPU, Ram, Disk, etc. usage.
|
||||||
|
///
|
||||||
|
/// ```BASH
|
||||||
|
/// curl -X GET http://127.0.0.1:8787/api/system
|
||||||
|
/// -H 'Content-Type: application/json' -H 'Authorization: Bearer <TOKEN>'
|
||||||
|
/// ```
|
||||||
|
#[get("/system/{id}")]
|
||||||
|
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||||
|
pub async fn get_system_stat(
|
||||||
|
pool: web::Data<Pool<Sqlite>>,
|
||||||
|
id: web::Path<i32>,
|
||||||
|
) -> Result<impl Responder, ServiceError> {
|
||||||
|
let (config, _) = playout_config(&pool.clone().into_inner(), &id).await?;
|
||||||
|
|
||||||
|
let stat = web::block(move || system::stat(config)).await?;
|
||||||
|
|
||||||
|
Ok(web::Json(stat))
|
||||||
|
}
|
||||||
|
@ -74,16 +74,13 @@ async fn create_schema(conn: &Pool<Sqlite>) -> Result<SqliteQueryResult, sqlx::E
|
|||||||
sqlx::query(query).execute(conn).await
|
sqlx::query(query).execute(conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn db_init(
|
pub async fn db_init(domain: Option<String>) -> Result<&'static str, Box<dyn std::error::Error>> {
|
||||||
domain: Option<String>,
|
let db_path = db_path()?;
|
||||||
db: &Option<String>,
|
|
||||||
) -> Result<&'static str, Box<dyn std::error::Error>> {
|
|
||||||
let db_path = db_path(db.clone())?;
|
|
||||||
|
|
||||||
if !Sqlite::database_exists(db_path).await.unwrap_or(false) {
|
if !Sqlite::database_exists(db_path).await.unwrap_or(false) {
|
||||||
Sqlite::create_database(db_path).await.unwrap();
|
Sqlite::create_database(db_path).await.unwrap();
|
||||||
|
|
||||||
let pool = db_pool(db).await?;
|
let pool = db_pool().await?;
|
||||||
|
|
||||||
match create_schema(&pool).await {
|
match create_schema(&pool).await {
|
||||||
Ok(_) => info!("Database created Successfully"),
|
Ok(_) => info!("Database created Successfully"),
|
||||||
@ -125,7 +122,7 @@ pub async fn db_init(
|
|||||||
('Scrolling Text', 'We have a very important announcement to make.', 'ifnot(ld(1),st(1,t));if(lt(t,ld(1)+1),w+4,w-w/12*mod(t-ld(1),12*(w+tw)/w))', '(h-line_h)*0.9',
|
('Scrolling Text', 'We have a very important announcement to make.', 'ifnot(ld(1),st(1,t));if(lt(t,ld(1)+1),w+4,w-w/12*mod(t-ld(1),12*(w+tw)/w))', '(h-line_h)*0.9',
|
||||||
'24', '4', '#ffffff', '1', '#000000@0x80', '4', '1.0', '1');";
|
'24', '4', '#ffffff', '1', '#000000@0x80', '4', '1.0', '1');";
|
||||||
|
|
||||||
let pool = db_pool(db).await?;
|
let pool = db_pool().await?;
|
||||||
|
|
||||||
sqlx::query(query)
|
sqlx::query(query)
|
||||||
.bind(secret)
|
.bind(secret)
|
||||||
|
@ -5,8 +5,8 @@ pub mod models;
|
|||||||
|
|
||||||
use crate::utils::db_path;
|
use crate::utils::db_path;
|
||||||
|
|
||||||
pub async fn db_pool(db: &Option<String>) -> Result<Pool<Sqlite>, sqlx::Error> {
|
pub async fn db_pool() -> Result<Pool<Sqlite>, sqlx::Error> {
|
||||||
let db_path = db_path(db.clone()).unwrap();
|
let db_path = db_path().unwrap();
|
||||||
let conn = SqlitePool::connect(db_path).await?;
|
let conn = SqlitePool::connect(db_path).await?;
|
||||||
|
|
||||||
Ok(conn)
|
Ok(conn)
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
use std::process::exit;
|
use std::{
|
||||||
|
env,
|
||||||
|
process::exit,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
dev::ServiceRequest, middleware::Logger, web, App, Error, HttpMessage, HttpServer,
|
dev::ServiceRequest, middleware::Logger, web, App, Error, HttpMessage, HttpServer,
|
||||||
@ -13,7 +17,10 @@ use actix_files::Files;
|
|||||||
use actix_web_static_files::ResourceFiles;
|
use actix_web_static_files::ResourceFiles;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use path_clean::PathClean;
|
||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
|
use sysinfo::{System, SystemExt};
|
||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
@ -28,6 +35,11 @@ use ffplayout_lib::utils::{init_logging, PlayoutConfig};
|
|||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref ARGS: Args = Args::parse();
|
||||||
|
pub static ref SYS: Arc<Mutex<System>> = Arc::new(Mutex::new(System::new_all()));
|
||||||
|
}
|
||||||
|
|
||||||
async fn validator(
|
async fn validator(
|
||||||
req: ServiceRequest,
|
req: ServiceRequest,
|
||||||
credentials: BearerAuth,
|
credentials: BearerAuth,
|
||||||
@ -48,8 +60,6 @@ async fn validator(
|
|||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
let args = Args::parse();
|
|
||||||
|
|
||||||
let mut config = PlayoutConfig::new(None);
|
let mut config = PlayoutConfig::new(None);
|
||||||
config.mail.recipient = String::new();
|
config.mail.recipient = String::new();
|
||||||
config.logging.log_to_file = false;
|
config.logging.log_to_file = false;
|
||||||
@ -58,11 +68,11 @@ async fn main() -> std::io::Result<()> {
|
|||||||
let logging = init_logging(&config, None, None);
|
let logging = init_logging(&config, None, None);
|
||||||
CombinedLogger::init(logging).unwrap();
|
CombinedLogger::init(logging).unwrap();
|
||||||
|
|
||||||
if let Err(c) = run_args(args.clone()).await {
|
if let Err(c) = run_args().await {
|
||||||
exit(c);
|
exit(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pool = match db_pool(&args.db).await {
|
let pool = match db_pool().await {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("{e}");
|
error!("{e}");
|
||||||
@ -70,8 +80,8 @@ async fn main() -> std::io::Result<()> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(conn) = args.listen {
|
if let Some(conn) = &ARGS.listen {
|
||||||
if db_path(args.db).is_err() {
|
if db_path().is_err() {
|
||||||
error!("Database is not initialized! Init DB first and add admin user.");
|
error!("Database is not initialized! Init DB first and add admin user.");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@ -129,15 +139,30 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.service(remove)
|
.service(remove)
|
||||||
.service(save_file)
|
.service(save_file)
|
||||||
.service(import_playlist)
|
.service(import_playlist)
|
||||||
.service(get_program),
|
.service(get_program)
|
||||||
|
.service(get_system_stat),
|
||||||
)
|
)
|
||||||
.service(get_file);
|
.service(get_file);
|
||||||
|
|
||||||
|
if let Some(public) = &ARGS.public {
|
||||||
|
let absolute_path = if public.is_absolute() {
|
||||||
|
public.to_path_buf()
|
||||||
|
} else {
|
||||||
|
env::current_dir().unwrap_or_default().join(public)
|
||||||
|
}
|
||||||
|
.clean();
|
||||||
|
|
||||||
|
web_app = web_app.service(Files::new("/", absolute_path));
|
||||||
|
} else {
|
||||||
|
web_app = web_app.service(get_public);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
{
|
{
|
||||||
// in release mode embed frontend
|
// in release mode embed frontend
|
||||||
let generated = generate();
|
let generated = generate();
|
||||||
web_app = web_app.service(ResourceFiles::new("/", generated));
|
web_app =
|
||||||
|
web_app.service(ResourceFiles::new("/", generated).resolve_not_found_to_root());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
@ -9,7 +11,10 @@ pub struct Args {
|
|||||||
pub ask: bool,
|
pub ask: bool,
|
||||||
|
|
||||||
#[clap(long, help = "path to database file")]
|
#[clap(long, help = "path to database file")]
|
||||||
pub db: Option<String>,
|
pub db: Option<PathBuf>,
|
||||||
|
|
||||||
|
#[clap(long, help = "path to public files")]
|
||||||
|
pub public: Option<PathBuf>,
|
||||||
|
|
||||||
#[clap(short, long, help = "Listen on IP:PORT, like: 127.0.0.1:8787")]
|
#[clap(short, long, help = "Listen on IP:PORT, like: 127.0.0.1:8787")]
|
||||||
pub listen: Option<String>,
|
pub listen: Option<String>,
|
||||||
|
@ -4,7 +4,7 @@ use std::{
|
|||||||
fmt,
|
fmt,
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
io::{stdin, stdout, Write},
|
io::{stdin, stdout, Write},
|
||||||
path::Path,
|
path::{Path, PathBuf},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -17,19 +17,22 @@ use serde::{de, Deserialize, Deserializer, Serialize};
|
|||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
use sqlx::{sqlite::SqliteRow, FromRow, Pool, Row, Sqlite};
|
use sqlx::{sqlite::SqliteRow, FromRow, Pool, Row, Sqlite};
|
||||||
|
|
||||||
|
use crate::ARGS;
|
||||||
|
|
||||||
pub mod args_parse;
|
pub mod args_parse;
|
||||||
pub mod channels;
|
pub mod channels;
|
||||||
pub mod control;
|
pub mod control;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod files;
|
pub mod files;
|
||||||
pub mod playlist;
|
pub mod playlist;
|
||||||
|
pub mod system;
|
||||||
|
|
||||||
use crate::db::{
|
use crate::db::{
|
||||||
db_pool,
|
db_pool,
|
||||||
handles::{db_init, insert_user, select_channel, select_global},
|
handles::{db_init, insert_user, select_channel, select_global},
|
||||||
models::{Channel, User},
|
models::{Channel, User},
|
||||||
};
|
};
|
||||||
use crate::utils::{args_parse::Args, errors::ServiceError};
|
use crate::utils::errors::ServiceError;
|
||||||
use ffplayout_lib::utils::{time_to_sec, PlayoutConfig};
|
use ffplayout_lib::utils::{time_to_sec, PlayoutConfig};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
@ -123,13 +126,12 @@ pub async fn init_config(conn: &Pool<Sqlite>) {
|
|||||||
INSTANCE.set(config).unwrap();
|
INSTANCE.set(config).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn db_path(db: Option<String>) -> Result<&'static str, Box<dyn std::error::Error>> {
|
pub fn db_path() -> Result<&'static str, Box<dyn std::error::Error>> {
|
||||||
if let Some(path) = db {
|
if let Some(path) = ARGS.db.clone() {
|
||||||
let path_buf = Path::new(&path);
|
let absolute_path = if path.is_absolute() {
|
||||||
let absolute_path = if path_buf.is_absolute() {
|
path
|
||||||
path_buf.to_path_buf()
|
|
||||||
} else {
|
} else {
|
||||||
env::current_dir()?.join(path_buf)
|
env::current_dir()?.join(path)
|
||||||
}
|
}
|
||||||
.clean();
|
.clean();
|
||||||
|
|
||||||
@ -160,7 +162,19 @@ pub fn db_path(db: Option<String>) -> Result<&'static str, Box<dyn std::error::E
|
|||||||
Ok(db_path)
|
Ok(db_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run_args(mut args: Args) -> Result<(), i32> {
|
pub fn public_path() -> PathBuf {
|
||||||
|
let path = PathBuf::from("/usr/share/ffplayout/public/");
|
||||||
|
|
||||||
|
if path.is_dir() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
PathBuf::from("./public/")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run_args() -> Result<(), i32> {
|
||||||
|
let mut args = ARGS.clone();
|
||||||
|
|
||||||
if !args.init && args.listen.is_none() && !args.ask && args.username.is_none() {
|
if !args.init && args.listen.is_none() && !args.ask && args.username.is_none() {
|
||||||
error!("Wrong number of arguments! Run ffpapi --help for more information.");
|
error!("Wrong number of arguments! Run ffpapi --help for more information.");
|
||||||
|
|
||||||
@ -168,7 +182,7 @@ pub async fn run_args(mut args: Args) -> Result<(), i32> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if args.init {
|
if args.init {
|
||||||
if let Err(e) = db_init(args.domain, &args.db).await {
|
if let Err(e) = db_init(args.domain).await {
|
||||||
panic!("{e}");
|
panic!("{e}");
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -232,7 +246,7 @@ pub async fn run_args(mut args: Args) -> Result<(), i32> {
|
|||||||
token: None,
|
token: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
match db_pool(&args.db).await {
|
match db_pool().await {
|
||||||
Ok(conn) => {
|
Ok(conn) => {
|
||||||
if let Err(e) = insert_user(&conn, user).await {
|
if let Err(e) = insert_user(&conn, user).await {
|
||||||
error!("{e}");
|
error!("{e}");
|
||||||
|
159
ffplayout-api/src/utils/system.rs
Normal file
159
ffplayout-api/src/utils/system.rs
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
// use std::cmp;
|
||||||
|
|
||||||
|
use local_ip_address::list_afinet_netifas;
|
||||||
|
use serde::Serialize;
|
||||||
|
use sysinfo::{CpuExt, DiskExt, NetworkExt, SystemExt};
|
||||||
|
|
||||||
|
use crate::SYS;
|
||||||
|
use ffplayout_lib::utils::PlayoutConfig;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct Cpu {
|
||||||
|
pub cores: f32,
|
||||||
|
pub usage: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize)]
|
||||||
|
pub struct Storage {
|
||||||
|
pub path: String,
|
||||||
|
pub total: u64,
|
||||||
|
pub used: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct Load {
|
||||||
|
pub one: f64,
|
||||||
|
pub five: f64,
|
||||||
|
pub fifteen: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct Memory {
|
||||||
|
pub total: u64,
|
||||||
|
pub used: u64,
|
||||||
|
pub free: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize)]
|
||||||
|
pub struct Network {
|
||||||
|
pub name: String,
|
||||||
|
pub current_in: u64,
|
||||||
|
pub total_in: u64,
|
||||||
|
pub current_out: u64,
|
||||||
|
pub total_out: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct MySystem {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub kernel: Option<String>,
|
||||||
|
pub version: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct Swap {
|
||||||
|
pub total: u64,
|
||||||
|
pub used: u64,
|
||||||
|
pub free: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct SystemStat {
|
||||||
|
pub cpu: Cpu,
|
||||||
|
pub load: Load,
|
||||||
|
pub memory: Memory,
|
||||||
|
pub network: Network,
|
||||||
|
pub storage: Storage,
|
||||||
|
pub swap: Swap,
|
||||||
|
pub system: MySystem,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stat(config: PlayoutConfig) -> SystemStat {
|
||||||
|
let mut sys = SYS.lock().unwrap();
|
||||||
|
let network_interfaces = list_afinet_netifas().unwrap_or_default();
|
||||||
|
let mut usage = 0.0;
|
||||||
|
let mut interfaces = vec![];
|
||||||
|
|
||||||
|
for (name, ip) in network_interfaces.iter() {
|
||||||
|
if !ip.is_loopback() {
|
||||||
|
interfaces.push((name, ip))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interfaces.dedup_by(|a, b| a.0 == b.0);
|
||||||
|
|
||||||
|
sys.refresh_cpu();
|
||||||
|
sys.refresh_disks();
|
||||||
|
sys.refresh_memory();
|
||||||
|
sys.refresh_networks();
|
||||||
|
|
||||||
|
let cores = sys.cpus().len() as f32;
|
||||||
|
|
||||||
|
for cpu in sys.cpus() {
|
||||||
|
usage += cpu.cpu_usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
let cpu = Cpu {
|
||||||
|
cores,
|
||||||
|
usage: usage * cores / 100.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut storage = Storage::default();
|
||||||
|
|
||||||
|
for disk in sys.disks() {
|
||||||
|
if disk.mount_point().to_string_lossy().len() > 1
|
||||||
|
&& config.storage.path.starts_with(disk.mount_point())
|
||||||
|
{
|
||||||
|
storage.path = disk.name().to_string_lossy().to_string();
|
||||||
|
storage.total = disk.total_space();
|
||||||
|
storage.used = disk.available_space();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let load_avg = sys.load_average();
|
||||||
|
let load = Load {
|
||||||
|
one: load_avg.one,
|
||||||
|
five: load_avg.five,
|
||||||
|
fifteen: load_avg.fifteen,
|
||||||
|
};
|
||||||
|
|
||||||
|
let memory = Memory {
|
||||||
|
total: sys.total_memory(),
|
||||||
|
used: sys.used_memory(),
|
||||||
|
free: sys.free_memory(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut network = Network::default();
|
||||||
|
|
||||||
|
for (interface_name, data) in sys.networks() {
|
||||||
|
if !interfaces.is_empty() && interface_name == interfaces[0].0 {
|
||||||
|
network.name = interface_name.clone();
|
||||||
|
network.current_in = data.received();
|
||||||
|
network.total_in = data.total_received();
|
||||||
|
network.current_out = data.transmitted();
|
||||||
|
network.total_out = data.total_transmitted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let swap = Swap {
|
||||||
|
total: sys.total_swap(),
|
||||||
|
used: sys.used_swap(),
|
||||||
|
free: sys.free_swap(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let system = MySystem {
|
||||||
|
name: sys.name(),
|
||||||
|
kernel: sys.kernel_version(),
|
||||||
|
version: sys.os_version(),
|
||||||
|
};
|
||||||
|
|
||||||
|
SystemStat {
|
||||||
|
cpu,
|
||||||
|
storage,
|
||||||
|
load,
|
||||||
|
memory,
|
||||||
|
network,
|
||||||
|
system,
|
||||||
|
swap,
|
||||||
|
}
|
||||||
|
}
|
@ -186,6 +186,8 @@ pub fn init_logging(
|
|||||||
.set_thread_level(LevelFilter::Off)
|
.set_thread_level(LevelFilter::Off)
|
||||||
.set_target_level(LevelFilter::Off)
|
.set_target_level(LevelFilter::Off)
|
||||||
.add_filter_ignore_str("hyper")
|
.add_filter_ignore_str("hyper")
|
||||||
|
.add_filter_ignore_str("libc")
|
||||||
|
.add_filter_ignore_str("neli")
|
||||||
.add_filter_ignore_str("reqwest")
|
.add_filter_ignore_str("reqwest")
|
||||||
.add_filter_ignore_str("rpc")
|
.add_filter_ignore_str("rpc")
|
||||||
.add_filter_ignore_str("rustls")
|
.add_filter_ignore_str("rustls")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user