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;
|
||||
|
||||
use std::net::TcpListener;
|
||||
use std::sync::Arc;
|
||||
use std::thread::spawn;
|
||||
use std::{net::TcpListener, sync::Mutex};
|
||||
use sysinfo::{Disks, Networks, System};
|
||||
use tungstenite::accept;
|
||||
|
||||
use crate::app_manager::{install_app, App};
|
||||
use crate::server_config::ServerStats;
|
||||
fn get_system_info(sys: &mut System, networks: &Networks, disks: &Disks) -> ServerStats {
|
||||
let cpus = sys.cpus();
|
||||
@@ -36,19 +38,26 @@ fn get_system_info(sys: &mut System, networks: &Networks, disks: &Disks) -> Serv
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let apps = app_manager::run_apps();
|
||||
|
||||
let server = TcpListener::bind("127.0.0.1:5273").unwrap();
|
||||
for stream in server.incoming() {
|
||||
let mut apps = apps.clone();
|
||||
|
||||
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 master_key = websocket.read().unwrap();
|
||||
let master_key = reader_socket.lock().unwrap().read().unwrap();
|
||||
|
||||
if !master_key.is_text()
|
||||
|| master_key.to_text().unwrap() != config.master_key.clone()
|
||||
{
|
||||
websocket
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send(tungstenite::Message::text("!Invalid master key"))
|
||||
.unwrap();
|
||||
|
||||
@@ -56,15 +65,32 @@ fn main() {
|
||||
}
|
||||
|
||||
websocket
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send(tungstenite::Message::text("Success"))
|
||||
.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 networks = Networks::new();
|
||||
let mut disks = Disks::new();
|
||||
|
||||
while websocket.can_write() {
|
||||
while websocket.lock().unwrap().can_write() {
|
||||
sys.refresh_all();
|
||||
std::thread::sleep(std::time::Duration::from_millis(200));
|
||||
sys.refresh_all();
|
||||
@@ -72,6 +98,8 @@ fn main() {
|
||||
disks.refresh(true);
|
||||
|
||||
websocket
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send(tungstenite::Message::text(
|
||||
serde_json::to_string(&get_system_info(&mut sys, &networks, &disks))
|
||||
.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.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