working app manager
This commit is contained in:
@@ -0,0 +1,146 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct App {
|
||||||
|
pub repo: String,
|
||||||
|
pub branch: String,
|
||||||
|
pub install_command: String,
|
||||||
|
pub run_command: String,
|
||||||
|
pub enabled: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_app(apps: &mut Vec<App>, index: usize) {
|
||||||
|
if let Some(app) = apps.get(index) {
|
||||||
|
let home = env::var("HOME").expect("Failed to get HOME environment variable");
|
||||||
|
let apps_dir = PathBuf::from(home).join("sentryx/apps");
|
||||||
|
let repo_name = app
|
||||||
|
.repo
|
||||||
|
.trim_end_matches(".git")
|
||||||
|
.rsplit('/')
|
||||||
|
.next()
|
||||||
|
.expect("Failed to extract repo name");
|
||||||
|
fs::remove_dir_all(&apps_dir.join(repo_name)).expect("Failed to remove app directory");
|
||||||
|
println!("Removed app: {}", app.repo);
|
||||||
|
} else {
|
||||||
|
println!("App not found: {}", index);
|
||||||
|
}
|
||||||
|
|
||||||
|
apps.remove(index);
|
||||||
|
save_apps(apps);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install_app(apps: &mut Vec<App>, app: App) {
|
||||||
|
let a2 = app.clone();
|
||||||
|
// Get home directory and create ~/sentryx/apps if needed
|
||||||
|
let home = env::var("HOME").expect("Failed to get HOME environment variable");
|
||||||
|
let apps_dir = PathBuf::from(home).join("sentryx/apps");
|
||||||
|
|
||||||
|
if !apps_dir.exists() {
|
||||||
|
fs::create_dir_all(&apps_dir).expect("Failed to create apps directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change to apps directory
|
||||||
|
env::set_current_dir(&apps_dir).expect("Failed to change to apps directory");
|
||||||
|
|
||||||
|
println!("Installing app from repo: {}", app.repo);
|
||||||
|
println!("Branch: {}", app.branch);
|
||||||
|
println!("Install command: {}", app.install_command);
|
||||||
|
println!("Run command: {}", app.run_command);
|
||||||
|
|
||||||
|
// Clone the repo
|
||||||
|
println!("Cloning repo");
|
||||||
|
Command::new("git")
|
||||||
|
.arg("clone")
|
||||||
|
.arg("-b")
|
||||||
|
.arg(&app.branch)
|
||||||
|
.arg(&app.repo)
|
||||||
|
.status()
|
||||||
|
.expect("Failed to clone repository");
|
||||||
|
|
||||||
|
// Extract repo name from URL (e.g., "https://github.com/user/myapp.git" -> "myapp")
|
||||||
|
let repo_name = app
|
||||||
|
.repo
|
||||||
|
.trim_end_matches(".git")
|
||||||
|
.rsplit('/')
|
||||||
|
.next()
|
||||||
|
.expect("Failed to extract repo name");
|
||||||
|
|
||||||
|
// Change to cloned repo directory
|
||||||
|
env::set_current_dir(&repo_name).expect("Failed to change to cloned repo directory");
|
||||||
|
|
||||||
|
// You are now inside the cloned repo
|
||||||
|
println!("Changed directory to {:?}", env::current_dir().unwrap());
|
||||||
|
|
||||||
|
apps.push(a2.clone());
|
||||||
|
save_apps(&apps);
|
||||||
|
|
||||||
|
Command::new("bash")
|
||||||
|
.arg("-c")
|
||||||
|
.arg(app.install_command)
|
||||||
|
.status()
|
||||||
|
.expect("Failed to execute Bash command");
|
||||||
|
|
||||||
|
run_app(a2);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_app(app: App) {
|
||||||
|
// Change to the app directory
|
||||||
|
let home = env::var("HOME").expect("Failed to get HOME environment variable");
|
||||||
|
let app_dir = PathBuf::from(home)
|
||||||
|
.join("sentryx/apps")
|
||||||
|
.join(app.repo.trim_end_matches(".git").split('/').last().unwrap());
|
||||||
|
|
||||||
|
env::set_current_dir(&app_dir).expect("Failed to change to app directory");
|
||||||
|
|
||||||
|
// Run the app
|
||||||
|
Command::new("bash")
|
||||||
|
.arg("-c")
|
||||||
|
.arg(app.run_command)
|
||||||
|
.status()
|
||||||
|
.expect("Failed to execute run command");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_apps() -> Vec<App> {
|
||||||
|
// Change to the app directory
|
||||||
|
let home = env::var("HOME").expect("Failed to get HOME environment variable");
|
||||||
|
let apps_dir = PathBuf::from(home).join("sentryx/apps");
|
||||||
|
|
||||||
|
if !apps_dir.exists() {
|
||||||
|
fs::create_dir_all(&apps_dir).expect("Failed to create apps directory");
|
||||||
|
fs::write(apps_dir.join("apps.json"), "[]").expect("Failed to create apps.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
let apps_json = serde_json::from_str::<Vec<App>>(
|
||||||
|
&fs::read_to_string(apps_dir.join("apps.json")).expect("Failed to read apps.json"),
|
||||||
|
)
|
||||||
|
.expect("Failed to parse apps.json");
|
||||||
|
|
||||||
|
for app in apps_json.clone() {
|
||||||
|
if app.enabled.unwrap_or(true) {
|
||||||
|
println!("Running app: {}", app.repo);
|
||||||
|
std::thread::spawn(|| {
|
||||||
|
run_app(app);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
println!("Skipping disabled app: {}", app.repo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apps_json
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_apps(apps: &[App]) {
|
||||||
|
let home = env::var("HOME").expect("Failed to get HOME environment variable");
|
||||||
|
let apps_dir = PathBuf::from(home).join("sentryx/apps");
|
||||||
|
|
||||||
|
fs::write(
|
||||||
|
apps_dir.join("apps.json"),
|
||||||
|
serde_json::to_string(apps).expect("Failed to serialize apps"),
|
||||||
|
)
|
||||||
|
.expect("Failed to write apps.json");
|
||||||
|
}
|
||||||
+32
-4
@@ -1,11 +1,13 @@
|
|||||||
|
pub mod app_manager;
|
||||||
pub mod server_config;
|
pub mod server_config;
|
||||||
|
|
||||||
use std::net::TcpListener;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread::spawn;
|
use std::thread::spawn;
|
||||||
|
use std::{net::TcpListener, sync::Mutex};
|
||||||
use sysinfo::{Disks, Networks, System};
|
use sysinfo::{Disks, Networks, System};
|
||||||
use tungstenite::accept;
|
use tungstenite::accept;
|
||||||
|
|
||||||
|
use crate::app_manager::{install_app, App};
|
||||||
use crate::server_config::ServerStats;
|
use crate::server_config::ServerStats;
|
||||||
fn get_system_info(sys: &mut System, networks: &Networks, disks: &Disks) -> ServerStats {
|
fn get_system_info(sys: &mut System, networks: &Networks, disks: &Disks) -> ServerStats {
|
||||||
let cpus = sys.cpus();
|
let cpus = sys.cpus();
|
||||||
@@ -36,19 +38,26 @@ fn get_system_info(sys: &mut System, networks: &Networks, disks: &Disks) -> Serv
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let apps = app_manager::run_apps();
|
||||||
|
|
||||||
let server = TcpListener::bind("127.0.0.1:5273").unwrap();
|
let server = TcpListener::bind("127.0.0.1:5273").unwrap();
|
||||||
for stream in server.incoming() {
|
for stream in server.incoming() {
|
||||||
|
let mut apps = apps.clone();
|
||||||
|
|
||||||
spawn(move || {
|
spawn(move || {
|
||||||
let mut websocket = accept(stream.unwrap()).unwrap();
|
let websocket = Arc::new(Mutex::new(accept(stream.unwrap()).unwrap()));
|
||||||
|
let reader_socket = Arc::clone(&websocket);
|
||||||
let config = Arc::new(server_config::ServerConfig::new());
|
let config = Arc::new(server_config::ServerConfig::new());
|
||||||
|
|
||||||
{
|
{
|
||||||
let master_key = websocket.read().unwrap();
|
let master_key = reader_socket.lock().unwrap().read().unwrap();
|
||||||
|
|
||||||
if !master_key.is_text()
|
if !master_key.is_text()
|
||||||
|| master_key.to_text().unwrap() != config.master_key.clone()
|
|| master_key.to_text().unwrap() != config.master_key.clone()
|
||||||
{
|
{
|
||||||
websocket
|
websocket
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
.send(tungstenite::Message::text("!Invalid master key"))
|
.send(tungstenite::Message::text("!Invalid master key"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -56,15 +65,32 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
websocket
|
websocket
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
.send(tungstenite::Message::text("Success"))
|
.send(tungstenite::Message::text("Success"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::thread::spawn(move || loop {
|
||||||
|
let app: App = serde_json::from_str(
|
||||||
|
&reader_socket
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.to_text()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
install_app(&mut apps, app);
|
||||||
|
});
|
||||||
|
|
||||||
let mut sys = System::new_all();
|
let mut sys = System::new_all();
|
||||||
let mut networks = Networks::new();
|
let mut networks = Networks::new();
|
||||||
let mut disks = Disks::new();
|
let mut disks = Disks::new();
|
||||||
|
|
||||||
while websocket.can_write() {
|
while websocket.lock().unwrap().can_write() {
|
||||||
sys.refresh_all();
|
sys.refresh_all();
|
||||||
std::thread::sleep(std::time::Duration::from_millis(200));
|
std::thread::sleep(std::time::Duration::from_millis(200));
|
||||||
sys.refresh_all();
|
sys.refresh_all();
|
||||||
@@ -72,6 +98,8 @@ fn main() {
|
|||||||
disks.refresh(true);
|
disks.refresh(true);
|
||||||
|
|
||||||
websocket
|
websocket
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
.send(tungstenite::Message::text(
|
.send(tungstenite::Message::text(
|
||||||
serde_json::to_string(&get_system_info(&mut sys, &networks, &disks))
|
serde_json::to_string(&get_system_info(&mut sys, &networks, &disks))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
|||||||
@@ -1,4 +1,14 @@
|
|||||||
const ws = new WebSocket('ws://localhost:5273');
|
const ws = new WebSocket("ws://localhost:5273");
|
||||||
|
|
||||||
ws.onmessage = (msg) => console.log("Received:", msg.data);
|
ws.onmessage = (msg) => console.log("Received:", msg.data);
|
||||||
ws.onopen = () => ws.send("my-key");
|
ws.onopen = () => {
|
||||||
|
ws.send("my-key");
|
||||||
|
ws.send(
|
||||||
|
JSON.stringify({
|
||||||
|
repo: "[email protected]:osui-rs/osui.git",
|
||||||
|
branch: "master",
|
||||||
|
install_command: "",
|
||||||
|
run_command: "cargo run",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user