From 332acf65c8d5f8735af9ad53e7d6134e59f91789 Mon Sep 17 00:00:00 2001 From: Samuel Walker Date: Sat, 13 Jul 2024 17:00:08 -0600 Subject: [PATCH] Started work on administrative functions --- FCLauncher/src-tauri/Cargo.toml | 1 + FCLauncher/src-tauri/src/admin.rs | 76 +++++++++++++++++++-- FCLauncher/src-tauri/src/https.rs | 8 +-- FCLauncher/src-tauri/src/main.rs | 3 +- FCLauncher/src-tauri/src/modpack.rs | 28 +++++--- FCLauncher/src-tauri/src/sftp.rs | 43 ++++++++++++ FCLauncher/src/Admin.html | 20 +++++- FCLauncher/src/admin.js | 47 ++++++++++++- FCLauncher/src/assets/add.png | Bin 0 -> 9710 bytes FCLauncher/src/assets/add.svg | 11 +++ FCLauncher/src/assets/down.png | Bin 0 -> 5110 bytes FCLauncher/src/assets/remove.png | Bin 0 -> 5988 bytes FCLauncher/src/assets/up.png | Bin 0 -> 5091 bytes FCLauncher/src/login.js | 1 + FCLauncher/src/main.js | 4 +- FCLauncher/src/styles.css | 100 ++++++++++++++++++++++++++++ 16 files changed, 315 insertions(+), 27 deletions(-) create mode 100644 FCLauncher/src-tauri/src/sftp.rs create mode 100644 FCLauncher/src/assets/add.png create mode 100644 FCLauncher/src/assets/add.svg create mode 100644 FCLauncher/src/assets/down.png create mode 100644 FCLauncher/src/assets/remove.png create mode 100644 FCLauncher/src/assets/up.png diff --git a/FCLauncher/src-tauri/Cargo.toml b/FCLauncher/src-tauri/Cargo.toml index ac06dba..d907a5f 100644 --- a/FCLauncher/src-tauri/Cargo.toml +++ b/FCLauncher/src-tauri/Cargo.toml @@ -24,6 +24,7 @@ self_update = "0.40.0" parking_lot = "0.12.3" reqwest = { version = "0.12.5", features = ["stream"] } futures-util = "0.3.30" +ssh2 = "0.9.4" [features] # This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!! diff --git a/FCLauncher/src-tauri/src/admin.rs b/FCLauncher/src-tauri/src/admin.rs index af9936f..2037578 100644 --- a/FCLauncher/src-tauri/src/admin.rs +++ b/FCLauncher/src-tauri/src/admin.rs @@ -1,13 +1,75 @@ -static USERNAME: parking_lot::Mutex = parking_lot::const_mutex(String::new()); -static PASSWORD: parking_lot::Mutex = parking_lot::const_mutex(String::new()); +use std::io::Cursor; +use std::path::PathBuf; + +use crate::sftp; +use crate::modpack; +use ssh2::Sftp; +use ssh2::Session; + +//static USERNAME: parking_lot::Mutex = parking_lot::const_mutex(String::new()); +//static PASSWORD: parking_lot::Mutex = parking_lot::const_mutex(String::new()); +static SESSION: tauri::async_runtime::Mutex> = tauri::async_runtime::Mutex::const_new(None); #[tauri::command] -pub fn login(username: String, password: String, window: tauri::Window) { - //if(test_cred(username.as_str(), password.as_str())){ +pub async fn login(username: String, password: String, window: tauri::Window) { + let res = sftp::connect(username, password); + if(res.is_ok()){ //*USERNAME.lock() = username; //*PASSWORD.lock() = password; - //window.emit("Login_Success", {}); - //}else{ + *SESSION.lock().await = Some(res.unwrap()); + window.emit("Login_Success", {}); + }else{ window.emit("Login_Failed", {}); - //} + } +} + +#[tauri::command] +pub async fn drop_session(){ + let ref mut session = *SESSION.lock().await; + if let Some(session) = session { + session.disconnect(None, "disconnecting", None); + } + *session = None; +} + +async fn update_modpacks(modpacks: Vec) -> Result<(), String>{ + let data = serde_json::to_string(&modpacks).or(Err("Unable to serialize json"))?; + let reader = Cursor::new(data.as_bytes()); + let ref mut session = *SESSION.lock().await; + if let Some(session) = session { + sftp::uplaod(None, session.clone(), PathBuf::from("/ftp/modpacks.json"), reader, format!("modpacks.json"), data.as_bytes().len()).await; + } + Ok(()) +} + +#[tauri::command] +pub async fn shift_up(id: String){ + let mut modpacks = modpack::get_modpacks().await; + let mut index = 0; + for pack in modpacks.as_slice() { + if(pack.id == id){ + break; + } + index += 1; + } + if index != 0 { + modpacks.swap(index, index-1); + } + update_modpacks(modpacks).await; +} + +#[tauri::command] +pub async fn shift_down(id: String){ + let mut modpacks = modpack::get_modpacks().await; + let mut index = 0; + for pack in modpacks.as_slice() { + if(pack.id == id){ + break; + } + index += 1; + } + if index != modpacks.len()-1 { + modpacks.swap(index, index+1); + } + update_modpacks(modpacks).await; } \ No newline at end of file diff --git a/FCLauncher/src-tauri/src/https.rs b/FCLauncher/src-tauri/src/https.rs index 0b50b49..4072879 100644 --- a/FCLauncher/src-tauri/src/https.rs +++ b/FCLauncher/src-tauri/src/https.rs @@ -7,10 +7,10 @@ use serde::Serialize; #[derive(Clone, Serialize)] pub struct DownloadStatus { - downloaded: usize, - total: usize, - time_elapsed: usize, - download_name: String + pub downloaded: usize, + pub total: usize, + pub time_elapsed: usize, + pub download_name: String } diff --git a/FCLauncher/src-tauri/src/main.rs b/FCLauncher/src-tauri/src/main.rs index 3539a9d..af1bae5 100644 --- a/FCLauncher/src-tauri/src/main.rs +++ b/FCLauncher/src-tauri/src/main.rs @@ -17,6 +17,7 @@ mod util; mod modpack; mod admin; mod https; +mod sftp; #[derive(Serialize, Deserialize)] struct ModpackEntry{ @@ -35,7 +36,7 @@ fn main() { //modpack::get_modpacks(); //prism::install_prism(); tauri::Builder::default() - .invoke_handler(tauri::generate_handler![greet, modpack::get_modpacks, modpack::launch_modpack, prism::launch_prism, prism::install_prism, admin::login]) + .invoke_handler(tauri::generate_handler![greet, modpack::get_modpacks, modpack::launch_modpack, modpack::get_versions, prism::launch_prism, prism::install_prism, admin::login, admin::drop_session, admin::shift_up, admin::shift_down]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/FCLauncher/src-tauri/src/modpack.rs b/FCLauncher/src-tauri/src/modpack.rs index 082331d..40a52a9 100644 --- a/FCLauncher/src-tauri/src/modpack.rs +++ b/FCLauncher/src-tauri/src/modpack.rs @@ -21,10 +21,11 @@ use crate::https; #[derive(Serialize, Deserialize, Clone)] pub struct ModpackEntry{ name: String, - id: String + pub id: String, + last_updated: String } -#[derive(Clone)] +#[derive(Serialize, Deserialize, Clone)] pub struct VersionEntry{ version: String, file: String, @@ -147,6 +148,7 @@ pub async fn get_modpacks() -> Vec { buf.rewind(); let res = serde_json::from_reader(buf); if !res.is_ok() { + println!("Result not ok!"); let paths = fs::read_dir(get_local_data_directory().join("prism").join("instances")).unwrap(); for path in paths { let path = path.unwrap(); @@ -158,27 +160,28 @@ pub async fn get_modpacks() -> Vec { continue; } - modpacks.push(ModpackEntry{name: name.clone(), id: name}) + modpacks.push(ModpackEntry{name: name.clone(), id: name, last_updated: format!("")}) } return modpacks.clone() } - let v: Value = res.unwrap(); - println!("{}", v[0]["name"]); - for pack in v.as_array().unwrap() { - modpacks.push(ModpackEntry{name: pack["name"].as_str().unwrap().to_string(), id: pack["id"].as_str().unwrap().to_string()}); - } + let modpacks: Vec = res.unwrap(); + //println!("{}", v[0].name); + //for pack in v.as_array().unwrap() { + //modpacks.push(ModpackEntry{name: pack["name"].as_str().unwrap().to_string(), id: pack["id"].as_str().unwrap().to_string(), last_updated: pack["last-updated"].as_str().unwrap().to_string()}); + //} //} return modpacks.clone(); //} } -async fn get_versions(id: String) -> Result,Box> { +#[tauri::command] +pub async fn get_versions(id: String) -> Result,String> { let mut versions: Vec = Vec::new(); let mut buf = Cursor::new(vec![]); //ftp::ftp_retr(None, PathBuf::new().join(id).join("versions.json"), &mut buf, |_, _, _| return); https::download(None, format!("https://gitea.piwalker.net/fclauncher/{}/versions.json", id.clone()), &mut buf, format!("{}/versions.json", id.clone())).await; buf.rewind(); - let v: Value = serde_json::from_reader(buf)?; + let v: Value = serde_json::from_reader(buf).or(Err(format!("Unable to parse json")))?; for version in v.as_array().unwrap() { versions.push(VersionEntry{version: version["Version"].as_str().unwrap().to_string(), file: version["File"].as_str().unwrap().to_string(), date: version["Date"].as_str().unwrap().to_string()}); } @@ -201,3 +204,8 @@ fn get_java_version(mc_version: &str) -> u8{ } return java; } + + +//pub fn create_json(modpacks: Vec) -> Result { + +//} \ No newline at end of file diff --git a/FCLauncher/src-tauri/src/sftp.rs b/FCLauncher/src-tauri/src/sftp.rs new file mode 100644 index 0000000..26a666d --- /dev/null +++ b/FCLauncher/src-tauri/src/sftp.rs @@ -0,0 +1,43 @@ +use std::io::prelude::*; +use std::net::TcpStream; +use std::path::Path; +use std::path::PathBuf; +use futures_util::io::BufReader; +use futures_util::io::Cursor; +use ssh2::OpenFlags; +use ssh2::Session; +use ssh2::Sftp; + +use crate::https; + +pub fn connect(username: String, password: String) -> Result { + let tcp = TcpStream::connect("gitea.piwalker.net:22").or(Err(format!("Unable to connect to host")))?; + let mut sess = Session::new().or(Err(format!("Unable to creat stream")))?; + sess.set_tcp_stream(tcp); + sess.handshake().unwrap(); + sess.userauth_password(username.as_str(), password.as_str()).or(Err(format!("Invalid username or password")))?; + Ok(sess) +} + +pub async fn uplaod(window: Option, sess: Session, path: PathBuf, mut reader: impl Read, upload_name: String, total_size: usize) -> Result<(), String>{ + let sftp = sess.sftp().or(Err("unable to open sftp session"))?; + let mut file = sftp.create(path.as_path()).or(Err("Unable to open file"))?; + let mut uploaded = 0; + while true { + let mut buf = vec![0u8; 1024]; + let res = reader.read(&mut buf).unwrap(); + if res <= 0 { + if let Some(window) = window.clone() { + window.emit("download_finished", true).or(Err(format!("Unable to signal window!")))?; + } + break; + } + file.write_all(buf.split_at(res).0).unwrap(); + uploaded += res; + println!("Uploading {} {}MB / {}MB", upload_name, uploaded/(1024*1024), total_size/(1024*1024)); + if let Some(window) = window.clone() { + window.emit("download_progress", https::DownloadStatus{downloaded: uploaded as usize, total: total_size as usize, time_elapsed: 0, download_name: upload_name.clone()}).or(Err(format!("Unable to signal window")))?; + } + } + Ok(()) +} \ No newline at end of file diff --git a/FCLauncher/src/Admin.html b/FCLauncher/src/Admin.html index b3cfc72..02792f5 100644 --- a/FCLauncher/src/Admin.html +++ b/FCLauncher/src/Admin.html @@ -23,8 +23,24 @@ -
-

Administration

+
+
+
+
+ + + +
+
+ + + + +
+
+
+ +
diff --git a/FCLauncher/src/admin.js b/FCLauncher/src/admin.js index d9f5c3b..f019af7 100644 --- a/FCLauncher/src/admin.js +++ b/FCLauncher/src/admin.js @@ -4,6 +4,8 @@ const { ask } = window.__TAURI__.dialog; const downBar = document.querySelector(".progressFinished"); //import { listen } from '@tauri-apps/api'; +var selected_pack = ""; + const download_progress = listen("download_progress", (progress) => { console.log("Downloading"); //console.log("Downloaded "+progress.payload.downloaded/(1024*1024) +"MB / " + progress.payload.total/(1024*1024) + "MB"); @@ -26,11 +28,54 @@ const download_finished = listen("download_finished", (event) => { }); window.addEventListener("DOMContentLoaded", () => { - + document.getElementById("up").addEventListener("click", up); + document.getElementById("down").addEventListener("click", down); document.getElementById("back").addEventListener("click", back); }); +window.onload = async function() { + refresh(); +} + +function up(){ + invoke("shift_up", { id: selected_pack }).then(refresh); +} + +function down(){ + invoke("shift_down", { id: selected_pack }).then(refresh); +} + function back(){ + invoke("drop_session"); window.location.href = "index.html"; +} + +function refresh(){ + invoke("get_modpacks").then(addModpacks); +} + +function addModpacks(modpacks) { + var modpacks_list = document.getElementById("modpacks"); + while (modpacks_list.firstChild) { + modpacks_list.removeChild(modpacks_list.lastChild); + } + for (let i = 0; i < modpacks.length; i++){ + var div = document.createElement("div"); + div.textContent = modpacks[i].name; + div.className = "modpack"; + div.id = modpacks[i].id; + if(modpacks[i].id == selected_pack){ + div.classList.add("modpack-selected"); + } + div.addEventListener("click", function() { modpackClick(modpacks[i].id) }); + modpacks_list.appendChild(div); + } +} + +function modpackClick(id){ + var old = selected_pack; + document.getElementById(id).classList.add("modpack-selected"); + selected_pack = id; + document.getElementById(old).classList.remove("modpack-selected"); } \ No newline at end of file diff --git a/FCLauncher/src/assets/add.png b/FCLauncher/src/assets/add.png new file mode 100644 index 0000000000000000000000000000000000000000..eeb1e23c840bcdef92eb292e1968d9bacb6dd33d GIT binary patch literal 9710 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 literal 0 HcmV?d00001 diff --git a/FCLauncher/src/assets/add.svg b/FCLauncher/src/assets/add.svg new file mode 100644 index 0000000..9e2743c --- /dev/null +++ b/FCLauncher/src/assets/add.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/FCLauncher/src/assets/down.png b/FCLauncher/src/assets/down.png new file mode 100644 index 0000000000000000000000000000000000000000..66c4f5e6012326737a018d1e85a22598a3fa1f6f GIT binary patch literal 5110 zcmeHKeN+=y7M}zaqtm~<3SwU&lo}%oV5Wd`=v&XaRe*|9M zo%inj-FtuUzMEuSYRa4d-*LVW1O@04wDZAyiyuB-;C-Z3oB*avRJzWr(@lqbzyr_} z41)x`4j6&~vQXd!@mBAE5e#Hrhackv!`*WkkjH!E%|M>&(cx`Sfe;D+b0ruuAdd#~ zK``i&9$$X}GXL^%|0#u#TcdZLqtmB~WD*pS$W_1|ktuLQiKB86is9fNLxF$6&i;sa zB;6kQ6cArA9T%6Xi;EN49gLaIr69;zx%^#CLc`1n=N23Gi4VQ9AaKd~)T*{`$8P+{ zH~v82+iMntB}<;Jo)Vy2U%IC?!`?xDc(3)_n(E(Gi+onBQbnnz)g`R!PV$9~8rA*A zQ@{QFsr*%`Qu&2@>jf3I?H_30YBDW6=ZX;e5P{3LZT%zFq?;cc)_7G@Sa`bhVa>(w zo=up9Sx{GaaN{=O+S&1E<=c;aQm{kSw7xd6FeTdd)$nz%o;>XA`t)RLtxMiu#<95* zri}5k)*MA6D#kq+y+nJN`+TfyVs+(lRnDF1IW6yS41t4o}+|yYy-6%8pZuB4X6HULSsY=Ht@ag=NB` z%$@Vt%677mu7sXhS`(R{BJ#2xz4t>#Qt1T2)9bEtc}a#xtG)=!y8GEC{ol8ADb^HD zbjIvqJ{Ni&pV?kJnQZ?-EyfQSH^Jk5BDCeSGC#6+izhsLphpK3913 zI0U`2h)zrA()GzW$yg7b6{7zZU%NE8wTj(5^aW#SpWqG*T7jL+B3_CSCqjX0a*?6_2#pPw(umrEFjMT)A` zYAGU<%49ImfY|~YM>t^{JDG>*!O&7H>7eZ#&DcadCShdqIE`2g_C>w%S?zj#AH0qA zumJdwItjZJl^{~9RXWgu<>HqD5KlnAXu+lx*eU6Jie>T~Bo)7uvT>6KLYT-tdwZTE z*PV`ulv25r6{xbnD>}#}f8+WrcnU1E)$XJ~I;}>``^QZTNtr2@_#dQIrCc zE6D*+IvdLoHj?6@0Jww(IC2?kjKs_sY%*byutJWKuo0DEFr_xCP!yps1sOR2Vy=S* zRY~LyjEaXc0Vtyq!3aWbf+-`2K_SBwunNNnSdN&~h|z2U?@Bk6iNt3!4l4nUleQ8T zN@}-R+#5XMcxoghYprGR0Sf>IC!tWv6#u!544 zFkn;-n?aKyP^H4G^3d9c&N5~$pKwsI7QiFm3Y4e&6G}AIeNfW|$C95-@uvU?gApZ+ z%F<*Aj)2cD#6y_$<);`)suhSDQ^7_V0;-)&8!O+>++1A!JapJ`G_V%u>u8kWvN~O}rj~}mCv7)J|slL9xq@=`Ru_PuY*3{Hs z7}nC#(%jr!S65eFUcPtlUaeMJRaJHH;K8FukA{VX-M)RBW!dQH=9KE}b_vDxxmh!U2pp+e z8+fbH@0;uE`Vf4A>K=yxTxVU!?vk5%54Jw98axEU_~I%HYxr6BRm`V=Jb)Ht4<%Y`s`x9bwO@Pzg^crB%?4N}o~C{fi&}SajX6yrA`1$JVIk z62@$MFlI#C`)7-Su74Eqcn!t>RBiigL*SKAnu(bG!cTI4{_#?^A^5iOf&6Ugb z_%2M~x8_gH)1u}*sot<<6dd|YRJtP_u8}^+bBDZmv0}veH-CIOjW2ab7oVa%JS%JU FzX3<<_zD03 literal 0 HcmV?d00001 diff --git a/FCLauncher/src/assets/remove.png b/FCLauncher/src/assets/remove.png new file mode 100644 index 0000000000000000000000000000000000000000..dc36fcc0eea9463d940547884ce7bd22fc6e3962 GIT binary patch literal 5988 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 literal 0 HcmV?d00001 diff --git a/FCLauncher/src/login.js b/FCLauncher/src/login.js index f959680..137b754 100644 --- a/FCLauncher/src/login.js +++ b/FCLauncher/src/login.js @@ -14,6 +14,7 @@ window.addEventListener("DOMContentLoaded", () => { }); function back(){ + invoke("drop_session"); window.location.href = "index.html"; } diff --git a/FCLauncher/src/main.js b/FCLauncher/src/main.js index 9c73265..c8ecca9 100644 --- a/FCLauncher/src/main.js +++ b/FCLauncher/src/main.js @@ -69,8 +69,8 @@ window.onload = async function() { function addModpacks(modpacks) { var dropdown = document.getElementById("Modpacks"); - modpacks.sort((a, b) => a.name.localeCompare(b.name)); - modpacks.reverse(); + //modpacks.sort((a, b) => a.name.localeCompare(b.name)); + //modpacks.reverse(); for (let i = 0; i < modpacks.length; i++){ var opt = document.createElement("option"); opt.text = modpacks[i].name; diff --git a/FCLauncher/src/styles.css b/FCLauncher/src/styles.css index c704f69..ddb6f24 100644 --- a/FCLauncher/src/styles.css +++ b/FCLauncher/src/styles.css @@ -27,6 +27,39 @@ text-align: center; } +.vertical { + margin: 0; + display: flex; + flex-direction: column; + justify-content: center; + text-align: center; + gap: 1em; +} + +.container-horizontal { + margin: 0; + padding-top: 30px; + display: flex; + flex-direction: row; + justify-content: center; + text-align: center; + align-items: left; + justify-content: left; + gap: 20px; +} + +.horizontal-input { + margin: 0; + margin-left: 5px; + display: flex; + flex-direction: row; + justify-content: center; + text-align: center; + align-items: left; + justify-content: left; + gap: 20px; +} + body { margin: 0px; padding: 0px; @@ -273,4 +306,71 @@ button { background-color: red; margin: 0; visibility: hidden; +} + +#modpacks{ + width: 30%; + height: 13em; + background-color: #666565; + overflow: auto; + margin-left: 10px; + border: 1px solid; +} + +.modpack { + width: 100%; +} + +.modpack:hover { + background-color: lightgray; + color: black; +} + +.modpack-selected { + background-color: blue; + color: white; +} + +.vertical-buttons { + display: flex; + margin-left: 0.5em; + margin-top: 0; + flex-direction: column; + height: 15em; + gap: 1.5em; +} + +.square-button { + width: 2em; + height: 2.5em; + top: 5px; + left: 5px; +} + +#up{ + background-image: url('assets/up.png'); + background-size:cover; + background-repeat: no-repeat; +} + +#down{ + background-image: url('assets/down.png'); + background-size:cover; + background-repeat: no-repeat; +} + +#add{ + background-image: url('assets/add.png'); + background-size:cover; + background-repeat: no-repeat; +} + +#remove{ + background-image: url('assets/remove.png'); + background-size:cover; + background-repeat: no-repeat; +} + +#pack_name{ + width: 13em; } \ No newline at end of file